ftrace: system crash when changing current_tracer from function_graph via echo
Asked Answered
L

1

27

I have been playing with ftrace recently to monitor some behavior characteristics of my system. I've been handling switching the trace on/off via a small script. After running the script, my system would crash and reboot itself. Initially, I believed that there might be an error with the script itself, but I have since determined that the crash and reboot is a result of echoing some tracer to /sys/kernel/debug/tracing/current_tracer when current_tracer is set to function_graph.

That is, the following sequence of commands will produce the crash/reboot:

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

Durning the reboot after the crash caused by the above echo statements, I see a lot of output that reads:

clearing orphaned inode <inode>

I tried to reproduce this problem by replacing the current_tracer value from function_graph to something else in a C program:

#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;
}

Oddly enough, the C program does not crash my system.

I originally encountered this problem while using Ubuntu (Unity environment) 16.04 LTS and confirmed it to be an issue on the 4.4.0 and 4.5.5 kernels. I have also tested this issue on a machine running Ubuntu (Mate environment) 15.10, on the 4.2.0 and 4.5.5 kernels, but was unable to reproduce the issue. This has only confused me further.

Can anyone give me insight on what is happening? Specifically, why would I be able to write() but not echo to /sys/kernel/debug/tracing/current_tracer?

Update

As vielmetti pointed out, others have had a similar issue (as seen here).

The ftrace_disable_ftrace_graph_caller() modifies jmp instruction at ftrace_graph_call assuming it's a 5 bytes near jmp (e9 ). However it's a short jmp consisting of 2 bytes only (eb ). And ftrace_stub() is located just below the ftrace_graph_caller so modification above breaks the instruction resulting in kernel oops on the ftrace_stub() with the invalid opcode like below:

The patch (shown below) solved the echo issue, but I still do not understand why echo was breaking previously when write() was not.

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)

via https://lkml.org/lkml/2016/5/16/493

Leftwich answered 31/5, 2016 at 11:1 Comment(8)
Have you tried reproducing using a single C program (with no exec calls except possibly for the call to dd)? Sometimes shells do funky stuff.Mocambique
Have you considered asking your question on one of the Linux Stack Exchange sites?Tirzah
@o11c, the error seems to occur only when writing to /sys/kernel/debug/tracing/current_tracer. I say this because I can produce this problem without invoking the entire script, but instead by just echoing to that file. I listened to your suggestion with this in mind, and I have updated my post.Leftwich
@ashes999, I had not originally considered that this issue might be better suited for one of the Linux Stack Exchange sites, and I'm not sure I would have known which one to choose. That being said, I've updated my post with some interesting information that does make me wonder if this is distro/environment related.Leftwich
Are you at all able to reproduce this crash on your laptop? Otherwise it is rather meaningless to mention something similar does not crash.Underfeed
@Underfeed No, I am not able to reproduce the problem at all on my laptop. After I am able to test the C program on my PC, and write the next update, I will remove the unnecessary information. I was thinking it might be important to mention because the same kernel version (4.5.5) is working on two different machines, meaning that the issue might not be related to specific kernel versions.Leftwich
You could try adding the dd activity in your c program or from command line, in between switching tracers. That is what happens in your shell script.Underfeed
@Underfeed I will be updating the post soon and I will try to make this more clear, but the PC does not actually need dd to be run to fail. After a reboot, I can simply cause the crash by echo'ing. I think it is time to remove the script, and the C program will reflect only the problematic behavior -- changing the tracer from function_graph to something else.Leftwich
P
3

Looks like you are not the only person to notice this behavior. I see

as a report of the problem, and

as a patch to the kernel that addresses it. Reading through that whole thread it appears that the issue is some compiler optimizations.

Poore answered 12/6, 2016 at 20:35 Comment(6)
The patch solves the issue, but I am still not clear on what exactly is happening. If you could be more specific in your answer as to what is going on (even if it involves quoting the article that you've linked) and offering insight as to why echoing the function_graph tracer would crash a system but write() would not, then I will gladly mark this answer as complete and award the bounty.Leftwich
The relevant text from the article reads as follows. I don't know if this applies 100% to your issue, but it should be helpful. "The ftrace_disable_ftrace_graph_caller() modifies jmp instruction at ftrace_graph_call assuming it's a 5 bytes near jmp (e9 <offset>). However it's a short jmp consisting of 2 bytes only (eb <offset>). And ftrace_stub() is located just below the ftrace_graph_caller so modification above breaks the instruction resulting in kernel oops on the ftrace_stub() with the invalid opcode."Poore
It does apply to my problem, but I am unclear on why this is the case only for echo and not write().Leftwich
I'm also unclear as to why the kernel does the right thing on write() but not echo. I presume that you are using the shell's built-in echo; does the behavior differ with /bin/echo? Does it do the same thing if you cat a file instead of echoing it? What shell are you using, and does it do the same thing in every shell that you could be using? I also notice that you are opening the file O_WRONLY; it's possible depending on the details of your "echo" implementation that there are other flags set, and you'd have to trace that.Poore
I was using the shell's built-in echo, and the behavior does not differ with /bin/echo. Cat'ing the file is fine, as I imagine it would be (since we are just reading the contents instead of writing).Leftwich
Also, I've put quite some thought in to this. Your answer solves my problem, but it does not answer my question (this might be partially my fault, as I realize now the question is slightly ambiguous). As such, I believe you deserve the bounty (for solving the problem) but that your answer should not be the accepted one (as it does not answer the question). Sorry about the mixup, and I'll be updating the question to reduce ambiguity.Leftwich

© 2022 - 2024 — McMap. All rights reserved.