Недопустимый аргумент (EINVAL) при попытке загрузить программу BPF

Я пытаюсь загрузить программу BPF с помощью системного вызова bpf, но в ответ получаю invalid argument (EINVAL). Из справочной страницы возможные причины этого находятся:

EINVAL

For BPF_PROG_LOAD, indicates an attempt to load an invalid program. 
eBPF programs can be deemed invalid due to unrecognized instructions, 
the use of reserved fields, jumps out of range, infinite loops or calls 
of unknown functions.

Похоже, что-то не так с моей программой BPF. Моя программа BPF выглядит следующим образом:

#include <uapi/linux/bpf.h>

int prog(struct pt_regs *ctx)
{
    return 0;
}

В чем уж точно не может быть ничего плохого.

Я компилирую с помощью Makefile здесь (я удалил большую часть код из test_overhead_kprobe_kern.c, чтобы дать очень простую программу для тестирования).

Что могло быть не так с моей программой, из-за чего она была отклонена?

uname -a: Linux ubuntu1710 4.13.0-32-универсальный #35-Ubuntu SMP Чт, 25 января, 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Мой полный код пользовательского пространства (в Go) выглядит следующим образом:

package main

/*
#include <stdio.h>
#include <stdlib.h>

void print(char* s) {
 printf("%s\n", s);
}
*/
import "C"

import (
    "unsafe"

    "golang.org/x/sys/unix"

    "github.com/cilium/cilium/pkg/bpf"
)
import (
    "fmt"
    "io/ioutil"
)

const (
    bufferSize           = 256
    sessionIDHTTPHeader  = "X-Session-ID"
    defaultServerAddress = "localhost"
    defaultPort          = 5050
)

const (
    BPF_PROG_TYPE_UNSPEC        = 0
    BPF_PROG_TYPE_SOCKET_FILTER = 1
    BPF_PROG_TYPE_KPROBE        = 2
    BPF_PROG_TYPE_SCHED_CLS     = 3
    BPF_PROG_TYPE_SCHED_ACT     = 4
)

type ttyWrite struct {
    Count     int32
    Buf       [bufferSize]byte
    SessionID int32
}

func main() { 

  //for i := 0; i < 6; i++ {
    //b, err := ioutil.ReadFile(fmt.Sprintf("bpf/test%d.o", i))
    b, err := ioutil.ReadFile("bpf/bpf_tty.o")
    if err != nil {
        fmt.Print(err)
    }

    err = loadProgram(BPF_PROG_TYPE_KPROBE, unsafe.Pointer(&b), len(b))
    if err != nil {
        fmt.Printf("%s\n", err)
    }
    //}

}

