0

I am trying to write a BPF program that examines the session ID of any process that calls the tty_write kernel function. I am trying to do this by retrieving a field from the current task_struct struct. My 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 pid pid;
    int sessionid;

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

    // do stuff with sessionid

    return 0;
}

Note that I am compiling my BPF program using clang into an ELF file and loading it with gobpf's ELF package. Unfortunately, the value of sessionid is always 0. Why is this? I don't think I'm accessing the session ID incorrectly as I have done this before using bcc on a 4.11 kernel (due to the how bcc rewrite's BPF programs, I cannot simply use the same code when I want to compile the program myself). The equivalent working bcc code for accessing the sessionid is as follows. Note that this only works on a 4.11 kernel, the following code did not work on a 4.13 kernel. The code above works on neither kernel however.

#!/usr/bin/python

from bcc import BPF
import ctypes as ct
import os
import threading
import time
import sys

prog=r"""
#include <uapi/linux/ptrace.h>

#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/nsproxy.h>
#include <linux/ns_common.h>

#define BUFSIZE 256
struct tty_write_t {
    int count;
    char buf[BUFSIZE];
    unsigned int sessionid;
};

// define maps
BPF_PERF_OUTPUT(tty_writes);

int kprobe__tty_write(struct pt_regs *ctx, struct file *file,
    const char __user *buf, size_t count)
{
    struct task_struct *task;
    struct pid_link pid_link;
    struct pid pid;
    int sessionid;

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

    // bpf_probe_read() can only use a fixed size, so truncate to count
    // in user space:
    struct tty_write_t tty_write = {};
    bpf_probe_read(&tty_write.buf, BUFSIZE, (void *)buf);
    if (count > BUFSIZE) {
        tty_write.count = BUFSIZE;
    } else {
        tty_write.count = count;
    }

    // add sessionid to tty_write structure and submit
    tty_write.sessionid = sessionid;
    tty_writes.perf_submit(ctx, &tty_write, sizeof(tty_write));

    return 0;
}

"""

b = BPF(text=prog)

BUFSIZE = 256
class TTYWrite(ct.Structure):
    _fields_ = [
        ("count", ct.c_int),
        ("buf", ct.c_char * BUFSIZE),
        ("sessionid", ct.c_int)
    ]

# process tty_write
def print_tty_write(cpu, data, size):
    tty_write = ct.cast(data, ct.POINTER(TTYWrite)).contents
    print(str(tty_write.sessionid))

b["tty_writes"].open_perf_buffer(print_tty_write)
while 1:
    b.kprobe_poll()

4.11 kernel:

uname -a:Linux ubuntu16 4.11.0-14-generic #20~16.04.1-Ubuntu SMP Wed Aug 9 09:06:22 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

4.13 kernel:

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

Qeole
  • 8,284
  • 1
  • 24
  • 52
dippynark
  • 2,743
  • 20
  • 58
  • The bcc version always prints sessionid=0 for me, same as your C version... – pchaigno Feb 14 '18 at 08:59
  • @pchaigno hmm I've definitely got it working before - I'll post my whole code tonight to check – dippynark Feb 14 '18 at 12:16
  • @pchaigno I've added my full BCC BPF code, for me that prints non-zero session IDs – dippynark Feb 14 '18 at 19:01
  • @pchaigno Ahhh no, it only print non-zero session IDs on a 4.11 kernel - I'll update the qustion – dippynark Feb 14 '18 at 19:03
  • 1
    The first example code works for me under Linux 4.11 if I load it using bcc (I disabled bcc's rewriter to be sure to load the same C code). Not sure what else could cause the difference in value... – pchaigno Feb 15 '18 at 10:37
  • @pchaigno that is really odd, I tried setting `sessionid` to 1 manually to check that I was outputting it correctly and it all worked, so it seems the issue is with the BPF program itself - confusing – dippynark Feb 15 '18 at 12:21

1 Answers1

1

This was due to me not understanding how to compile a BPF program for a specific kernel version - I was compiling using a Docker container with kernel headers installed for the 4.15 kernel, but was trying to run the program on a 4.11 kernel.

To fix this I compiled my program using the 4.11 kernel headers.

dippynark
  • 2,743
  • 20
  • 58