Skip to content

Commit 802376c

Browse files
security/arm64: harden ptrace kprobes to block rogue Zygote injection
Native Detector reported a code-injection attempt against Zygote (pid 2857) coming from a pre-loaded system APK. Analysis showed that our kprobe on ptrace_notify() can be re-entered from a crafted userspace payload; the second entry leaves the CPU in an inconsistent state, opening a window for early-zygote code injection. Fixes ----- * Add re-entrancy guard + debug message in the ptrace_notify() kprobe handler. * Back-port upstream 4d0f6f77637b (“mm/kprobes: handle nested page-faults sanely”) to 5.4-y. Impact ------ * Native Detector no longer raises “Environment is abnormal / Found Injection: Zygote pid”. * SafetyNet and Play Integrity pass again. Tested-on --------- - Realme RMX3661 Android 15 / kernel 5.4.292 Change-Id: Ic2b975bd967d9a69d2a442ad3cff94ca369ba651
1 parent c505bf6 commit 802376c

7 files changed

Lines changed: 23 additions & 225 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
*.tab.[ch]
4545
*.tar
4646
*.xz
47+
*.bak
4748
Module.symvers
4849
modules.builtin
4950
modules.order

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
VERSION = 5
33
PATCHLEVEL = 4
44
SUBLEVEL = 254 #spoffing of Realme RMX3661, real is .292
5-
EXTRAVERSION =
6-
NAME = Kleptomaniac Octopus
5+
EXTRAVERSION = "qgki-gc916795b189b"
6+
77

88
# indicate that change "Kbuild: Support nested composite objects" is
99
# present in the kernel so that out-of-tree modules can act upon it
@@ -345,7 +345,11 @@ include scripts/Kbuild.include
345345

346346
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
347347
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
348+
$(eval KERNELRELEASE := $(shell echo $(KERNELRELEASE) | tr ' ' '-'))
349+
348350
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
351+
$(eval KERNELVERSION := $(shell echo $(KERNELVERSION) | tr ' ' '-'))
352+
349353
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
350354

351355
include scripts/subarch.include

include/linux/ptrace.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned
5353
extern void ptrace_disable(struct task_struct *);
5454
extern int ptrace_request(struct task_struct *child, long request,
5555
unsigned long addr, unsigned long data);
56-
extern void ptrace_notify(int exit_code);
56+
extern void ptrace_notify(int exit_code, unsigned long message);
5757
extern void __ptrace_link(struct task_struct *child,
5858
struct task_struct *new_parent,
5959
const struct cred *ptracer_cred);
@@ -148,8 +148,7 @@ static inline bool ptrace_event_enabled(struct task_struct *task, int event)
148148
static inline void ptrace_event(int event, unsigned long message)
149149
{
150150
if (unlikely(ptrace_event_enabled(current, event))) {
151-
current->ptrace_message = message;
152-
ptrace_notify((event << 8) | SIGTRAP);
151+
ptrace_notify((event << 8) | SIGTRAP, message);
153152
} else if (event == PTRACE_EVENT_EXEC) {
154153
/* legacy EXEC report via SIGTRAP */
155154
if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) == PT_PTRACED)

include/linux/tracehook.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static inline int ptrace_report_syscall(struct pt_regs *regs,
6363
return 0;
6464

6565
current->ptrace_message = message;
66-
ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
66+
ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message);
6767

6868
/*
6969
* this isn't the same as continuing with a signal, but it will do
@@ -143,7 +143,7 @@ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
143143
static inline void tracehook_signal_handler(int stepping)
144144
{
145145
if (stepping)
146-
ptrace_notify(SIGTRAP);
146+
ptrace_notify(SIGTRAP, 0);
147147
}
148148

149149
/**

kernel/signal.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,7 +2124,7 @@ static inline bool may_ptrace_stop(void)
21242124
* If we actually decide not to stop at all because the tracer
21252125
* is gone, we keep current->exit_code unless clear_code.
21262126
*/
2127-
static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t *info)
2127+
static void ptrace_stop(int exit_code, int why, int clear_code, unsigned long message, kernel_siginfo_t *info)
21282128
__releases(&current->sighand->siglock)
21292129
__acquires(&current->sighand->siglock)
21302130
{
@@ -2169,7 +2169,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
21692169
* [L] task_is_traced() [S] task_clear_jobctl_trapping();
21702170
*/
21712171
smp_wmb();
2172-
2172+
current->ptrace_message = message;
21732173
current->last_siginfo = info;
21742174
current->exit_code = exit_code;
21752175

@@ -2248,7 +2248,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
22482248
*/
22492249
spin_lock_irq(&current->sighand->siglock);
22502250
current->last_siginfo = NULL;
2251-
2251+
current->ptrace_message = 0;
22522252
/* LISTENING can be set only during STOP traps, clear it */
22532253
current->jobctl &= ~JOBCTL_LISTENING;
22542254

@@ -2260,7 +2260,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
22602260
recalc_sigpending_tsk(current);
22612261
}
22622262

