Search code examples
cbpfebpf

Permission denied when retrieving a field from a pointer to a large structure in a BPF program


I am trying to write a BPF program that examines the session ID of any process that calls the tty_write kernel function. In order to retrieve the ID I need to follow a number of fields from a pointer to the current task_struct, however retrieving the group_leader pointer from a pointer to the current task seems to error because the offset from the current task pointer is too large. My BPF program code is as follows:

SEC("kprobe/tty_write")
int kprobe__tty_write(struct pt_regs *ctx)
{
    struct task_struct *task;
    struct task_struct *group_leader;
    struct pid_link pid_link;
    struct upid upid;
    int sessionid;

    // get current sessionid
    task = (struct task_struct *)bpf_get_current_task();
    bpf_probe_read(&group_leader, sizeof(group_leader), (void *)task->group_leader);
    bpf_probe_read(&pid_link, sizeof(pid_link), (void *)(group_leader->pids + PIDTYPE_SID));
    bpf_probe_read(&upid, sizeof(upid), (void *)pid_link.pid->numbers); 
    sessionid = upid.nr;

    // do stuff with sessionid

    return 0;
}

This fails with the following error. Note I am using gobpf's elf package to load the compiled program:

failed to load BPF module: error while loading "kprobe/tty_write" (permission denied):
0: (bf) r6 = r1
1: (85) call bpf_get_current_task#35
2: (79) r3 = *(u64 *)(r0 +1464)
R0 invalid mem access 'inv'

How can I work around this issue and what is the cause? I thought it might be due to the 512 byte limit on the stack size but I'm not why that would matter in this case.

uname -a: Linux ubuntu1710 4.13.0-32-generic #35-Ubuntu SMP Thu Jan 25 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux


Solution

  • I don't think it's an issue with the offset being too large. There's an issue with field randomization that affects struct task_struct in Linux 4.13.

    You can either use a different kernel, or put the following, before your #includes:

    #define randomized_struct_fields_start  struct {
    #define randomized_struct_fields_end    };
    

    You have a second issue with your first bpf_probe_read. The third argument needs to be a pointer to the value you want to retrieve (here a pointer to a pointer):

    bpf_probe_read(&group_leader, sizeof(group_leader), &task->group_leader);