diff mbox series

[4.19,38/92] kprobes: Fix NULL pointer dereference at kprobe_ftrace_handler

Message ID 20200820091539.592290034@linuxfoundation.org
State New
Headers show
Series None | expand

Commit Message

Greg KH Aug. 20, 2020, 9:21 a.m. UTC
From: Muchun Song <songmuchun@bytedance.com>

commit 0cb2f1372baa60af8456388a574af6133edd7d80 upstream.

We found a case of kernel panic on our server. The stack trace is as
follows(omit some irrelevant information):

  BUG: kernel NULL pointer dereference, address: 0000000000000080
  RIP: 0010:kprobe_ftrace_handler+0x5e/0xe0
  RSP: 0018:ffffb512c6550998 EFLAGS: 00010282
  RAX: 0000000000000000 RBX: ffff8e9d16eea018 RCX: 0000000000000000
  RDX: ffffffffbe1179c0 RSI: ffffffffc0535564 RDI: ffffffffc0534ec0
  RBP: ffffffffc0534ec1 R08: ffff8e9d1bbb0f00 R09: 0000000000000004
  R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
  R13: ffff8e9d1f797060 R14: 000000000000bacc R15: ffff8e9ce13eca00
  CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  CR2: 0000000000000080 CR3: 00000008453d0005 CR4: 00000000003606e0
  DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
  DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
  Call Trace:
   <IRQ>
   ftrace_ops_assist_func+0x56/0xe0
   ftrace_call+0x5/0x34
   tcpa_statistic_send+0x5/0x130 [ttcp_engine]

The tcpa_statistic_send is the function being kprobed. After analysis,
the root cause is that the fourth parameter regs of kprobe_ftrace_handler
is NULL. Why regs is NULL? We use the crash tool to analyze the kdump.

  crash> dis tcpa_statistic_send -r
         <tcpa_statistic_send>: callq 0xffffffffbd8018c0 <ftrace_caller>

The tcpa_statistic_send calls ftrace_caller instead of ftrace_regs_caller.
So it is reasonable that the fourth parameter regs of kprobe_ftrace_handler
is NULL. In theory, we should call the ftrace_regs_caller instead of the
ftrace_caller. After in-depth analysis, we found a reproducible path.

  Writing a simple kernel module which starts a periodic timer. The
  timer's handler is named 'kprobe_test_timer_handler'. The module
  name is kprobe_test.ko.

  1) insmod kprobe_test.ko
  2) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}'
  3) echo 0 > /proc/sys/kernel/ftrace_enabled
  4) rmmod kprobe_test
  5) stop step 2) kprobe
  6) insmod kprobe_test.ko
  7) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}'

We mark the kprobe as GONE but not disarm the kprobe in the step 4).
The step 5) also do not disarm the kprobe when unregister kprobe. So
we do not remove the ip from the filter. In this case, when the module
loads again in the step 6), we will replace the code to ftrace_caller
via the ftrace_module_enable(). When we register kprobe again, we will
not replace ftrace_caller to ftrace_regs_caller because the ftrace is
disabled in the step 3). So the step 7) will trigger kernel panic. Fix
this problem by disarming the kprobe when the module is going away.

Link: https://lkml.kernel.org/r/20200728064536.24405-1-songmuchun@bytedance.com

Cc: stable@vger.kernel.org
Fixes: ae6aa16fdc16 ("kprobes: introduce ftrace based optimization")
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Muchun Song <songmuchun@bytedance.com>
Co-developed-by: Chengming Zhou <zhouchengming@bytedance.com>
Signed-off-by: Chengming Zhou <zhouchengming@bytedance.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

---
 kernel/kprobes.c |    7 +++++++
 1 file changed, 7 insertions(+)

Comments

