[PATCH bpf-next 1/1] bpf: Add bpf_copy_from_user_remote to read a process VM given its PID.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add a new BPF helper to read the VM of a process identified by PID.
Whilst PIDs are ambiguous without a namespace, many traditional
observability tools, like profilers and debuggers, accept a PID to
attach to a running process. The new helper proposed by this patch
is aimed at providing the capability of reading a remote process VM
to similar tools.

Signed-off-by: Gabriele N. Tornetta <phoenix1987@xxxxxxxxx>
---
 include/linux/bpf.h                           |  1 +
 include/uapi/linux/bpf.h                      |  9 +++++++
 kernel/bpf/helpers.c                          | 26 +++++++++++++++++++
 kernel/trace/bpf_trace.c                      |  2 ++
 scripts/bpf_doc.py                            |  1 +
 tools/include/uapi/linux/bpf.h                |  9 +++++++
 .../selftests/bpf/prog_tests/test_lsm.c       |  7 +++++
 tools/testing/selftests/bpf/progs/lsm.c       | 18 +++++++++++++
 8 files changed, 73 insertions(+)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 6e947cd91152..96efa8912bbd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2222,6 +2222,7 @@ extern const struct bpf_func_proto bpf_kallsyms_lookup_name_proto;
 extern const struct bpf_func_proto bpf_find_vma_proto;
 extern const struct bpf_func_proto bpf_loop_proto;
 extern const struct bpf_func_proto bpf_strncmp_proto;
