Программа BPF, прикрепленная к getname, не будет вызвана при вызове системного вызова renameat2.

Вопрос или проблема

Я экспериментирую с программой 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-программ абсолютно отсутствует, необходимо:

  1. Проверить соответствие UID.
  2. Убедиться в наличии ссылок на функции в ядре.
  3. Изучить другие функции, которые могут быть вызваны вместо getname.

Такой подход поможет вам детально разобраться в том, как работает ваш BPF-код, и вычислить, где именно происходит сбой в вызовах.

Оцените материал
Добавить комментарий

Капча загружается...