From 921b7369a36a07caa76552388a0043b2c2d20692 Mon Sep 17 00:00:00 2001 From: Rong Tao Date: Sun, 13 Nov 2022 18:09:45 +0800 Subject: [PATCH] tools: filelife: Check btf struct field for CO-RE and add vfs_open() Since kernel commit 6521f8917082("namei: prepare for idmapped mounts"), the vfs_unlink/create function add argument 'struct user_namespace'. And add vfs_open() probe if 'f_mode = FMODE_CREATED'. In the same commit, 'struct renamedata' add 'old_mnt_userns' field, use 'old_mnt_userns' to determine which function to use. Signed-off-by: Rong Tao --- tools/filelife.py | 62 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/tools/filelife.py b/tools/filelife.py index e2d8d480e1d0..5aeeb0ae81a5 100755 --- a/tools/filelife.py +++ b/tools/filelife.py @@ -16,6 +16,7 @@ # # 08-Feb-2015 Brendan Gregg Created this. # 17-Feb-2016 Allan McAleavy updated for BPF_PERF_OUTPUT +# 13-Nov-2022 Rong Tao Check btf struct field for CO-RE and add vfs_open() from __future__ import print_function from bcc import BPF @@ -24,11 +25,11 @@ # arguments examples = """examples: - ./filelife # trace all stat() syscalls + ./filelife # trace lifecycle of file(create->remove) ./filelife -p 181 # only trace PID 181 """ parser = argparse.ArgumentParser( - description="Trace stat() syscalls", + description="Trace lifecycle of file", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("-p", "--pid", @@ -54,8 +55,7 @@ BPF_HASH(birth, struct dentry *); BPF_PERF_OUTPUT(events); -// trace file creation time -int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) +static int probe_dentry(struct pt_regs *ctx, struct dentry *dentry) { u32 pid = bpf_get_current_pid_tgid() >> 32; FILTER @@ -64,10 +64,35 @@ birth.update(&dentry, &ts); return 0; +} + +// trace file creation time +TRACE_CREATE_FUNC +{ + return probe_dentry(ctx, dentry); +}; + +// trace file security_inode_create time +int trace_security_inode_create(struct pt_regs *ctx, struct inode *dir, + struct dentry *dentry) +{ + return probe_dentry(ctx, dentry); +}; + +// trace file open time +int trace_open(struct pt_regs *ctx, struct path *path, struct file *file) +{ + struct dentry *dentry = path->dentry; + + if (!(file->f_mode & FMODE_CREATED)) { + return 0; + } + + return probe_dentry(ctx, dentry); }; // trace file deletion and output details -int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) +TRACE_UNLINK_FUNC { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid() >> 32; @@ -99,6 +124,22 @@ } """ +trace_create_text_old=""" +int trace_create(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) +""" +trace_create_text_new=""" +int trace_create(struct pt_regs *ctx, struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry) +""" + +trace_unlink_text_old=""" +int trace_unlink(struct pt_regs *ctx, struct inode *dir, struct dentry *dentry) +""" +trace_unlink_text_new=""" +int trace_unlink(struct pt_regs *ctx, struct user_namespace *mnt_userns, + struct inode *dir, struct dentry *dentry) +""" + if args.pid: bpf_text = bpf_text.replace('FILTER', 'if (pid != %s) { return 0; }' % args.pid) @@ -109,12 +150,21 @@ if args.ebpf: exit() +if BPF.kernel_struct_has_field(b'renamedata', b'old_mnt_userns') == 1: + bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_new) + bpf_text = bpf_text.replace('TRACE_UNLINK_FUNC', trace_unlink_text_new) +else: + bpf_text = bpf_text.replace('TRACE_CREATE_FUNC', trace_create_text_old) + bpf_text = bpf_text.replace('TRACE_UNLINK_FUNC', trace_unlink_text_old) + # initialize BPF b = BPF(text=bpf_text) b.attach_kprobe(event="vfs_create", fn_name="trace_create") +# newer kernels may don't fire vfs_create, call vfs_open instead: +b.attach_kprobe(event="vfs_open", fn_name="trace_open") # newer kernels (say, 4.8) may don't fire vfs_create, so record (or overwrite) # the timestamp in security_inode_create(): -b.attach_kprobe(event="security_inode_create", fn_name="trace_create") +b.attach_kprobe(event="security_inode_create", fn_name="trace_security_inode_create") b.attach_kprobe(event="vfs_unlink", fn_name="trace_unlink") # header