Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754078AbbFQJ5M (ORCPT ); Wed, 17 Jun 2015 05:57:12 -0400 Received: from szxga02-in.huawei.com ([119.145.14.65]:35260 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751480AbbFQJ5E (ORCPT ); Wed, 17 Jun 2015 05:57:04 -0400 From: Wang Nan To: , , , , , CC: , , Subject: [PATCH v2] perf tools: Fix a problem when opening old perf.data with different byte order Date: Wed, 17 Jun 2015 09:56:39 +0000 Message-ID: <1434534999-85347-1-git-send-email-wangnan0@huawei.com> X-Mailer: git-send-email 1.8.3.4 In-Reply-To: <20150617084616.GA8535@krava.redhat.com> References: <20150617084616.GA8535@krava.redhat.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.107.197.200] X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4168 Lines: 115 Following error occurs when trying to use 'perf report' on x86_64 to cross analysis a perf.data generated by an old perf on a big-endian machine: # perf report *** Error in `/home/w00229757/perf': free(): invalid next size (fast): 0x00000000032c99f0 *** ======= Backtrace: ========= /lib64/libc.so.6(+0x6eeef)[0x7ff6ff7e2eef] /lib64/libc.so.6(+0x78cae)[0x7ff6ff7eccae] /lib64/libc.so.6(+0x79987)[0x7ff6ff7ed987] /path/to/perf[0x4ac734] /path/to/perf[0x4ac829] /path/to/perf(perf_header__process_sections+0x129)[0x4ad2c9] /path/to/perf(perf_session__read_header+0x2e1)[0x4ad9e1] /path/to/perf(perf_session__new+0x168)[0x4bd458] /path/to/perf(cmd_report+0xfa0)[0x43eb70] /path/to/perf[0x47adc3] /path/to/perf(main+0x5f6)[0x42fd06] /lib64/libc.so.6(__libc_start_main+0xf5)[0x7ff6ff795bd5] /path/to/perf[0x42fe35] ======= Memory map: ======== [SNIP] The bug is in perf_event__attr_swap(). It swaps all fields in 'struct perf_event_attr' without checking whether the swapped field exist or not. In addition, in read_event_desc() allocs memory for attr according to size read from perf.data. Therefore, if the perf.data is collected by an old perf (without aux_watermark, for example), when perf_event__attr_swap() swaping attr->aux_watermark it destroy malloc's metadata. This patch introduces boundary checking in perf_event__attr_swap(). It adds macros bswap_field_64 and bswap_field_32 into perf_event__attr_swap() to make it only swap exist fields. Signed-off-by: Wang Nan --- Patch v2: - Undefine macros inside perf_event__attr_swap() - Check read_format + 1 before swap bitfields after read_format. --- tools/perf/util/session.c | 50 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88d87bf..425ec5c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -517,20 +517,42 @@ void perf_event__attr_swap(struct perf_event_attr *attr) { attr->type = bswap_32(attr->type); attr->size = bswap_32(attr->size); - attr->config = bswap_64(attr->config); - attr->sample_period = bswap_64(attr->sample_period); - attr->sample_type = bswap_64(attr->sample_type); - attr->read_format = bswap_64(attr->read_format); - attr->wakeup_events = bswap_32(attr->wakeup_events); - attr->bp_type = bswap_32(attr->bp_type); - attr->bp_addr = bswap_64(attr->bp_addr); - attr->bp_len = bswap_64(attr->bp_len); - attr->branch_sample_type = bswap_64(attr->branch_sample_type); - attr->sample_regs_user = bswap_64(attr->sample_regs_user); - attr->sample_stack_user = bswap_32(attr->sample_stack_user); - attr->aux_watermark = bswap_32(attr->aux_watermark); - - swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); + +#define bswap_safe(f, n) \ + (attr->size > (offsetof(struct perf_event_attr, f) + \ + sizeof(attr->f) * (n))) +#define bswap_field(f, sz) \ +do { \ + if (bswap_safe(f, 0)) \ + attr->f = bswap_##sz(attr->f); \ +} while(0) +#define bswap_field_32(f) bswap_field(f, 32) +#define bswap_field_64(f) bswap_field(f, 64) + + bswap_field_64(config); + bswap_field_64(sample_period); + bswap_field_64(sample_type); + bswap_field_64(read_format); + bswap_field_32(wakeup_events); + bswap_field_32(bp_type); + bswap_field_64(bp_addr); + bswap_field_64(bp_len); + bswap_field_64(branch_sample_type); + bswap_field_64(sample_regs_user); + bswap_field_32(sample_stack_user); + bswap_field_32(aux_watermark); + + /* + * After read_format are bitfields. Check read_format because + * we are unable to use offsetof on bitfield. + */ + if (bswap_safe(read_format, 1)) + swap_bitfield((u8 *) (&attr->read_format + 1), + sizeof(u64)); +#undef bswap_field_64 +#undef bswap_field_32 +#undef bswap_field +#undef bswap_safe } static void perf_event__hdr_attr_swap(union perf_event *event, -- 1.8.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/