diff mbox series

[bpf-next,1/2] bpf: Add bpf_task_cwd_from_pid() kfunc

Message ID tencent_97F8B56B340F51DB604B482FEBF012460505@qq.com
State New
Headers show
Series [bpf-next,1/2] bpf: Add bpf_task_cwd_from_pid() kfunc | expand

Commit Message

Rong Tao May 29, 2025, 3:32 a.m. UTC
From: Rong Tao <rongtao@cestc.cn>

It is a bit troublesome to get cwd based on pid in bpf program, such as
bpftrace example [1].

This patch therefore adds a new bpf_task_cwd_from_pid() kfunc which
allows BPF programs to get cwd from a pid.

[1] https://github.com/bpftrace/bpftrace/issues/3314

Signed-off-by: Rong Tao <rongtao@cestc.cn>
---
 kernel/bpf/helpers.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

Comments

Rong Tao May 30, 2025, 1:28 a.m. UTC | #1
On 5/29/25 13:44, Alexei Starovoitov wrote:
> On Wed, May 28, 2025 at 8:37 PM Rong Tao <rtoax@foxmail.com> wrote:
>> From: Rong Tao <rongtao@cestc.cn>
>>
>> It is a bit troublesome to get cwd based on pid in bpf program, such as
>> bpftrace example [1].
>>
>> This patch therefore adds a new bpf_task_cwd_from_pid() kfunc which
>> allows BPF programs to get cwd from a pid.
>>
>> [1] https://github.com/bpftrace/bpftrace/issues/3314
> Yes. This is cumbersome, but adding a very specific kfunc
> to the kernel is not a solution.
> This is tracing, no need for precise cwd. probe_read_kernel
> can do the job. bpftrace needs to have better C interop.
> Once that happens any kind of tracing extraction will be
> easy to write in C. Like this bpf_task_cwd_from_pid()
> can already be written as C bpf program.
Thanks for your reply, Yesterday I tried many ways to implement
the solution of getting cwd from pid/task, but all failed. The basic
idea is to rewrite the d_path() code, but in the bpf program, there
will be various memory security access problems, even if enough
  `if (!ptr)` are added, the program cannot be loaded successfully.

