Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions zygisk/module/action.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
MANAGER_PACKAGE_NAME="@MANAGER_PACKAGE_NAME@"
INJECTED_PACKAGE_NAME="@INJECTED_PACKAGE_NAME@"

# The manager also ships inside this module as manager.apk and may have been
# installed as a regular package. When it has, launching its activity directly
# is more reliable than the injection path below, which needs
# ${INJECTED_PACKAGE_NAME} to start a 2D activity at all and to receive the
# injected manager dex -- neither holds in every environment (some systems
# never let the shell host an activity, and dynamic code loading restrictions
# can block the dex the way GrapheneOS does, see #805).
if pm path "$MANAGER_PACKAGE_NAME" >/dev/null 2>&1; then
am start -n "$MANAGER_PACKAGE_NAME/.ui.MainActivity" >/dev/null 2>&1 && exit 0
fi

am start -c "${MANAGER_PACKAGE_NAME}.LAUNCH_MANAGER" "${INJECTED_PACKAGE_NAME}/.BugreportWarningActivity"
7 changes: 5 additions & 2 deletions zygisk/module/daemon
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ if [ "$debug" = "true" ]; then
fi

# Mount an empty temporary file system over /data/resource-cache to avoid possible overlay conflicts.
# Note that this script runs inside an isolated mount namespace (via unshare).
mount tmpfs -t tmpfs /data/resource-cache
# Only when service.sh has established a mount namespace that cannot propagate this mount back to the
# host: without one it would hide the real resource cache from every process on the device.
if [ "${VECTOR_MOUNTNS_ISOLATED:-0}" = 1 ]; then
mount tmpfs -t tmpfs /data/resource-cache
fi

# Wait for Zygote socket using a polling loop
if [ ! -S "/dev/socket/zygote" ]; then
Expand Down
78 changes: 75 additions & 3 deletions zygisk/module/service.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,78 @@
# Extract the directory path and change directory
# Extract the directory path and change directory
MODDIR="${0%/*}"
cd "$MODDIR" || exit 1

# Start the daemon directly in the background within a private mount namespace
unshare --propagation slave -m "$MODDIR/daemon" --system-server-max-retry=3 "$@" &
# The service stage has no console, and the daemon cannot log anything before it
# exists, so a start that fails is invisible by construction. Keep one line per
# boot, and mirror it to logcat when log is around as well.
TRACE_LOG=/data/local/tmp/vector-service.log
trace() {
printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null)" "$*" >>"$TRACE_LOG" 2>/dev/null
command -v log >/dev/null 2>&1 && log -p i -t VectorService "$*" >/dev/null 2>&1
}

# A replayed service stage (KernelSU's emulated soft reboot) leaves the old
# daemon alive, and tryLock() in the daemon does not reliably turn the second
# one away (#878): the two then claim the same proxy service and break
# injection. The survivor handles a system_server restart through its binder
# death recipient, so leaving it alone is the repair; a second daemon is what
# breaks injection.
daemon_running() {
command -v pgrep >/dev/null 2>&1 && pgrep -f '^vectord' >/dev/null 2>&1 && return 0
command -v pidof >/dev/null 2>&1 && pidof vectord >/dev/null 2>&1 && return 0
return 1
}

if daemon_running; then
trace "vectord already running, not starting a second one"
exit 0
fi

# toybox builds that implement only a subset of util-linux reject
# --propagation outright, which leaves the daemon dead with nothing anywhere to
# say so. The root solution usually ships a fuller busybox, so prefer its
# unshare, then the system one, and only then a namespace whose propagation
# type cannot be set.
find_busybox() {
for bb in "${MAGISKTMP:-/nonexistent}/busybox" \
"${MAGISKTMP:-/nonexistent}/.magisk/busybox" \
/data/adb/ksu/bin/busybox \
/data/adb/magisk/busybox \
/data/adb/ap/bin/busybox \
/system/xbin/busybox; do
[ -f "$bb" ] && [ -x "$bb" ] && { printf '%s\n' "$bb"; return 0; }
done
bb="$(command -v busybox 2>/dev/null)" && [ -f "$bb" ] && [ -x "$bb" ] && printf '%s\n' "$bb"
}
BB="$(find_busybox)"
DAEMON="$MODDIR/daemon"

# The daemon mounts a tmpfs over /data/resource-cache, which is only safe when
# this namespace cannot propagate mounts back to the host.
# VECTOR_MOUNTNS_ISOLATED records that service.sh has established exactly that.
if [ -n "$BB" ] && "$BB" unshare --propagation slave -m true >/dev/null 2>&1; then
trace "starting vectord with $BB unshare --propagation slave -m"
VECTOR_MOUNTNS_ISOLATED=1 "$BB" unshare --propagation slave -m "$DAEMON" --system-server-max-retry=3 "$@" &
elif unshare --propagation slave -m true >/dev/null 2>&1; then
trace "starting vectord with unshare --propagation slave -m"
VECTOR_MOUNTNS_ISOLATED=1 unshare --propagation slave -m "$DAEMON" --system-server-max-retry=3 "$@" &
elif unshare -m /system/bin/sh -c '
mount --make-rprivate / 2>/dev/null || mount --make-rslave / 2>/dev/null || exit 1
! grep -q " shared:" /proc/self/mountinfo
' >/dev/null 2>&1; then
trace "starting vectord with unshare -m and a private mount tree"
unshare -m /system/bin/sh -c '
if mount --make-rprivate / 2>/dev/null || mount --make-rslave / 2>/dev/null; then
if ! grep -q " shared:" /proc/self/mountinfo; then
export VECTOR_MOUNTNS_ISOLATED=1
fi
fi
exec "$0" "$@"
' "$DAEMON" --system-server-max-retry=3 "$@" &
elif unshare -m true >/dev/null 2>&1; then
trace "starting vectord with unshare -m (propagation unchanged, mount skipped)"
unshare -m "$DAEMON" --system-server-max-retry=3 "$@" &
else
trace "starting vectord without a mount namespace (resource-cache mount skipped)"
"$DAEMON" --system-server-max-retry=3 "$@" &
fi