Message ID | 20240926115328.105634-2-puranjay@kernel.org (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | BPF |
Headers | show |
Series | Implement mechanism to signal other threads | expand |
On Thu, Sep 26, 2024 at 4:53 AM Puranjay Mohan <puranjay@kernel.org> wrote: > > Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- > 1 file changed, 77 insertions(+), 1 deletion(-) > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index a582cd25ca876..51b27db1321fc 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -802,6 +802,9 @@ struct send_signal_irq_work { > struct task_struct *task; > u32 sig; > enum pid_type type; > + bool is_siginfo; > + kernel_siginfo_t info; > + int value; > }; > > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) > struct send_signal_irq_work *work; > > work = container_of(entry, struct send_signal_irq_work, irq_work); > - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > + if (work->is_siginfo) > + group_send_sig_info(work->sig, &work->info, work->task, work->type); > + else > + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > + > put_task_struct(work->task); > } > > @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) > * irq works get executed. > */ > work->task = get_task_struct(current); > + work->is_siginfo = false; > work->sig = sig; > work->type = type; > irq_work_queue(&work->irq_work); > @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, > + int value) > +{ > + struct send_signal_irq_work *work = NULL; > + kernel_siginfo_t info; > + > + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) > + return -EINVAL; > + 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(task))) > + return -EPERM; > + > + 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_int = value; > + > + if (irqs_disabled()) { > + /* Do an early check on signal validity. Otherwise, > + * the error is lost in deferred irq_work. > + */ > + if (unlikely(!valid_signal(sig))) > + return -EINVAL; > + > + work = this_cpu_ptr(&send_signal_work); > + if (irq_work_is_busy(&work->irq_work)) > + return -EBUSY; > + > + work->task = get_task_struct(task); > + work->is_siginfo = true; > + work->info = info; > + work->sig = sig; > + work->type = type; > + work->value = value; > + irq_work_queue(&work->irq_work); > + return 0; > + } > + > + return group_send_sig_info(sig, &info, task, type); This is very similar with bpf_send_signal_common(). Pls avoid copy paste and share the code instead. > +} > + > +__bpf_kfunc_end_defs(); > + > +BTF_KFUNCS_START(send_signal_kfunc_ids) > +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) > +BTF_KFUNCS_END(send_signal_kfunc_ids) > + > +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { > + .owner = THIS_MODULE, > + .set = &send_signal_kfunc_ids, > +}; > + > +static int __init bpf_send_signal_kfuncs_init(void) > +{ > + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); > +} > + > +late_initcall(bpf_send_signal_kfuncs_init); Let's avoid all this later_init proliferation. We have way too many of them across the whole kernel. Reuse one of the existing places and add kfunc there. With that extra bpf_send_signal_kfunc_set and send_signal_kfunc_ids won't be necessary. pw-bot: cr
On Thu, Sep 26, 2024 at 4:53 AM Puranjay Mohan <puranjay@kernel.org> wrote: > > Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- > 1 file changed, 77 insertions(+), 1 deletion(-) > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index a582cd25ca876..51b27db1321fc 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -802,6 +802,9 @@ struct send_signal_irq_work { > struct task_struct *task; > u32 sig; > enum pid_type type; > + bool is_siginfo; > + kernel_siginfo_t info; > + int value; > }; > > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) > struct send_signal_irq_work *work; > > work = container_of(entry, struct send_signal_irq_work, irq_work); > - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > + if (work->is_siginfo) > + group_send_sig_info(work->sig, &work->info, work->task, work->type); > + else > + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > + > put_task_struct(work->task); > } > > @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) > * irq works get executed. > */ > work->task = get_task_struct(current); > + work->is_siginfo = false; > work->sig = sig; > work->type = type; > irq_work_queue(&work->irq_work); > @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, > + int value) > +{ > + struct send_signal_irq_work *work = NULL; > + kernel_siginfo_t info; > + > + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) > + return -EINVAL; > + 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(task))) > + return -EPERM; > + > + 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_int = value; It seems like it could be either int sival_int or `void *sival_ptr`, i.e., it's actually a 64-bit value on 64-bit architectures. Can we allow passing a full u64 here and assign it to sival_ptr (with a cast)? > + > + if (irqs_disabled()) { > + /* Do an early check on signal validity. Otherwise, > + * the error is lost in deferred irq_work. > + */ > + if (unlikely(!valid_signal(sig))) > + return -EINVAL; > + > + work = this_cpu_ptr(&send_signal_work); > + if (irq_work_is_busy(&work->irq_work)) > + return -EBUSY; > + > + work->task = get_task_struct(task); > + work->is_siginfo = true; > + work->info = info; > + work->sig = sig; > + work->type = type; > + work->value = value; > + irq_work_queue(&work->irq_work); > + return 0; > + } > + > + return group_send_sig_info(sig, &info, task, type); > +} > + > +__bpf_kfunc_end_defs(); > + > +BTF_KFUNCS_START(send_signal_kfunc_ids) > +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) > +BTF_KFUNCS_END(send_signal_kfunc_ids) > + > +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { > + .owner = THIS_MODULE, > + .set = &send_signal_kfunc_ids, > +}; > + > +static int __init bpf_send_signal_kfuncs_init(void) > +{ > + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); let's allow it for other program types (at least kprobes, tracepoints, raw_tp, etc, etc)? Is there any problem just allowing it for any program type? > +} > + > +late_initcall(bpf_send_signal_kfuncs_init); > -- > 2.40.1 >
On Mon, Sep 30, 2024 at 2:48 PM Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote: > > On Thu, Sep 26, 2024 at 4:53 AM Puranjay Mohan <puranjay@kernel.org> wrote: > > > > Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- > > 1 file changed, 77 insertions(+), 1 deletion(-) > > > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > > index a582cd25ca876..51b27db1321fc 100644 > > --- a/kernel/trace/bpf_trace.c > > +++ b/kernel/trace/bpf_trace.c > > @@ -802,6 +802,9 @@ struct send_signal_irq_work { > > struct task_struct *task; > > u32 sig; > > enum pid_type type; > > + bool is_siginfo; > > + kernel_siginfo_t info; > > + int value; > > }; > > > > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > > @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) > > struct send_signal_irq_work *work; > > > > work = container_of(entry, struct send_signal_irq_work, irq_work); > > - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > > + if (work->is_siginfo) > > + group_send_sig_info(work->sig, &work->info, work->task, work->type); > > + else > > + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); > > + > > put_task_struct(work->task); > > } > > > > @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) > > * irq works get executed. > > */ > > work->task = get_task_struct(current); > > + work->is_siginfo = false; > > work->sig = sig; > > work->type = type; > > irq_work_queue(&work->irq_work); > > @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, > > + int value) Bikeshedding here a bit, but would bpf_send_signal_task() be a better name for something that accepts task_struct? > > +{ > > + struct send_signal_irq_work *work = NULL; > > + kernel_siginfo_t info; > > + > > + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) > > + return -EINVAL; > > + 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(task))) > > + return -EPERM; > > + > > + 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_int = value; > > It seems like it could be either int sival_int or `void *sival_ptr`, > i.e., it's actually a 64-bit value on 64-bit architectures. > > Can we allow passing a full u64 here and assign it to sival_ptr (with a cast)? Seems like Alexei already suggested that on patch #2, I support the request. > > > + > > + if (irqs_disabled()) { > > + /* Do an early check on signal validity. Otherwise, > > + * the error is lost in deferred irq_work. > > + */ > > + if (unlikely(!valid_signal(sig))) > > + return -EINVAL; > > + > > + work = this_cpu_ptr(&send_signal_work); > > + if (irq_work_is_busy(&work->irq_work)) > > + return -EBUSY; > > + > > + work->task = get_task_struct(task); > > + work->is_siginfo = true; > > + work->info = info; > > + work->sig = sig; > > + work->type = type; > > + work->value = value; > > + irq_work_queue(&work->irq_work); > > + return 0; > > + } > > + > > + return group_send_sig_info(sig, &info, task, type); > > +} > > + > > +__bpf_kfunc_end_defs(); > > + > > +BTF_KFUNCS_START(send_signal_kfunc_ids) > > +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) > > +BTF_KFUNCS_END(send_signal_kfunc_ids) > > + > > +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { > > + .owner = THIS_MODULE, > > + .set = &send_signal_kfunc_ids, > > +}; > > + > > +static int __init bpf_send_signal_kfuncs_init(void) > > +{ > > + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); > > let's allow it for other program types (at least kprobes, tracepoints, > raw_tp, etc, etc)? Is there any problem just allowing it for any > program type? > > > > +} > > + > > +late_initcall(bpf_send_signal_kfuncs_init); > > -- > > 2.40.1 > >
Andrii Nakryiko <andrii.nakryiko@gmail.com> writes: > On Thu, Sep 26, 2024 at 4:53 AM Puranjay Mohan <puranjay@kernel.org> wrote: >> >> Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 77 insertions(+), 1 deletion(-) >> >> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c >> index a582cd25ca876..51b27db1321fc 100644 >> --- a/kernel/trace/bpf_trace.c >> +++ b/kernel/trace/bpf_trace.c >> @@ -802,6 +802,9 @@ struct send_signal_irq_work { >> struct task_struct *task; >> u32 sig; >> enum pid_type type; >> + bool is_siginfo; >> + kernel_siginfo_t info; >> + int value; >> }; >> >> static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); >> @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) >> struct send_signal_irq_work *work; >> >> work = container_of(entry, struct send_signal_irq_work, irq_work); >> - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); >> + if (work->is_siginfo) >> + group_send_sig_info(work->sig, &work->info, work->task, work->type); >> + else >> + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); >> + >> put_task_struct(work->task); >> } >> >> @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) >> * irq works get executed. >> */ >> work->task = get_task_struct(current); >> + work->is_siginfo = false; >> work->sig = sig; >> work->type = type; >> irq_work_queue(&work->irq_work); >> @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, >> + int value) >> +{ >> + struct send_signal_irq_work *work = NULL; >> + kernel_siginfo_t info; >> + >> + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) >> + return -EINVAL; >> + 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(task))) >> + return -EPERM; >> + >> + 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_int = value; > > It seems like it could be either int sival_int or `void *sival_ptr`, > i.e., it's actually a 64-bit value on 64-bit architectures. > > Can we allow passing a full u64 here and assign it to sival_ptr (with a cast)? Yes, I initially thought of allowing the kfunc to take the union itself but turns out unions are not supported, so I will just use a cast to put the value in sival_ptr. >> + >> + if (irqs_disabled()) { >> + /* Do an early check on signal validity. Otherwise, >> + * the error is lost in deferred irq_work. >> + */ >> + if (unlikely(!valid_signal(sig))) >> + return -EINVAL; >> + >> + work = this_cpu_ptr(&send_signal_work); >> + if (irq_work_is_busy(&work->irq_work)) >> + return -EBUSY; >> + >> + work->task = get_task_struct(task); >> + work->is_siginfo = true; >> + work->info = info; >> + work->sig = sig; >> + work->type = type; >> + work->value = value; >> + irq_work_queue(&work->irq_work); >> + return 0; >> + } >> + >> + return group_send_sig_info(sig, &info, task, type); >> +} >> + >> +__bpf_kfunc_end_defs(); >> + >> +BTF_KFUNCS_START(send_signal_kfunc_ids) >> +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) >> +BTF_KFUNCS_END(send_signal_kfunc_ids) >> + >> +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { >> + .owner = THIS_MODULE, >> + .set = &send_signal_kfunc_ids, >> +}; >> + >> +static int __init bpf_send_signal_kfuncs_init(void) >> +{ >> + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); > > let's allow it for other program types (at least kprobes, tracepoints, > raw_tp, etc, etc)? Is there any problem just allowing it for any > program type? I guess we can allow it for all program types. > >> +} >> + >> +late_initcall(bpf_send_signal_kfuncs_init); >> -- >> 2.40.1 >>
Andrii Nakryiko <andrii.nakryiko@gmail.com> writes: > On Mon, Sep 30, 2024 at 2:48 PM Andrii Nakryiko > <andrii.nakryiko@gmail.com> wrote: >> >> On Thu, Sep 26, 2024 at 4:53 AM Puranjay Mohan <puranjay@kernel.org> wrote: >> > >> > Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- >> > 1 file changed, 77 insertions(+), 1 deletion(-) >> > >> > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c >> > index a582cd25ca876..51b27db1321fc 100644 >> > --- a/kernel/trace/bpf_trace.c >> > +++ b/kernel/trace/bpf_trace.c >> > @@ -802,6 +802,9 @@ struct send_signal_irq_work { >> > struct task_struct *task; >> > u32 sig; >> > enum pid_type type; >> > + bool is_siginfo; >> > + kernel_siginfo_t info; >> > + int value; >> > }; >> > >> > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); >> > @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) >> > struct send_signal_irq_work *work; >> > >> > work = container_of(entry, struct send_signal_irq_work, irq_work); >> > - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); >> > + if (work->is_siginfo) >> > + group_send_sig_info(work->sig, &work->info, work->task, work->type); >> > + else >> > + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); >> > + >> > put_task_struct(work->task); >> > } >> > >> > @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) >> > * irq works get executed. >> > */ >> > work->task = get_task_struct(current); >> > + work->is_siginfo = false; >> > work->sig = sig; >> > work->type = type; >> > irq_work_queue(&work->irq_work); >> > @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, >> > + int value) > > Bikeshedding here a bit, but would bpf_send_signal_task() be a better > name for something that accepts task_struct? I agree, will use that name in the next version. >> > +{ >> > + struct send_signal_irq_work *work = NULL; >> > + kernel_siginfo_t info; >> > + >> > + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) >> > + return -EINVAL; >> > + 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(task))) >> > + return -EPERM; >> > + >> > + 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_int = value; >> >> It seems like it could be either int sival_int or `void *sival_ptr`, >> i.e., it's actually a 64-bit value on 64-bit architectures. >> >> Can we allow passing a full u64 here and assign it to sival_ptr (with a cast)? > > Seems like Alexei already suggested that on patch #2, I support the request. > >> >> > + >> > + if (irqs_disabled()) { >> > + /* Do an early check on signal validity. Otherwise, >> > + * the error is lost in deferred irq_work. >> > + */ >> > + if (unlikely(!valid_signal(sig))) >> > + return -EINVAL; >> > + >> > + work = this_cpu_ptr(&send_signal_work); >> > + if (irq_work_is_busy(&work->irq_work)) >> > + return -EBUSY; >> > + >> > + work->task = get_task_struct(task); >> > + work->is_siginfo = true; >> > + work->info = info; >> > + work->sig = sig; >> > + work->type = type; >> > + work->value = value; >> > + irq_work_queue(&work->irq_work); >> > + return 0; >> > + } >> > + >> > + return group_send_sig_info(sig, &info, task, type); >> > +} >> > + >> > +__bpf_kfunc_end_defs(); >> > + >> > +BTF_KFUNCS_START(send_signal_kfunc_ids) >> > +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) >> > +BTF_KFUNCS_END(send_signal_kfunc_ids) >> > + >> > +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { >> > + .owner = THIS_MODULE, >> > + .set = &send_signal_kfunc_ids, >> > +}; >> > + >> > +static int __init bpf_send_signal_kfuncs_init(void) >> > +{ >> > + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); >> >> let's allow it for other program types (at least kprobes, tracepoints, >> raw_tp, etc, etc)? Is there any problem just allowing it for any >> program type? >> >> >> > +} >> > + >> > +late_initcall(bpf_send_signal_kfuncs_init); >> > -- >> > 2.40.1 >> >
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index a582cd25ca876..51b27db1321fc 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -802,6 +802,9 @@ struct send_signal_irq_work { struct task_struct *task; u32 sig; enum pid_type type; + bool is_siginfo; + kernel_siginfo_t info; + int value; }; static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); @@ -811,7 +814,11 @@ static void do_bpf_send_signal(struct irq_work *entry) struct send_signal_irq_work *work; work = container_of(entry, struct send_signal_irq_work, irq_work); - group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); + if (work->is_siginfo) + group_send_sig_info(work->sig, &work->info, work->task, work->type); + else + group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); + put_task_struct(work->task); } @@ -848,6 +855,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) * irq works get executed. */ work->task = get_task_struct(current); + work->is_siginfo = false; work->sig = sig; work->type = type; irq_work_queue(&work->irq_work); @@ -3484,3 +3492,71 @@ 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_remote(struct task_struct *task, int sig, enum pid_type type, + int value) +{ + struct send_signal_irq_work *work = NULL; + kernel_siginfo_t info; + + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) + return -EINVAL; + 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(task))) + return -EPERM; + + 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_int = value; + + if (irqs_disabled()) { + /* Do an early check on signal validity. Otherwise, + * the error is lost in deferred irq_work. + */ + if (unlikely(!valid_signal(sig))) + return -EINVAL; + + work = this_cpu_ptr(&send_signal_work); + if (irq_work_is_busy(&work->irq_work)) + return -EBUSY; + + work->task = get_task_struct(task); + work->is_siginfo = true; + work->info = info; + work->sig = sig; + work->type = type; + work->value = value; + irq_work_queue(&work->irq_work); + return 0; + } + + return group_send_sig_info(sig, &info, task, type); +} + +__bpf_kfunc_end_defs(); + +BTF_KFUNCS_START(send_signal_kfunc_ids) +BTF_ID_FLAGS(func, bpf_send_signal_remote, KF_TRUSTED_ARGS) +BTF_KFUNCS_END(send_signal_kfunc_ids) + +static const struct btf_kfunc_id_set bpf_send_signal_kfunc_set = { + .owner = THIS_MODULE, + .set = &send_signal_kfunc_ids, +}; + +static int __init bpf_send_signal_kfuncs_init(void) +{ + return register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_send_signal_kfunc_set); +} + +late_initcall(bpf_send_signal_kfuncs_init);
Implement bpf_send_signal_remote 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/trace/bpf_trace.c | 78 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-)