+extern const struct bpf_func_proto bpf_copy_from_user_remote_proto;
 
 const struct bpf_func_proto *tracing_prog_func_proto(
   enum bpf_func_id func_id, const struct bpf_prog *prog);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b0383d371b9a..167ec1bc7248 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5018,6 +5018,14 @@ union bpf_attr {
  *
  *	Return
  *		The number of arguments of the traced function.
+ *
+ * long bpf_copy_from_user_remote(void *dst, u32 size, pid_t pid, const void *user_ptr)
+ *	Description
+ *		Read *size* bytes from user space address *user_ptr* of the prodess
+ *		*pid* and store the data in *dst*. This is essentially a wrapper of
+ *		**access_process_vm**\ ().
+ *	Return
+ *		The number of bytes read on success, or a negative error in case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5206,6 +5214,7 @@ union bpf_attr {
 	FN(get_func_arg),		\
 	FN(get_func_ret),		\
 	FN(get_func_arg_cnt),		\
+	FN(copy_from_user_remote),	\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 01cfdf40c838..c055eec77466 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -16,6 +16,7 @@
 #include <linux/pid_namespace.h>
 #include <linux/proc_ns.h>
 #include <linux/security.h>
+#include <linux/mm.h>
 
 #include "../../lib/kstrtox.h"
 
@@ -671,6 +672,31 @@ const struct bpf_func_proto bpf_copy_from_user_proto = {
 	.arg3_type	= ARG_ANYTHING,
 };
 
+BPF_CALL_4(bpf_copy_from_user_remote, void *, dst, u32, size,
+	   pid_t, pid, const void __user *, user_ptr)
+{
+	struct task_struct *task;
+
+	if (unlikely(size == 0))
+		return 0;
+
+	task = find_get_task_by_vpid(pid);
+	if (!task)
+		return -ESRCH;
+
+	return access_process_vm(task, (unsigned long)user_ptr, dst, size, 0);
+}
+
+const struct bpf_func_proto bpf_copy_from_user_remote_proto = {
+	.func		= bpf_copy_from_user_remote,
+	.gpl_only	= false,
+	.ret_type	= RET_INTEGER,
+	.arg1_type	= ARG_PTR_TO_UNINIT_MEM,
+	.arg2_type	= ARG_CONST_SIZE_OR_ZERO,
+	.arg3_type	= ARG_ANYTHING,
+	.arg4_type	= ARG_ANYTHING,
+};
+
 BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
 {
 	if (cpu >= nr_cpu_ids)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 21aa30644219..b30cd5e6d703 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1257,6 +1257,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return &bpf_find_vma_proto;
 	case BPF_FUNC_trace_vprintk:
 		return bpf_get_trace_vprintk_proto();
+	case BPF_FUNC_copy_from_user_remote:
+		return prog->aux->sleepable ? &bpf_copy_from_user_remote_proto : NULL;
 	default:
 		return bpf_base_func_proto(func_id);
 	}
diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index a6403ddf5de7..bd092f1692e2 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -614,6 +614,7 @@ class PrinterHelpers(Printer):
             'const struct sk_buff': 'const struct __sk_buff',
             'struct sk_msg_buff': 'struct sk_msg_md',
             'struct xdp_buff': 'struct xdp_md',
+            "pid_t": "int",
     }
     # Helpers overloaded for different context types.
     overloaded_helpers = [
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index b0383d371b9a..167ec1bc7248 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5018,6 +5018,14 @@ union bpf_attr {
  *
  *	Return
  *		The number of arguments of the traced function.
+ *
+ * long bpf_copy_from_user_remote(void *dst, u32 size, pid_t pid, const void *user_ptr)
+ *	Description
+ *		Read *size* bytes from user space address *user_ptr* of the prodess
+ *		*pid* and store the data in *dst*. This is essentially a wrapper of
+ *		**access_process_vm**\ ().
+ *	Return
+ *		The number of bytes read on success, or a negative error in case of failure.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5206,6 +5214,7 @@ union bpf_attr {
 	FN(get_func_arg),		\
 	FN(get_func_ret),		\
 	FN(get_func_arg_cnt),		\
+	FN(copy_from_user_remote),	\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/tools/testing/selftests/bpf/prog_tests/test_lsm.c b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
index 244c01125126..71014141c675 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_lsm.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_lsm.c
@@ -56,6 +56,7 @@ static int test_lsm(struct lsm *skel)
 	struct bpf_link *link;
 	int buf = 1234;
 	int err;
+	int fd;
 
 	err = lsm__attach(skel);
 	if (!ASSERT_OK(err, "attach"))
@@ -86,6 +87,12 @@ static int test_lsm(struct lsm *skel)
 
 	ASSERT_EQ(skel->bss->copy_test, 3, "copy_test");
 
+	fd = syscall(__NR_open, "/dev/null", syscall(__NR_getpid));
+	if (fd >= 0)
+		syscall(__NR_close, fd);
+
+	ASSERT_EQ(skel->bss->copy_remote_test, 1, "copy_remote_test");
+
 	lsm__detach(skel);
 
 	skel->bss->copy_test = 0;
diff --git a/tools/testing/selftests/bpf/progs/lsm.c b/tools/testing/selftests/bpf/progs/lsm.c
index 33694ef8acfa..469ff988b0e9 100644
--- a/tools/testing/selftests/bpf/progs/lsm.c
+++ b/tools/testing/selftests/bpf/progs/lsm.c
@@ -177,3 +177,21 @@ int BPF_PROG(test_sys_setdomainname, struct pt_regs *regs)
 		copy_test++;
 	return 0;
 }
+
+int copy_remote_test = 0;
+
+SEC("fentry.s/__x64_sys_open")
+int BPF_PROG(test_sys_open, struct pt_regs *regs)
+{
+	void *ptr = (void *)PT_REGS_PARM1(regs);
+	pid_t pid = (pid_t)PT_REGS_PARM2(regs);
+	char path[4];
+	long ret;
+
+	ret = bpf_copy_from_user_remote(&path, sizeof(path), pid, ptr);
+
+	if (ret == sizeof(path) && !__builtin_memcmp("/dev", path, 4))
+		copy_remote_test++;
+
+	return 0;
+}
-- 
2.30.2




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux