Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r14-3549-gf687fc1ff6d4a4.

gcc/analyzer/ChangeLog:
        PR analyzer/105899
        * kf.cc (kf_strdup::impl_call_pre): Set size of
        dynamically-allocated buffer.  Simulate copying the string from
        the source region to the new buffer.

gcc/testsuite/ChangeLog:
        PR analyzer/105899
        * c-c++-common/analyzer/pr99193-2.c: Add
        -Wno-analyzer-too-complex.
        * gcc.dg/analyzer/strdup-1.c: Include "analyzer-decls.h".
        (test_concrete_strlen): New.
        (test_symbolic_strlen): New.
---
 gcc/analyzer/kf.cc                            | 28 +++++++++++++------
 .../c-c++-common/analyzer/pr99193-2.c         |  2 ++
 gcc/testsuite/gcc.dg/analyzer/strdup-1.c      | 27 ++++++++++++++++++
 3 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index 333ffd9751ae..37792aed909c 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -1301,17 +1301,27 @@ public:
   void impl_call_pre (const call_details &cd) const final override
   {
     region_model *model = cd.get_model ();
+    region_model_context *ctxt = cd.get_ctxt ();
     region_model_manager *mgr = cd.get_manager ();
-    cd.check_for_null_terminated_string_arg (0);
-    /* Ideally we'd get the size here, and simulate copying the bytes.  */
-    const region *new_reg
-      = model->get_or_create_region_for_heap_alloc (NULL, cd.get_ctxt ());
-    model->mark_region_as_unknown (new_reg, NULL);
-    if (cd.get_lhs_type ())
+    const svalue *bytes_to_copy;
+    if (const svalue *num_bytes_read_sval
+       = cd.check_for_null_terminated_string_arg (0, true, &bytes_to_copy))
       {
-       const svalue *ptr_sval
-         = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
-       cd.maybe_set_lhs (ptr_sval);
+       const region *new_reg
+         = model->get_or_create_region_for_heap_alloc (num_bytes_read_sval,
+                                                       ctxt);
+       model->write_bytes (new_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
+       if (cd.get_lhs_type ())
+         {
+           const svalue *ptr_sval
+             = mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
+           cd.maybe_set_lhs (ptr_sval);
+         }
+      }
+    else
+      {
+       if (ctxt)
+         ctxt->terminate_path ();
       }
   }
 };
diff --git a/gcc/testsuite/c-c++-common/analyzer/pr99193-2.c 
b/gcc/testsuite/c-c++-common/analyzer/pr99193-2.c
index 791b857dcc56..12326ef61f1c 100644
--- a/gcc/testsuite/c-c++-common/analyzer/pr99193-2.c
+++ b/gcc/testsuite/c-c++-common/analyzer/pr99193-2.c
@@ -3,6 +3,8 @@
    Based on 
https://github.com/libguestfs/libguestfs/blob/f19fd566f6387ce7e4d82409528c9dde374d25e0/df/main.c#L404
    which is GPLv2 or later.  */
 
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
 typedef __SIZE_TYPE__ size_t;
 typedef __builtin_va_list va_list;
 
diff --git a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c 
b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c
index f6c176f174eb..11bc964922b5 100644
--- a/gcc/testsuite/gcc.dg/analyzer/strdup-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/strdup-1.c
@@ -1,6 +1,8 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "analyzer-decls.h"
+
 extern void requires_nonnull (void *ptr)
   __attribute__((nonnull));
 
@@ -52,3 +54,28 @@ char *test_uninitialized (void)
   return strdup (buf); /* { dg-warning "use of uninitialized value 
'buf\\\[0\\\]'" } */
   /* { dg-message "while looking for null terminator for argument 1 
\\('&buf'\\) of 'strdup'..." "event" { target *-*-* } .-1 } */
 }
+
+char *test_concrete_strlen (void)
+{
+  char *p = strdup ("abc");
+  if (!p)
+    return p;
+  __analyzer_eval (__analyzer_get_strlen (p) == 3); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[0] == 'a'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[1] == 'b'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[2] == 'c'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (p[3] == '\0'); /* { dg-warning "TRUE" } */
+  return p;
+}
+
+char *test_symbolic_strlen (const char *p)
+{
+  char *q = strdup (p);
+  if (!q)
+    return q;
+  __analyzer_eval (__analyzer_get_strlen (p) == __analyzer_get_strlen (q)); /* 
{ dg-warning "UNKNOWN" } */
+  // TODO: should be TRUE
+  __analyzer_eval (p[0] == q[0]); /* { dg-warning "UNKNOWN" } */
+  // TODO: should be TRUE
+  return q;
+}
-- 
2.26.3

Reply via email to