From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <siddhesh@gotplt.org>
Received: from eggs.gnu.org (eggs.gnu.org [IPv6:2001:470:142:3::10])
	by sourceware.org (Postfix) with ESMTPS id 7809F3858C20
	for <gcc-patches@gcc.gnu.org>; Mon, 29 Aug 2022 14:17:14 +0000 (GMT)
DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7809F3858C20
Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org
Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=gotplt.org
Received: from cyan.elm.relay.mailchannels.net ([23.83.212.47]:65137)
	by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
	(Exim 4.90_1)
	(envelope-from <siddhesh@gotplt.org>)
	id 1oSfZd-0007hs-PU
	for gcc-patches@gcc.gnu.org; Mon, 29 Aug 2022 10:17:13 -0400
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 61DD923B2B;
	Mon, 29 Aug 2022 14:16:57 +0000 (UTC)
Received: from pdx1-sub0-mail-a304 (unknown [127.0.0.6])
	(Authenticated sender: dreamhost)
	by relay.mailchannels.net (Postfix) with ESMTPA id E027723535;
	Mon, 29 Aug 2022 14:16:56 +0000 (UTC)
ARC-Seal: i=1; s=arc-2022; d=mailchannels.net; t=1661782617; a=rsa-sha256;
	cv=none;
	b=KDjS3atiq7i1oi7cZMU8crBgc6dCZb1QiWUtb0+PicpBjHSj/zqhJifRUV5thZj6a5vE1O
	S4G3DHVDaILHGh1j7YL7W++ZWkHIZImRCB0nI678t6cA4u6CNegdo/+DOk3QxRSFpXBTDD
	yQ4IeOKfLKfWyLfE+3opxC76ovb5r5dd9TJg7vr7FDedrlWHIIMmdHU+3dhjKyJqxX6zvJ
	m4E5oKnPyk82AbrqUOcf13/2n9nnZJqPR2OjWPwWZUP27Lsl/JHjlkEJVg1iHOjwRGVObL
	tkIF6r+FkWc8Cr6rfBOukoxmmlpbM1Xgk++LOnsOZW2k2fhr0grFpRrx27AsHQ==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed;
 d=mailchannels.net;
	s=arc-2022; t=1661782617;
	h=from:from:reply-to:subject:subject:date:date:message-id:message-id:
	 to:to:cc:cc:mime-version:mime-version:content-type:content-type:
	 content-transfer-encoding:content-transfer-encoding:
	 in-reply-to:in-reply-to:references:references:dkim-signature;
	bh=m6sxvIPiTlMEk3YMNjUtiHvpeR7okMYYFjjZWZFoD/E=;
	b=JnCXk39k8PBJjnxK663T0MlupuBQSt8pcV6Lm27BKtVEYbmcM9a3SJjgfAncm2dUyfq34K
	I2F5RDxjnoZWuQ5zGCXJF2gLhZKI6r+25CvsnCgpUA3+cSHlEAgK1Kku4kAnvyi1p2Ve0D
	HxuksxHPi3wWi4xRY4X/lGQGyYPquFGuq7U1t/Pjyv1sBhBfpjTr/TtjwnkMwOZxDvPhLK
	lAUfrKg65HYxeGodKUjQ0CapCkxu0rzq6s+izsDYzjh5EQ4pOj4acKONH5EzX0fdrCe65v
	IXTWNe5alrPknXtrVqybWcY/Wi2oXgrz5cXzH1UKCinzsasEZFUBfwv09NNiQA==
ARC-Authentication-Results: i=1;
	rspamd-64cc6f7466-g7nv8;
	auth=pass smtp.auth=dreamhost smtp.mailfrom=siddhesh@gotplt.org
X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org
X-MC-Relay: Neutral
X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org
X-MailChannels-Auth-Id: dreamhost
X-Inform-Scare: 2afa39c565424d95_1661782617241_2120746168
X-MC-Loop-Signature: 1661782617241:4284309751
X-MC-Ingress-Time: 1661782617240
Received: from pdx1-sub0-mail-a304 (pop.dreamhost.com [64.90.62.162])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384)
	by 100.124.238.66 (trex/6.7.1);
	Mon, 29 Aug 2022 14:16:57 +0000