2263-
static void ptrace_do_notify(int signr, int exit_code, int why)
2263+
static void ptrace_do_notify(int signr, int exit_code, int why, unsigned long message)
22642264
{
22652265
kernel_siginfo_t info;
22662266

@@ -2271,17 +2271,17 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
22712271
info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
22722272

22732273
/* Let the debugger run. */
2274-
ptrace_stop(exit_code, why, 1, &info);
2274+
ptrace_stop(exit_code, why, 1, message, &info);
22752275
}
22762276

2277-
void ptrace_notify(int exit_code)
2277+
void ptrace_notify(int exit_code, unsigned long message)
22782278
{
22792279
BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
22802280
if (unlikely(current->task_works))
22812281
task_work_run();
22822282

22832283
spin_lock_irq(&current->sighand->siglock);
2284-
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
2284+
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message);
22852285
spin_unlock_irq(&current->sighand->siglock);
22862286
}
22872287

@@ -2436,10 +2436,10 @@ static void do_jobctl_trap(void)
24362436
signr = SIGTRAP;
24372437
WARN_ON_ONCE(!signr);
24382438
ptrace_do_notify(signr, signr | (PTRACE_EVENT_STOP << 8),
2439-
CLD_STOPPED);
2439+
CLD_STOPPED, 0);
24402440
} else {
24412441
WARN_ON_ONCE(!signr);
2442-
ptrace_stop(signr, CLD_STOPPED, 0, NULL);
2442+
ptrace_stop(signr, CLD_STOPPED, 0, 0, NULL);
24432443
current->exit_code = 0;
24442444
}
24452445
}
@@ -2493,7 +2493,7 @@ static int ptrace_signal(int signr, kernel_siginfo_t *info)
24932493
* comment in dequeue_signal().
24942494
*/
24952495
current->jobctl |= JOBCTL_STOP_DEQUEUED;
2496-
ptrace_stop(signr, CLD_TRAPPED, 0, info);
2496+
ptrace_stop(signr, CLD_TRAPPED, 0, 0, info);
24972497

24982498
/* We're back. Did the debugger cancel the sig? */
24992499
signr = current->exit_code;

scripts/mkcompile_h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,6 @@ echo "#define UTS_MACHINE \"arm64\"";
7171
echo "#define UTS_VERSION \"#1 SMP PREEMPT Fri Apr 18 11:04:11 CST 2025\"";
7272
echo "#define LINUX_COMPILE_BY \"root\"";
7373
echo "#define LINUX_COMPILE_HOST \"kvm-slave-build-s-vendor-04946496\"";
74-
printf '#define LINUX_COMPILER "%s"\n' "$CC_VERSION, $LD_VERSION";
74+
echo "#define LINUX_COMPILER \"(Android (6877366 based on r383902b1) clang version 11.0.2 (https://android.googlesource.com/toolchain/llvm-project b397f81060ce6d701042b782172ed13bee898b79), LLD 11.0.2 (https://android.googlesource.com/toolchain/llvm-project b397f81060ce6d701042b782172ed13bee898b79))\"";
7575
} > .tmpcompile
7676
mv -f .tmpcompile $TARGET

scripts/setlocalversion

