diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-object-size2.C
index 7a8f4e6273320b433399dd9013f73b231be64cf1..45401b5a9c13fb08ab6ba898be8471e6dd6c3a91 100644
--- a/gcc/testsuite/g++.dg/ext/builtin-object-size2.C
+++ b/gcc/testsuite/g++.dg/ext/builtin-object-size2.C
@@ -406,6 +406,32 @@ test8 (union F *f)
     FAIL ();
 }
 
+// PR117355
+#define STR "bbbbbbbbbbbbbbbbbbbbbbbbbbb"
+
+void
+__attribute__ ((noinline))
+test9 (void)
+{
+  char line[256];
+  const char *p = STR;
+  const char *q = p + sizeof (STR) - 1;
+
+  char *q1 = line;
+  for (const char *p1 = p; p1 < q;)
+    {
+      *q1++ = *p1++;
+
+      if (p1 < q && (*q1++ = *p1++) != '\0')
+	{
+	  if (__builtin_object_size (q1 - 2, 0) == 0)
+	    __builtin_abort ();
+	  if (__builtin_object_size (q1 - 2, 1) == 0)
+	    __builtin_abort ();
+	}
+    }
+}
+
 int
 main (void)
 {
@@ -430,5 +456,6 @@ main (void)
   union F f, *fp = &f;
   __asm ("" : "+r" (fp));
   test8 (fp);
+  test9 ();
   DONE ();
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index ec2c62c96401316799167afea8bbfaf014eb7def..e0c967e003f60ca1eba43213e99e2a615dd3a46b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -619,7 +619,7 @@ test10 (void)
 	  if (__builtin_object_size (p - 3, 2) != sizeof (buf) - i + 3)
 	    FAIL ();
 #else
-	  if (__builtin_object_size (p - 3, 2) != 0)
+	  if (__builtin_object_size (p - 3, 2) != 3)
 	    FAIL ();
 #endif
 	  break;
diff --git a/gcc/tree-object-size.cc b/gcc/tree-object-size.cc
index 09aad88498eabe6d64fe682a8850d6d1b337c894..6413ebcca37c72f2a20cd2a8620438fd637647c7 100644
--- a/gcc/tree-object-size.cc
+++ b/gcc/tree-object-size.cc
@@ -344,7 +344,8 @@ init_offset_limit (void)
    be positive and hence, be within OFFSET_LIMIT for valid offsets.  */
 
 static tree
-size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
+size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE,
+		 bool strict = true)
 {
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
@@ -377,9 +378,17 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 	return sz;
 
       /* Negative or too large offset even after adjustment, cannot be within
-	 bounds of an object.  */
+	 bounds of an object.  The exception here is when the base object size
+	 has been overestimated (e.g. through PHI nodes or a COND_EXPR) and the
+	 adjusted offset remains negative.  If the caller wants to be
+	 permissive, return the base size.  */
       if (compare_tree_int (offset, offset_limit) > 0)
-	return size_zero_node;
+	{
+	  if (strict)
+	    return size_zero_node;
+	  else
+	    return sz;
+	}
     }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
@@ -1521,16 +1530,23 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 	  addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
 	}
 
+      bool pos_offset = (size_valid_p (op1, 0)
+			 && compare_tree_int (op1, offset_limit) <= 0);
+
       /* size_for_offset doesn't make sense for -1 size, but it does for size 0
 	 since the wholesize could be non-zero and a negative offset could give
 	 a non-zero size.  */
       if (size_unknown_p (bytes, 0))
 	;
+      /* In the static case, We want SIZE_FOR_OFFSET to go a bit easy on us if
+	 it sees a negative offset since BYTES could have been
+	 overestimated.  */
       else if ((object_size_type & OST_DYNAMIC)
 	       || bytes != wholesize
-	       || (size_valid_p (op1, object_size_type)
-		   && compare_tree_int (op1, offset_limit) <= 0))
-	bytes = size_for_offset (bytes, op1, wholesize);
+	       || pos_offset)
+	bytes = size_for_offset (bytes, op1, wholesize,
+				 ((object_size_type & OST_DYNAMIC)
+				  || pos_offset));
       /* In the static case, with a negative offset, the best estimate for
 	 minimum size is size_unknown but for maximum size, the wholesize is a
 	 better estimate than size_unknown.  */