diff mbox series

[bpf-next,v3,1/2] bpf: implement bpf_send_signal_task() kfunc

Message ID 20241007103426.128923-2-puranjay@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Implement mechanism to signal other threads | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 6 this patch: 7
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 8 maintainers not CCed: mhiramat@kernel.org mathieu.desnoyers@efficios.com haoluo@google.com linux-trace-kernel@vger.kernel.org sdf@fomichev.me mattbobrowski@google.com rostedt@goodmis.org jolsa@kernel.org
netdev/build_clang success Errors and warnings before: 6 this patch: 6
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 75 this patch: 77
netdev/checkpatch warning WARNING: line length of 84 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 10 this patch: 10
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / veristat

Commit Message

Puranjay Mohan Oct. 7, 2024, 10:34 a.m. UTC
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 | 54 ++++++++++++++++++++++++++++++++++------
 2 files changed, 47 insertions(+), 8 deletions(-)

Comments

kernel test robot Oct. 8, 2024, 1:41 a.m. UTC | #1
Hi Puranjay,

kernel test robot noticed the following build warnings:

[auto build test WARNING on bpf-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Puranjay-Mohan/bpf-implement-bpf_send_signal_task-kfunc/20241007-183648
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20241007103426.128923-2-puranjay%40kernel.org
patch subject: [PATCH bpf-next v3 1/2] bpf: implement bpf_send_signal_task() kfunc
config: i386-randconfig-141-20241008 (https://download.01.org/0day-ci/archive/20241008/202410080907.DFxFxfor-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241008/202410080907.DFxFxfor-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410080907.DFxFxfor-lkp@intel.com/

All warnings (new ones prefixed by >>):

   kernel/trace/bpf_trace.c: In function 'bpf_send_signal_common':
>> kernel/trace/bpf_trace.c:839:43: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     839 |                 info.si_value.sival_ptr = (void *)value;
         |                                           ^


vim +839 kernel/trace/bpf_trace.c

   822	
   823	static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *tsk, u64 value)
   824	{
   825		struct send_signal_irq_work *work = NULL;
   826		kernel_siginfo_t info;
   827		bool has_siginfo = false;
   828	
   829		if (!tsk) {
   830			tsk = current;
   831		} else {
   832			has_siginfo = true;
   833			clear_siginfo(&info);
   834			info.si_signo = sig;
   835			info.si_errno = 0;
   836			info.si_code = SI_KERNEL;
   837			info.si_pid = 0;
   838			info.si_uid = 0;
 > 839			info.si_value.sival_ptr = (void *)value;
   840		}
   841	
   842		/* Similar to bpf_probe_write_user, task needs to be
   843		 * in a sound condition and kernel memory access be
   844		 * permitted in order to send signal to the current
   845		 * task.
   846		 */
   847		if (unlikely(tsk->flags & (PF_KTHREAD | PF_EXITING)))
   848			return -EPERM;
   849		if (unlikely(!nmi_uaccess_okay()))
   850			return -EPERM;
   851		/* Task should not be pid=1 to avoid kernel panic. */
   852		if (unlikely(is_global_init(tsk)))
   853			return -EPERM;
   854	
   855		if (irqs_disabled()) {
   856			/* Do an early check on signal validity. Otherwise,
   857			 * the error is lost in deferred irq_work.
   858			 */
   859			if (unlikely(!valid_signal(sig)))
   860				return -EINVAL;
   861	
   862			work = this_cpu_ptr(&send_signal_work);
   863			if (irq_work_is_busy(&work->irq_work))
   864				return -EBUSY;
   865	
   866			/* Add the current task, which is the target of sending signal,
   867			 * to the irq_work. The current task may change when queued
   868			 * irq works get executed.
   869			 */
   870			work->task = get_task_struct(tsk);
   871			work->has_siginfo = has_siginfo;
   872			work->info = info;
   873			work->sig = sig;
   874			work->type = type;
   875			irq_work_queue(&work->irq_work);
   876			return 0;
   877		}
   878	
   879		if (has_siginfo)
   880			return group_send_sig_info(sig, &info, tsk, type);
   881	
   882		return group_send_sig_info(sig, SEND_SIG_PRIV, tsk, type);
   883	}
   884
Andrii Nakryiko Oct. 8, 2024, 4:24 a.m. UTC | #2
On Mon, Oct 7, 2024 at 3:34 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 | 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
>
kernel test robot Oct. 8, 2024, 6:18 a.m. UTC | #3
Hi Puranjay,

kernel test robot noticed the following build warnings:

[auto build test WARNING on bpf-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Puranjay-Mohan/bpf-implement-bpf_send_signal_task-kfunc/20241007-183648
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20241007103426.128923-2-puranjay%40kernel.org
patch subject: [PATCH bpf-next v3 1/2] bpf: implement bpf_send_signal_task() kfunc
config: x86_64-randconfig-121-20241008 (https://download.01.org/0day-ci/archive/20241008/202410081401.fYRMhL8t-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241008/202410081401.fYRMhL8t-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202410081401.fYRMhL8t-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> kernel/trace/bpf_trace.c:839:41: sparse: sparse: incorrect type in assignment (different address spaces) @@     expected void [noderef] __user *[addressable] [assigned] [usertype] sival_ptr @@     got void * @@
   kernel/trace/bpf_trace.c:839:41: sparse:     expected void [noderef] __user *[addressable] [assigned] [usertype] sival_ptr
   kernel/trace/bpf_trace.c:839:41: sparse:     got void *
   kernel/trace/bpf_trace.c: note: in included file (through include/linux/smp.h, include/linux/lockdep.h, include/linux/spinlock.h, ...):
   include/linux/list.h:83:21: sparse: sparse: self-comparison always evaluates to true
   kernel/trace/bpf_trace.c: note: in included file (through include/linux/rbtree.h, include/linux/mm_types.h, include/linux/mmzone.h, ...):
   include/linux/rcupdate.h:880:25: sparse: sparse: context imbalance in 'uprobe_prog_run' - unexpected unlock

vim +839 kernel/trace/bpf_trace.c

   822	
   823	static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *tsk, u64 value)
   824	{
   825		struct send_signal_irq_work *work = NULL;
   826		kernel_siginfo_t info;
   827		bool has_siginfo = false;
   828	
   829		if (!tsk) {
   830			tsk = current;
   831		} else {
   832			has_siginfo = true;
   833			clear_siginfo(&info);
   834			info.si_signo = sig;
   835			info.si_errno = 0;
   836			info.si_code = SI_KERNEL;
   837			info.si_pid = 0;
   838			info.si_uid = 0;
 > 839			info.si_value.sival_ptr = (void *)value;
   840		}
   841	
   842		/* Similar to bpf_probe_write_user, task needs to be
   843		 * in a sound condition and kernel memory access be
   844		 * permitted in order to send signal to the current
   845		 * task.
   846		 */
   847		if (unlikely(tsk->flags & (PF_KTHREAD | PF_EXITING)))
   848			return -EPERM;
   849		if (unlikely(!nmi_uaccess_okay()))
   850			return -EPERM;
   851		/* Task should not be pid=1 to avoid kernel panic. */
   852		if (unlikely(is_global_init(tsk)))
   853			return -EPERM;
   854	
   855		if (irqs_disabled()) {
   856			/* Do an early check on signal validity. Otherwise,
   857			 * the error is lost in deferred irq_work.
   858			 */
   859			if (unlikely(!valid_signal(sig)))
   860				return -EINVAL;
   861	
   862			work = this_cpu_ptr(&send_signal_work);
   863			if (irq_work_is_busy(&work->irq_work))
   864				return -EBUSY;
   865	
   866			/* Add the current task, which is the target of sending signal,
   867			 * to the irq_work. The current task may change when queued
   868			 * irq works get executed.
   869			 */
   870			work->task = get_task_struct(tsk);
   871			work->has_siginfo = has_siginfo;
   872			work->info = info;
   873			work->sig = sig;
   874			work->type = type;
   875			irq_work_queue(&work->irq_work);
   876			return 0;
   877		}
   878	
   879		if (has_siginfo)
   880			return group_send_sig_info(sig, &info, tsk, type);
   881	
   882		return group_send_sig_info(sig, SEND_SIG_PRIV, tsk, type);
   883	}
   884
Puranjay Mohan Oct. 8, 2024, 10:17 a.m. UTC | #4
On Tue, Oct 8, 2024 at 6:24 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Mon, Oct 7, 2024 at 3:34 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 | 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

I tried reading the man pages of sigqueue and it allows using all signals.

To test it, I sent SIGCHLD to a process with si_value.sival_ptr using
sigqueue() and it worked as expected.

It shouldn't affect us as we are not populating all fields of
__sifields anyway. For example if you send SIGCHLD using
this new kfunc, there is no way to set _utime and _stime or even _pid
and _uid, here only the signal number
and this u64 value is relevant.

I will make all the other suggested changes in the next version.


Thanks,
Puranjay
Andrii Nakryiko Oct. 8, 2024, 6:38 p.m. UTC | #5
On Tue, Oct 8, 2024 at 3:17 AM Puranjay Mohan <puranjay12@gmail.com> wrote:
>
> On Tue, Oct 8, 2024 at 6:24 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Mon, Oct 7, 2024 at 3:34 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 | 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
>
> I tried reading the man pages of sigqueue and it allows using all signals.
>
> To test it, I sent SIGCHLD to a process with si_value.sival_ptr using
> sigqueue() and it worked as expected.
>
> It shouldn't affect us as we are not populating all fields of
> __sifields anyway. For example if you send SIGCHLD using

But __sifields is *a union*, where there is a separate struct for
kill, separate for timer signals, separate for POSIX.1b signals, and
yet another struct (inside the union, so they are all mutually
exclusive) for SIGCHLD. Then another group for SIGILL, SIGFPE,
SIGSEGV, SIGBUS, SIGTRAP, SIGEMT. SIGPOLL is separate, and SIGSYS is
separate still.

So I'm confused. Sure, C will allow you to set _rt._sigval fields, but
the question is which part of that union is the kernel using for
different signals? Or are you saying that whatever is sent with
group_send_sig_info() will use __sifields._rt part of the union,
regardless of the actual signal?

> this new kfunc, there is no way to set _utime and _stime or even _pid
> and _uid, here only the signal number
> and this u64 value is relevant.
>
> I will make all the other suggested changes in the next version.
>
>
> Thanks,
> Puranjay
diff mbox series

Patch

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;
 };
 
 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);
 	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)
 {
 	struct send_signal_irq_work *work = NULL;
+	kernel_siginfo_t info;
+	bool has_siginfo = false;
+
+	if (!tsk) {
+		tsk = current;
+	} else {
+		has_siginfo = true;
+		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;
+	}
 
 	/* 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;
 		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);
 }
 
 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();