iovisor / bcc

BCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more
Apache License 2.0
20.34k stars 3.86k forks source link

Failed to load program: last insn is not an exit or jmp #2265

Open mephi42 opened 5 years ago

mephi42 commented 5 years ago

Caused by:

#!/usr/bin/env python3
import bcc
u = bcc.USDT(path='/lib64/libc.so.6')
u.enable_probe(probe='__v1setjmp', fn_name='foo')
b = bcc.BPF(text=r'''
#include <uapi/linux/ptrace.h>

static int foo_1(struct pt_regs *ctx)
{
    u64 arg;
    bpf_usdt_readarg(1, ctx, &arg);
    return 0;
}

int foo(struct pt_regs *ctx)
{
    return foo_1(ctx);
}
''', usdt_contexts=[u], debug=65535)

Looking at generated C:

__attribute__((always_inline))
static __always_inline int _bpf_readarg_foo_1(struct pt_regs *ctx, void *dest, size_t len) {
  if (len != sizeof(uint64_t)) return -1;
  *((uint64_t *)dest) = ctx->gprs[2]; __asm__ __volatile__("": : :"memory");
  return 0;
}
__attribute__((always_inline))
static __always_inline int _bpf_readarg_foo_2(struct pt_regs *ctx, void *dest, size_t len) {
  if (len != sizeof(int32_t)) return -1;
  *((int32_t *)dest) = ctx->gprs[3]; __asm__ __volatile__("": : :"memory");
  return 0;
}
__attribute__((always_inline))
static __always_inline int _bpf_readarg_foo_3(struct pt_regs *ctx, void *dest, size_t len) {
  if (len != sizeof(uint64_t)) return -1;
  *((uint64_t *)dest) = ctx->gprs[14]; __asm__ __volatile__("": : :"memory");
  return 0;
}

#include <uapi/linux/ptrace.h>

__attribute__((always_inline))
static int foo_1(struct pt_regs *ctx)
{
    u64 arg;
    _bpf_readarg__1(ctx, &arg, sizeof(*(&arg)));
    return 0;
}

__attribute__((section(".bpf.fn.foo")))
int foo(struct pt_regs *ctx)
{

    return foo_1(ctx);
}

foo_1 calls non-existent _bpf_readarg__1, which leads to the following BPF:

Disassembly of section .bpf.fn.foo:
foo:
; { // Line  43
   0:   bf 2a 00 00 00 00 00 00     r2 = r10
; int foo(struct pt_regs *ctx) // Line  42
   1:   07 20 00 00 ff ff ff f8     r2 += -8
; _bpf_readarg__1(ctx, &arg, sizeof(*(&arg))); // Line  37
   2:   b7 30 00 00 00 00 00 08     r3 = 8
   3:   85 01 00 00 ff ff ff ff     call -1
; return foo_1(ctx); // Line  45
   4:   b7 00 00 00 00 00 00 00     r0 = 0
   5:   95 00 00 00 00 00 00 00     exit

in which call -1 apparently confuses the verifier.

Edit: this looks like #1255, but the error message has become extremely confusing.

yonghong-song commented 5 years ago

The usdt does not support using bpf_usdt_readarg outside the main probe function. The readarg function names like _bpf_readarg_foo_2 are generated based on main probe function "name", and bpf_usdt_readarg needs to be in function foo body to be rewritten correctly.