| From db01d7ff7d3bd510d51304053c0eb0f1bdbffd90 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 7 Sep 2023 12:28:20 -0400 |
| Subject: ring-buffer: Do not attempt to read past "commit" |
| |
| From: Steven Rostedt (Google) <rostedt@goodmis.org> |
| |
| [ Upstream commit 95a404bd60af6c4d9d8db01ad14fe8957ece31ca ] |
| |
| When iterating over the ring buffer while the ring buffer is active, the |
| writer can corrupt the reader. There's barriers to help detect this and |
| handle it, but that code missed the case where the last event was at the |
| very end of the page and has only 4 bytes left. |
| |
| The checks to detect the corruption by the writer to reads needs to see the |
| length of the event. If the length in the first 4 bytes is zero then the |
| length is stored in the second 4 bytes. But if the writer is in the process |
| of updating that code, there's a small window where the length in the first |
| 4 bytes could be zero even though the length is only 4 bytes. That will |
| cause rb_event_length() to read the next 4 bytes which could happen to be off the |
| allocated page. |
| |
| To protect against this, fail immediately if the next event pointer is |
| less than 8 bytes from the end of the commit (last byte of data), as all |
| events must be a minimum of 8 bytes anyway. |
| |
| Link: https://lore.kernel.org/all/20230905141245.26470-1-Tze-nan.Wu@mediatek.com/ |
| Link: https://lore.kernel.org/linux-trace-kernel/20230907122820.0899019c@gandalf.local.home |
| |
| Cc: Masami Hiramatsu <mhiramat@kernel.org> |
| Cc: Mark Rutland <mark.rutland@arm.com> |
| Reported-by: Tze-nan Wu <Tze-nan.Wu@mediatek.com> |
| Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| kernel/trace/ring_buffer.c | 5 +++++ |
| 1 file changed, 5 insertions(+) |
| |
| diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c |
| index b15d72284c7f7..69db849ae7dad 100644 |
| --- a/kernel/trace/ring_buffer.c |
| +++ b/kernel/trace/ring_buffer.c |
| @@ -2352,6 +2352,11 @@ rb_iter_head_event(struct ring_buffer_iter *iter) |
| */ |
| commit = rb_page_commit(iter_head_page); |
| smp_rmb(); |
| + |
| + /* An event needs to be at least 8 bytes in size */ |
| + if (iter->head > commit - 8) |
| + goto reset; |
| + |
| event = __rb_page_index(iter_head_page, iter->head); |
| length = rb_event_length(event); |
| |
| -- |
| 2.40.1 |
| |