In order to enable additional debugging features, Android init needs a way to distinguish MTE-related SEGVs (with si_code of SEGV_MTEAERR) from other SEGVs. This is not possible with current APIs, neither by the existing information in /proc/<pid>/stat, nor via waitpid. Tested with the following program int main(int argc, char** argv) { int pid = fork(); if (!pid) { if (strcmp(argv[1], "sigqueue") == 0) { union sigval value; value.sival_int = 0; sigqueue(getpid(), SIGSEGV, value); } else if (strcmp(argv[1], "raise") == 0) { raise(SIGSEGV); } else if (strcmp(argv[1], "kill") == 0) { kill(getpid(), SIGSEGV); } else if (strcmp(argv[1], "raisestop") == 0) { raise(SIGSTOP); } else if (strcmp(argv[1], "crash") == 0) { volatile int* x = (int*)(0x23); *x = 1; } else if (strcmp(argv[1], "mte") == 0) { volatile char* y = malloc(1); y += 100; *y = 1; } } else { printf("%d\n", pid); sleep(5); char buf[1024]; sprintf(buf, "/proc/%d/stat", pid); int fd = open(buf, O_RDONLY); char statb[1024]; read(fd, statb, sizeof(statb)); printf("%s\n", statb); } } Signed-off-by: Florian Mayer <fmayer@xxxxxxxxxx> --- Documentation/filesystems/proc.rst | 2 ++ fs/coredump.c | 17 ++++++++++------- fs/proc/array.c | 12 ++++++++---- include/linux/sched/signal.h | 1 + include/linux/sched/task.h | 2 +- kernel/exit.c | 5 +++-- kernel/pid_namespace.c | 4 +++- kernel/signal.c | 29 +++++++++++++++++++---------- 8 files changed, 47 insertions(+), 25 deletions(-) diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index e7aafc82be99..12ad5ecd7434 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -381,6 +381,8 @@ It's slow but very precise. env_end address below which program environment is placed exit_code the thread's exit_code in the form reported by the waitpid system call + exit_sicode if the process was stopped or terminated by a signal, the + signal's si_code. 0 otherwise ============= =============================================================== The /proc/PID/maps file contains the currently mapped memory regions and diff --git a/fs/coredump.c b/fs/coredump.c index 9f4aae202109..61e9f27d2bf8 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -349,7 +349,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm, return ispipe; } -static int zap_process(struct task_struct *start, int exit_code) +static int zap_process(struct task_struct *start, int exit_code, int sicode) { struct task_struct *t; int nr = 0; @@ -357,6 +357,7 @@ static int zap_process(struct task_struct *start, int exit_code) /* ignore all signals except SIGKILL, see prepare_signal() */ start->signal->flags = SIGNAL_GROUP_EXIT; start->signal->group_exit_code = exit_code; + start->signal->group_exit_sicode = sicode; start->signal->group_stop_count = 0; for_each_thread(start, t) { @@ -371,8 +372,8 @@ static int zap_process(struct task_struct *start, int exit_code) return nr; } -static int zap_threads(struct task_struct *tsk, - struct core_state *core_state, int exit_code) +static int zap_threads(struct task_struct *tsk, struct core_state *core_state, + int exit_code, int sicode) { struct signal_struct *signal = tsk->signal; int nr = -EAGAIN; @@ -380,7 +381,7 @@ static int zap_threads(struct task_struct *tsk, spin_lock_irq(&tsk->sighand->siglock); if (!(signal->flags & SIGNAL_GROUP_EXIT) && !signal->group_exec_task) { signal->core_state = core_state; - nr = zap_process(tsk, exit_code); + nr = zap_process(tsk, exit_code, sicode); clear_tsk_thread_flag(tsk, TIF_SIGPENDING); tsk->flags |= PF_DUMPCORE; atomic_set(&core_state->nr_threads, nr); @@ -389,7 +390,8 @@ static int zap_threads(struct task_struct *tsk, return nr; } -static int coredump_wait(int exit_code, struct core_state *core_state) +static int coredump_wait(int exit_code, int sicode, + struct core_state *core_state) { struct task_struct *tsk = current; int core_waiters = -EBUSY; @@ -398,7 +400,7 @@ static int coredump_wait(int exit_code, struct core_state *core_state) core_state->dumper.task = tsk; core_state->dumper.next = NULL; - core_waiters = zap_threads(tsk, core_state, exit_code); + core_waiters = zap_threads(tsk, core_state, exit_code, sicode); if (core_waiters > 0) { struct core_thread *ptr; @@ -560,7 +562,8 @@ void do_coredump(const kernel_siginfo_t *siginfo) need_suid_safe = true; } - retval = coredump_wait(siginfo->si_signo, &core_state); + retval = + coredump_wait(siginfo->si_signo, siginfo->si_code, &core_state); if (retval < 0) goto fail_creds; diff --git a/fs/proc/array.c b/fs/proc/array.c index 99fcbfda8e25..23553460627c 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -474,6 +474,7 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, unsigned long rsslim = 0; unsigned long flags; int exit_code = task->exit_code; + int exit_sicode = 0; state = *get_task_state(task); vsize = eip = esp = 0; @@ -538,8 +539,10 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, thread_group_cputime_adjusted(task, &utime, &stime); gtime += sig->gtime; - if (sig->flags & (SIGNAL_GROUP_EXIT | SIGNAL_STOP_STOPPED)) + if (sig->flags & (SIGNAL_GROUP_EXIT | SIGNAL_STOP_STOPPED)) { exit_code = sig->group_exit_code; + exit_sicode = sig->group_exit_sicode; + } } sid = task_session_nr_ns(task, ns); @@ -638,10 +641,11 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, } else seq_puts(m, " 0 0 0 0 0 0 0"); - if (permitted) + if (permitted) { seq_put_decimal_ll(m, " ", exit_code); - else - seq_puts(m, " 0"); + seq_put_decimal_ll(m, " ", exit_sicode); + } else + seq_puts(m, " 0 0"); seq_putc(m, '\n'); if (mm) diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index cafbe03eed01..1631dba7a7db 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -109,6 +109,7 @@ struct signal_struct { /* thread group exit support */ int group_exit_code; + int group_exit_sicode; /* notify group_exec_task when notify_count is less or equal to 0 */ int notify_count; struct task_struct *group_exec_task; diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index 81cab4b01edc..6ff4825fc88a 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -82,7 +82,7 @@ static inline void exit_thread(struct task_struct *tsk) { } #endif -extern __noreturn void do_group_exit(int); +extern __noreturn void do_group_exit(int,int); extern void exit_files(struct task_struct *); extern void exit_itimers(struct task_struct *); diff --git a/kernel/exit.c b/kernel/exit.c index 84021b24f79e..278469d13433 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -897,7 +897,7 @@ SYSCALL_DEFINE1(exit, int, error_code) * as well as by sys_exit_group (below). */ void __noreturn -do_group_exit(int exit_code) +do_group_exit(int exit_code, int sicode) { struct signal_struct *sig = current->signal; @@ -916,6 +916,7 @@ do_group_exit(int exit_code) exit_code = 0; else { sig->group_exit_code = exit_code; + sig->group_exit_sicode = sicode; sig->flags = SIGNAL_GROUP_EXIT; zap_other_threads(current); } @@ -933,7 +934,7 @@ do_group_exit(int exit_code) */ SYSCALL_DEFINE1(exit_group, int, error_code) { - do_group_exit((error_code & 0xff) << 8); + do_group_exit((error_code & 0xff) << 8, 0); /* NOTREACHED */ return 0; } diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index f4f8cb0435b4..c80db136726d 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -248,8 +248,10 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns) } __set_current_state(TASK_RUNNING); - if (pid_ns->reboot) + if (pid_ns->reboot) { current->signal->group_exit_code = pid_ns->reboot; + current->signal->group_exit_sicode = 0; + } acct_exit_ns(pid_ns); return; diff --git a/kernel/signal.c b/kernel/signal.c index 6f86fda5e432..180310a9171c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -963,6 +963,7 @@ static bool prepare_signal(int sig, struct task_struct *p, bool force) signal_set_stop_flags(signal, why | SIGNAL_STOP_CONTINUED); signal->group_stop_count = 0; signal->group_exit_code = 0; + signal->group_exit_sicode = 0; } } @@ -994,7 +995,8 @@ static inline bool wants_signal(int sig, struct task_struct *p) return task_curr(p) || !task_sigpending(p); } -static void complete_signal(int sig, struct task_struct *p, enum pid_type type) +static void complete_signal(int sig, int code, struct task_struct *p, + enum pid_type type) { struct signal_struct *signal = p->signal; struct task_struct *t; @@ -1051,6 +1053,7 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type) */ signal->flags = SIGNAL_GROUP_EXIT; signal->group_exit_code = sig; + signal->group_exit_sicode = code; signal->group_stop_count = 0; t = p; do { @@ -1082,6 +1085,7 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, struct sigqueue *q; int override_rlimit; int ret = 0, result; + int code = 0; lockdep_assert_held(&t->sighand->siglock); @@ -1129,7 +1133,7 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, clear_siginfo(&q->info); q->info.si_signo = sig; q->info.si_errno = 0; - q->info.si_code = SI_USER; + code = q->info.si_code = SI_USER; q->info.si_pid = task_tgid_nr_ns(current, task_active_pid_ns(t)); rcu_read_lock(); @@ -1142,12 +1146,13 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, clear_siginfo(&q->info); q->info.si_signo = sig; q->info.si_errno = 0; - q->info.si_code = SI_KERNEL; + code = q->info.si_code = SI_KERNEL; q->info.si_pid = 0; q->info.si_uid = 0; break; default: copy_siginfo(&q->info, info); + code = info->si_code; break; } } else if (!is_si_special(info) && @@ -1186,7 +1191,7 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, } } - complete_signal(sig, t, type); + complete_signal(sig, code, t, type); ret: trace_signal_generate(sig, info, t, type != PIDTYPE_PID, result); return ret; @@ -1960,6 +1965,7 @@ void sigqueue_free(struct sigqueue *q) int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type) { int sig = q->info.si_signo; + int code = q->info.si_code; struct sigpending *pending; struct task_struct *t; unsigned long flags; @@ -1995,7 +2001,7 @@ int send_sigqueue(struct sigqueue *q, struct pid *pid, enum pid_type type) pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending; list_add_tail(&q->list, &pending->list); sigaddset(&pending->signal, sig); - complete_signal(sig, t, type); + complete_signal(sig, code, t, type); result = TRACE_SIGNAL_DELIVERED; out: trace_signal_generate(sig, &q->info, t, type != PIDTYPE_PID, result); @@ -2380,7 +2386,7 @@ int ptrace_notify(int exit_code, unsigned long message) * %false if group stop is already cancelled or ptrace trap is scheduled. * %true if participated in group stop. */ -static bool do_signal_stop(int signr) +static bool do_signal_stop(int signr, int sicode) __releases(¤t->sighand->siglock) { struct signal_struct *sig = current->signal; @@ -2415,8 +2421,10 @@ static bool do_signal_stop(int signr) * an intervening stop signal is required to cause two * continued events regardless of ptrace. */ - if (!(sig->flags & SIGNAL_STOP_STOPPED)) + if (!(sig->flags & SIGNAL_STOP_STOPPED)) { sig->group_exit_code = signr; + sig->group_exit_sicode = sicode; + } sig->group_stop_count = 0; @@ -2701,7 +2709,7 @@ bool get_signal(struct ksignal *ksig) } if (unlikely(current->jobctl & JOBCTL_STOP_PENDING) && - do_signal_stop(0)) + do_signal_stop(0, 0)) goto relock; if (unlikely(current->jobctl & @@ -2806,7 +2814,8 @@ bool get_signal(struct ksignal *ksig) spin_lock_irq(&sighand->siglock); } - if (likely(do_signal_stop(ksig->info.si_signo))) { + if (likely(do_signal_stop(ksig->info.si_signo, + ksig->info.si_code))) { /* It released the siglock. */ goto relock; } @@ -2854,7 +2863,7 @@ bool get_signal(struct ksignal *ksig) /* * Death signals, no core dump. */ - do_group_exit(ksig->info.si_signo); + do_group_exit(ksig->info.si_signo, ksig->info.si_code); /* NOTREACHED */ } spin_unlock_irq(&sighand->siglock); -- 2.37.1.559.g78731f0fdb-goog