lkml.org 
[lkml]   [2013]   [Jul]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH V5 05/12] perf tools: tidy up sample parsing overflow checking
Date
The size of data retrieved from a sample event must be
validated to ensure it does not go past the end of the
event. That was being done sporadically and without
considering integer overflows.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/evsel.c | 101 ++++++++++++++++++++++++++++++------------------
1 file changed, 63 insertions(+), 38 deletions(-)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 724b75a..febff21 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1114,24 +1114,38 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
return 0;
}

-static bool sample_overlap(const union perf_event *event,
- const void *offset, u64 size)
+static inline bool overflow_one(const void *endp, const void *offset)
{
- const void *base = event;
-
- if (offset + size > base + event->header.size)
- return true;
+ return offset + sizeof(u64) > endp;
+}

- return false;
+static inline bool overflow(const void *endp, u16 max_size, const void *offset,
+ u64 size)
+{
+ return size > max_size || offset + size > endp;
}

+#define OVERFLOW_CHECK_ONE(offset) \
+ do { \
+ if (overflow_one(endp, (offset))) \
+ return -EFAULT; \
+ } while (0)
+
+#define OVERFLOW_CHECK(offset, size) \
+ do { \
+ if (overflow(endp, max_size, (offset), (size))) \
+ return -EFAULT; \
+ } while (0)
+
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
struct perf_sample *data)
{
u64 type = evsel->attr.sample_type;
- u64 regs_user = evsel->attr.sample_regs_user;
bool swapped = evsel->needs_swap;
const u64 *array;
+ u16 max_size = event->header.size;
+ const void *endp = (void *)event + max_size;
+ u64 sz;

/*
* used for cross-endian analysis. See git commit 65014ab3
@@ -1153,6 +1167,11 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,

array = event->sample.array;

+ /*
+ * sample_size is based on PERF_SAMPLE_MASK which includes up to
+ * PERF_SAMPLE_PERIOD. After that overflow_one() or overflow() must be
+ * used to check the format does not go past the end of the event.
+ */
if (evsel->sample_size + sizeof(event->header) > event->header.size)
return -EFAULT;

@@ -1221,20 +1240,19 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
}

if (type & PERF_SAMPLE_CALLCHAIN) {
- if (sample_overlap(event, array, sizeof(data->callchain->nr)))
- return -EFAULT;
-
- data->callchain = (struct ip_callchain *)array;
+ const u64 max_callchain_nr = UINT64_MAX / sizeof(u64);

- if (sample_overlap(event, array, data->callchain->nr))
+ OVERFLOW_CHECK_ONE(array);
+ data->callchain = (struct ip_callchain *)array++;
+ if (data->callchain->nr > max_callchain_nr)
return -EFAULT;
-
- array += 1 + data->callchain->nr;
+ sz = data->callchain->nr * sizeof(u64);
+ OVERFLOW_CHECK(array, sz);
+ array = (void*)array + sz;
}

if (type & PERF_SAMPLE_RAW) {
- const u64 *pdata;
-
+ OVERFLOW_CHECK_ONE(array);
u.val64 = *array;
if (WARN_ONCE(swapped,
"Endianness of raw data not corrected!\n")) {
@@ -1243,65 +1261,72 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
}
-
- if (sample_overlap(event, array, sizeof(u32)))
- return -EFAULT;
-
data->raw_size = u.val32[0];
- pdata = (void *) array + sizeof(u32);
+ array = (void *)array + sizeof(u32);

- if (sample_overlap(event, pdata, data->raw_size))
- return -EFAULT;
-
- data->raw_data = (void *) pdata;
-
- array = (void *)array + data->raw_size + sizeof(u32);
+ OVERFLOW_CHECK(array, data->raw_size);
+ data->raw_data = (void *)array;
+ array = (void *)array + data->raw_size;
}

if (type & PERF_SAMPLE_BRANCH_STACK) {
- u64 sz;
+ const u64 max_branch_nr = UINT64_MAX / sizeof(struct branch_entry);

- data->branch_stack = (struct branch_stack *)array;
- array++; /* nr */
+ OVERFLOW_CHECK_ONE(array);
+ data->branch_stack = (struct branch_stack *)array++;

+ if (data->branch_stack->nr > max_branch_nr)
+ return -EFAULT;
sz = data->branch_stack->nr * sizeof(struct branch_entry);
- sz /= sizeof(u64);
- array += sz;
+ OVERFLOW_CHECK(array, sz);
+ array = (void *)array + sz;
}

if (type & PERF_SAMPLE_REGS_USER) {
+ u64 avail;
+
/* First u64 tells us if we have any regs in sample. */
- u64 avail = *array++;
+ OVERFLOW_CHECK_ONE(array);
+ avail = *array++;

if (avail) {
+ u64 regs_user = evsel->attr.sample_regs_user;
+
+ sz = hweight_long(regs_user) * sizeof(u64);
+ OVERFLOW_CHECK(array, sz);
data->user_regs.regs = (u64 *)array;
- array += hweight_long(regs_user);
+ array = (void *)array + sz;
}
}

if (type & PERF_SAMPLE_STACK_USER) {
- u64 size = *array++;
+ OVERFLOW_CHECK_ONE(array);
+ sz = *array++;

data->user_stack.offset = ((char *)(array - 1)
- (char *) event);

- if (!size) {
+ if (!sz) {
data->user_stack.size = 0;
} else {
+ OVERFLOW_CHECK(array, sz);
data->user_stack.data = (char *)array;
- array += size / sizeof(*array);
+ array = (void *)array + sz;
+ OVERFLOW_CHECK_ONE(array);
data->user_stack.size = *array++;
}
}

data->weight = 0;
if (type & PERF_SAMPLE_WEIGHT) {
+ OVERFLOW_CHECK_ONE(array);
data->weight = *array;
array++;
}

data->data_src = PERF_MEM_DATA_SRC_NONE;
if (type & PERF_SAMPLE_DATA_SRC) {
+ OVERFLOW_CHECK_ONE(array);
data->data_src = *array;
array++;
}
--
1.7.11.7


\
 
 \ /
  Last update: 2013-07-11 16:01    [W:0.211 / U:0.464 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site