Может ли eBPF изменить возвращаемое значение или параметры системного вызова?

Чтобы смоделировать некоторое поведение, я хотел бы прикрепить зонд к системному вызову и изменить возвращаемое значение при передаче определенных параметров. В качестве альтернативы также было бы достаточно изменить параметры функции до того, как они станут процессами.

Возможно ли это с BPF?


person Georg Schölly    schedule 24.03.2017    source источник
comment
systemtap может это сделать   -  person fche    schedule 02.08.2017
comment
Если кто-то зайдет сюда, заинтересованный в использовании eBPF для сети, есть XDP, который позволяет некоторый контроль над пакетами. Пример переадресации   -  person pedro_sland    schedule 17.02.2018


Ответы (4)


Я считаю, что присоединение eBPF к kprobes/kretprobes дает вам доступ для чтения к аргументам функции и возвращаемым значениям, но вы не можете вмешиваться в них. Я НЕ уверен на 100%; хорошими местами, где можно запросить подтверждение, является проект IO Visor список рассылки или канал IRC. (#iovisor на irc.oftc.net).

В качестве альтернативного решения я знаю, что вы можете по крайней мере изменить возвращаемое значение системного вызова с помощью strace с параметром -e . Цитирование страницы руководства:

-e inject=set[:error=errno|:retval=value][:signal=sig][:when=expr]
       Perform syscall tampering for the specified set of syscalls.

Кроме того, на Fosdem 2017 была представлена ​​презентация по этому вопросу и внедрению ошибок. если это представляет для вас какой-либо интерес. Вот один пример команды из слайдов:

strace -P precious.txt -efault=unlink:retval=0 unlink precious.txt

Редактировать: Как заявил Бен, eBPF на kprobes и tracepoints окончательно доступен только для чтения для отслеживания и мониторинга вариантов использования. Я также получил подтверждение об этом на IRC.

person Qeole    schedule 26.03.2017

Внутри зондов ядра (kprobes) виртуальная машина eBPF имеет доступ только для чтения к параметрам системного вызова и возвращаемому значению.

Однако программа eBPF будет иметь собственный код возврата. Можно применить профиль seccomp, который перехватывает коды возврата BPF (НЕ eBPF; спасибо @qeole) и прерывает системный вызов во время выполнения.

Допустимые модификации во время выполнения:

  • SECCOMP_RET_KILL: Немедленное убийство с помощью SIGSYS
  • SECCOMP_RET_TRAP: отправить перехватываемый SIGSYS, дающий возможность эмулировать системный вызов
  • SECCOMP_RET_ERRNO: Принудительное значение errno
  • SECCOMP_RET_TRACE: отдайте решение ptracer или установите errno на -ENOSYS
  • SECCOMP_RET_ALLOW: Разрешить

https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt

Метод SECCOMP_RET_TRACE позволяет изменять выполняемый системный вызов, аргументы или возвращаемое значение. Это зависит от архитектуры, и изменение обязательных внешних ссылок может вызвать ошибку ENOSYS.

Он делает это, передавая выполнение ожидающей пользовательской ptrace, которая имеет возможность изменять отслеживаемую память процесса, регистры и файловые дескрипторы.

Трассировщик должен вызвать ptrace, а затем waitpid. Пример:

ptrace(PTRACE_SETOPTIONS, tracee_pid, 0, PTRACE_O_TRACESECCOMP);
waitpid(tracee_pid, &status, 0);

http://man7.org/linux/man-pages/man2/ptrace.2.html

Когда waitpid возвращается, в зависимости от содержимого status можно получить возвращаемое значение seccomp с помощью операции PTRACE_GETEVENTMSG ptrace. Это позволит получить значение seccomp SECCOMP_RET_DATA, которое представляет собой 16-битное поле, установленное программой BPF. Пример:

ptrace(PTRACE_GETEVENTMSG, tracee_pid, 0, &data);

Аргументы системного вызова можно изменить в памяти перед продолжением работы. Вы можете выполнить один вход системного вызова или выйти с помощью шага PTRACE_SYSCALL. Возвращаемые системным вызовом значения могут быть изменены в пользовательском пространстве перед возобновлением выполнения; базовая программа не сможет увидеть, что значения, возвращаемые системным вызовом, были изменены.

Пример реализации: Фильтрация и изменение системных вызовов с помощью seccomp и ptrace

person Ben Walther    schedule 07.04.2017
comment
Небольшая поправка: seccomp поддерживает только «классическую» версию BPF. В настоящее время поддержка eBPF ("расширенный BPF") не предусмотрена. - person Qeole; 07.04.2017
comment
Обновлено! Спасибо Кёле. - person Ben Walther; 08.04.2017

Можно вводить ошибки в вызов системного вызова с помощью eBPF: https://lwn.net/Articles/740146/

Существует функция bpf с именем bpf_override_return(), которая может переопределить возвращаемое значение вызова. Это пример использования скрытой копии в качестве внешнего интерфейса: https://github.com/iovisor/bcc/blob/master/tools/inject.py

Согласно справочной странице Linux:

bpf_override_return() доступен только в том случае, если ядро ​​было скомпилировано с параметром конфигурации CONFIG_BPF_KPROBE_OVERRIDE, и в этом случае оно работает только с функциями, помеченными ALLOW_ERROR_INJECTION в коде ядра.

Кроме того, помощник доступен только для архитектур с параметром CONFIG_FUNCTION_ERROR_INJECTION. На момент написания этой статьи только архитектура x86 поддерживает эту функцию.

В структуру внедрения ошибок можно добавить функцию. Дополнительную информацию можно найти здесь: https://github.com/iovisor/bcc/issues/2485

person gluckzhang    schedule 17.03.2021
comment
Спасибо! Важное предостережение: это работает только для небольшого числа функций ядра из белого списка. - person Georg Schölly; 18.03.2021
comment
Спасибо @GeorgSchölly :) Я обновил ответ о белом списке. Но я не нашел точного белого списка по умолчанию для функции внедрения ошибок. Кажется, он поддерживает большинство функций системных вызовов в ядре. - person gluckzhang; 18.03.2021
comment
Похоже, это всего лишь несколько функций, большинство из которых связаны с файловой системой btrfs. Эти функции снабжены аннотациями ALLOW_ERROR_INJECTION в исходном коде. - person Georg Schölly; 26.03.2021

