Message ID | 87k0hvkgvj.fsf_-_@disp2133 (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | signal: Add SA_IMMUTABLE to ensure forced siganls do not get changed | expand |
On Fri, Oct 29, 2021 at 10:09:04AM -0500, Eric W. Biederman wrote: > > As Andy pointed out that there are races between > force_sig_info_to_task and sigaction[1] when force_sig_info_task. As > Kees discovered[2] ptrace is also able to change these signals. > > In the case of seeccomp killing a process with a signal it is a > security violation to allow the signal to be caught or manipulated. > > Solve this problem by introducing a new flag SA_IMMUTABLE that > prevents sigaction and ptrace from modifying these forced signals. > This flag is carefully made kernel internal so that no new ABI is > introduced. > > Longer term I think this can be solved by guaranteeing short circuit > delivery of signals in this case. Unfortunately reliable and > guaranteed short circuit delivery of these signals is still a ways off > from being implemented, tested, and merged. So I have implemented a much > simpler alternative for now. > > [1] https://lkml.kernel.org/r/b5d52d25-7bde-4030-a7b1-7c6f8ab90660@www.fastmail.com > [2] https://lkml.kernel.org/r/202110281136.5CE65399A7@keescook > Cc: stable@vger.kernel.org > Fixes: 307d522f5eb8 ("signal/seccomp: Refactor seccomp signal and coredump generation") > Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> > --- FWIW I've tested this patch and I confirm that it fixes the failure that I reported with the seccomp_bpf selftest. Tested-by: Andrea Righi <andrea.righi@canonical.com> Thanks! -Andrea > > I have tested this patch and this changed works for me to fix the issue. > > I believe this closes all of the races that force_sig_info_to_task > has when sigdfl is specified. So this should be enough for anything > that needs a guaranteed that userspace can not race with the kernel > is handled. > > Can folks look this over and see if I missed something? > Thank you, > Eric > > > include/linux/signal_types.h | 3 +++ > include/uapi/asm-generic/signal-defs.h | 1 + > kernel/signal.c | 8 +++++++- > 3 files changed, 11 insertions(+), 1 deletion(-) > > diff --git a/include/linux/signal_types.h b/include/linux/signal_types.h > index 34cb28b8f16c..927f7c0e5bff 100644 > --- a/include/linux/signal_types.h > +++ b/include/linux/signal_types.h > @@ -70,6 +70,9 @@ struct ksignal { > int sig; > }; > > +/* Used to kill the race between sigaction and forced signals */ > +#define SA_IMMUTABLE 0x008000000 > + > #ifndef __ARCH_UAPI_SA_FLAGS > #ifdef SA_RESTORER > #define __ARCH_UAPI_SA_FLAGS SA_RESTORER > diff --git a/include/uapi/asm-generic/signal-defs.h b/include/uapi/asm-generic/signal-defs.h > index fe929e7b77ca..7572f2f46ee8 100644 > --- a/include/uapi/asm-generic/signal-defs.h > +++ b/include/uapi/asm-generic/signal-defs.h > @@ -45,6 +45,7 @@ > #define SA_UNSUPPORTED 0x00000400 > #define SA_EXPOSE_TAGBITS 0x00000800 > /* 0x00010000 used on mips */ > +/* 0x00800000 used for internal SA_IMMUTABLE */ > /* 0x01000000 used on x86 */ > /* 0x02000000 used on x86 */ > /* > diff --git a/kernel/signal.c b/kernel/signal.c > index 6a5e1802b9a2..056a107e3cbc 100644 > --- a/kernel/signal.c > +++ b/kernel/signal.c > @@ -1336,6 +1336,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool > blocked = sigismember(&t->blocked, sig); > if (blocked || ignored || sigdfl) { > action->sa.sa_handler = SIG_DFL; > + action->sa.sa_flags |= SA_IMMUTABLE; > if (blocked) { > sigdelset(&t->blocked, sig); > recalc_sigpending_and_wake(t); > @@ -2760,7 +2761,8 @@ bool get_signal(struct ksignal *ksig) > if (!signr) > break; /* will return 0 */ > > - if (unlikely(current->ptrace) && signr != SIGKILL) { > + if (unlikely(current->ptrace) && (signr != SIGKILL) && > + !(sighand->action[signr -1].sa.sa_flags & SA_IMMUTABLE)) { > signr = ptrace_signal(signr, &ksig->info); > if (!signr) > continue; > @@ -4110,6 +4112,10 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) > k = &p->sighand->action[sig-1]; > > spin_lock_irq(&p->sighand->siglock); > + if (k->sa.sa_flags & SA_IMMUTABLE) { > + spin_unlock_irq(&p->sighand->siglock); > + return -EINVAL; > + } > if (oact) > *oact = *k; > > -- > 2.20.1
Andrea Righi <andrea.righi@canonical.com> writes: > On Fri, Oct 29, 2021 at 10:09:04AM -0500, Eric W. Biederman wrote: >> >> As Andy pointed out that there are races between >> force_sig_info_to_task and sigaction[1] when force_sig_info_task. As >> Kees discovered[2] ptrace is also able to change these signals. >> >> In the case of seeccomp killing a process with a signal it is a >> security violation to allow the signal to be caught or manipulated. >> >> Solve this problem by introducing a new flag SA_IMMUTABLE that >> prevents sigaction and ptrace from modifying these forced signals. >> This flag is carefully made kernel internal so that no new ABI is >> introduced. >> >> Longer term I think this can be solved by guaranteeing short circuit >> delivery of signals in this case. Unfortunately reliable and >> guaranteed short circuit delivery of these signals is still a ways off >> from being implemented, tested, and merged. So I have implemented a much >> simpler alternative for now. >> >> [1] https://lkml.kernel.org/r/b5d52d25-7bde-4030-a7b1-7c6f8ab90660@www.fastmail.com >> [2] https://lkml.kernel.org/r/202110281136.5CE65399A7@keescook >> Cc: stable@vger.kernel.org >> Fixes: 307d522f5eb8 ("signal/seccomp: Refactor seccomp signal and coredump generation") >> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> >> --- > > FWIW I've tested this patch and I confirm that it fixes the failure that > I reported with the seccomp_bpf selftest. > > Tested-by: Andrea Righi <andrea.righi@canonical.com> Sigh. Except for the extra 0 in the definition of SA_IMMUTABLE that caused it to conflict with the x86 specific signal numbers. Eric
diff --git a/include/linux/signal_types.h b/include/linux/signal_types.h index 34cb28b8f16c..927f7c0e5bff 100644 --- a/include/linux/signal_types.h +++ b/include/linux/signal_types.h @@ -70,6 +70,9 @@ struct ksignal { int sig; }; +/* Used to kill the race between sigaction and forced signals */ +#define SA_IMMUTABLE 0x008000000 + #ifndef __ARCH_UAPI_SA_FLAGS #ifdef SA_RESTORER #define __ARCH_UAPI_SA_FLAGS SA_RESTORER diff --git a/include/uapi/asm-generic/signal-defs.h b/include/uapi/asm-generic/signal-defs.h index fe929e7b77ca..7572f2f46ee8 100644 --- a/include/uapi/asm-generic/signal-defs.h +++ b/include/uapi/asm-generic/signal-defs.h @@ -45,6 +45,7 @@ #define SA_UNSUPPORTED 0x00000400 #define SA_EXPOSE_TAGBITS 0x00000800 /* 0x00010000 used on mips */ +/* 0x00800000 used for internal SA_IMMUTABLE */ /* 0x01000000 used on x86 */ /* 0x02000000 used on x86 */ /* diff --git a/kernel/signal.c b/kernel/signal.c index 6a5e1802b9a2..056a107e3cbc 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1336,6 +1336,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, bool blocked = sigismember(&t->blocked, sig); if (blocked || ignored || sigdfl) { action->sa.sa_handler = SIG_DFL; + action->sa.sa_flags |= SA_IMMUTABLE; if (blocked) { sigdelset(&t->blocked, sig); recalc_sigpending_and_wake(t); @@ -2760,7 +2761,8 @@ bool get_signal(struct ksignal *ksig) if (!signr) break; /* will return 0 */ - if (unlikely(current->ptrace) && signr != SIGKILL) { + if (unlikely(current->ptrace) && (signr != SIGKILL) && + !(sighand->action[signr -1].sa.sa_flags & SA_IMMUTABLE)) { signr = ptrace_signal(signr, &ksig->info); if (!signr) continue; @@ -4110,6 +4112,10 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) k = &p->sighand->action[sig-1]; spin_lock_irq(&p->sighand->siglock); + if (k->sa.sa_flags & SA_IMMUTABLE) { + spin_unlock_irq(&p->sighand->siglock); + return -EINVAL; + } if (oact) *oact = *k;
As Andy pointed out that there are races between force_sig_info_to_task and sigaction[1] when force_sig_info_task. As Kees discovered[2] ptrace is also able to change these signals. In the case of seeccomp killing a process with a signal it is a security violation to allow the signal to be caught or manipulated. Solve this problem by introducing a new flag SA_IMMUTABLE that prevents sigaction and ptrace from modifying these forced signals. This flag is carefully made kernel internal so that no new ABI is introduced. Longer term I think this can be solved by guaranteeing short circuit delivery of signals in this case. Unfortunately reliable and guaranteed short circuit delivery of these signals is still a ways off from being implemented, tested, and merged. So I have implemented a much simpler alternative for now. [1] https://lkml.kernel.org/r/b5d52d25-7bde-4030-a7b1-7c6f8ab90660@www.fastmail.com [2] https://lkml.kernel.org/r/202110281136.5CE65399A7@keescook Cc: stable@vger.kernel.org Fixes: 307d522f5eb8 ("signal/seccomp: Refactor seccomp signal and coredump generation") Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> --- I have tested this patch and this changed works for me to fix the issue. I believe this closes all of the races that force_sig_info_to_task has when sigdfl is specified. So this should be enough for anything that needs a guaranteed that userspace can not race with the kernel is handled. Can folks look this over and see if I missed something? Thank you, Eric include/linux/signal_types.h | 3 +++ include/uapi/asm-generic/signal-defs.h | 1 + kernel/signal.c | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-)