Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Both bpf_strncmp and user defined strncmp not work #5225

Open
kirbyzhou opened this issue Mar 5, 2025 · 0 comments
Open

Both bpf_strncmp and user defined strncmp not work #5225

kirbyzhou opened this issue Mar 5, 2025 · 0 comments

Comments

@kirbyzhou
Copy link

kirbyzhou commented Mar 5, 2025

The bug

I want to do some string compare in bpfcc uprobe functions.
But both the built-in bpf_strncmp function and my strncmp can not work.

bpf_strncmp will always meet Permission denied.

my_strncmp seems to have encountered some compiler problems,
which can be bypassed by using -O0, but "-O0" may also fail under complicated circumstances.
another kind of workaround is to force the array to be on the stack by modifying the contents of the array.

ubuntu 24.04 with kernel 6.8.0-54-generic

deb version of bcc

python3-bpfcc = 0.29.1+ds-1ubuntu7
llvm-18-runtime = 1:18.1.3-1

git version of bcc

bcc = 0.33.0+c8515f71-py3.12
compiled with llvm-toolchain-19 = 1:19.1.1-1ubuntu1~24.04.2

The code

# buggy.py
import sys, os, os.path
try:
    from bpfcc import BPF
except:
    from bcc import BPF


# bpf 程序
bpf_program = """
#include <uapi/linux/ptrace.h>

#define NAMEBUF_SIZE 32
#define COMM_SIZE 20

static __always_inline int k_strncmp(const char *cs, int size, const char *ct)
{
    int len = 0;
    unsigned char c1, c2;
    for (len=0; len<size; len++) {
        c1 = *cs++;
        c2 = *ct++;
        if (c1 != c2) return c1 < c2 ? -1 : 1;
        if (!c1) break;
     }
     return 0;
}

BPF_PERF_OUTPUT(getcwd_events);

////////////////////////////////////////////////////////////////
//  prototype:
//    char *getcwd(char *buf, size_t size);

int hook_getcwd_ret1(struct pt_regs *ctx) {
    char comm[COMM_SIZE];
    bpf_get_current_comm(&comm, sizeof(comm));
    char comm_pattern[] = "pwd";
    // important, the first k_strncmp will not fail, but it must be here to trigger the bug at the second.
    if (k_strncmp(comm, COMM_SIZE, comm_pattern) == 0 ) {
        char *retval = (char *)PT_REGS_RC(ctx);
        char ret_name_buf[NAMEBUF_SIZE];
        if (retval != NULL) {
            bpf_probe_read_str(&ret_name_buf, sizeof(ret_name_buf), retval);
            char dir_pattern[NAMEBUF_SIZE] = "/home/xbox";
            //dir_pattern[NAMEBUF_SIZE-1] = 0; // workaround 1, it works without "-O0"
            if (k_strncmp(ret_name_buf, NAMEBUF_SIZE, dir_pattern)==0) {
                bpf_trace_printk("hitted\\n");
            }
        }
    }
    return 0;
}

int hook_getcwd_ret2(struct pt_regs *ctx) {
    char comm[COMM_SIZE];
    bpf_get_current_comm(&comm, sizeof(comm));
    const char comm_pattern[] = "pwd";
    //const char* comm_pattern  = "pwd"; // also not work
    if (bpf_strncmp(comm, COMM_SIZE, comm_pattern) == 0 ) {
        bpf_trace_printk("hitted\\n");
    }
    return 0;
}

"""


b = BPF(text = bpf_program.encode(encoding="utf-8"))
#b = BPF(text = bpf_program.encode(encoding="utf-8"), cflags = ["-O0"]) # workaround 2, -O0 -O3 works, -O1 -O2 and fail.

lib_paths = [
        b"/usr/lib/x86_64-linux-gnu/libc.so.6",
        b"/usr/lib64/libc.so.6"]
for lib_path in lib_paths:
    try:
        b.attach_uretprobe(name = lib_path, sym = b"getcwd", fn_name = "hook_getcwd_ret1")
    except Exception as e:
        print(e)
    try:
        b.attach_uretprobe(name = lib_path, sym = b"getcwd", fn_name = "hook_getcwd_ret2")
    except Exception as e:
        print(e)

