From patchwork Fri Nov 4 12:48:00 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 59897 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D5CB13858426 for ; Fri, 4 Nov 2022 12:48:44 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from weasel.birch.relay.mailchannels.net (weasel.birch.relay.mailchannels.net [23.83.209.247]) by sourceware.org (Postfix) with ESMTPS id 5A841385842B for ; Fri, 4 Nov 2022 12:48:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5A841385842B Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 07794880D19; Fri, 4 Nov 2022 12:48:15 +0000 (UTC) Received: from pdx1-sub0-mail-a306.dreamhost.com (unknown [127.0.0.6]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 40045880EA0; Fri, 4 Nov 2022 12:48:14 +0000 (UTC) ARC-Seal: i=1; s=arc-2022; d=mailchannels.net; t=1667566094; a=rsa-sha256; cv=none; b=66Ln9Xql/OmNaI1owzil7QjlxKaE7DBegKbtUR9XKCdShrr/X4aO/LjY/2SHdYqf4WWJ5N vQ2A3LVg563ZYZMq8wDLGOZogm6FLvZVxjqtP9ufL6620Vd/ZamCMVIECGNpLv2we8/RfG zwnef3qSPl3ZjqykBEmALlayf6VPM6XWmLtTDpxdKab6GvLcUAC3FLKrKMT6xyrGv29ao6 lQzdo7yJ3icdcbVrZ4bLfa8zfu9wnWjX5hAB+X1yMy4Oz5nACIQT/oOjwCQ12pl5DtiiUH Y6mFDXGd1lHrT55RoxzLIKbbkrwLqAcWBHR0Gha8dJPsSONgX+mgOWOQeavEmg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=mailchannels.net; s=arc-2022; t=1667566094; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=7WXrdjYz7U8Zkovm5TbslRchcMh3VHMvxj1m6QR4hLE=; b=VamqlHEjdvSIJVMvInEumZQ6Qnr+KUYFnLqRMZ3LIzcgijkmTQy3Ep7hzEYR+LzVG+oxqQ WdfNJyu1V80hmZgfd1jtG/ZY5vV7wG/yXXYgr0PQZhzuUvujPlzc2NhlIWxhGOYwGPP+X+ hFximXKiXQymkiiopovqeVxnW3hErpy2EPz4XYsnL4RTj+gSkjpA3HevnYYtOWM31J/Y5k IcPM3XH69KeVnBR+qWJ0ld5MivMLnUDbMf8pwW46EUCynziahulvMrRfLfQ2HC2j3BrVwW viDWCQpyiEG//BMQ2ZzmUEd3Va16GRZ+z5OnQZcMYkUnvftU9nOktdnQZzOApg== ARC-Authentication-Results: i=1; rspamd-5cb65d95c4-p6gv9; auth=pass smtp.auth=dreamhost smtp.mailfrom=siddhesh@gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org X-MC-Relay: Bad X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Language-Stop: 47d5d8905fc45242_1667566094768_4030032335 X-MC-Loop-Signature: 1667566094767:2469572777 X-MC-Ingress-Time: 1667566094767 Received: from pdx1-sub0-mail-a306.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.105.95.133 (trex/6.7.1); Fri, 04 Nov 2022 12:48:14 +0000 Received: from fedora.redhat.com (bras-vprn-toroon4834w-lp130-15-184-147-71-211.dsl.bell.ca [184.147.71.211]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a306.dreamhost.com (Postfix) with ESMTPSA id 4N3gQj4GGwzVP; Fri, 4 Nov 2022 05:48:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gotplt.org; s=dreamhost; t=1667566093; bh=7WXrdjYz7U8Zkovm5TbslRchcMh3VHMvxj1m6QR4hLE=; h=From:To:Cc:Subject:Date:Content-Transfer-Encoding; b=vMSOnZtsxhQg1NF8PGNZAqU0sjb9DHTqPH1aObRIGRuosjgb/Obfw3rdMbAE86FC7 xcNlKcaZ0v6oSW5dOtC8sEh71vCdIolwkIKyKkWShjpJBOfLWGFx+t0n9WyITVther JWryd9MlljuqPCVX/2JwYHBC0dFBFoe4IfLx9nSxlvjcN6zTrISTFtJSl8UJx7UbLs sQ1aGXe2xvVYXiGPrCkui1kOXNpFWUzcAFVSnTgd3EcP4cfFYDA+0oPGvp7+9K+dS3 O3+u7ta28KEumyGN0Jj82srMfqV8iaq+HU5kgPjzXsoTn+NxiPY2fCSI764MaZXWfU zLFFz/kNYflpA== From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH v2] tree-object-size: Support strndup and strdup Date: Fri, 4 Nov 2022 08:48:00 -0400 Message-Id: <20221104124800.910588-1-siddhesh@gotplt.org> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20220815192311.763473-1-siddhesh@gotplt.org> References: <20220815192311.763473-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3037.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jakub@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Use string length of input to strdup to determine the usable size of the resulting object. Avoid doing the same for strndup since there's a chance that the input may be too large, resulting in an unnecessary overhead or worse, the input may not be NULL terminated, resulting in a crash where there would otherwise have been none. gcc/ChangeLog: * tree-object-size.cc (todo): New variable. (object_sizes_execute): Use it. (strdup_object_size): New function. (call_object_size): Use it. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c (test_strdup, test_strndup, test_strdup_min, test_strndup_min): New tests. (main): Call them. * gcc.dg/builtin-dynamic-object-size-1.c: Silence overread warnings. * gcc.dg/builtin-dynamic-object-size-2.c: Likewise. * gcc.dg/builtin-dynamic-object-size-3.c: Likewise. * gcc.dg/builtin-dynamic-object-size-4.c: Likewise. * gcc.dg/builtin-object-size-1.c: Silence overread warnings. Declare free, strdup and strndup. (test11): New test. (main): Call it. * gcc.dg/builtin-object-size-2.c: Silence overread warnings. Declare free, strdup and strndup. (test9): New test. (main): Call it. * gcc.dg/builtin-object-size-3.c: Silence overread warnings. Declare free, strdup and strndup. (test11): New test. (main): Call it. * gcc.dg/builtin-object-size-4.c: Silence overread warnings. Declare free, strdup and strndup. (test9): New test. (main): Call it. --- Tested: - x86_64 bootstrap and testsuite run - i686 build and testsuite run - ubsan bootstrap .../gcc.dg/builtin-dynamic-object-size-0.c | 43 +++++++++ .../gcc.dg/builtin-dynamic-object-size-1.c | 2 +- .../gcc.dg/builtin-dynamic-object-size-2.c | 2 +- .../gcc.dg/builtin-dynamic-object-size-3.c | 2 +- .../gcc.dg/builtin-dynamic-object-size-4.c | 2 +- gcc/testsuite/gcc.dg/builtin-object-size-1.c | 94 +++++++++++++++++- gcc/testsuite/gcc.dg/builtin-object-size-2.c | 94 +++++++++++++++++- gcc/testsuite/gcc.dg/builtin-object-size-3.c | 95 ++++++++++++++++++- gcc/testsuite/gcc.dg/builtin-object-size-4.c | 94 +++++++++++++++++- gcc/tree-object-size.cc | 84 +++++++++++++++- 10 files changed, 502 insertions(+), 10 deletions(-) diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c index 01a280b2d7b..4f1606a486b 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c @@ -479,6 +479,40 @@ test_loop (int *obj, size_t sz, size_t start, size_t end, int incr) return __builtin_dynamic_object_size (ptr, 0); } +/* strdup/strndup. */ + +size_t +__attribute__ ((noinline)) +test_strdup (const char *in) +{ + char *res = __builtin_strdup (in); + return __builtin_dynamic_object_size (res, 0); +} + +size_t +__attribute__ ((noinline)) +test_strndup (const char *in, size_t bound) +{ + char *res = __builtin_strndup (in, bound); + return __builtin_dynamic_object_size (res, 0); +} + +size_t +__attribute__ ((noinline)) +test_strdup_min (const char *in) +{ + char *res = __builtin_strdup (in); + return __builtin_dynamic_object_size (res, 2); +} + +size_t +__attribute__ ((noinline)) +test_strndup_min (const char *in, size_t bound) +{ + char *res = __builtin_strndup (in, bound); + return __builtin_dynamic_object_size (res, 2); +} + /* Other tests. */ struct TV4 @@ -651,6 +685,15 @@ main (int argc, char **argv) int *t = test_pr105736 (&val3); if (__builtin_dynamic_object_size (t, 0) != -1) FAIL (); + const char *str = "hello world"; + if (test_strdup (str) != __builtin_strlen (str) + 1) + FAIL (); + if (test_strndup (str, 4) != 5) + FAIL (); + if (test_strdup_min (str) != __builtin_strlen (str) + 1) + FAIL (); + if (test_strndup_min (str, 4) != 1) + FAIL (); if (nfails > 0) __builtin_abort (); diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c index 7cc8b1c9488..8f17c8edcaf 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ #define __builtin_object_size __builtin_dynamic_object_size diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c index 267dbf48ca7..3677782ff1c 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ #define __builtin_object_size __builtin_dynamic_object_size diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c index fb9dc56da7e..5b6987b7773 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ #define __builtin_object_size __builtin_dynamic_object_size diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c index 870548b4206..9d796224e96 100644 --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ #define __builtin_object_size __builtin_dynamic_object_size diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index b772e2da9b9..c6e5b4c29f8 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ typedef __SIZE_TYPE__ size_t; @@ -7,10 +7,13 @@ extern void abort (void); extern void exit (int); extern void *malloc (size_t); extern void *calloc (size_t, size_t); +extern void free (void *); extern void *alloca (size_t); extern void *memcpy (void *, const void *, size_t); extern void *memset (void *, int, size_t); extern char *strcpy (char *, const char *); +extern char *strdup (const char *); +extern char *strndup (const char *, size_t); struct A { @@ -629,6 +632,94 @@ test10 (void) } } +/* Tests for strdup/strndup. */ +size_t +__attribute__ ((noinline)) +test11 (void) +{ + int i = 0; + const char *ptr = "abcdefghijklmnopqrstuvwxyz"; + char *res = strndup (ptr, 21); + if (__builtin_object_size (res, 0) != 22) + abort (); + + free (res); + + res = strndup (ptr, 32); + if (__builtin_object_size (res, 0) != 27) + abort (); + + free (res); + + res = strdup (ptr); + if (__builtin_object_size (res, 0) != 27) + abort (); + + free (res); + + char *ptr2 = malloc (64); + strcpy (ptr2, ptr); + + res = strndup (ptr2, 21); + if (__builtin_object_size (res, 0) != 22) + abort (); + + free (res); + + res = strndup (ptr2, 32); + if (__builtin_object_size (res, 0) != 33) + abort (); + + free (res); + + res = strndup (ptr2, 128); + if (__builtin_object_size (res, 0) != 64) + abort (); + + free (res); + + res = strdup (ptr2); +#ifdef __builtin_object_size + if (__builtin_object_size (res, 0) != 27) +#else + if (__builtin_object_size (res, 0) != (size_t) -1) +#endif + abort (); + free (res); + free (ptr2); + + ptr = "abcd\0efghijklmnopqrstuvwxyz"; + res = strdup (ptr); + if (__builtin_object_size (res, 0) != 5) + abort (); + free (res); + + res = strndup (ptr, 24); + if (__builtin_object_size (res, 0) != 5) + abort (); + free (res); + + res = strndup (ptr, 2); + if (__builtin_object_size (res, 0) != 3) + abort (); + free (res); + + res = strdup (&ptr[4]); + if (__builtin_object_size (res, 0) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 4); + if (__builtin_object_size (res, 0) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 1); + if (__builtin_object_size (res, 0) != 1) + abort (); + free (res); +} + int main (void) { @@ -644,5 +735,6 @@ main (void) test8 (); test9 (1); test10 (); + test11 (); exit (0); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c index 2729538da17..639a83cfd39 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ typedef __SIZE_TYPE__ size_t; @@ -7,10 +7,13 @@ extern void abort (void); extern void exit (int); extern void *malloc (size_t); extern void *calloc (size_t, size_t); +extern void free (void *); extern void *alloca (size_t); extern void *memcpy (void *, const void *, size_t); extern void *memset (void *, int, size_t); extern char *strcpy (char *, const char *); +extern char *strdup (const char *); +extern char *strndup (const char *, size_t); struct A { @@ -544,6 +547,94 @@ test8 (unsigned cond) #endif } +/* Tests for strdup/strndup. */ +size_t +__attribute__ ((noinline)) +test9 (void) +{ + const char *ptr = "abcdefghijklmnopqrstuvwxyz"; + char *res = strndup (ptr, 21); + if (__builtin_object_size (res, 1) != 22) + abort (); + + free (res); + + res = strndup (ptr, 32); + if (__builtin_object_size (res, 1) != 27) + abort (); + + free (res); + + res = strdup (ptr); + if (__builtin_object_size (res, 1) != 27) + abort (); + + free (res); + + char *ptr2 = malloc (64); + strcpy (ptr2, ptr); + + res = strndup (ptr2, 21); + if (__builtin_object_size (res, 1) != 22) + abort (); + + free (res); + + res = strndup (ptr2, 32); + if (__builtin_object_size (res, 1) != 33) + abort (); + + free (res); + + res = strndup (ptr2, 128); + if (__builtin_object_size (res, 1) != 64) + abort (); + + free (res); + + res = strdup (ptr2); +#ifdef __builtin_object_size + if (__builtin_object_size (res, 1) != 27) +#else + if (__builtin_object_size (res, 1) != (size_t) -1) +#endif + abort (); + + free (res); + free (ptr2); + + ptr = "abcd\0efghijklmnopqrstuvwxyz"; + res = strdup (ptr); + if (__builtin_object_size (res, 1) != 5) + abort (); + free (res); + + res = strndup (ptr, 24); + if (__builtin_object_size (res, 1) != 5) + abort (); + free (res); + + res = strndup (ptr, 2); + if (__builtin_object_size (res, 1) != 3) + abort (); + free (res); + + res = strdup (&ptr[4]); + if (__builtin_object_size (res, 1) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 4); + if (__builtin_object_size (res, 1) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 1); + if (__builtin_object_size (res, 1) != 1) + abort (); + free (res); +} + int main (void) { @@ -557,5 +648,6 @@ main (void) test6 (); test7 (); test8 (1); + test9 (); exit (0); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c index 44a99189776..ff4f1747334 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ typedef __SIZE_TYPE__ size_t; @@ -7,10 +7,13 @@ extern void abort (void); extern void exit (int); extern void *malloc (size_t); extern void *calloc (size_t, size_t); +extern void free (void *); extern void *alloca (size_t); extern void *memcpy (void *, const void *, size_t); extern void *memset (void *, int, size_t); extern char *strcpy (char *, const char *); +extern char *strdup (const char *); +extern char *strndup (const char *, size_t); struct A { @@ -636,6 +639,95 @@ test10 (void) } } +/* Tests for strdup/strndup. */ +size_t +__attribute__ ((noinline)) +test11 (void) +{ + const char *ptr = "abcdefghijklmnopqrstuvwxyz"; + char *res = strndup (ptr, 21); + if (__builtin_object_size (res, 2) != 22) + abort (); + + free (res); + + res = strndup (ptr, 32); + if (__builtin_object_size (res, 2) != 27) + abort (); + + free (res); + + res = strdup (ptr); + if (__builtin_object_size (res, 2) != 27) + abort (); + + free (res); + + char *ptr2 = malloc (64); + strcpy (ptr2, ptr); + + res = strndup (ptr2, 21); + if (__builtin_object_size (res, 2) != 1) + abort (); + + free (res); + + res = strndup (ptr2, 32); + if (__builtin_object_size (res, 2) != 1) + abort (); + + free (res); + + res = strndup (ptr2, 128); + if (__builtin_object_size (res, 2) != 1) + abort (); + + free (res); + + res = strdup (ptr2); + +#ifdef __builtin_object_size + if (__builtin_object_size (res, 2) != 27) +#else + if (__builtin_object_size (res, 2) != 1) +#endif + abort (); + + free (res); + free (ptr2); + + ptr = "abcd\0efghijklmnopqrstuvwxyz"; + res = strdup (ptr); + if (__builtin_object_size (res, 2) != 5) + abort (); + free (res); + + res = strndup (ptr, 24); + if (__builtin_object_size (res, 2) != 5) + abort (); + free (res); + + res = strndup (ptr, 2); + if (__builtin_object_size (res, 2) != 3) + abort (); + free (res); + + res = strdup (&ptr[4]); + if (__builtin_object_size (res, 2) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 4); + if (__builtin_object_size (res, 2) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 1); + if (__builtin_object_size (res, 2) != 1) + abort (); + free (res); +} + int main (void) { @@ -651,5 +743,6 @@ main (void) test8 (); test9 (1); test10 (); + test11 (); exit (0); } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c index b9fddfed036..4c007c364b7 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2" } */ +/* { dg-options "-O2 -Wno-stringop-overread" } */ /* { dg-require-effective-target alloca } */ typedef __SIZE_TYPE__ size_t; @@ -7,10 +7,13 @@ extern void abort (void); extern void exit (int); extern void *malloc (size_t); extern void *calloc (size_t, size_t); +extern void free (void *); extern void *alloca (size_t); extern void *memcpy (void *, const void *, size_t); extern void *memset (void *, int, size_t); extern char *strcpy (char *, const char *); +extern char *strdup (const char *); +extern char *strndup (const char *, size_t); struct A { @@ -517,6 +520,94 @@ test8 (unsigned cond) #endif } +/* Tests for strdup/strndup. */ +size_t +__attribute__ ((noinline)) +test9 (void) +{ + const char *ptr = "abcdefghijklmnopqrstuvwxyz"; + char *res = strndup (ptr, 21); + if (__builtin_object_size (res, 3) != 22) + abort (); + + free (res); + + res = strndup (ptr, 32); + if (__builtin_object_size (res, 3) != 27) + abort (); + + free (res); + + res = strdup (ptr); + if (__builtin_object_size (res, 3) != 27) + abort (); + + free (res); + + char *ptr2 = malloc (64); + strcpy (ptr2, ptr); + + res = strndup (ptr2, 21); + if (__builtin_object_size (res, 3) != 1) + abort (); + + free (res); + + res = strndup (ptr2, 32); + if (__builtin_object_size (res, 3) != 1) + abort (); + + free (res); + + res = strndup (ptr2, 128); + if (__builtin_object_size (res, 3) != 1) + abort (); + + free (res); + + res = strdup (ptr2); +#ifdef __builtin_object_size + if (__builtin_object_size (res, 3) != 27) +#else + if (__builtin_object_size (res, 3) != 1) +#endif + abort (); + + free (res); + free (ptr2); + + ptr = "abcd\0efghijklmnopqrstuvwxyz"; + res = strdup (ptr); + if (__builtin_object_size (res, 3) != 5) + abort (); + free (res); + + res = strndup (ptr, 24); + if (__builtin_object_size (res, 3) != 5) + abort (); + free (res); + + res = strndup (ptr, 2); + if (__builtin_object_size (res, 3) != 3) + abort (); + free (res); + + res = strdup (&ptr[4]); + if (__builtin_object_size (res, 3) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 4); + if (__builtin_object_size (res, 3) != 1) + abort (); + free (res); + + res = strndup (&ptr[4], 1); + if (__builtin_object_size (res, 3) != 1) + abort (); + free (res); +} + int main (void) { @@ -530,5 +621,6 @@ main (void) test6 (); test7 (); test8 (1); + test9 (); exit (0); } diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc index 1f04cb80fd0..08e5731617d 100644 --- a/gcc/tree-object-size.cc +++ b/gcc/tree-object-size.cc @@ -89,6 +89,10 @@ static bitmap computed[OST_END]; /* Maximum value of offset we consider to be addition. */ static unsigned HOST_WIDE_INT offset_limit; +/* Tell the generic SSA updater what kind of update is needed after the pass + executes. */ +static unsigned todo; + /* Return true if VAL represents an initial size for OBJECT_SIZE_TYPE. */ static inline bool @@ -787,6 +791,73 @@ alloc_object_size (const gcall *call, int object_size_type) return bytes ? bytes : size_unknown (object_size_type); } +/* Compute __builtin_object_size for CALL, which is a call to either + BUILT_IN_STRDUP or BUILT_IN_STRNDUP; IS_STRNDUP indicates which it is. + OBJECT_SIZE_TYPE is the second argument from __builtin_object_size. + If unknown, return size_unknown (object_size_type). */ + +static tree +strdup_object_size (const gcall *call, int object_size_type, bool is_strndup) +{ + tree src = gimple_call_arg (call, 0); + tree sz = size_unknown (object_size_type); + tree n = NULL_TREE; + + if (is_strndup) + n = fold_build2 (PLUS_EXPR, sizetype, size_one_node, + gimple_call_arg (call, 1)); + + + /* For strdup, simply emit strlen (SRC) + 1 and let the optimizer fold it the + way it likes. */ + if (!is_strndup) + { + tree strlen_fn = builtin_decl_implicit (BUILT_IN_STRLEN); + if (strlen_fn) + { + sz = fold_build2 (PLUS_EXPR, sizetype, size_one_node, + build_call_expr (strlen_fn, 1, src)); + todo = TODO_update_ssa_only_virtuals; + } + } + + /* In all other cases, return the size of SRC since the object size cannot + exceed that. We cannot do this for OST_MINIMUM unless SRC points into a + string constant since otherwise the object size could go all the way down + to zero. */ + if (!size_valid_p (sz, object_size_type) + || size_unknown_p (sz, object_size_type)) + { + tree wholesrc = NULL_TREE; + if (TREE_CODE (src) == ADDR_EXPR) + wholesrc = get_base_address (TREE_OPERAND (src, 0)); + + /* If the source points within a string constant, we try to get its + length. */ + if (wholesrc && TREE_CODE (wholesrc) == STRING_CST) + { + tree len = c_strlen (src, 0); + if (len) + sz = fold_build2 (PLUS_EXPR, sizetype, size_one_node, len); + } + + /* For maximum estimate, our next best guess is the object size of the + source. */ + if (size_unknown_p (sz, object_size_type) + && !(object_size_type & OST_MINIMUM)) + compute_builtin_object_size (src, object_size_type, &sz); + } + + /* String duplication allocates at least one byte, so we should never fail + for OST_MINIMUM. */ + if ((!size_valid_p (sz, object_size_type) + || size_unknown_p (sz, object_size_type)) + && (object_size_type & OST_MINIMUM)) + sz = size_one_node; + + /* Factor in the N. */ + return n ? fold_build2 (MIN_EXPR, sizetype, n, sz) : sz; +} /* If object size is propagated from one of function's arguments directly to its return value, return that argument for GIMPLE_CALL statement CALL. @@ -1233,12 +1304,19 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call) { int object_size_type = osi->object_size_type; unsigned int varno = SSA_NAME_VERSION (ptr); + tree bytes = NULL_TREE; gcc_assert (is_gimple_call (call)); gcc_assert (!object_sizes_unknown_p (object_size_type, varno)); gcc_assert (osi->pass == 0); - tree bytes = alloc_object_size (call, object_size_type); + + bool is_strdup = gimple_call_builtin_p (call, BUILT_IN_STRDUP); + bool is_strndup = gimple_call_builtin_p (call, BUILT_IN_STRNDUP); + if (is_strdup || is_strndup) + bytes = strdup_object_size (call, object_size_type, is_strndup); + else + bytes = alloc_object_size (call, object_size_type); if (!size_valid_p (bytes, object_size_type)) bytes = size_unknown (object_size_type); @@ -1998,6 +2076,8 @@ dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call) static unsigned int object_sizes_execute (function *fun, bool early) { + todo = 0; + basic_block bb; FOR_EACH_BB_FN (bb, fun) { @@ -2094,7 +2174,7 @@ object_sizes_execute (function *fun, bool early) } fini_object_sizes (); - return 0; + return todo; } /* Simple pass to optimize all __builtin_object_size () builtins. */