Вопрос или проблема
Я экспериментирую с программой BPF, которая должна прикрепляться к двум функциям “getname”, вызываемым из системного вызова renameat2
, определенного в linux/fs/namei.c следующим образом:
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname, unsigned int, flags)
{
return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname),
flags);
}
getname
вызывает getname_flags
, которая, в свою очередь, вызывает strncpy_from_user
. Мне нужно получить доступ к параметру char __user * name
, поэтому я пытался создать kprobes, fentries и fexits (с простой программой “print”), чтобы попытаться перехватить все три функции.
С getname* я получаю много выходных данных, что означает, что моя программа BPF действительно выполняется. Однако, когда я вызываю “renameat2” (например, используя команду mv
в linux), я совсем не получаю выходных данных.
Это, по сути, программа, которую я сейчас использую, и которая не вызывается при использовании команды mv
:
SEC("fentry/getname_flags")
int BPF_PROG(hijack_getname, char *filename) {
uid_t uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
if (uid == 1002) { //хардкодированный uid
bpf_printk("<вход в getname_flags> [%s]", filename);
}
}
Если я создаю программу трассировки BPF, которая прикрепляется к входу и выходу renameat2, я могу четко видеть, что между входом и выходом нет вызова “getname”.
Как я и говорил, я также пробовал с kprobe и fexit. Я не могу прикрепиться к strncpy_from_user
без получения странных ошибок о “Os: 22 – недействительный аргумент”.
Я действительно не могу понять, что происходит, поэтому любая помощь будет оценена :,)
(P.S. Я также опубликовал это на stackoverflow)
Ответ или решение
Ответ на вопрос о том, почему программа BPF, прикрепленная к функции getname
, не вызывается при обращении к системному вызову renameat2
, требует внимательного анализа вызовов функций и внутренней работы ядра Linux.
1. Понимание связки renameat2
и getname
Системный вызов renameat2
определен как:
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname, unsigned int, flags)
{
return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname), flags);
}
При вызове renameat2
, функции getname
вызываются для преобразования пользовательских строк (путь к файлу) в структуры, управляемые ядром. Однако стоит учитывать, что реализация getname
проходит не через стандартные пути, в которые могут быть привязаны программы BPF, такие как fentry и fexit.
2. Потенциальные причины отсутствия вызовов
-
Оптимизация кодом: В некоторых версиях ядра функции, такие как
getname
, могут быть инлайнаны или оптимизированы, что препятствует их нормальному вызову и, соответственно, биндению BPF программ. -
Контекст вызова: При выполнении системного вызова
renameat2
, контекст может отличаться от ожидаемого. Например, еслиgetname
вызывается в контексте потока, настройки юзера или состояния процесса могут не соответствовать указанному uid.
3. Рекомендации по диагностике
-
Используйте трассировку системных вызовов: Применение инструментов, таких как
strace
, может помочь понять порядок вызовов на уровне системных вызовов и выделить неявные вызовы. -
Анализируйте другие точки вызова: Убедитесь, что вызывается не сам
getname
, а его соседние функции, такие какvfs_rename
, которые могут использовать более обобщенные механизмы.
4. Настройка программы BPF
При прикреплении к strncpy_from_user
нужно убедиться, что реализация вычисления адресов корректна. В зависимости от версии ядра возможны различные проблемы совместимости. Следует проверить, какие именно интерфейсы и параметры используются.
Пример программы для трассировки strncpy_from_user
может выглядеть так:
SEC("kprobe/strncpy_from_user")
int trace_strncpy_from_user(void *ctx) {
char __user *src = (char __user *)PT_REGS_PARM1(ctx);
unsigned long num = PT_REGS_PARM2(ctx);
char dest[256];
if (num < sizeof(dest)) {
bpf_copy_from_user(dest, src, num);
bpf_printk("<enter strncpy_from_user> [%s]", dest);
}
return 0;
}
5. Прикрепление к системному вызову
Прежде всего, подтвердите, что ваши BPF-программы действительно могут быть к ним прикреплены. Например, убедитесь, что используете правильные опции для загрузки и назначения:
bpftool prog load <your-bpf.o> /sys/fs/bpf/your_prog
bpftool prog attach /sys/fs/bpf/your_prog kprobe:strncpy_from_user
Заключение
Если вывод BPF-программ абсолютно отсутствует, необходимо:
- Проверить соответствие UID.
- Убедиться в наличии ссылок на функции в ядре.
- Изучить другие функции, которые могут быть вызваны вместо
getname
.
Такой подход поможет вам детально разобраться в том, как работает ваш BPF-код, и вычислить, где именно происходит сбой в вызовах.