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.  */
