Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Program too large" threshold greater than actual instruction count

Tags:

bpf

ebpf

bcc-bpf

I've written a couple production BPF agents, but my approach is very iterative until I please the verifier and can move on. I've reached my limit again.

Here's a program that works if I have one fewer && condition -- and breaks otherwise. The confusing part is that the warning implies that 103 insns is greater-than at most 4096 insns. There's obviously something I'm misunderstanding about how this is all strung together.

My ultimate goal is to do logging based on a process' environment -- so alternative approaches are welcome. :)

Error:

$ sudo python foo.py
bpf: Argument list too long. Program  too large (103 insns), at most 4096 insns

Failed to load BPF program b'tracepoint__sched__sched_process_exec': Argument list too long

BPF Source:

#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/version.h>

int tracepoint__sched__sched_process_exec(
  struct tracepoint__sched__sched_process_exec* args
) {
  struct task_struct* task = (typeof(task))bpf_get_current_task();

  const struct mm_struct* mm = task->mm;

  unsigned long env_start = mm->env_start;
  unsigned long env_end = mm->env_end;

  // Read up to 512 environment variables -- only way I could find to "limit"
  // the loop to satisfy the verifier.
  char var[12];
  for (int n = 0; n < 512; n++) {
    int result = bpf_probe_read_str(&var, sizeof var, (void*)env_start);
    if (result <= 0) {
      break;
    }
    env_start += result;
    if (
      var[0] == 'H' &&
      var[1] == 'I' &&
      var[2] == 'S' &&
      var[3] == 'T' &&
      var[4] == 'S' &&
      var[5] == 'I' &&
      var[6] == 'Z' &&
      var[7] == 'E'
    ) {
      bpf_trace_printk("Got it: %s\n", var);
      break;
    }
  }

  return 0;
}

Basic loader program for reproducing:

#!/usr/bin/env python3

import sys

from bcc import BPF


if __name__ == '__main__':
    source = open("./foo.c").read()
    try:
        BPF(text=source.encode("utf-8")).trace_print()
    except Exception as e:
        error = str(e)
        sys.exit(error)

like image 927
coxley Avatar asked Oct 24 '25 03:10

coxley


1 Answers

bpf: Argument list too long. Program too large (103 insns), at most 4096 insns

Looking at the error message, my guess would be that your program has 103 instructions and it's rejected because it's too complex. That is, the verifier gave up before analyzing all instructions on all paths.

On Linux 5.15 with a privileged user, the verifier gives up after reading 1 million instructions (the complexity limit). Since it has to analyze all paths through the program, a program with a small number of instructions can have a very high complexity. That's particularly the case when you have loops and many conditions, as is your case.


Why is the error message confusing? This error message is coming from libbpf.c:

if (ret < 0 && errno == E2BIG) {
  fprintf(stderr,
          "bpf: %s. Program %s too large (%u insns), at most %d insns\n\n",
          strerror(errno), attr->name, insns_cnt, BPF_MAXINSNS);
  return -1;
}

Since the bpf(2) syscall returns E2BIG both when the program is too large and when its complexity is too high, libbpf prints the same error message for both cases, always with at most 4096 instructions. I'm confident upstream would accept a patch to improve that error message.

like image 196
pchaigno Avatar answered Oct 26 '25 00:10

pchaigno



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!