This group is locked. No changes can be made to the group while it is locked.
Date
1 - 3 of 3
Verifier error: variable stack access var_off
Brendan Gregg
I'm hoping someone knows a workaround here.
I have a char buf[128] and I'd like to write to arbitrary offsets, but keep hitting this error. Any workaround? I've included a sample bcc program below, which has a block comment as to what I'm trying to do (join an argv[]). Thanks, Brendan ---execsnoop2.py--- #!/usr/bin/python # From execsnoop (bcc/eBPF). from __future__ import print_function from bcc import BPF from bcc.utils import ArgString, printb import bcc.utils as utils import argparse import ctypes as ct import re import time # arguments examples = """examples: ./execsnoop # trace all exec() syscalls """ parser = argparse.ArgumentParser( description="Trace exec() syscalls", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples) parser.add_argument("--ebpf", action="store_true", help=argparse.SUPPRESS) args = parser.parse_args() # define BPF program bpf_text = """ #include <uapi/linux/ptrace.h> #include <linux/sched.h> #include <linux/fs.h> #define ARGSIZE 128 struct data_t { char argv[ARGSIZE]; }; BPF_PERF_OUTPUT(events); // // Here's what I'm trying to do. Let's say this has: // __argv[0] = "ls" // __argv[1] = "-l" // I'm trying to create a buffer with "ls -l", by doing bpf_probe_read_str() for // each element into the buffer, while keeping track of the length of // the previous read so I can insert a space delimiter at that offset, // and begin the next read after the delimiter. // int syscall__execve(struct pt_regs *ctx, const char __user *filename, const char __user *const __user *__argv, const char __user *const __user *__envp) { struct data_t data = {}; uint64_t max = sizeof(data.argv); const char *argp = NULL; bpf_probe_read(&argp, sizeof(argp), (void *)&__argv[0]); uint64_t len = bpf_probe_read_str(&data.argv, max, argp); len &= 0xffffffff; // to avoid: "math between fp pointer and register errs" bpf_trace_printk("len: %d\\n", len); // sanity check: len is indeed valid data.argv[len] = ' '; // XXX this fails with: // "variable stack access var_off=(0x0; 0xffffffff) off=-128 size=1" // how do I fix this? // events.perf_submit(ctx, &data, len); // XXX this fails with: // "R5 min value is negative, either use unsigned or 'var &= const'" // how do I fix this? In the meantime, I'm passing the whole buffer out: events.perf_submit(ctx, &data, sizeof(data.argv)); out: return 0; } """ if args.ebpf: print(bpf_text) exit() # initialize BPF b = BPF(text=bpf_text) execve_fnname = b.get_syscall_fnname("execve") b.attach_kprobe(event=execve_fnname, fn_name="syscall__execve") ARGSIZE = 128 # should match #define in C above class Data(ct.Structure): _fields_ = [ ("argv", ct.c_char * ARGSIZE), ] print("running"); # process event def print_event(cpu, data, size): event = ct.cast(data, ct.POINTER(Data)).contents printb(b"%s" % event.argv) # loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: b.perf_buffer_poll() ---execsnoop2.py--- |
Teng Qin
Firstly, note that bpf_probe_read_str adds an extra \0 to the read string. So your max should be sizeof(data.argv) - 1 instead in order for data.argv[len] = ' ' to work (from Verifier's perspective, logically you don't need that extra delimiter~)
Then, Yonghong had a patch a few month ago addressing very similar issue. See the example in patch series bpf: improve verifier ARG_CONST_SIZE_OR_ZERO semantics Does your Kernel have those patches? However, even with all those the data.argv[len] = ' ' part still fails with something about stack offset not being fixed. I will try debug more to see how to fix that. For now, you can use a per-CPU array of size 1 for the data instead of allocating it on the stack. The following works for me: #include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fs.h>
#define ARGSIZE 128
struct data_t {
char argv[ARGSIZE];
};
BPF_PERF_OUTPUT(events);
BPF_PERCPU_ARRAY(mem, struct data_t, 1);
//
// Here's what I'm trying to do. Let's say this has:
// __argv[0] = "ls"
// __argv[1] = "-l"
// I'm trying to create a buffer with "ls -l", by doing bpf_probe_read_str() for
// each element into the buffer, while keeping track of the length of
// the previous read so I can insert a space delimiter at that offset,
// and begin the next read after the delimiter.
//
int on_event(struct pt_regs *ctx,
const char __user *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
int zero = 0;
struct data_t* data = mem.lookup(&zero);
if (!data)
return 0;
uint64_t max = sizeof(data->argv) - 1;
const char *argp = NULL;
bpf_probe_read(&argp, sizeof(argp), (void *)&__argv[0]);
uint64_t len = bpf_probe_read_str(&(data->argv), max, argp);
len &= 0xffffffff; // to avoid: "math between fp pointer and register errs"
bpf_trace_printk("len: %d\\n", len); // sanity check: len is indeed valid
data->argv[len] = ' ';
events.perf_submit(ctx, data, len);
out:
return 0;
} |
Yonghong Song
The kernel needs to a constant offset from the stack for write. The
toggle quoted message
Show quoted text
corresponding kernel verifier code below: /* stack accesses must be at a fixed offset, so that we can * determine what type of data were returned. * See check_stack_read(). */ if (!tnum_is_const(reg->var_off)) { char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); verbose(env, "variable stack access var_off=%s off=%d size=%d", tn_buf, off, size); return -EACCES; } You can initialize the array with ' ' to workaround the issue: struct data_t data; uint64_t max = sizeof(data.argv); const char *argp = NULL; memset(&data, ' ', sizeof(data)); bpf_probe_read(&argp, sizeof(argp), (void *)&__argv[0]); uint64_t len = bpf_probe_read_str(&data.argv, max, argp); len &= 0xffffffff; // to avoid: "math between fp pointer and register errs" bpf_trace_printk("len: %d\\n", len); // sanity check: len is indeed valid // data.argv[len] = ' '; On Sun, Jul 1, 2018 at 6:20 PM, Teng Qin <palmtenor@...> wrote:
Firstly, note that bpf_probe_read_str adds an extra \0 to the read string. |