SPinSO
SPinSO

Reputation: 3

eBPF: symbol "function_name": unsatisfied program reference

I have the ebpf code for tracing execve with tracepoints:

#define EXECVE_COMM_LENGTH 1024
#define EXECVE_ARGS_COUNT 100
#define EXECVE_ARG_SIZE 20
#define DEFAULT_SUB_BUF_LEN 16
#define DEFAULT_SUB_BUF_SIZE 255

struct p_exec_program_event
{
    u64 ktime;
    u32 pid;
    u8 filename[EXECVE_COMM_LENGTH];
    u8 args[EXECVE_ARGS_COUNT][EXECVE_ARG_SIZE];
    u8 pwd[DEFAULT_SUB_BUF_LEN][DEFAULT_SUB_BUF_SIZE];
};

inline int read_dentry_strings(struct dentry *dentry, u8 buf[DEFAULT_SUB_BUF_LEN][DEFAULT_SUB_BUF_SIZE])
{
    struct dentry *curr_dtry = dentry;
    struct dentry *lastdtryp = dentry;
    unsigned int i = 0;

    if (buf)
    {
        bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name)); //  problems start here
        for (i = 1; i < DEFAULT_SUB_BUF_LEN; i++)
        {
            struct dentry *parent =  BPF_CORE_READ(curr_dtry, d_parent);
            if (parent != lastdtryp)
            {
                lastdtryp = parent;
                curr_dtry = parent;
                bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name));
            }
            else
                break;
        }
    }

    return 0;
}

inline int read_argv(const char *const *argv, u8 args[EXECVE_ARGS_COUNT][EXECVE_ARG_SIZE])
{
    for (int i = 1; i < EXECVE_ARGS_COUNT; i++)
    {
        char arg[EXECVE_ARG_SIZE];
        void *curr_arg = (void *)&argv[i];
        if (curr_arg != NULL)
        {
            const char *argp = NULL;
            bpf_probe_read_user(&argp, sizeof(argp), curr_arg);
            if (argp != NULL)
            {
                bpf_probe_read_user_str(args[i - 1], EXECVE_ARG_SIZE, (void *)argp);
            }
            else
            {
                break;
            }
        }
        else
        {
            break;
        }
    }
    return 0;
}

inline int handle_execveat(char *filename, const char *const *argv)
{
    struct p_exec_program_event *event_info;

    if (!filename || !argv)
    {
        return 0;
    }

    event_info = bpf_ringbuf_reserve(&p_exec_program_events, sizeof(struct p_exec_program_event), 0);
    if (!event_info)
    {
        return 0;
    }

    event_info->ktime = bpf_ktime_get_ns();
    event_info->pid = bpf_get_current_pid_tgid();

    long res = bpf_probe_read_user_str(event_info->filename, sizeof(event_info->filename), filename);
    if (res)
    {
        if (event_info->filename[0] == '.')
        {
            struct task_struct *task = (struct task_struct *)bpf_get_current_task();
            struct dentry *dentry = BPF_CORE_READ(task, fs, pwd.dentry);
            read_dentry_strings(dentry, event_info->pwd);
        }
        // else -> absolute path in filename already resolved
    }

    read_argv(argv, event_info->args);

    bpf_ringbuf_submit(event_info, 0);

    return 0;
}

SEC("tracepoint/syscalls/sys_enter_execve")
int sys_enter_execve(struct enter_execve_info *ctx)
{
    return handle_execveat(ctx->filename, ctx->argv);
}

When I run this code I've got the error message:

2023/12/27 21:41:28 Loading objects: field SysEnterExecve: program sys_enter_execve: Call at insn 36: symbol "handle_execveat": unsatisfied program reference

exit status 1

Can you help me to understand what's wrong with code in read_dentry_strings (or in code at all)?

I am using ebpf2go (latest version on github)

I experimented with code and found that problem is in read_dentry_strings - without calling this function, program works fine. I began to comment all lines and found that error appears when I call bpf_probe_read_str(buf[i], DEFAULT_SUB_BUF_SIZE, BPF_CORE_READ(curr_dtry, d_name.name));

Upvotes: 0

Views: 239

Answers (1)

pchaigno
pchaigno

Reputation: 13133

TL;DR. The error indicates the function handle_execveat wasn't inlined. You can use __attribute__((always_inline)) to ensure the compiler will inline it.


Error Explanation

2023/12/27 21:41:28 Loading objects: field SysEnterExecve: program sys_enter_execve: Call at insn 36: symbol "handle_execveat": unsatisfied program reference

This error means that the loader—in your case, cilium/ebpf—tried to resolve the symbol handle_execveat in a function call.

BPF-to-BPF function calls are possible (with limitations), but given the use of inline, this is probably not what you are trying to achieve. So we can conclude that handle_execveat wasn't inlined as expected.

Enforcing Inlining

Making a function inline doesn't actually guarantee the compiler will inline it. Making the function static increases the chances the compiler will actually inline it. Marking it __attribute__((always_inline)) ensures it will be inline.

Upvotes: 0

Related Questions