lkml.org 
[lkml]   [2013]   [Jul]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH V8 05/15] 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 | 102 ++++++++++++++++++++++++++++++------------------
    1 file changed, 64 insertions(+), 38 deletions(-)

    diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
    index 724b75a..20e2ed9 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,73 @@ 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-18 15:21    [W:4.397 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site