From patchwork Wed Mar 9 03:05:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 11283 Received: (qmail 33895 invoked by alias); 9 Mar 2016 03:05:30 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 33781 invoked by uid 89); 9 Mar 2016 03:05:29 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=crashed, 475, 80bit, 756 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Wed, 09 Mar 2016 03:05:24 +0000 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (Postfix) with ESMTPS id EB31819CB83 for ; Wed, 9 Mar 2016 03:05:22 +0000 (UTC) Received: from cascais.lan (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u2935LRu010157 for ; Tue, 8 Mar 2016 22:05:22 -0500 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [pushed] Fix floating conversion buffer overrun when host/target format matches Date: Wed, 9 Mar 2016 03:05:21 +0000 Message-Id: <1457492721-12157-1-git-send-email-palves@redhat.com> Running the testsuite with a gdb configured with --enable-libmcheck reveals a problem: (gdb) ptype 3 * 2.0 type = <12-byte float> memory clobbered past end of allocated block ERROR: Process no longer exists UNRESOLVED: gdb.ada/ptype_arith_binop.exp: ptype 3 * 2.0 (gdb) PASS: gdb.dlang/expression.exp: ptype 0x1.FFFFFFFFFFFFFp1023 ptype 0x1p-52L type = real memory clobbered past end of allocated block ERROR: Process no longer exists UNRESOLVED: gdb.dlang/expression.exp: ptype 0x1p-52L Even though this shows up with Ada and D, it's easy to reproduce in C too. We just need to print a long double, when the current arch is 32-bit, which is the default when gdb starts up: $ ./gdb -q -ex "ptype 1.0L" type = long double memory clobbered past end of allocated block Aborted (core dumped) Valgrind shows: ==22159== Invalid write of size 8 ==22159== at 0x8464A9: floatformat_from_doublest (doublest.c:756) ==22159== by 0x846822: store_typed_floating (doublest.c:867) ==22159== by 0x6A7959: value_from_double (value.c:3662) ==22159== by 0x6A9F2D: evaluate_subexp_standard (eval.c:745) ==22159== by 0x7F31AF: evaluate_subexp_c (c-lang.c:716) ==22159== by 0x6A8986: evaluate_subexp (eval.c:79) ==22159== by 0x6A8BA3: evaluate_type (eval.c:174) ==22159== by 0x817CCF: whatis_exp (typeprint.c:456) ==22159== by 0x817EAA: ptype_command (typeprint.c:508) ==22159== by 0x5F267B: do_cfunc (cli-decode.c:105) ==22159== by 0x5F5618: cmd_func (cli-decode.c:1885) ==22159== by 0x83622A: execute_command (top.c:475) ==22159== Address 0x8c6cb28 is 8 bytes inside a block of size 12 alloc'd ==22159== at 0x4C2AA98: calloc (vg_replace_malloc.c:711) ==22159== by 0x87384A: xcalloc (common-utils.c:83) ==22159== by 0x873889: xzalloc (common-utils.c:93) ==22159== by 0x6A34CB: allocate_value_contents (value.c:1036) ==22159== by 0x6A3501: allocate_value (value.c:1047) ==22159== by 0x6A790A: value_from_double (value.c:3656) ==22159== by 0x6A9F2D: evaluate_subexp_standard (eval.c:745) ==22159== by 0x7F31AF: evaluate_subexp_c (c-lang.c:716) ==22159== by 0x6A8986: evaluate_subexp (eval.c:79) ==22159== by 0x6A8BA3: evaluate_type (eval.c:174) ==22159== by 0x817CCF: whatis_exp (typeprint.c:456) ==22159== by 0x817EAA: ptype_command (typeprint.c:508) ==22159== type = long double (gdb) Even if the target and host floating-point formats match, the length of the types might still be different. On x86, long double is the 80-bit extended precision type on both 32-bit and 64-bit ABIs, but by default it is stored as 12 bytes on 32-bit, and 16 bytes on 64-bit, for alignment reasons. Several places in doublest.c already consider this, but floatformat_to_doublest and floatformat_from_doublest miss it. E.g., convert_typed_floating and store_typed_floating, Tested on x86-64 Fedora 23 with --enable-libmcheck, where it fixes the crashed above. gdb/ChangeLog: 2016-03-09 Pedro Alves * doublest.c: Extend comments. (floatformat_to_doublest, floatformat_from_doublest): Copy the floatformat's total size, not the host type's size. --- gdb/ChangeLog | 6 ++++++ gdb/doublest.c | 30 ++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index abb224d..1718ff1 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,11 @@ 2016-03-09 Pedro Alves + * doublest.c: Extend comments. + (floatformat_to_doublest, floatformat_from_doublest): Copy the + floatformat's total size, not the host type's size. + +2016-03-09 Pedro Alves + * doublest.c (floatformat_totalsize_bytes): New function. (floatformat_from_type): Assert that the type's length is at least as long as the floatformat's totalsize. diff --git a/gdb/doublest.c b/gdb/doublest.c index fe06fec..088037e 100644 --- a/gdb/doublest.c +++ b/gdb/doublest.c @@ -686,7 +686,15 @@ floatformat_mantissa (const struct floatformat *fmt, If the host and target formats agree, we just copy the raw data into the appropriate type of variable and return, letting the host increase precision as necessary. Otherwise, we call the conversion - routine and let it do the dirty work. */ + routine and let it do the dirty work. Note that even if the target + and host floating-point formats match, the length of the types + might still be different, so the conversion routines must make sure + to not overrun any buffers. For example, on x86, long double is + the 80-bit extended precision type on both 32-bit and 64-bit ABIs, + but by default it is stored as 12 bytes on 32-bit, and 16 bytes on + 64-bit, for alignment reasons. See comment in store_typed_floating + for a discussion about zeroing out remaining bytes in the target + buffer. */ static const struct floatformat *host_float_format = GDB_HOST_FLOAT_FORMAT; static const struct floatformat *host_double_format = GDB_HOST_DOUBLE_FORMAT; @@ -707,25 +715,26 @@ floatformat_to_doublest (const struct floatformat *fmt, const void *in, DOUBLEST *out) { gdb_assert (fmt != NULL); + if (fmt == host_float_format) { - float val; + float val = 0; - memcpy (&val, in, sizeof (val)); + memcpy (&val, in, floatformat_totalsize_bytes (fmt)); *out = val; } else if (fmt == host_double_format) { - double val; + double val = 0; - memcpy (&val, in, sizeof (val)); + memcpy (&val, in, floatformat_totalsize_bytes (fmt)); *out = val; } else if (fmt == host_long_double_format) { - long double val; + long double val = 0; - memcpy (&val, in, sizeof (val)); + memcpy (&val, in, floatformat_totalsize_bytes (fmt)); *out = val; } else @@ -737,23 +746,24 @@ floatformat_from_doublest (const struct floatformat *fmt, const DOUBLEST *in, void *out) { gdb_assert (fmt != NULL); + if (fmt == host_float_format) { float val = *in; - memcpy (out, &val, sizeof (val)); + memcpy (out, &val, floatformat_totalsize_bytes (fmt)); } else if (fmt == host_double_format) { double val = *in; - memcpy (out, &val, sizeof (val)); + memcpy (out, &val, floatformat_totalsize_bytes (fmt)); } else if (fmt == host_long_double_format) { long double val = *in; - memcpy (out, &val, sizeof (val)); + memcpy (out, &val, floatformat_totalsize_bytes (fmt)); } else convert_doublest_to_floatformat (fmt, in, out);