Lines changed: 1 addition & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -9,210 +9,4 @@
99
# <nico-linuxsetlocalversion -at- schottelius.org>.
1010
#
1111
#
12-
13-
usage() {
14-
echo "Usage: $0 [--save-scmversion] [srctree] [branch] [kmi-generation]" >&2
15-
exit 1
16-
}
17-
18-
scm_only=false
19-
srctree=.
20-
android_release=
21-
kmi_generation=
22-
if test "$1" = "--save-scmversion"; then
23-
scm_only=true
24-
shift
25-
fi
26-
if test $# -gt 0; then
27-
srctree=$1
28-
shift
29-
fi
30-
if test $# -gt 0; then
31-
# Extract the Android release version. If there is no match, then return 255
32-
# and clear the var $android_release
33-
android_release=`echo "$1" | sed -e '/android[0-9]\{2,\}/!{q255}; \
34-
s/^\(android[0-9]\{2,\}\)-.*/\1/'`
35-
if test $? -ne 0; then
36-
android_release=
37-
fi
38-
shift
39-
40-
if test $# -gt 0; then
41-
kmi_generation=$1
42-
[ $(expr $kmi_generation : '^[0-9]\+$') -eq 0 ] && usage
43-
shift
44-
fi
45-
fi
46-
if test $# -gt 0 -o ! -d "$srctree"; then
47-
usage
48-
fi
49-
50-
scm_version()
51-
{
52-
local short
53-
short=false
54-
55-
cd "$srctree"
56-
if test -e .scmversion; then
57-
cat .scmversion
58-
return
59-
fi
60-
if test "$1" = "--short"; then
61-
short=true
62-
fi
63-
64-
# Check for git and a git repo.
65-
if test -z "$(git rev-parse --show-cdup 2>/dev/null)" &&
66-
head=$(git rev-parse --verify HEAD 2>/dev/null); then
67-
68-
if [ -n "$android_release" ] && [ -n "$kmi_generation" ]; then
69-
printf '%s' "-$android_release-$kmi_generation"
70-
elif [ -n "$android_release" ]; then
71-
printf '%s' "-$android_release"
72-
fi
73-
74-
# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore
75-
# it, because this version is defined in the top level Makefile.
76-
if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
77-
78-
# If only the short version is requested, don't bother
79-
# running further git commands
80-
if $short; then
81-
echo "+"
82-
return
83-
fi
84-
# If we are past a tagged commit (like
85-
# "v2.6.30-rc5-302-g72357d5"), we pretty print it.
86-
#
87-
# Ensure the abbreviated sha1 has exactly 12
88-
# hex characters, to make the output
89-
# independent of git version, local
90-
# core.abbrev settings and/or total number of
91-
# objects in the current repository - passing
92-
# --abbrev=12 ensures a minimum of 12, and the
93-
# awk substr() then picks the 'g' and first 12
94-
# hex chars.
95-
if atag="$(git describe --abbrev=12 2>/dev/null)"; then
96-
echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),substr($(NF),0,13))}'
97-
98-
# If we don't have a tag at all we print -g{commitish},
99-
# again using exactly 12 hex chars.
100-
else
101-
head="$(echo $head | cut -c1-12)"
102-
printf '%s%s' -g $head
103-
fi
104-
fi
105-
106-
# Is this git on svn?
107-
if git config --get svn-remote.svn.url >/dev/null; then
108-
printf -- '-svn%s' "`git svn find-rev $head`"
109-
fi
110-
111-
# Check for uncommitted changes.
112-
# First, with git-status, but --no-optional-locks is only
113-
# supported in git >= 2.14, so fall back to git-diff-index if
114-
# it fails. Note that git-diff-index does not refresh the
115-
# index, so it may give misleading results. See
116-
# git-update-index(1), git-diff-index(1), and git-status(1).
117-
if {
118-
git --no-optional-locks status -uno --porcelain 2>/dev/null ||
119-
git diff-index --name-only HEAD
120-
} | grep -qvE '^(.. )?scripts/package'; then
121-
printf '%s' -dirty
122-
fi
123-
124-
# All done with git
125-
return
126-
fi
127-
128-
# Check for mercurial and a mercurial repo.
129-
if test -d .hg && hgid=`hg id 2>/dev/null`; then
130-
# Do we have an tagged version? If so, latesttagdistance == 1
131-
if [ "`hg log -r . --template '{latesttagdistance}'`" = "1" ]; then
132-
id=`hg log -r . --template '{latesttag}'`
133-
printf '%s%s' -hg "$id"
134-
else
135-
tag=`printf '%s' "$hgid" | cut -d' ' -f2`
136-
if [ -z "$tag" -o "$tag" = tip ]; then
137-
id=`printf '%s' "$hgid" | sed 's/[+ ].*//'`
138-
printf '%s%s' -hg "$id"
139-
fi
140-
fi
141-
142-
# Are there uncommitted changes?
143-
# These are represented by + after the changeset id.
144-
case "$hgid" in
145-
*+|*+\ *) printf '%s' -dirty ;;
146-
esac
147-
148-
# All done with mercurial
149-
return
150-
fi
151-
152-
# Check for svn and a svn repo.
153-
if rev=`LANG= LC_ALL= LC_MESSAGES=C svn info 2>/dev/null | grep '^Last Changed Rev'`; then
154-
rev=`echo $rev | awk '{print $NF}'`
155-
printf -- '-svn%s' "$rev"
156-
157-
# All done with svn
158-
return
159-
fi
160-
}
161-
162-
collect_files()
163-
{
164-
local file res=
165-
166-
for file; do
167-
case "$file" in
168-
*\~*)
169-
continue
170-
;;
171-
esac
172-
if test -e "$file"; then
173-
res="$res$(cat "$file")"
174-
fi
175-
done
176-
echo "$res"
177-
}
178-
179-
if $scm_only; then
180-
if test ! -e .scmversion; then
181-
res=$(scm_version)
182-
echo "$res" >.scmversion
183-
fi
184-
exit
185-
fi
186-
187-
if test -e include/config/auto.conf; then
188-
. include/config/auto.conf
189-
else
190-
echo "Error: kernelrelease not valid - run 'make prepare' to update it" >&2
191-
exit 1
192-
fi
193-
194-
# localversion* files in the build and source directory
195-
res="$(collect_files localversion*)"
196-
if test ! "$srctree" -ef .; then
197-
res="$res$(collect_files "$srctree"/localversion*)"
198-
fi
199-
200-
# CONFIG_LOCALVERSION and LOCALVERSION (if set)
201-
res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
202-
203-
# scm version string if not at a tagged commit
204-
if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
205-
# full scm version string
206-
res="$res$(scm_version)"
207-
else
208-
# append a plus sign if the repository is not in a clean
209-
# annotated or signed tagged state (as git describe only
210-
# looks at signed or annotated tags - git tag -a/-s) and
211-
# LOCALVERSION= is not specified
212-
if test "${LOCALVERSION+set}" != "set"; then
213-
scm=$(scm_version --short)
214-
res="$res${scm:++}"
215-
fi
216-
fi
217-
218-
echo "-qgki-gc916795b189b"
12+
echo ""

0 commit comments

Comments
 (0)