Naresh Kamboju Sept. 28, 2020, 8:02 p.m. UTC | #1
On Thu, 20 Aug 2020 at 15:23, Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> From: Muchun Song <songmuchun@bytedance.com>
>
> commit 0cb2f1372baa60af8456388a574af6133edd7d80 upstream.
>
> We found a case of kernel panic on our server. The stack trace is as
> follows(omit some irrelevant information):
>
>   BUG: kernel NULL pointer dereference, address: 0000000000000080
>   RIP: 0010:kprobe_ftrace_handler+0x5e/0xe0
>   RSP: 0018:ffffb512c6550998 EFLAGS: 00010282
>   RAX: 0000000000000000 RBX: ffff8e9d16eea018 RCX: 0000000000000000
>   RDX: ffffffffbe1179c0 RSI: ffffffffc0535564 RDI: ffffffffc0534ec0
>   RBP: ffffffffc0534ec1 R08: ffff8e9d1bbb0f00 R09: 0000000000000004
>   R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
>   R13: ffff8e9d1f797060 R14: 000000000000bacc R15: ffff8e9ce13eca00
>   CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>   CR2: 0000000000000080 CR3: 00000008453d0005 CR4: 00000000003606e0
>   DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>   DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
>   Call Trace:
>    <IRQ>
>    ftrace_ops_assist_func+0x56/0xe0
>    ftrace_call+0x5/0x34
>    tcpa_statistic_send+0x5/0x130 [ttcp_engine]
>
> The tcpa_statistic_send is the function being kprobed. After analysis,
> the root cause is that the fourth parameter regs of kprobe_ftrace_handler
> is NULL. Why regs is NULL? We use the crash tool to analyze the kdump.
>
>   crash> dis tcpa_statistic_send -r
>          <tcpa_statistic_send>: callq 0xffffffffbd8018c0 <ftrace_caller>
>
> The tcpa_statistic_send calls ftrace_caller instead of ftrace_regs_caller.
> So it is reasonable that the fourth parameter regs of kprobe_ftrace_handler
> is NULL. In theory, we should call the ftrace_regs_caller instead of the
> ftrace_caller. After in-depth analysis, we found a reproducible path.
>
>   Writing a simple kernel module which starts a periodic timer. The
>   timer's handler is named 'kprobe_test_timer_handler'. The module
>   name is kprobe_test.ko.
>
>   1) insmod kprobe_test.ko
>   2) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}'
>   3) echo 0 > /proc/sys/kernel/ftrace_enabled
>   4) rmmod kprobe_test
>   5) stop step 2) kprobe
>   6) insmod kprobe_test.ko
>   7) bpftrace -e 'kretprobe:kprobe_test_timer_handler {}'
>
> We mark the kprobe as GONE but not disarm the kprobe in the step 4).
> The step 5) also do not disarm the kprobe when unregister kprobe. So
> we do not remove the ip from the filter. In this case, when the module
> loads again in the step 6), we will replace the code to ftrace_caller
> via the ftrace_module_enable(). When we register kprobe again, we will
> not replace ftrace_caller to ftrace_regs_caller because the ftrace is
> disabled in the step 3). So the step 7) will trigger kernel panic. Fix
> this problem by disarming the kprobe when the module is going away.
>
> Link: https://lkml.kernel.org/r/20200728064536.24405-1-songmuchun@bytedance.com
>
> Cc: stable@vger.kernel.org
> Fixes: ae6aa16fdc16 ("kprobes: introduce ftrace based optimization")
> Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
> Signed-off-by: Muchun Song <songmuchun@bytedance.com>
> Co-developed-by: Chengming Zhou <zhouchengming@bytedance.com>
> Signed-off-by: Chengming Zhou <zhouchengming@bytedance.com>
> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>


stable rc branch 4.19 build warning on arm64.

../kernel/kprobes.c: In function ‘kill_kprobe’:
../kernel/kprobes.c:1070:33: warning: statement with no effect [-Wunused-value]
 1070 | #define disarm_kprobe_ftrace(p) (-ENODEV)
      |                                 ^
../kernel/kprobes.c:2090:3: note: in expansion of macro ‘disarm_kprobe_ftrace’
 2090 |   disarm_kprobe_ftrace(p);
      |   ^~~~~~~~~~~~~~~~~~~~


>
> ---
>  kernel/kprobes.c |    7 +++++++
>  1 file changed, 7 insertions(+)
>
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -2077,6 +2077,13 @@ static void kill_kprobe(struct kprobe *p
>          * the original probed function (which will be freed soon) any more.
>          */
>         arch_remove_kprobe(p);
> +
> +       /*
> +        * The module is going away. We should disarm the kprobe which
> +        * is using ftrace.
> +        */
> +       if (kprobe_ftrace(p))
> +               disarm_kprobe_ftrace(p);
>  }
>
>  /* Disable one kprobe */
>
Steven Rostedt Sept. 28, 2020, 10:09 p.m. UTC | #2
On Tue, 29 Sep 2020 01:32:59 +0530
Naresh Kamboju <naresh.kamboju@linaro.org> wrote:

> stable rc branch 4.19 build warning on arm64.
> 
> ../kernel/kprobes.c: In function ‘kill_kprobe’:
> ../kernel/kprobes.c:1070:33: warning: statement with no effect [-Wunused-value]
>  1070 | #define disarm_kprobe_ftrace(p) (-ENODEV)
>       |                                 ^
> ../kernel/kprobes.c:2090:3: note: in expansion of macro ‘disarm_kprobe_ftrace’
>  2090 |   disarm_kprobe_ftrace(p);
>       |   ^~~~~~~~~~~~~~~~~~~~

Seems to affect upstream as well.

-- Steve
Steven Rostedt Sept. 28, 2020, 10:15 p.m. UTC | #3
On Mon, 28 Sep 2020 18:09:42 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue, 29 Sep 2020 01:32:59 +0530

> Naresh Kamboju <naresh.kamboju@linaro.org> wrote:

> 

> > stable rc branch 4.19 build warning on arm64.

> > 

> > ../kernel/kprobes.c: In function ‘kill_kprobe’:

> > ../kernel/kprobes.c:1070:33: warning: statement with no effect [-Wunused-value]

> >  1070 | #define disarm_kprobe_ftrace(p) (-ENODEV)

> >       |                                 ^

> > ../kernel/kprobes.c:2090:3: note: in expansion of macro ‘disarm_kprobe_ftrace’

> >  2090 |   disarm_kprobe_ftrace(p);

> >       |   ^~~~~~~~~~~~~~~~~~~~  

> 

> Seems to affect upstream as well.

> 


Bah, no (tested the wrong kernel).

You want this commit too:

10de795a5addd ("kprobes: Fix compiler warning for !CONFIG_KPROBES_ON_FTRACE")

-- Steve
Masami Hiramatsu (Google) Sept. 29, 2020, 5:49 a.m. UTC | #4
Hi,

On Mon, 28 Sep 2020 18:15:35 -0400
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Mon, 28 Sep 2020 18:09:42 -0400
> Steven Rostedt <rostedt@goodmis.org> wrote:
> 
> > On Tue, 29 Sep 2020 01:32:59 +0530
> > Naresh Kamboju <naresh.kamboju@linaro.org> wrote:
> > 
> > > stable rc branch 4.19 build warning on arm64.
> > > 
> > > ../kernel/kprobes.c: In function ‘kill_kprobe’:
> > > ../kernel/kprobes.c:1070:33: warning: statement with no effect [-Wunused-value]
> > >  1070 | #define disarm_kprobe_ftrace(p) (-ENODEV)
> > >       |                                 ^
> > > ../kernel/kprobes.c:2090:3: note: in expansion of macro ‘disarm_kprobe_ftrace’
> > >  2090 |   disarm_kprobe_ftrace(p);
> > >       |   ^~~~~~~~~~~~~~~~~~~~  
> > 
> > Seems to affect upstream as well.
> > 
> 
> Bah, no (tested the wrong kernel).
> 
> You want this commit too:
> 
> 10de795a5addd ("kprobes: Fix compiler warning for !CONFIG_KPROBES_ON_FTRACE")

It seems that this commit's Fixes tag is wrong.

