[PATCH bpf-next] Update perf ring buffer to prevent corruption

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>From 8425426d0fb256acf7c2e50f0aa642450adc366a Mon Sep 17 00:00:00 2001
From: Kevin Sheldrake <kevin.sheldrake@xxxxxxxxxxxxx>
Date: Wed, 4 Nov 2020 15:42:54 +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.

Truncate the raw sample type used by EBPF so that the total size of the
sample is < U16_MAX. The size parameter of the received sample will match the
size of the truncated sample, so users can be confident about how much data
was received.

Signed-off-by: Kevin Sheldrake <kevin.sheldrake@xxxxxxxxxxxxx>
---
 kernel/events/core.c | 83 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 58 insertions(+), 25 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index da467e1..45684a6 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7016,6 +7016,21 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
 	return callchain ?: &__empty_callchain;
 }
 
+bool
+__prep_frag_size(u32 sum, int *frag_size, u16 header_size)
+{
+	u32 size, diff;
+	size = round_up(sum + *frag_size + sizeof(u32), sizeof(u64));
+	if (header_size + size < U16_MAX)
+		return false;
+	/* fragment is too big, need to truncate */
+	diff = round_up(header_size + size - U16_MAX, sizeof(u64));
+	*frag_size = round_up(*frag_size - diff, sizeof(u32));
+	if (*frag_size % 8 == 0)
+		*frag_size += sizeof(u32);
+	return true;
+}
+
 void perf_prepare_sample(struct perf_event_header *header,
 			 struct perf_sample_data *data,
 			 struct perf_event *event,
@@ -7045,31 +7060,6 @@ void perf_prepare_sample(struct perf_event_header *header,
 		header->size += size * sizeof(u64);
 	}
 
-	if (sample_type & PERF_SAMPLE_RAW) {
-		struct perf_raw_record *raw = data->raw;
-		int size;
-
-		if (raw) {
-			struct perf_raw_frag *frag = &raw->frag;
-			u32 sum = 0;
-
-			do {
-				sum += frag->size;
-				if (perf_raw_frag_last(frag))
-					break;
-				frag = frag->next;
-			} while (1);
-
-			size = round_up(sum + sizeof(u32), sizeof(u64));
-			raw->size = size - sizeof(u32);
-			frag->pad = raw->size - sum;
-		} else {
-			size = sizeof(u64);
-		}
-
-		header->size += size;
-	}
-
 	if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
 		int size = sizeof(u64); /* nr */
 		if (data->br_stack) {
@@ -7170,6 +7160,49 @@ void perf_prepare_sample(struct perf_event_header *header,
 		WARN_ON_ONCE(size + header->size > U16_MAX);
 		header->size += size;
 	}
+
+	if (sample_type & PERF_SAMPLE_RAW) {
+		struct perf_raw_record *raw = data->raw;
+		int size;
+		bool truncate = false;
+
+		/*
+		 * Given the 16bit nature of header::size, a RAW sample can
+		 * easily overflow it. Make sure this doesn't happen by using
+		 * up to U16_MAX bytes per sample in total (rounded down to 8
+		 * byte boundary). This requires modification of the fragment
+		 * sizes, so the first oversized fragment is truncated to
+		 * the maximum safe size, and every subsequent fragment is
+		 * truncated to 0 size.
+		 */
+
+		if (raw) {
+			struct perf_raw_frag *frag = &raw->frag;
+			u32 sum = 0;
+
+			do {
+				if (truncate) {
+					frag->size = 0;
+				} else {
+					truncate = __prep_frag_size(sum,
+						&frag->size, header->size);
+				}
+				sum += frag->size;
+				if (perf_raw_frag_last(frag))
+					break;
+				frag = frag->next;
+			} while (1);
+
+			size = round_up(sum + sizeof(u32), sizeof(u64));
+			raw->size = size - sizeof(u32);
+			frag->pad = raw->size - sum;
+		} else {
+			size = sizeof(u64);
+		}
+
+		header->size += size;
+	}
+
 	/*
 	 * If you're adding more sample types here, you likely need to do
 	 * something about the overflowing header::size, like repurpose the
-- 
2.7.4





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux