Implement bpf_send_signal_task kfunc that is similar to
bpf_send_signal_thread and bpf_send_signal helpers but can be used to
send signals to other threads and processes. It also supports sending a
cookie with the signal similar to sigqueue().
If the receiving process establishes a handler for the signal using the
SA_SIGINFO flag to sigaction(), then it can obtain this cookie via the
si_value field of the siginfo_t structure passed as the second argument
to the handler.
Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
---
kernel/bpf/helpers.c | 1 +
kernel/trace/bpf_trace.c | 52 +++++++++++++++++++++++++++++++++-------
2 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 4053f279ed4cc..2fd3feefb9d94 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3035,6 +3035,7 @@ BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
#endif
BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_throw)
+BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS)
BTF_KFUNCS_END(generic_btf_ids)
static const struct btf_kfunc_id_set generic_kfunc_set = {
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index a582cd25ca876..d9662e84510d3 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -802,6 +802,8 @@ struct send_signal_irq_work {
struct task_struct *task;
u32 sig;
enum pid_type type;
+ bool has_siginfo;
+ struct kernel_siginfo info;
};
static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work);
@@ -809,27 +811,46 @@ static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work);
static void do_bpf_send_signal(struct irq_work *entry)
{
struct send_signal_irq_work *work;
+ struct kernel_siginfo *siginfo;
work = container_of(entry, struct send_signal_irq_work, irq_work);
- group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type);
+ siginfo = work->has_siginfo ? &work->info : SEND_SIG_PRIV;
+
+ group_send_sig_info(work->sig, siginfo, work->task, work->type);
put_task_struct(work->task);
}
-static int bpf_send_signal_common(u32 sig, enum pid_type type)
+static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value)
{
struct send_signal_irq_work *work = NULL;
+ struct kernel_siginfo info;
+ struct kernel_siginfo *siginfo;
+
+ if (!task) {
+ task = current;
+ siginfo = SEND_SIG_PRIV;
+ } else {
+ clear_siginfo(&info);
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = SI_KERNEL;
+ info.si_pid = 0;
+ info.si_uid = 0;
+ info.si_value.sival_ptr = (void *)(unsigned long)value;
+ siginfo = &info;
+ }
/* Similar to bpf_probe_write_user, task needs to be
* in a sound condition and kernel memory access be
* permitted in order to send signal to the current
* task.
*/
- if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING)))
+ if (unlikely(task->flags & (PF_KTHREAD | PF_EXITING)))
return -EPERM;
if (unlikely(!nmi_uaccess_okay()))
return -EPERM;
/* Task should not be pid=1 to avoid kernel panic. */
- if (unlikely(is_global_init(current)))
+ if (unlikely(is_global_init(task)))
return -EPERM;
if (irqs_disabled()) {
@@ -847,19 +868,21 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type)
* to the irq_work. The current task may change when queued
* irq works get executed.
*/
- work->task = get_task_struct(current);
+ work->task = get_task_struct(task);
+ work->has_siginfo = siginfo == &info;
+ copy_siginfo(&work->info, &info);
work->sig = sig;
work->type = type;
irq_work_queue(&work->irq_work);
return 0;
}
- return group_send_sig_info(sig, SEND_SIG_PRIV, current, type);
+ return group_send_sig_info(sig, siginfo, task, type);
}
BPF_CALL_1(bpf_send_signal, u32, sig)
{
- return bpf_send_signal_common(sig, PIDTYPE_TGID);
+ return bpf_send_signal_common(sig, PIDTYPE_TGID, NULL, 0);
}
static const struct bpf_func_proto bpf_send_signal_proto = {
@@ -871,7 +894,7 @@ static const struct bpf_func_proto bpf_send_signal_proto = {
BPF_CALL_1(bpf_send_signal_thread, u32, sig)
{
- return bpf_send_signal_common(sig, PIDTYPE_PID);
+ return bpf_send_signal_common(sig, PIDTYPE_PID, NULL, 0);
}
static const struct bpf_func_proto bpf_send_signal_thread_proto = {
@@ -3484,3 +3507,16 @@ static int __init bpf_kprobe_multi_kfuncs_init(void)
}
late_initcall(bpf_kprobe_multi_kfuncs_init);
+
+__bpf_kfunc_start_defs();
+
+__bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type,
+ u64 value)
+{
+ if (type != PIDTYPE_PID && type != PIDTYPE_TGID)
+ return -EINVAL;
+
+ return bpf_send_signal_common(sig, type, task, value);
+}
+
+__bpf_kfunc_end_defs();
--
2.40.1
On Tue, Oct 8, 2024 at 4:49 AM Puranjay Mohan <puranjay@kernel.org> wrote: > > Implement bpf_send_signal_task kfunc that is similar to > bpf_send_signal_thread and bpf_send_signal helpers but can be used to > send signals to other threads and processes. It also supports sending a > cookie with the signal similar to sigqueue(). > > If the receiving process establishes a handler for the signal using the > SA_SIGINFO flag to sigaction(), then it can obtain this cookie via the > si_value field of the siginfo_t structure passed as the second argument > to the handler. > > Signed-off-by: Puranjay Mohan <puranjay@kernel.org> > --- > kernel/bpf/helpers.c | 1 + > kernel/trace/bpf_trace.c | 52 +++++++++++++++++++++++++++++++++------- > 2 files changed, 45 insertions(+), 8 deletions(-) > > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c > index 4053f279ed4cc..2fd3feefb9d94 100644 > --- a/kernel/bpf/helpers.c > +++ b/kernel/bpf/helpers.c > @@ -3035,6 +3035,7 @@ BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL) > #endif > BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL) > BTF_ID_FLAGS(func, bpf_throw) > +BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS) > BTF_KFUNCS_END(generic_btf_ids) > > static const struct btf_kfunc_id_set generic_kfunc_set = { > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index a582cd25ca876..d9662e84510d3 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -802,6 +802,8 @@ struct send_signal_irq_work { > struct task_struct *task; > u32 sig; > enum pid_type type; > + bool has_siginfo; > + struct kernel_siginfo info; > }; > > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > @@ -809,27 +811,46 @@ static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > static void do_bpf_send_signal(struct irq_work *entry) > { > struct send_signal_irq_work *work; > + struct kernel_siginfo *siginfo; > > work = container_of(entry, struct send_signal_irq_work, irq_work); > - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > + siginfo = work->has_siginfo ? &work->info : SEND_SIG_PRIV; > + > + group_send_sig_info(work->sig, siginfo, work->task, work->type); > put_task_struct(work->task); > } > > -static int bpf_send_signal_common(u32 sig, enum pid_type type) > +static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value) > { > struct send_signal_irq_work *work = NULL; > + struct kernel_siginfo info; > + struct kernel_siginfo *siginfo; > + > + if (!task) { > + task = current; > + siginfo = SEND_SIG_PRIV; > + } else { > + clear_siginfo(&info); > + info.si_signo = sig; > + info.si_errno = 0; > + info.si_code = SI_KERNEL; > + info.si_pid = 0; > + info.si_uid = 0; > + info.si_value.sival_ptr = (void *)(unsigned long)value; > + siginfo = &info; > + } > > /* Similar to bpf_probe_write_user, task needs to be > * in a sound condition and kernel memory access be > * permitted in order to send signal to the current > * task. > */ > - if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING))) > + if (unlikely(task->flags & (PF_KTHREAD | PF_EXITING))) > return -EPERM; > if (unlikely(!nmi_uaccess_okay())) > return -EPERM; > /* Task should not be pid=1 to avoid kernel panic. */ > - if (unlikely(is_global_init(current))) > + if (unlikely(is_global_init(task))) > return -EPERM; > > if (irqs_disabled()) { > @@ -847,19 +868,21 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) > * to the irq_work. The current task may change when queued > * irq works get executed. > */ > - work->task = get_task_struct(current); > + work->task = get_task_struct(task); > + work->has_siginfo = siginfo == &info; > + copy_siginfo(&work->info, &info); we shouldn't copy_siginfo() if !work->has_siginfo, no? other than that, lgtm Acked-by: Andrii Nakryiko <andrii@kernel.org> > work->sig = sig; > work->type = type; > irq_work_queue(&work->irq_work); > return 0; > } > > - return group_send_sig_info(sig, SEND_SIG_PRIV, current, type); > + return group_send_sig_info(sig, siginfo, task, type); > } > > BPF_CALL_1(bpf_send_signal, u32, sig) > { > - return bpf_send_signal_common(sig, PIDTYPE_TGID); > + return bpf_send_signal_common(sig, PIDTYPE_TGID, NULL, 0); > } > > static const struct bpf_func_proto bpf_send_signal_proto = { > @@ -871,7 +894,7 @@ static const struct bpf_func_proto bpf_send_signal_proto = { > > BPF_CALL_1(bpf_send_signal_thread, u32, sig) > { > - return bpf_send_signal_common(sig, PIDTYPE_PID); > + return bpf_send_signal_common(sig, PIDTYPE_PID, NULL, 0); > } > > static const struct bpf_func_proto bpf_send_signal_thread_proto = { > @@ -3484,3 +3507,16 @@ static int __init bpf_kprobe_multi_kfuncs_init(void) > } > > late_initcall(bpf_kprobe_multi_kfuncs_init); > + > +__bpf_kfunc_start_defs(); > + > +__bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, > + u64 value) > +{ > + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) > + return -EINVAL; > + > + return bpf_send_signal_common(sig, type, task, value); > +} > + > +__bpf_kfunc_end_defs(); > -- > 2.40.1 >
Andrii Nakryiko <andrii.nakryiko@gmail.com> writes: > On Tue, Oct 8, 2024 at 4:49 AM Puranjay Mohan <puranjay@kernel.org> wrote: >> >> Implement bpf_send_signal_task kfunc that is similar to >> bpf_send_signal_thread and bpf_send_signal helpers but can be used to >> send signals to other threads and processes. It also supports sending a >> cookie with the signal similar to sigqueue(). >> >> If the receiving process establishes a handler for the signal using the >> SA_SIGINFO flag to sigaction(), then it can obtain this cookie via the >> si_value field of the siginfo_t structure passed as the second argument >> to the handler. >> >> Signed-off-by: Puranjay Mohan <puranjay@kernel.org> >> --- >> kernel/bpf/helpers.c | 1 + >> kernel/trace/bpf_trace.c | 52 +++++++++++++++++++++++++++++++++------- >> 2 files changed, 45 insertions(+), 8 deletions(-) >> >> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c >> index 4053f279ed4cc..2fd3feefb9d94 100644 >> --- a/kernel/bpf/helpers.c >> +++ b/kernel/bpf/helpers.c >> @@ -3035,6 +3035,7 @@ BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL) >> #endif >> BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL) >> BTF_ID_FLAGS(func, bpf_throw) >> +BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS) >> BTF_KFUNCS_END(generic_btf_ids) >> >> static const struct btf_kfunc_id_set generic_kfunc_set = { >> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c >> index a582cd25ca876..d9662e84510d3 100644 >> --- a/kernel/trace/bpf_trace.c >> +++ b/kernel/trace/bpf_trace.c >> @@ -802,6 +802,8 @@ struct send_signal_irq_work { >> struct task_struct *task; >> u32 sig; >> enum pid_type type; >> + bool has_siginfo; >> + struct kernel_siginfo info; >> }; >> >> static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); >> @@ -809,27 +811,46 @@ static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); >> static void do_bpf_send_signal(struct irq_work *entry) >> { >> struct send_signal_irq_work *work; >> + struct kernel_siginfo *siginfo; >> >> work = container_of(entry, struct send_signal_irq_work, irq_work); >> - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); >> + siginfo = work->has_siginfo ? &work->info : SEND_SIG_PRIV; >> + >> + group_send_sig_info(work->sig, siginfo, work->task, work->type); >> put_task_struct(work->task); >> } >> >> -static int bpf_send_signal_common(u32 sig, enum pid_type type) >> +static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value) >> { >> struct send_signal_irq_work *work = NULL; >> + struct kernel_siginfo info; >> + struct kernel_siginfo *siginfo; >> + >> + if (!task) { >> + task = current; >> + siginfo = SEND_SIG_PRIV; >> + } else { >> + clear_siginfo(&info); >> + info.si_signo = sig; >> + info.si_errno = 0; >> + info.si_code = SI_KERNEL; >> + info.si_pid = 0; >> + info.si_uid = 0; >> + info.si_value.sival_ptr = (void *)(unsigned long)value; >> + siginfo = &info; >> + } >> >> /* Similar to bpf_probe_write_user, task needs to be >> * in a sound condition and kernel memory access be >> * permitted in order to send signal to the current >> * task. >> */ >> - if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING))) >> + if (unlikely(task->flags & (PF_KTHREAD | PF_EXITING))) >> return -EPERM; >> if (unlikely(!nmi_uaccess_okay())) >> return -EPERM; >> /* Task should not be pid=1 to avoid kernel panic. */ >> - if (unlikely(is_global_init(current))) >> + if (unlikely(is_global_init(task))) >> return -EPERM; >> >> if (irqs_disabled()) { >> @@ -847,19 +868,21 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) >> * to the irq_work. The current task may change when queued >> * irq works get executed. >> */ >> - work->task = get_task_struct(current); >> + work->task = get_task_struct(task); >> + work->has_siginfo = siginfo == &info; >> + copy_siginfo(&work->info, &info); > > we shouldn't copy_siginfo() if !work->has_siginfo, no? Yes, but it is only used when has_siginfo is true, so copying it doesn't cause any problems. I just didn't want to add another check here. > other than that, lgtm > > Acked-by: Andrii Nakryiko <andrii@kernel.org> Thanks for the Ack. I hope this can go in now! Thanks, Puranjay Mohan
On Tue, Oct 15, 2024 at 2:57 AM Puranjay Mohan <puranjay@kernel.org> wrote: > > Andrii Nakryiko <andrii.nakryiko@gmail.com> writes: > > > On Tue, Oct 8, 2024 at 4:49 AM Puranjay Mohan <puranjay@kernel.org> wrote: > >> > >> Implement bpf_send_signal_task kfunc that is similar to > >> bpf_send_signal_thread and bpf_send_signal helpers but can be used to > >> send signals to other threads and processes. It also supports sending a > >> cookie with the signal similar to sigqueue(). > >> > >> If the receiving process establishes a handler for the signal using the > >> SA_SIGINFO flag to sigaction(), then it can obtain this cookie via the > >> si_value field of the siginfo_t structure passed as the second argument > >> to the handler. > >> > >> Signed-off-by: Puranjay Mohan <puranjay@kernel.org> > >> --- > >> kernel/bpf/helpers.c | 1 + > >> kernel/trace/bpf_trace.c | 52 +++++++++++++++++++++++++++++++++------- > >> 2 files changed, 45 insertions(+), 8 deletions(-) > >> > >> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c > >> index 4053f279ed4cc..2fd3feefb9d94 100644 > >> --- a/kernel/bpf/helpers.c > >> +++ b/kernel/bpf/helpers.c > >> @@ -3035,6 +3035,7 @@ BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL) > >> #endif > >> BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL) > >> BTF_ID_FLAGS(func, bpf_throw) > >> +BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS) > >> BTF_KFUNCS_END(generic_btf_ids) > >> > >> static const struct btf_kfunc_id_set generic_kfunc_set = { > >> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > >> index a582cd25ca876..d9662e84510d3 100644 > >> --- a/kernel/trace/bpf_trace.c > >> +++ b/kernel/trace/bpf_trace.c > >> @@ -802,6 +802,8 @@ struct send_signal_irq_work { > >> struct task_struct *task; > >> u32 sig; > >> enum pid_type type; > >> + bool has_siginfo; > >> + struct kernel_siginfo info; > >> }; > >> > >> static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > >> @@ -809,27 +811,46 @@ static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > >> static void do_bpf_send_signal(struct irq_work *entry) > >> { > >> struct send_signal_irq_work *work; > >> + struct kernel_siginfo *siginfo; > >> > >> work = container_of(entry, struct send_signal_irq_work, irq_work); > >> - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > >> + siginfo = work->has_siginfo ? &work->info : SEND_SIG_PRIV; > >> + > >> + group_send_sig_info(work->sig, siginfo, work->task, work->type); > >> put_task_struct(work->task); > >> } > >> > >> -static int bpf_send_signal_common(u32 sig, enum pid_type type) > >> +static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value) > >> { > >> struct send_signal_irq_work *work = NULL; > >> + struct kernel_siginfo info; > >> + struct kernel_siginfo *siginfo; > >> + > >> + if (!task) { > >> + task = current; > >> + siginfo = SEND_SIG_PRIV; > >> + } else { > >> + clear_siginfo(&info); > >> + info.si_signo = sig; > >> + info.si_errno = 0; > >> + info.si_code = SI_KERNEL; > >> + info.si_pid = 0; > >> + info.si_uid = 0; > >> + info.si_value.sival_ptr = (void *)(unsigned long)value; > >> + siginfo = &info; > >> + } > >> > >> /* Similar to bpf_probe_write_user, task needs to be > >> * in a sound condition and kernel memory access be > >> * permitted in order to send signal to the current > >> * task. > >> */ > >> - if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING))) > >> + if (unlikely(task->flags & (PF_KTHREAD | PF_EXITING))) > >> return -EPERM; > >> if (unlikely(!nmi_uaccess_okay())) > >> return -EPERM; > >> /* Task should not be pid=1 to avoid kernel panic. */ > >> - if (unlikely(is_global_init(current))) > >> + if (unlikely(is_global_init(task))) > >> return -EPERM; > >> > >> if (irqs_disabled()) { > >> @@ -847,19 +868,21 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) > >> * to the irq_work. The current task may change when queued > >> * irq works get executed. > >> */ > >> - work->task = get_task_struct(current); > >> + work->task = get_task_struct(task); > >> + work->has_siginfo = siginfo == &info; > >> + copy_siginfo(&work->info, &info); > > > > we shouldn't copy_siginfo() if !work->has_siginfo, no? > > Yes, but it is only used when has_siginfo is true, so copying it doesn't > cause any problems. I just didn't want to add another check here. Still, let's avoid a pointless copy. If I'm reading it correctly it will copy uninitialized memory and sanitizers won't be happy. Pls respin.
© 2016 - 2024 Red Hat, Inc.