Подтвердить что ты не робот

Ftrace: сбой системы при изменении current_tracer из функции function_graph через echo

Недавно я играл с ftrace, чтобы отслеживать некоторые характеристики поведения моей системы. Я обрабатывал включение/выключение трассировки с помощью небольшого script. После запуска script моя система выйдет из строя и перезагрузится. Первоначально я считал, что может произойти ошибка с самим script, но с тех пор я решил, что авария и перезагрузка являются результатом echo использования некоторого трассера для /sys/kernel/debug/tracing/current _tracer, когда current_tracer установлено значение function_graph.

То есть следующая последовательность команд приведет к сбою/перезагрузке:

echo "function_graph" > /sys/kernel/debug/tracing/current_tracer
echo "function" > /sys/kernel/debug/tracing/current_tracer

Завершение перезагрузки после сбоя, вызванного указанными выше операторами echo, я вижу много результатов, которые читаются:

очистка осиротевшего inode <inode>

Я попытался воспроизвести эту проблему, заменив значение current_tracer на function_graph на что-то еще в программе на C:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int openCurrentTracer()
{
        int fd = open("/sys/kernel/debug/tracing/current_tracer", O_WRONLY);
        if(fd < 0)
                exit(1);

        return fd;
}

int writeTracer(int fd, char* tracer)
{
        if(write(fd, tracer, strlen(tracer)) != strlen(tracer)) {
                printf("Failure writing %s\n", tracer);
                return 0;
        }

        return 1;
}

int main(int argc, char* argv[])
{
        int fd = openCurrentTracer();

        char* blockTracer = "blk";
        if(!writeTracer(fd, blockTracer))
                return 1;
        close(fd);

        fd = openCurrentTracer();
        char* graphTracer = "function_graph";
        if(!writeTracer(fd, graphTracer))
                return 1;
        close(fd);

        printf("Preparing to fail!\n");

        fd = openCurrentTracer();
        if(!writeTracer(fd, blockTracer))
                return 1;
        close(fd);

        return 0;
}

Как ни странно, программа C не разбивает мою систему.

Первоначально я столкнулся с этой проблемой при использовании Ubuntu (Unity environment) 16.04 LTS и подтвердил, что это проблема в ядрах 4.4.0 и 4.5.5. Я также протестировал эту проблему на машине с Ubuntu (среда Mate) 15.10, в ядрах 4.2.0 и 4.5.5, но не смог воспроизвести проблему. Это только смутило меня дальше.

Может ли кто-нибудь дать мне понять, что происходит? В частности, почему я мог бы write(), но не echo в /sys/kernel/debug/tracing/current _tracer?

Обновление

Как отметил vielmetti, у других была аналогичная проблема (см. здесь).

ftrace_disable_ftrace_graph_caller() изменяет инструкцию jmp в ftrace_graph_call, предположив, что он равен 5 байтам около jmp (e9). Однако это короткий jmp, состоящий только из 2 байтов (eb). А также ftrace_stub() находится чуть ниже ftrace_graph_caller, поэтому выше, нарушает инструкцию, приводящую к ftrace_stub() с недопустимым кодом операции, как показано ниже:

Патч (показан ниже) решил проблему echo, но я до сих пор не понимаю, почему echo ломался ранее, когда write() не был.

diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S
index ed48a9f465f8..e13a695c3084 100644
--- a/arch/x86/kernel/mcount_64.S
+++ b/arch/x86/kernel/mcount_64.S
@@ -182,7 +182,8 @@ GLOBAL(ftrace_graph_call)
    jmp ftrace_stub
  #endif

 -GLOBAL(ftrace_stub)
 +/* This is weak to keep gas from relaxing the jumps */
 +WEAK(ftrace_stub)
    retq
  END(ftrace_caller)

через https://lkml.org/lkml/2016/5/16/493

4b9b3361

Ответ 1

Похоже, вы не единственный человек, который заметил это поведение. Я вижу

как отчет о проблеме, и

как патч к ядру, которое обращается к нему. Прочитав весь этот поток, кажется, что проблема связана с некоторыми оптимизациями компилятора.