print("Tracing... Press Ctrl+C to exit.")
while True:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()
]# python3 buggy.py
bpf: Failed to load program: Permission denied
0: R1=ctx() R10=fp0
; int hook_getcwd_ret1(struct pt_regs *ctx) {
0: (bf) r6 = r1                       ; R1=ctx() R6_w=ctx()
1: (bf) r1 = r10                      ; R1_w=fp0 R10=fp0
2: (07) r1 += -32                     ; R1_w=fp-32
; bpf_get_current_comm(&comm, sizeof(comm));
3: (b7) r2 = 20                       ; R2_w=20
4: (85) call bpf_get_current_comm#16          ; R0_w=scalar() fp-16=????mmmm fp-24=mmmmmmmm fp-32=mmmmmmmm
; c1 = *cs++;
5: (71) r1 = *(u8 *)(r10 -29)         ; R1_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R10=fp0 fp-32=mmmmmmmm
; if (c1 != c2) return c1 < c2 ? -1 : 1;
6: (67) r1 <<= 24                     ; R1_w=scalar(smin=0,smax=umax=umax32=0xff000000,smax32=0x7f000000,var_off=(0x0; 0xff000000))
; c1 = *cs++;
7: (71) r2 = *(u8 *)(r10 -30)         ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R10=fp0 fp-32=mmmmmmmm
; if (c1 != c2) return c1 < c2 ? -1 : 1;
8: (57) r2 &= 255                     ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
9: (67) r2 <<= 16                     ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xff0000,var_off=(0x0; 0xff0000))
10: (4f) r1 |= r2                     ; R1_w=scalar() R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xff0000,var_off=(0x0; 0xff0000))
; c1 = *cs++;
11: (71) r2 = *(u8 *)(r10 -31)        ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R10=fp0 fp-32=mmmmmmmm
; if (c1 != c2) return c1 < c2 ? -1 : 1;
12: (67) r2 <<= 8                     ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xff00,var_off=(0x0; 0xff00))
; c1 = *cs++;
13: (71) r3 = *(u8 *)(r10 -32)        ; R3_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R10=fp0 fp-32=mmmmmmmm
; if (c1 != c2) return c1 < c2 ? -1 : 1;
14: (57) r3 &= 255                    ; R3_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
15: (4f) r3 |= r2                     ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xff00,var_off=(0x0; 0xff00)) R3_w=scalar()
16: (57) r3 &= 65535                  ; R3_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xffff,var_off=(0x0; 0xffff))
17: (4f) r3 |= r1                     ; R1_w=scalar() R3_w=scalar()
; if (c1 != c2) return c1 < c2 ? -1 : 1;
18: (67) r3 <<= 32                    ; R3_w=scalar(smax=0x7fffffff00000000,umax=0xffffffff00000000,smin32=0,smax32=umax32=0,var_off=(0x0; 0xffffffff00000000))
19: (77) r3 >>= 32                    ; R3_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
20: (55) if r3 != 0x647770 goto pc+26         ; R3_w=0x647770
; char *retval = (char *)PT_REGS_RC(ctx);
21: (79) r3 = *(u64 *)(r6 +80)        ; R3=scalar() R6=ctx()
; if (retval != NULL) {
22: (15) if r3 == 0x0 goto pc+24      ; R3=scalar(umin=1)
23: (bf) r1 = r10                     ; R1_w=fp0 R10=fp0
24: (07) r1 += -64                    ; R1_w=fp-64
; bpf_probe_read_str(&ret_name_buf, sizeof(ret_name_buf), retval);
25: (b7) r2 = 32                      ; R2_w=32
26: (85) call bpf_probe_read_str#45   ; R0_w=scalar(smin=smin32=-4095,smax=smax32=32) fp-40=mmmmmmmm fp-48=mmmmmmmm fp-56=mmmmmmmm fp-64=mmmmmmmm
27: (b7) r1 = 0                       ; R1_w=0
; c1 = *cs++;
28: (18) r3 = 0x0                     ; R3_w=0
30: (0f) r3 += r1                     ; R1_w=0 R3_w=0
31: (bf) r2 = r10                     ; R2_w=fp0 R10=fp0
32: (07) r2 += -64                    ; R2_w=fp-64
33: (0f) r2 += r1                     ; R1_w=0 R2_w=fp-64
34: (71) r2 = *(u8 *)(r2 +0)          ; R2_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) fp-64=mmmmmmmm
; c2 = *ct++;
35: (71) r3 = *(u8 *)(r3 +0)
R3 invalid mem access 'scalar'
processed 35 insns (limit 1000000) max_states_per_insn 0 total_states 1 peak_states 1 mark_read 1

Failed to load BPF program b'hook_getcwd_ret1': Permission denied
bpf: Failed to load program: Permission denied
0: R1=ctx() R10=fp0
; int hook_getcwd_ret2(struct pt_regs *ctx) {
0: (bf) r6 = r10                      ; R6_w=fp0 R10=fp0
1: (07) r6 += -32                     ; R6_w=fp-32
; bpf_get_current_comm(&comm, sizeof(comm));
2: (bf) r1 = r6                       ; R1_w=fp-32 R6_w=fp-32
3: (b7) r2 = 20                       ; R2_w=20
4: (85) call bpf_get_current_comm#16          ; R0_w=scalar() fp-16=????mmmm fp-24=mmmmmmmm fp-32=mmmmmmmm
5: (b7) r1 = 6584176                  ; R1_w=0x647770
; const char comm_pattern[] = "pwd";
6: (63) *(u32 *)(r10 -36) = r1        ; R1_w=0x647770 R10=fp0 fp-40=mmmm????
7: (bf) r3 = r10                      ; R3_w=fp0 R10=fp0
8: (07) r3 += -36                     ; R3_w=fp-36
; if (bpf_strncmp(comm, COMM_SIZE, comm_pattern) == 0 ) {
9: (bf) r1 = r6                       ; R1_w=fp-32 R6_w=fp-32
10: (b7) r2 = 20                      ; R2_w=20
11: (85) call bpf_strncmp#182
R3 type=fp expected=map_value
processed 12 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Failed to load BPF program b'hook_getcwd_ret2': Permission denied

My opinion

I have no idea with hook_getcwd_ret2 / bpf_strncmp, maybe my usage is wrong.
But hook_getcwd_ret1 looks very much like a compiler problem, which is obviously a null pointer dereference.

···
27: (b7) r1 = 0 ; R1_w=0
28: (18) r3 = 0x0 ; R3_w=0
30: (0f) r3 += r1 ; R1_w=0 R3_w=0
35: (71) r3 = *(u8 *)(r3 +0)
···

@kirbyzhou kirbyzhou changed the title Both bpf_strncmp and user define strncmp not work Both bpf_strncmp and user definedstrncmp not work Mar 5, 2025
@kirbyzhou kirbyzhou changed the title Both bpf_strncmp and user definedstrncmp not work Both bpf_strncmp and user defined strncmp not work Mar 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant