All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Sheldrake <Kevin.Sheldrake@microsoft.com>
To: "bpf@vger.kernel.org" <bpf@vger.kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>,
	KP Singh <kpsingh@google.com>
Subject: [PATCH bpf-next v2] Update perf ring buffer to prevent corruption
Date: Thu, 5 Nov 2020 15:16:14 +0000	[thread overview]
Message-ID: <VI1PR8303MB00802FE5D289E0D7BA95B7DDFBEE0@VI1PR8303MB0080.EURPRD83.prod.outlook.com> (raw)

Resent due to some failure at my end.  Apologies if it arrives twice.

From 63e34d4106b4dd767f9bfce951f8a35f14b52072 Mon Sep 17 00:00:00 2001
From: Kevin Sheldrake <kevin.sheldrake@microsoft.com>
Date: Thu, 5 Nov 2020 12:18:53 +0000
Subject: [PATCH] Update perf ring buffer to prevent corruption from
 bpf_perf_output_event()

The bpf_perf_output_event() helper takes a sample size parameter of u64, but
the underlying perf ring buffer uses a u16 internally. This 64KB maximum size
has to also accommodate a variable sized header. Failure to observe this
restriction can result in corruption of the perf ring buffer as samples
overlap.

Track the sample size and return -E2BIG if too big to fit into the u16
size parameter.

Signed-off-by: Kevin Sheldrake <kevin.sheldrake@microsoft.com>
---
 include/linux/perf_event.h |  2 +-
 kernel/events/core.c       | 40 ++++++++++++++++++++++++++--------------
 2 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 0c19d27..b9802e5 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1060,7 +1060,7 @@ extern void perf_output_sample(struct perf_output_handle *handle,
 			       struct perf_event_header *header,
 			       struct perf_sample_data *data,
 			       struct perf_event *event);
-extern void perf_prepare_sample(struct perf_event_header *header,
+extern int perf_prepare_sample(struct perf_event_header *header,
 				struct perf_sample_data *data,
 				struct perf_event *event,
 				struct pt_regs *regs);
diff --git a/kernel/events/core.c b/kernel/events/core.c
index da467e1..c6c4a3c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7016,15 +7016,17 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
 	return callchain ?: &__empty_callchain;
 }
 
-void perf_prepare_sample(struct perf_event_header *header,
+int perf_prepare_sample(struct perf_event_header *header,
 			 struct perf_sample_data *data,
 			 struct perf_event *event,
 			 struct pt_regs *regs)
 {
 	u64 sample_type = event->attr.sample_type;
+	u32 header_size = header->size;
+
 
 	header->type = PERF_RECORD_SAMPLE;
-	header->size = sizeof(*header) + event->header_size;
+	header_size = sizeof(*header) + event->header_size;
 
 	header->misc = 0;
 	header->misc |= perf_misc_flags(regs);
@@ -7042,7 +7044,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 
 		size += data->callchain->nr;
 
-		header->size += size * sizeof(u64);
+		header_size += size * sizeof(u64);
 	}
 
 	if (sample_type & PERF_SAMPLE_RAW) {
@@ -7067,7 +7069,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 			size = sizeof(u64);
 		}
 
-		header->size += size;
+		header_size += size;
 	}
 
 	if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
@@ -7079,7 +7081,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 			size += data->br_stack->nr
 			      * sizeof(struct perf_branch_entry);
 		}
-		header->size += size;
+		header_size += size;
 	}
 
 	if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER))
@@ -7095,7 +7097,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 			size += hweight64(mask) * sizeof(u64);
 		}
 
-		header->size += size;
+		header_size += size;
 	}
 
 	if (sample_type & PERF_SAMPLE_STACK_USER) {
@@ -7108,7 +7110,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 		u16 stack_size = event->attr.sample_stack_user;
 		u16 size = sizeof(u64);
 
-		stack_size = perf_sample_ustack_size(stack_size, header->size,
+		stack_size = perf_sample_ustack_size(stack_size, header_size,
 						     data->regs_user.regs);
 
 		/*
@@ -7120,7 +7122,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 			size += sizeof(u64) + stack_size;
 
 		data->stack_user_size = stack_size;
-		header->size += size;
+		header_size += size;
 	}
 
 	if (sample_type & PERF_SAMPLE_REGS_INTR) {
@@ -7135,7 +7137,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 			size += hweight64(mask) * sizeof(u64);
 		}
 
-		header->size += size;
+		header_size += size;
 	}
 
 	if (sample_type & PERF_SAMPLE_PHYS_ADDR)
@@ -7154,7 +7156,7 @@ void perf_prepare_sample(struct perf_event_header *header,
 	if (sample_type & PERF_SAMPLE_AUX) {
 		u64 size;
 
-		header->size += sizeof(u64); /* size */
+		header_size += sizeof(u64); /* size */
 
 		/*
 		 * Given the 16bit nature of header::size, an AUX sample can
@@ -7162,14 +7164,20 @@ void perf_prepare_sample(struct perf_event_header *header,
 		 * Make sure this doesn't happen by using up to U16_MAX bytes
 		 * per sample in total (rounded down to 8 byte boundary).
 		 */
-		size = min_t(size_t, U16_MAX - header->size,
+		size = min_t(size_t, U16_MAX - header_size,
 			     event->attr.aux_sample_size);
 		size = rounddown(size, 8);
 		size = perf_prepare_sample_aux(event, data, size);
 
-		WARN_ON_ONCE(size + header->size > U16_MAX);
-		header->size += size;
+		WARN_ON_ONCE(size + header_size > U16_MAX);
+		header_size += size;
 	}
+
+	if (header_size > U16_MAX)
+		return -E2BIG;
+
+	header->size = header_size;
+
 	/*
 	 * If you're adding more sample types here, you likely need to do
 	 * something about the overflowing header::size, like repurpose the
@@ -7179,6 +7187,8 @@ void perf_prepare_sample(struct perf_event_header *header,
 	 * do here next.
 	 */
 	WARN_ON_ONCE(header->size & 7);
+
+	return 0;
 }
 
 static __always_inline int
@@ -7196,7 +7206,9 @@ __perf_event_output(struct perf_event *event,
 	/* protect the callchain buffers */
 	rcu_read_lock();
 
-	perf_prepare_sample(&header, data, event, regs);
+	err = perf_prepare_sample(&header, data, event, regs);
+	if (err)
+		goto exit;
 
 	err = output_begin(&handle, event, header.size);
 	if (err)
-- 
2.7.4


             reply	other threads:[~2020-11-05 15:16 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-05 15:16 Kevin Sheldrake [this message]
2020-11-05 19:07 ` [PATCH bpf-next v2] Update perf ring buffer to prevent corruption Andrii Nakryiko
2020-11-05 19:37   ` KP Singh
2020-11-06  4:19 ` Alexei Starovoitov
2020-11-09 11:29   ` Peter Zijlstra
2020-11-09 14:22     ` [EXTERNAL] " Kevin Sheldrake
2020-11-09 17:55       ` Peter Zijlstra
2020-11-09 18:22   ` Peter Zijlstra

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=VI1PR8303MB00802FE5D289E0D7BA95B7DDFBEE0@VI1PR8303MB0080.EURPRD83.prod.outlook.com \
    --to=kevin.sheldrake@microsoft.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=kpsingh@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.