-1

my environment: ubuntu 20.04 kernel version: 5.15.0-46-generic x86_64

below is my code:

import sys
import signal
from bcc import BPF
import os

# define BPF program
bpf_text = """
#include <linux/bpf.h>
#include <linux/version.h>
#include <linux/sockptr.h>
#include<linux/bpfptr.h>

int fn_kprobe(struct pt_regs *ctx,int cmd, bpfptr_t uattr){

    int pid=bpf_get_current_pid_tgid();
    char tmp[40];
    switch (cmd) {
        case BPF_MAP_CREATE:
              memcpy(tmp,"BPF_MAP_CREATE",39);
              bpf_trace_printk("the cmd is %s",tmp);
              break;
        case BPF_MAP_LOOKUP_ELEM:
              memcpy(tmp,"BPF_MAP_LOOKUP_ELEM",39);
              bpf_trace_printk("the cmd is %s",tmp);
              break;
        default:
              break;
    }
    return 0;
}

it raises this error:

/virtual/main.c:21:30: error: initializing 'bpfptr_t' (aka 'sockptr_t') with an expression of incompatible type 'unsigned long' int cmd = ctx->di; bpfptr_t uattr = ctx->si; ^ ~~~~~~~ 1 error generated.

I think this error may due to bpfptr_t is a struct rather than a pointer, but I am not sure.

I have tried to get the second parameter through PT_REGS_PARM2, but it did not work.

————————————————————————————————————

I opened an issue in github and have partially solved my problem. below is the modified code:

import sys
from bcc import BPF

bpf_text = """
#include <linux/bpf.h>
#include <linux/version.h>
#include <linux/sockptr.h>
#include<linux/bpfptr.h>

int fn_kprobe(struct pt_regs *ctx){
    union bpf_attr attr;
    bpfptr_t uattr={0};
    unsigned int size=0;
    int cmd=0;

    bpf_probe_read_kernel((void*)(&cmd), sizeof(int), &PT_REGS_PARM1(ctx));
    bpf_probe_read_kernel((void*)(&uattr), sizeof(bpfptr_t), &PT_REGS_PARM2(ctx));
    bpf_probe_read_kernel((void*)(&size), sizeof(unsigned int), &PT_REGS_PARM3(ctx));

    bpf_trace_printk("the first parameter is %d",cmd);
    bpf_trace_printk("the uattr.user is %lx",uattr.user);
    bpf_trace_printk("the third parameter is %u",size);
    return 0;
}
"""

b = BPF(text=bpf_text)
b.attach_kprobe(event="__sys_bpf", fn_name="fn_kprobe")
while True:
    try:
        b.trace_print()
    except KeyboardInterrupt:
        sys.exit(0)

I can successfully get the first and the seconde parameter of __sys_bpf, but it's weird that the third parameter is always 0(the third parameter represents the size of bpf_attr, it should not be 0):

b'           <...>-73493   [002] d...1 510983.010254: bpf_trace_printk: the first parameter is 5'
b'           <...>-73493   [002] d...1 510983.010268: bpf_trace_printk: the address of uattr.user is 0x7ffcbedebd60'
b'           <...>-73493   [002] d...1 510983.010269: bpf_trace_printk: the third parameter is 0'
b'           <...>-73493   [002] d...1 510983.010476: bpf_trace_printk: the first parameter is 18'
b'           <...>-73493   [002] d...1 510983.010485: bpf_trace_printk: the address of uattr.user is 0x7ffcbedebe90'
b'           <...>-73493   [002] d...1 510983.010485: bpf_trace_printk: the third parameter is 0'

another question is: before I meet this problem, I can get parameter like this:

int fn_kprobe(struct pt_regs *ctx,int cmd){
    bpf_trace_printk("the cmd is %d",cmd);
    return 0;
}

but in __sys_bpf I have to use bpf_probe_read_kernel, which really confuses me.

luke zou
  • 21
  • 4
  • What's the kernel function you are trying to trace? It looks like it may be a bug in the rewrite that bcc performs. Could you also share the code for your attempt with `PT_REGS_PARM2`? – pchaigno Oct 25 '22 at 14:06

1 Answers1

1

For static int __sys_bpf(int cmd, bpfptr_t uattr, unsigned int size);, the second argument is struct (TYPEDEF of sockptr_t), NOT pointer to struct.

And in BTF, we have:

[2905] STRUCT '(anon)' size=16 vlen=2
        '(anon)' type_id=2904 bits_offset=0
        'is_kernel' type_id=40 bits_offset=64 bitfield_size=1
[2906] TYPEDEF 'sockptr_t' type_id=2905

The size of bpfptr_t is 16, so the second argument should span two registers.

So we need to read the fourth register(namely, PT_REGS_PARM4(ctx)) to get the third parameter:

    bpf_probe_read_kernel((void*)(&size), sizeof(unsigned int), &PT_REGS_PARM4(ctx));

One thing interesting is what will happen if the size of a parameter too huge to be stored in registers?

luke zou
  • 21
  • 4