On Mon, Oct 7, 2024 at 3:34 AM Puranjay Mohan <puranjay@xxxxxxxxxx> 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@xxxxxxxxxx> > --- > kernel/bpf/helpers.c | 1 + > kernel/trace/bpf_trace.c | 54 ++++++++++++++++++++++++++++++++++------ > 2 files changed, 47 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..ae8c9fa8b04d1 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; > + kernel_siginfo_t info; group_send_sig_info() refers to this as `struct kernel_siginfo`, let's use that and avoid unnecessary typedefs > }; > > static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); > @@ -811,25 +813,43 @@ 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->has_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); There is lots of duplication while the only difference is between providing SEND_SIG_PRIV and our own &work->info. So maybe let's just have something like struct kernel_siginfo *siginfo; 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 *tsk, u64 value) task? why tsk? > { > struct send_signal_irq_work *work = NULL; > + kernel_siginfo_t info; > + bool has_siginfo = false; > + > + if (!tsk) { > + tsk = current; > + } else { > + has_siginfo = true; nit: I find it less confusing for cases like with has_siginfo here, for the variable to be explicitly assigned in both branches, instead of defaulting to false and then reassigned in one of the branches > + 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 *)value; > + } kernel test bot complains that this should probably be (void *)(unsigned long)value (which will truncate on 32-bit archtes, but oh well) but can you please double check that it's ok to set info.si_value.sival_ptr for any signal? Because si_value.sival_ptr is actually defined inside __sifields._rt._sigval, which clearly would conflict with _kill, _timer, _sigchld and other groups of signals. so I suspect we'd need to have a list of signals that are OK accepting this extra u64 value, and reject it otherwise (instead of silently corrupting data inside __sifields pw-bot: cr > > /* 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(tsk->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(tsk))) > return -EPERM; > > if (irqs_disabled()) { > @@ -847,19 +867,24 @@ 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(tsk); > + work->has_siginfo = has_siginfo; > + work->info = info; if you are using clear_siginfo(), you probably should use copy_siginfo() here? > 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); > + if (has_siginfo) > + return group_send_sig_info(sig, &info, tsk, type); > + > + return group_send_sig_info(sig, SEND_SIG_PRIV, tsk, type); Similarly to what I mentioned at the very top, the only difference is a pointer to struct kernel_siginfo, so make it explicit? struct kernel_siginfo *siginfo; siginfo = task == current ? SEND_SIG_PRIV : &info; ? > } > > 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 +896,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 +3509,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 >