ae6aa16fdc163 (Masami Hiramatsu           2012-06-05 19:28:32 +0900 1079) #define prepare_kprobe(p)     arch_prepare_kprobe(p)
12310e3437554 (Jessica Yu                 2018-01-10 00:51:23 +0100 1080) #define arm_kprobe_ftrace(p)  (-ENODEV)
297f9233b53a0 (Jessica Yu                 2018-01-10 00:51:24 +0100 1081) #define disarm_kprobe_ftrace(p)       (-ENODEV)

Thus, it should have "Fixes: 297f9233b53a ("kprobes: Propagate error from disarm_kprobe_ftrace()")"

$ git tag -l --contains 297f9233b53a | grep "^v[[:digit:].]*$" | cut -f1-2 -d. | uniq
v4.16
v4.17
v4.18
v4.19
v4.20
v5.0
v5.1
v5.2
v5.3
v5.4
v5.5
v5.6
v5.7
v5.8

So the commit 10de795a5addd must be backported to 4.19.y and 5.4.y.

Thank you,
Greg KH Sept. 29, 2020, 6:52 a.m. UTC | #5
On Tue, Sep 29, 2020 at 02:49:54PM +0900, Masami Hiramatsu wrote:
> Hi,

> 

> On Mon, 28 Sep 2020 18:15:35 -0400

> Steven Rostedt <rostedt@goodmis.org> wrote:

> 

> > On Mon, 28 Sep 2020 18:09:42 -0400

> > Steven Rostedt <rostedt@goodmis.org> wrote:

> > 

> > > On Tue, 29 Sep 2020 01:32:59 +0530

> > > Naresh Kamboju <naresh.kamboju@linaro.org> wrote:

> > > 

> > > > stable rc branch 4.19 build warning on arm64.

> > > > 

> > > > ../kernel/kprobes.c: In function ‘kill_kprobe’:

> > > > ../kernel/kprobes.c:1070:33: warning: statement with no effect [-Wunused-value]

> > > >  1070 | #define disarm_kprobe_ftrace(p) (-ENODEV)

> > > >       |                                 ^

> > > > ../kernel/kprobes.c:2090:3: note: in expansion of macro ‘disarm_kprobe_ftrace’

> > > >  2090 |   disarm_kprobe_ftrace(p);

> > > >       |   ^~~~~~~~~~~~~~~~~~~~  

> > > 

> > > Seems to affect upstream as well.

> > > 

> > 

> > Bah, no (tested the wrong kernel).

> > 

> > You want this commit too:

> > 

> > 10de795a5addd ("kprobes: Fix compiler warning for !CONFIG_KPROBES_ON_FTRACE")

> 

> It seems that this commit's Fixes tag is wrong.

> 

> ae6aa16fdc163 (Masami Hiramatsu           2012-06-05 19:28:32 +0900 1079) #define prepare_kprobe(p)     arch_prepare_kprobe(p)

> 12310e3437554 (Jessica Yu                 2018-01-10 00:51:23 +0100 1080) #define arm_kprobe_ftrace(p)  (-ENODEV)

> 297f9233b53a0 (Jessica Yu                 2018-01-10 00:51:24 +0100 1081) #define disarm_kprobe_ftrace(p)       (-ENODEV)

> 

> Thus, it should have "Fixes: 297f9233b53a ("kprobes: Propagate error from disarm_kprobe_ftrace()")"

> 

> $ git tag -l --contains 297f9233b53a | grep "^v[[:digit:].]*$" | cut -f1-2 -d. | uniq

> v4.16

> v4.17

> v4.18

> v4.19

> v4.20

> v5.0

> v5.1

> v5.2

> v5.3

> v5.4

> v5.5

> v5.6

> v5.7

> v5.8

> 

> So the commit 10de795a5addd must be backported to 4.19.y and 5.4.y.


Now queued up, thanks.

greg k-h
diff mbox series

Patch

--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -2077,6 +2077,13 @@  static void kill_kprobe(struct kprobe *p
 	 * the original probed function (which will be freed soon) any more.
 	 */
 	arch_remove_kprobe(p);
+
+	/*
+	 * The module is going away. We should disarm the kprobe which
+	 * is using ftrace.
+	 */
+	if (kprobe_ftrace(p))
+		disarm_kprobe_ftrace(p);
 }
 
 /* Disable one kprobe */