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