Можно изменить часть памяти пользовательского пространства с помощью eBPF. Как указано в заголовочном файле bpf.h:

 * int bpf_probe_write_user(void *dst, const void *src, u32 len)
 *  Description
 *      Attempt in a safe way to write *len* bytes from the buffer
 *      *src* to *dst* in memory. It only works for threads that are in
 *      user context, and *dst* must be a valid user space address.
 *
 *      This helper should not be used to implement any kind of
 *      security mechanism because of TOC-TOU attacks, but rather to
 *      debug, divert, and manipulate execution of semi-cooperative
 *      processes.
 *
 *      Keep in mind that this feature is meant for experiments, and it
 *      has a risk of crashing the system and running programs.
 *      Therefore, when an eBPF program using this helper is attached,
 *      a warning including PID and process name is printed to kernel
 *      logs.
 *  Return
 *      0 on success, or a negative error in case of failure.

Кроме того, цитата из Вопросы и ответы по дизайну BPF:

Программы трассировки BPF могут перезаписывать пользовательскую память текущей задачи с помощью bpf_probe_write_user(). Каждый раз, когда такая программа загружается, ядро ​​​​выводит предупреждающее сообщение, поэтому этот помощник полезен только для экспериментов и прототипов. Программы трассировки BPF доступны только для root.

Ваш eBPF может записывать данные в области памяти пользовательского пространства. Обратите внимание, что вы по-прежнему не можете изменять структуры ядра из программы eBPF.

person Gilad Reti    schedule 02.03.2020