https://github.com/Rtoax/bcc/commit/2ba7a2389fc1183264e5195ff26561d93038886c

     bcc/tools$ sudo ./opensnoop.py -F

     ; if (dentry == vfsmnt->mnt_root || dentry == dentry->d_parent) { @ 
main.c:174
     109: (79) r2 = *(u64 *)(r7 +0)
     R7 invalid mem access 'scalar'

At the same time, bpf_d_path cannot be used because it can only be
applied to functions in btf_allowlist_d_path. Currently, it is
impossible to get cwd from pid/task in user mode. Any suggestions?

In addition, I fully tested this patch yesterday and it performed well.

Rong Tao
Yonghong Song May 30, 2025, 1:55 a.m. UTC | #2
On 5/29/25 6:28 PM, Rong Tao wrote:
>
> On 5/29/25 13:44, Alexei Starovoitov wrote:
>> On Wed, May 28, 2025 at 8:37 PM Rong Tao <rtoax@foxmail.com> wrote:
>>> From: Rong Tao <rongtao@cestc.cn>
>>>
>>> It is a bit troublesome to get cwd based on pid in bpf program, such as
>>> bpftrace example [1].
>>>
>>> This patch therefore adds a new bpf_task_cwd_from_pid() kfunc which
>>> allows BPF programs to get cwd from a pid.
>>>
>>> [1] https://github.com/bpftrace/bpftrace/issues/3314
>> Yes. This is cumbersome, but adding a very specific kfunc
>> to the kernel is not a solution.
>> This is tracing, no need for precise cwd. probe_read_kernel
>> can do the job. bpftrace needs to have better C interop.
>> Once that happens any kind of tracing extraction will be
>> easy to write in C. Like this bpf_task_cwd_from_pid()
>> can already be written as C bpf program.
> Thanks for your reply, Yesterday I tried many ways to implement
> the solution of getting cwd from pid/task, but all failed. The basic
> idea is to rewrite the d_path() code, but in the bpf program, there
> will be various memory security access problems, even if enough
>  `if (!ptr)` are added, the program cannot be loaded successfully.
>
> https://github.com/Rtoax/bcc/commit/2ba7a2389fc1183264e5195ff26561d93038886c 
>
>
>     bcc/tools$ sudo ./opensnoop.py -F
>
>     ; if (dentry == vfsmnt->mnt_root || dentry == dentry->d_parent) { 
> @ main.c:174
>     109: (79) r2 = *(u64 *)(r7 +0)
>     R7 invalid mem access 'scalar'

I think you can use bpf_probe_read_kernel() helper to get r2?

>
> At the same time, bpf_d_path cannot be used because it can only be
> applied to functions in btf_allowlist_d_path. Currently, it is
> impossible to get cwd from pid/task in user mode. Any suggestions?
>
> In addition, I fully tested this patch yesterday and it performed well.
>
> Rong Tao
>
>
Rong Tao May 30, 2025, 6:34 a.m. UTC | #3
On 5/30/25 09:55, Yonghong Song wrote:
>
>
> On 5/29/25 6:28 PM, Rong Tao wrote:
>>
>> On 5/29/25 13:44, Alexei Starovoitov wrote:
>>> On Wed, May 28, 2025 at 8:37 PM Rong Tao <rtoax@foxmail.com> wrote:
>>>> From: Rong Tao <rongtao@cestc.cn>
>>>>
>>>> It is a bit troublesome to get cwd based on pid in bpf program, 
>>>> such as
>>>> bpftrace example [1].
>>>>
>>>> This patch therefore adds a new bpf_task_cwd_from_pid() kfunc which
>>>> allows BPF programs to get cwd from a pid.
>>>>
>>>> [1] https://github.com/bpftrace/bpftrace/issues/3314
>>> Yes. This is cumbersome, but adding a very specific kfunc
>>> to the kernel is not a solution.
>>> This is tracing, no need for precise cwd. probe_read_kernel
>>> can do the job. bpftrace needs to have better C interop.
>>> Once that happens any kind of tracing extraction will be
>>> easy to write in C. Like this bpf_task_cwd_from_pid()
>>> can already be written as C bpf program.
>> Thanks for your reply, Yesterday I tried many ways to implement
>> the solution of getting cwd from pid/task, but all failed. The basic
>> idea is to rewrite the d_path() code, but in the bpf program, there
>> will be various memory security access problems, even if enough
>>  `if (!ptr)` are added, the program cannot be loaded successfully.
>>
>> https://github.com/Rtoax/bcc/commit/2ba7a2389fc1183264e5195ff26561d93038886c 
>>
>>
>>     bcc/tools$ sudo ./opensnoop.py -F
>>
>>     ; if (dentry == vfsmnt->mnt_root || dentry == dentry->d_parent) { 
>> @ main.c:174
>>     109: (79) r2 = *(u64 *)(r7 +0)
>>     R7 invalid mem access 'scalar'
>
> I think you can use bpf_probe_read_kernel() helper to get r2?
Thanks a lot, bpf_probe_read_kernel() works :)
>
>>
>> At the same time, bpf_d_path cannot be used because it can only be
>> applied to functions in btf_allowlist_d_path. Currently, it is
>> impossible to get cwd from pid/task in user mode. Any suggestions?
>>
>> In addition, I fully tested this patch yesterday and it performed well.
>>
>> Rong Tao
>>
>>
>
Dan Carpenter June 2, 2025, 6:20 a.m. UTC | #4
Hi Rong,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Rong-Tao/selftests-bpf-Add-selftests-for-bpf_task_cwd_from_pid/20250529-113933
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/tencent_97F8B56B340F51DB604B482FEBF012460505%40qq.com
patch subject: [PATCH bpf-next 1/2] bpf: Add bpf_task_cwd_from_pid() kfunc
config: x86_64-randconfig-161-20250529 (https://download.01.org/0day-ci/archive/20250530/202505300432.nZC50gOu-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0

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>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202505300432.nZC50gOu-lkp@intel.com/

smatch warnings:
kernel/bpf/helpers.c:2687 bpf_task_cwd_from_pid() warn: inconsistent returns 'rcu_read'.

vim +/rcu_read +2687 kernel/bpf/helpers.c

b24383bde5a454 Rong Tao     2025-05-29  2657  __bpf_kfunc int bpf_task_cwd_from_pid(s32 pid, char *buf, u32 buf_len)
b24383bde5a454 Rong Tao     2025-05-29  2658  {
b24383bde5a454 Rong Tao     2025-05-29  2659  	struct path pwd;
b24383bde5a454 Rong Tao     2025-05-29  2660  	char kpath[256], *path;
b24383bde5a454 Rong Tao     2025-05-29  2661  	struct task_struct *task;
b24383bde5a454 Rong Tao     2025-05-29  2662  
b24383bde5a454 Rong Tao     2025-05-29  2663  	if (!buf || buf_len == 0)
b24383bde5a454 Rong Tao     2025-05-29  2664  		return -EINVAL;
b24383bde5a454 Rong Tao     2025-05-29  2665  
b24383bde5a454 Rong Tao     2025-05-29  2666  	rcu_read_lock();
b24383bde5a454 Rong Tao     2025-05-29  2667  	task = pid_task(find_vpid(pid), PIDTYPE_PID);
b24383bde5a454 Rong Tao     2025-05-29  2668  	if (!task) {
b24383bde5a454 Rong Tao     2025-05-29  2669  		rcu_read_unlock();
b24383bde5a454 Rong Tao     2025-05-29  2670  		return -ESRCH;
b24383bde5a454 Rong Tao     2025-05-29  2671  	}
b24383bde5a454 Rong Tao     2025-05-29  2672  	task_lock(task);
b24383bde5a454 Rong Tao     2025-05-29  2673  	if (!task->fs) {
b24383bde5a454 Rong Tao     2025-05-29  2674  		task_unlock(task);
b24383bde5a454 Rong Tao     2025-05-29  2675  		return -ENOENT;

rcu_read_unlock();

b24383bde5a454 Rong Tao     2025-05-29  2676  	}
b24383bde5a454 Rong Tao     2025-05-29  2677  	get_fs_pwd(task->fs, &pwd);
b24383bde5a454 Rong Tao     2025-05-29  2678  	task_unlock(task);
b24383bde5a454 Rong Tao     2025-05-29  2679  	rcu_read_unlock();
b24383bde5a454 Rong Tao     2025-05-29  2680  
b24383bde5a454 Rong Tao     2025-05-29  2681  	path = d_path(&pwd, kpath, sizeof(kpath));
b24383bde5a454 Rong Tao     2025-05-29  2682  	path_put(&pwd);
b24383bde5a454 Rong Tao     2025-05-29  2683  	if (IS_ERR(path))
b24383bde5a454 Rong Tao     2025-05-29  2684  		return PTR_ERR(path);
b24383bde5a454 Rong Tao     2025-05-29  2685  
b24383bde5a454 Rong Tao     2025-05-29  2686  	strncpy(buf, path, buf_len);
b24383bde5a454 Rong Tao     2025-05-29 @2687  	return 0;
b24383bde5a454 Rong Tao     2025-05-29  2688  }
diff mbox series

Patch

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b71e428ad936..0f32fbc997bb 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -24,6 +24,10 @@ 
 #include <linux/bpf_mem_alloc.h>
 #include <linux/kasan.h>
 #include <linux/bpf_verifier.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/path.h>
+#include <linux/string.h>
 
 #include "../../lib/kstrtox.h"
 
@@ -2643,6 +2647,46 @@  __bpf_kfunc struct task_struct *bpf_task_from_vpid(s32 vpid)
 	return p;
 }
 
+/**
+ * bpf_task_cwd_from_pid - Get a task's absolute pathname of the current
+ * working directory from its pid.
+ * @pid: The pid of the task being looked up.
+ * @buf: The array pointed to by buf.
+ * @buf_len: buf length.
+ */
+__bpf_kfunc int bpf_task_cwd_from_pid(s32 pid, char *buf, u32 buf_len)
+{
+	struct path pwd;
+	char kpath[256], *path;
+	struct task_struct *task;
+
+	if (!buf || buf_len == 0)
+		return -EINVAL;
+
+	rcu_read_lock();
+	task = pid_task(find_vpid(pid), PIDTYPE_PID);
+	if (!task) {
+		rcu_read_unlock();
+		return -ESRCH;
+	}
+	task_lock(task);
+	if (!task->fs) {
+		task_unlock(task);
+		return -ENOENT;
+	}
+	get_fs_pwd(task->fs, &pwd);
+	task_unlock(task);
+	rcu_read_unlock();
+
+	path = d_path(&pwd, kpath, sizeof(kpath));
+	path_put(&pwd);
+	if (IS_ERR(path))
+		return PTR_ERR(path);
+
+	strncpy(buf, path, buf_len);
+	return 0;
+}
+
 /**
  * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data.
  * @p: The dynptr whose data slice to retrieve
@@ -3314,6 +3358,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_task_from_vpid, KF_ACQUIRE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_task_cwd_from_pid, KF_RET_NULL)
 BTF_ID_FLAGS(func, bpf_throw)
 #ifdef CONFIG_BPF_EVENTS
 BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS)