On Tue, Oct 15, 2024 at 2:57 AM Puranjay Mohan <puranjay@xxxxxxxxxx> wrote: > > Andrii Nakryiko <andrii.nakryiko@xxxxxxxxx> writes: > > > On Tue, Oct 8, 2024 at 4:49 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 | 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.