Received: from [192.168.0.182] (bras-vprn-toroon4834w-lp130-16-184-147-84-238.dsl.bell.ca [184.147.84.238])
	(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 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-a304 (Postfix) with ESMTPSA id 4MGXYz71KxzZR;
	Mon, 29 Aug 2022 07:16:55 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gotplt.org;
	s=dreamhost; t=1661782616;
	bh=m6sxvIPiTlMEk3YMNjUtiHvpeR7okMYYFjjZWZFoD/E=;
	h=Date:Subject:From:To:Cc:Content-Type:Content-Transfer-Encoding;
	b=PxBu7/veXeEU9HiSDLRF9kogs2ZHLVfSC7cIBp84RcGtBM1J9Q9+zdhgitGKmg1ID
	 ObW/GtuVHc6QQ27tmXRCOWkkpjNDp+NPeXeerXMXbN2d7DCCA5j2I5HN96YdGjyLPx
	 bQIwfkclqiG/HLkBh7C8ZbWwKLtDIjkc2VbXjT6/kKg+wxfF0TrwzhkDkUy0JqO6lQ
	 Em3fiio2vSkRcPXkE2lEV802uzqdcQdByhtXajdnFUKE9bw//t7j7ohDW1spkY850D
	 jAmOTQSHpvizqIAESbXIGpdvqXzIwfY7LT7wQUxrOh1SC0bD7utx27liU1g1831T75
	 3DMCGnPBujMxA==
Message-ID: <14f0d167-0f75-fba1-b326-071184737448@gotplt.org>
Date: Mon, 29 Aug 2022 10:16:54 -0400
MIME-Version: 1.0
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101
 Thunderbird/91.12.0
Subject: Re: [PATCH] tree-object-size: Support strndup and strdup
Content-Language: en-US
From: Siddhesh Poyarekar <siddhesh@gotplt.org>
To: gcc-patches@gcc.gnu.org
Cc: jakub@redhat.com
References: <20220815192311.763473-1-siddhesh@gotplt.org>
In-Reply-To: <20220815192311.763473-1-siddhesh@gotplt.org>
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Received-SPF: pass client-ip=23.83.212.47; envelope-from=siddhesh@gotplt.org; helo=cyan.elm.relay.mailchannels.net
X-Spam_score_int: -20
X-Spam_score: -2.1
X-Spam_bar: --
X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9,DKIM_SIGNED=0.1,DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1,DKIM_VALID_EF=-0.1,NICE_REPLY_A=-0.001,RCVD_IN_DNSWL_NONE=-0.0001,RCVD_IN_MSPIKE_H2=-0.001,SPF_HELO_NONE=0.001,SPF_PASS=-0.001,T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-Spam-Status: No, score=-3038.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,NICE_REPLY_A,SPF_FAIL,SPF_HELO_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6
X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org
List-Id: <gcc-patches.gcc.gnu.org>

Ping!

On 2022-08-15 15:23, Siddhesh Poyarekar wrote:
> 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 (get_whole_object): New function.
> 	(addr_object_size): Use it.
> 	(strdup_object_size): New function.
> 	(call_object_size): Use it.
> 	(pass_data_object_sizes, pass_data_early_object_sizes): Set
> 	todo_flags_finish to TODO_update_ssa_no_phi.
> 
> 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.
> ---
>   .../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  | 64 +++++++++++++++-
>   gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 63 ++++++++++++++-
>   gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 63 ++++++++++++++-
>   gcc/testsuite/gcc.dg/builtin-object-size-4.c  | 63 ++++++++++++++-
>   gcc/tree-object-size.cc                       | 76 +++++++++++++++++--
>   10 files changed, 366 insertions(+), 14 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..7f023708b15 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) != 0)
> +    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..4fbd372d97a 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,64 @@ 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) != 64)
> +#endif
> +    abort ();
> +
> +  free (res);
> +  free (ptr2);
> +}
> +
>   int
>   main (void)
>   {
> @@ -644,5 +705,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..beb271c5afc 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,63 @@ 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) != 64)
> +#endif
> +    abort ();
> +
> +  free (res);
> +  free (ptr2);
> +}
> +
>   int
>   main (void)
>   {
> @@ -557,5 +617,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..5c878a14647 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,63 @@ 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) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strndup (ptr2, 32);
> +  if (__builtin_object_size (res, 2) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strndup (ptr2, 128);
> +  if (__builtin_object_size (res, 2) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strdup (ptr2);
> +#ifdef __builtin_object_size
> +  if (__builtin_object_size (res, 2) != 27)
> +#else
> +  if (__builtin_object_size (res, 2) != 0)
> +#endif
> +    abort ();
> +
> +  free (res);
> +  free (ptr2);
> +}
> +
>   int
>   main (void)
>   {
> @@ -651,5 +711,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..0b1cb1e528a 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,63 @@ 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) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strndup (ptr2, 32);
> +  if (__builtin_object_size (res, 3) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strndup (ptr2, 128);
> +  if (__builtin_object_size (res, 3) != 0)
> +    abort ();
> +
> +  free (res);
> +
> +  res = strdup (ptr2);
> +#ifdef __builtin_object_size
> +  if (__builtin_object_size (res, 3) != 27)
> +#else
> +  if (__builtin_object_size (res, 3) != 0)
> +#endif
> +    abort ();
> +
> +  free (res);
> +  free (ptr2);
> +}
> +
>   int
>   main (void)
>   {
> @@ -530,5 +590,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 4eb454a4a33..c075b71db56 100644
> --- a/gcc/tree-object-size.cc
> +++ b/gcc/tree-object-size.cc
> @@ -495,6 +495,18 @@ decl_init_size (tree decl, bool min)
>     return size;
>   }
>   
> +/* Get the outermost object that PTR may point into.  */
> +
> +static tree
> +get_whole_object (const_tree ptr)
> +{
> +  tree pt_var = TREE_OPERAND (ptr, 0);
> +  while (handled_component_p (pt_var))
> +    pt_var = TREE_OPERAND (pt_var, 0);
> +
> +  return pt_var;
> +}
> +
>   /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
>      OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
>      If unknown, return size_unknown (object_size_type).  */
> @@ -514,9 +526,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
>     if (pwholesize)
>       *pwholesize = size_unknown (object_size_type);
>   
> -  pt_var = TREE_OPERAND (ptr, 0);
> -  while (handled_component_p (pt_var))
> -    pt_var = TREE_OPERAND (pt_var, 0);
> +  pt_var = get_whole_object (ptr);
>   
>     if (!pt_var)
>       return false;
> @@ -789,6 +799,53 @@ 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 = is_strndup ? gimple_call_arg (call, 1) : NULL_TREE;
> +
> +  /* 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));
> +    }
> +
> +  /* 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_whole_object (src);
> +
> +      if (!(object_size_type & OST_MINIMUM)
> +	  || (wholesrc && TREE_CODE (wholesrc) == STRING_CST))
> +	compute_builtin_object_size (src, object_size_type, &sz);
> +    }
> +
> +  if (!n)
> +    return sz;
> +
> +  /* Factor in the N.  Note that with OST_MINIMUM, even if N is known we return
> +     0 since the size could be less than N.  */
> +  return fold_build2 (MIN_EXPR, sizetype,
> +		      fold_build2 (PLUS_EXPR, sizetype, size_one_node, n),
> +		      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.
> @@ -1235,12 +1292,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);
> @@ -2113,7 +2177,7 @@ const pass_data pass_data_object_sizes =
>     PROP_objsz, /* properties_provided */
>     0, /* properties_destroyed */
>     0, /* todo_flags_start */
> -  0, /* todo_flags_finish */
> +  TODO_update_ssa_no_phi, /* todo_flags_finish */
>   };
>   
>   class pass_object_sizes : public gimple_opt_pass
> @@ -2153,7 +2217,7 @@ const pass_data pass_data_early_object_sizes =
>     0, /* properties_provided */
>     0, /* properties_destroyed */
>     0, /* todo_flags_start */
> -  0, /* todo_flags_finish */
> +  TODO_update_ssa_no_phi, /* todo_flags_finish */
>   };
>   
>   class pass_early_object_sizes : public gimple_opt_pass