func loadProgram(progType int, insns unsafe.Pointer, insnCnt int) error {

    licenseBuf := "GPL"
    licenseStr := C.CString(licenseBuf)
    defer C.free(unsafe.Pointer(licenseStr))

    logStr := C.CString("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
    defer C.free(unsafe.Pointer(logStr))

    lba := struct {
        progType uint32
        //pad0        [4]byte
        insnCnt uint32
        //pad1        [4]byte
        insns    uint64
        license  uint64
        logLevel uint32
        //pad2        [4]byte
        logSize uint32
        //pad3    [4]byte
        logBuf  uint64
        kernVersion uint32
        //pad4        [4]byte
    }{
        progType: uint32(progType),     
        insnCnt:  uint32(insnCnt),
        insns:    uint64(uintptr(insns)),
        license:  uint64(uintptr(unsafe.Pointer(licenseStr))),      
        logLevel: uint32(1),
        logSize:  uint32(50),
        logBuf:   uint64(uintptr(unsafe.Pointer(logStr))),
        //logBuf: uint64(uintptr(unsafe.Pointer(bufStr))),
        // /usr/src/linux-headers-4.13.0-32-generic/include/generated/uapi/linux/version.h
        kernVersion: uint32(265485),
    }

    ret, _, err := unix.Syscall(
        unix.SYS_BPF,
        bpf.BPF_PROG_LOAD,
        uintptr(unsafe.Pointer(&lba)),
        unsafe.Sizeof(lba),
    )

    //fmt.Printf("%s\n", logBuf)
    //cs := C.CString("XXXXXXXXXX")
    C.print(logStr)
    //fmt.Printf("%c\n", *logStr)

    if ret != 0 || err != 0 {
        //fmt.Printf("%#v %d\n", logBuf, unsafe.Sizeof(lba))
        return fmt.Errorf("Unable to load program: ret: %d: %s", int(ret), err)
    }

    return nil
}

person dippynark    schedule 07.02.2018    source источник
comment
Это работает, если вы добавите SEC("kprobe/__set_task_comm") обратно?   -  person pchaigno    schedule 08.02.2018
comment
Мне пришлось добавить #include "bpf_helpers.h", но все равно не работает, с той же ошибкой :/   -  person dippynark    schedule 08.02.2018
comment
В порядке. У вас не было предупреждений во время компиляции? Он должен жаловаться на отсутствие #include <linux/ptrace.h> для struct pt_regs AFAIK.   -  person pchaigno    schedule 08.02.2018
comment
Да, у меня было предупреждение, но он все равно скомпилировался без ошибок. Я снова добавил #include <linux/ptrace.h>, и он скомпилировался без предупреждений и ошибок, но я получил ту же ошибку от системного вызова bpf.   -  person dippynark    schedule 08.02.2018
comment
В порядке. А как насчет двух последних строк, которые вы удалили, о версии и лицензии? Вы пробовали с такими?   -  person pchaigno    schedule 08.02.2018
comment
нет, та же ошибка: / мне пришлось добавить #include <linux/version.h>   -  person dippynark    schedule 08.02.2018
comment
Какой код вы используете для загрузки и присоединения вашей программы? Получаете ли вы какой-либо вывод от верификатора ядра? Вы пытались предоставить ненулевое значение log-level для bpf()? (Последнее не нужно, если я правильно помню, верификатор все равно должен печатать сообщения об ошибках, если проблема возникает на этом этапе… но на всякий случай.)   -  person Qeole    schedule 08.02.2018
comment
Я пытался предоставить буфер log_level, но он никогда не заполнялся - возможно, проблема в том, как я это делаю. Код, который я использую для загрузки программы, аналогичен следующему вопросу: stackoverflow.com/questions/48653164/   -  person dippynark    schedule 08.02.2018
comment
Итак, вы попробовали непустой буфер и указали его длину в bpf() вместе с ненулевым log-level, верно? И вы ничего не получаете в этом буфере после возврата bpf()? Кстати, после последнего вопроса вам удалось исправить свой загрузчик, чтобы получить правильные инструкции байт-кода eBPF для передачи на bpf()?   -  person Qeole    schedule 08.02.2018
comment
@Qeole Да, с уровнем журнала = 1, и буфер содержит те же символы, что и то, с чем я его инициализировал: / Пока не удалось добиться успешного возврата bpf. Я добавил параметр kern_version, который проверяется только тогда, когда тип программы равен kprobe (как в моем случае), и я получил EPERM, поэтому мне пришлось запустить мою программу с sudo (как и ожидалось, требуется CAP_SYS_ADMIN) - поэтому ошибки EINVAL я было получено ранее, возможно, из-за того, что kern_version был установлен на 0, трудно сказать. Ошибка снова стала EINVAL при использовании sudo   -  person dippynark    schedule 08.02.2018
comment
@Qeole в любом случае, мой текущий план состоит в том, чтобы понять и изменить tcptracer-bpf, который, кажется, делает что-то очень близкое к тому, что я хочу   -  person dippynark    schedule 08.02.2018
comment
@dippynark Не могли бы вы добавить свой код пользовательского пространства с вызовом системного вызова bpf?   -  person pchaigno    schedule 08.02.2018
comment
Вы уверены, что у вас есть доступный компилятор BPF C и что вы используете его для компиляции этой программы? Программы BPF не являются собственными исполняемыми файлами.   -  person    schedule 08.02.2018
comment
@pchaigno извините за поздний ответ - я добавил свой полный код пользовательского пространства в конец вопроса. Я оставил комментарии, так как это может быть полезно для того, что я пробовал. Как упомянул Qeole здесь может быть дело в том, что я передаю загрузку заголовков ELF, которые не нравятся ядру, все еще нужно изучить это   -  person dippynark    schedule 08.02.2018
comment
@duskwuff да, я компилирую программу BPF со ссылкой на Makefile   -  person dippynark    schedule 08.02.2018


Ответы (1)


Как Qeole указал в вашем предыдущем вопросе, вашей программе Go в пользовательском пространстве необходимо извлечь инструкции BPF (раздел .text) из объекта файл. В противном случае ядро ​​попытается интерпретировать двоичное содержимое как инструкции BPF и неизбежно потерпит неудачу.

person pchaigno    schedule 10.02.2018
comment
начал использовать пакет elf gobpf для загрузки программы, и теперь он работает, хотя спасибо за объяснение - изменил выбрал ответ на мой другой вопрос и на ответ Qeole :) - person dippynark; 12.02.2018