* [PATCH bpf-next v4 0/2] Implement mechanism to signal other threads @ 2024-10-08 11:49 Puranjay Mohan 2024-10-08 11:49 ` [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc Puranjay Mohan 2024-10-08 11:49 ` [PATCH bpf-next v4 2/2] selftests/bpf: Augment send_signal test with remote signaling Puranjay Mohan 0 siblings, 2 replies; 6+ messages in thread From: Puranjay Mohan @ 2024-10-08 11:49 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, linux-kernel, puranjay12 This set implements a kfunc called bpf_send_signal_task() that is similar to sigqueue() as it can send a signal along with a cookie to a thread or thread group. The send_signal selftest has been updated to also test this new kfunc under all contexts. Changes in v4: v3: https://lore.kernel.org/all/20241007103426.128923-1-puranjay@kernel.org/ - Fix the selftest to make it work for big-endian archs. - Fix a build warning on 32-bit archs. - Some style changes and code refactors suggested by Andrii Changes in v3: v2: https://lore.kernel.org/all/20240926115328.105634-1-puranjay@kernel.org/ - make the cookie u64 instead of int. - re use code from bpf_send_signal_common Changes in v2: v1: https://lore.kernel.org/bpf/20240724113944.75977-1-puranjay@kernel.org/ - Convert to a kfunc - Add mechanism to send a cookie with the signal. Puranjay Mohan (2): bpf: implement bpf_send_signal_task() kfunc selftests/bpf: Augment send_signal test with remote signaling kernel/bpf/helpers.c | 1 + kernel/trace/bpf_trace.c | 52 +++++-- .../selftests/bpf/prog_tests/send_signal.c | 133 +++++++++++++----- .../bpf/progs/test_send_signal_kern.c | 35 ++++- 4 files changed, 175 insertions(+), 46 deletions(-) -- 2.40.1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc 2024-10-08 11:49 [PATCH bpf-next v4 0/2] Implement mechanism to signal other threads Puranjay Mohan @ 2024-10-08 11:49 ` Puranjay Mohan 2024-10-08 18:19 ` Andrii Nakryiko 2024-10-08 11:49 ` [PATCH bpf-next v4 2/2] selftests/bpf: Augment send_signal test with remote signaling Puranjay Mohan 1 sibling, 1 reply; 6+ messages in thread From: Puranjay Mohan @ 2024-10-08 11:49 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, linux-kernel, puranjay12 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 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc 2024-10-08 11:49 ` [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc Puranjay Mohan @ 2024-10-08 18:19 ` Andrii Nakryiko 2024-10-15 9:56 ` Puranjay Mohan 0 siblings, 1 reply; 6+ messages in thread From: Andrii Nakryiko @ 2024-10-08 18:19 UTC (permalink / raw) To: Puranjay Mohan Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, linux-kernel, puranjay12 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 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc 2024-10-08 18:19 ` Andrii Nakryiko @ 2024-10-15 9:56 ` Puranjay Mohan 2024-10-15 18:20 ` Alexei Starovoitov 0 siblings, 1 reply; 6+ messages in thread From: Puranjay Mohan @ 2024-10-15 9:56 UTC (permalink / raw) To: Andrii Nakryiko Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, linux-kernel [-- Attachment #1: Type: text/plain, Size: 4933 bytes --] 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 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 255 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc 2024-10-15 9:56 ` Puranjay Mohan @ 2024-10-15 18:20 ` Alexei Starovoitov 0 siblings, 0 replies; 6+ messages in thread From: Alexei Starovoitov @ 2024-10-15 18:20 UTC (permalink / raw) To: Puranjay Mohan Cc: Andrii Nakryiko, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, LKML 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. ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v4 2/2] selftests/bpf: Augment send_signal test with remote signaling 2024-10-08 11:49 [PATCH bpf-next v4 0/2] Implement mechanism to signal other threads Puranjay Mohan 2024-10-08 11:49 ` [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc Puranjay Mohan @ 2024-10-08 11:49 ` Puranjay Mohan 1 sibling, 0 replies; 6+ messages in thread From: Puranjay Mohan @ 2024-10-08 11:49 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend, KP Singh, bpf, linux-kernel, puranjay12 Add testcases to test bpf_send_signal_task(). In these new test cases, the main process triggers the BPF program and the forked process receives the signals. The target process's signal handler receives a cookie from the bpf program. Signed-off-by: Puranjay Mohan <puranjay@kernel.org> --- .../selftests/bpf/prog_tests/send_signal.c | 133 +++++++++++++----- .../bpf/progs/test_send_signal_kern.c | 35 ++++- 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c index 6cc69900b3106..1aed94ec14efb 100644 --- a/tools/testing/selftests/bpf/prog_tests/send_signal.c +++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c @@ -8,17 +8,25 @@ static int sigusr1_received; static void sigusr1_handler(int signum) { - sigusr1_received = 1; + sigusr1_received = 8; +} + +static void sigusr1_siginfo_handler(int s, siginfo_t *i, void *v) +{ + sigusr1_received = (int)(long long)i->si_value.sival_ptr; } static void test_send_signal_common(struct perf_event_attr *attr, - bool signal_thread) + bool signal_thread, bool remote) { struct test_send_signal_kern *skel; + struct sigaction sa; int pipe_c2p[2], pipe_p2c[2]; int err = -1, pmu_fd = -1; + volatile int j = 0; char buf[256]; pid_t pid; + int old_prio; if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p")) return; @@ -39,11 +47,14 @@ static void test_send_signal_common(struct perf_event_attr *attr, } if (pid == 0) { - int old_prio; - volatile int j = 0; - /* install signal handler and notify parent */ - ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); + if (remote) { + sa.sa_sigaction = sigusr1_siginfo_handler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + ASSERT_NEQ(sigaction(SIGUSR1, &sa, NULL), -1, "sigaction"); + } else { + ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); + } close(pipe_c2p[0]); /* close read */ close(pipe_p2c[1]); /* close write */ @@ -52,10 +63,12 @@ static void test_send_signal_common(struct perf_event_attr *attr, * that if an interrupt happens, the underlying task * is this process. */ - errno = 0; - old_prio = getpriority(PRIO_PROCESS, 0); - ASSERT_OK(errno, "getpriority"); - ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); + if (!remote) { + errno = 0; + old_prio = getpriority(PRIO_PROCESS, 0); + ASSERT_OK(errno, "getpriority"); + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); + } /* notify parent signal handler is installed */ ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); @@ -66,20 +79,25 @@ static void test_send_signal_common(struct perf_event_attr *attr, /* wait a little for signal handler */ for (int i = 0; i < 1000000000 && !sigusr1_received; i++) { j /= i + j + 1; - if (!attr) - /* trigger the nanosleep tracepoint program. */ - usleep(1); + if (remote) + sleep(1); + else + if (!attr) + /* trigger the nanosleep tracepoint program. */ + usleep(1); } - buf[0] = sigusr1_received ? '2' : '0'; - ASSERT_EQ(sigusr1_received, 1, "sigusr1_received"); + buf[0] = sigusr1_received; + + ASSERT_EQ(sigusr1_received, 8, "sigusr1_received"); ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); /* wait for parent notification and exit */ ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); /* restore the old priority */ - ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); + if (!remote) + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); close(pipe_c2p[1]); close(pipe_p2c[0]); @@ -93,6 +111,17 @@ static void test_send_signal_common(struct perf_event_attr *attr, if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) goto skel_open_load_failure; + /* boost with a high priority so we got a higher chance + * that if an interrupt happens, the underlying task + * is this process. + */ + if (remote) { + errno = 0; + old_prio = getpriority(PRIO_PROCESS, 0); + ASSERT_OK(errno, "getpriority"); + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); + } + if (!attr) { err = test_send_signal_kern__attach(skel); if (!ASSERT_OK(err, "skel_attach")) { @@ -100,8 +129,12 @@ static void test_send_signal_common(struct perf_event_attr *attr, goto destroy_skel; } } else { - pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, - -1 /* group id */, 0 /* flags */); + if (!remote) + pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, + -1 /* group id */, 0 /* flags */); + else + pmu_fd = syscall(__NR_perf_event_open, attr, getpid(), -1 /* cpu */, + -1 /* group id */, 0 /* flags */); if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) { err = -1; goto destroy_skel; @@ -119,11 +152,30 @@ static void test_send_signal_common(struct perf_event_attr *attr, /* trigger the bpf send_signal */ skel->bss->signal_thread = signal_thread; skel->bss->sig = SIGUSR1; - skel->bss->pid = pid; + if (!remote) { + skel->bss->target_pid = 0; + skel->bss->pid = pid; + } else { + skel->bss->target_pid = pid; + skel->bss->pid = getpid(); + } /* notify child that bpf program can send_signal now */ ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); + /* For the remote test, the BPF program is triggered from this + * process but the other process/thread is signaled. + */ + if (remote) { + if (!attr) { + for (int i = 0; i < 10; i++) + usleep(1); + } else { + for (int i = 0; i < 100000000; i++) + j /= i + 1; + } + } + /* wait for result */ err = read(pipe_c2p[0], buf, 1); if (!ASSERT_GE(err, 0, "reading pipe")) @@ -133,7 +185,7 @@ static void test_send_signal_common(struct perf_event_attr *attr, goto disable_pmu; } - ASSERT_EQ(buf[0], '2', "incorrect result"); + ASSERT_EQ(buf[0], 8, "incorrect result"); /* notify child safe to exit */ ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); @@ -142,18 +194,21 @@ static void test_send_signal_common(struct perf_event_attr *attr, close(pmu_fd); destroy_skel: test_send_signal_kern__destroy(skel); + /* restore the old priority */ + if (remote) + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); skel_open_load_failure: close(pipe_c2p[0]); close(pipe_p2c[1]); wait(NULL); } -static void test_send_signal_tracepoint(bool signal_thread) +static void test_send_signal_tracepoint(bool signal_thread, bool remote) { - test_send_signal_common(NULL, signal_thread); + test_send_signal_common(NULL, signal_thread, remote); } -static void test_send_signal_perf(bool signal_thread) +static void test_send_signal_perf(bool signal_thread, bool remote) { struct perf_event_attr attr = { .freq = 1, @@ -162,10 +217,10 @@ static void test_send_signal_perf(bool signal_thread) .config = PERF_COUNT_SW_CPU_CLOCK, }; - test_send_signal_common(&attr, signal_thread); + test_send_signal_common(&attr, signal_thread, remote); } -static void test_send_signal_nmi(bool signal_thread) +static void test_send_signal_nmi(bool signal_thread, bool remote) { struct perf_event_attr attr = { .sample_period = 1, @@ -191,21 +246,35 @@ static void test_send_signal_nmi(bool signal_thread) close(pmu_fd); } - test_send_signal_common(&attr, signal_thread); + test_send_signal_common(&attr, signal_thread, remote); } void test_send_signal(void) { if (test__start_subtest("send_signal_tracepoint")) - test_send_signal_tracepoint(false); + test_send_signal_tracepoint(false, false); if (test__start_subtest("send_signal_perf")) - test_send_signal_perf(false); + test_send_signal_perf(false, false); if (test__start_subtest("send_signal_nmi")) - test_send_signal_nmi(false); + test_send_signal_nmi(false, false); if (test__start_subtest("send_signal_tracepoint_thread")) - test_send_signal_tracepoint(true); + test_send_signal_tracepoint(true, false); if (test__start_subtest("send_signal_perf_thread")) - test_send_signal_perf(true); + test_send_signal_perf(true, false); if (test__start_subtest("send_signal_nmi_thread")) - test_send_signal_nmi(true); + test_send_signal_nmi(true, false); + + /* Signal remote thread and thread group */ + if (test__start_subtest("send_signal_tracepoint_remote")) + test_send_signal_tracepoint(false, true); + if (test__start_subtest("send_signal_perf_remote")) + test_send_signal_perf(false, true); + if (test__start_subtest("send_signal_nmi_remote")) + test_send_signal_nmi(false, true); + if (test__start_subtest("send_signal_tracepoint_thread_remote")) + test_send_signal_tracepoint(true, true); + if (test__start_subtest("send_signal_perf_thread_remote")) + test_send_signal_perf(true, true); + if (test__start_subtest("send_signal_nmi_thread_remote")) + test_send_signal_nmi(true, true); } diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c index 92354cd720440..176a355e30624 100644 --- a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c +++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c @@ -1,27 +1,50 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Facebook -#include <linux/bpf.h> +#include <vmlinux.h> #include <linux/version.h> #include <bpf/bpf_helpers.h> -__u32 sig = 0, pid = 0, status = 0, signal_thread = 0; +struct task_struct *bpf_task_from_pid(int pid) __ksym; +void bpf_task_release(struct task_struct *p) __ksym; +int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, u64 value) __ksym; + +__u32 sig = 0, pid = 0, status = 0, signal_thread = 0, target_pid = 0; static __always_inline int bpf_send_signal_test(void *ctx) { + struct task_struct *target_task = NULL; int ret; + u64 value; if (status != 0 || pid == 0) return 0; if ((bpf_get_current_pid_tgid() >> 32) == pid) { - if (signal_thread) - ret = bpf_send_signal_thread(sig); - else - ret = bpf_send_signal(sig); + if (target_pid) { + target_task = bpf_task_from_pid(target_pid); + if (!target_task) + return 0; + value = 8; + } + + if (signal_thread) { + if (target_pid) + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_PID, value); + else + ret = bpf_send_signal_thread(sig); + } else { + if (target_pid) + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_TGID, value); + else + ret = bpf_send_signal(sig); + } if (ret == 0) status = 1; } + if (target_task) + bpf_task_release(target_task); + return 0; } -- 2.40.1 ^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-10-15 18:20 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-10-08 11:49 [PATCH bpf-next v4 0/2] Implement mechanism to signal other threads Puranjay Mohan 2024-10-08 11:49 ` [PATCH bpf-next v4 1/2] bpf: implement bpf_send_signal_task() kfunc Puranjay Mohan 2024-10-08 18:19 ` Andrii Nakryiko 2024-10-15 9:56 ` Puranjay Mohan 2024-10-15 18:20 ` Alexei Starovoitov 2024-10-08 11:49 ` [PATCH bpf-next v4 2/2] selftests/bpf: Augment send_signal test with remote signaling Puranjay Mohan
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.