https://gcc.gnu.org/g:0008050b9d6046ba4e811a03b406fb5d98707cae

commit r15-3574-g0008050b9d6046ba4e811a03b406fb5d98707cae
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Tue Sep 10 18:32:58 2024 +0200

    c++: Fix get_member_function_from_ptrfunc with -fsanitize=bounds [PR116449]
    
    The following testcase is miscompiled, because
    get_member_function_from_ptrfunc
    emits something like
    (((FUNCTION.__pfn & 1) != 0)
     ? ptr + FUNCTION.__delta + FUNCTION.__pfn - 1
     : FUNCTION.__pfn) (ptr + FUNCTION.__delta, ...)
    or so, so FUNCTION tree is used there 5 times.  There is
    if (TREE_SIDE_EFFECTS (function)) function = save_expr (function);
    but in this case function doesn't have side-effects, just nested ARRAY_REFs.
    Now, if all the FUNCTION trees would be shared, it would work fine,
    FUNCTION is evaluated in the first operand of COND_EXPR; but unfortunately
    that isn't the case, both the BIT_AND_EXPR shortening and conversion to
    bool done for build_conditional_expr actually unshare_expr that first
    expression, but none of the other 4 are unshared.  With -fsanitize=bounds,
    .UBSAN_BOUNDS calls are added to the ARRAY_REFs and use save_expr to avoid
    evaluating the argument multiple times, but because that FUNCTION tree is
    first used in the second argument of COND_EXPR (i.e. conditionally), the
    SAVE_EXPR initialization is done just there and then the third argument
    of COND_EXPR just uses the uninitialized temporary and so does the first
    argument computation as well.
    
    The following patch fixes that by doing save_expr even if 
!TREE_SIDE_EFFECTS,
    but to avoid doing that too often only if !nonvirtual and if the expression
    isn't a simple decl.
    
    2024-09-10  Jakub Jelinek  <ja...@redhat.com>
    
            PR c++/116449
            * typeck.cc (get_member_function_from_ptrfunc): Use save_expr
            on instance_ptr and function even if it doesn't have side-effects,
            as long as it isn't a decl.
    
            * g++.dg/ubsan/pr116449.C: New test.

Diff:
---
 gcc/cp/typeck.cc                      | 19 ++++++++++++++++---
 gcc/testsuite/g++.dg/ubsan/pr116449.C | 14 ++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index e4e260645f65..b6835286cff3 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -4193,10 +4193,23 @@ get_member_function_from_ptrfunc (tree 
*instance_ptrptr, tree function,
       if (!nonvirtual && is_dummy_object (instance_ptr))
        nonvirtual = true;
 
-      if (TREE_SIDE_EFFECTS (instance_ptr))
-       instance_ptr = instance_save_expr = save_expr (instance_ptr);
+      /* Use save_expr even when instance_ptr doesn't have side-effects,
+        unless it is a simple decl (save_expr won't do anything on
+        constants), so that we don't ubsan instrument the expression
+        multiple times.  See PR116449.  */
+      if (TREE_SIDE_EFFECTS (instance_ptr)
+         || (!nonvirtual && !DECL_P (instance_ptr)))
+       {
+         instance_save_expr = save_expr (instance_ptr);
+         if (instance_save_expr == instance_ptr)
+           instance_save_expr = NULL_TREE;
+         else
+           instance_ptr = instance_save_expr;
+       }
 
-      if (TREE_SIDE_EFFECTS (function))
+      /* See above comment.  */
+      if (TREE_SIDE_EFFECTS (function)
+         || (!nonvirtual && !DECL_P (function)))
        function = save_expr (function);
 
       /* Start by extracting all the information from the PMF itself.  */
diff --git a/gcc/testsuite/g++.dg/ubsan/pr116449.C 
b/gcc/testsuite/g++.dg/ubsan/pr116449.C
new file mode 100644
index 000000000000..f13368a51b00
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ubsan/pr116449.C
@@ -0,0 +1,14 @@
+// PR c++/116449
+// { dg-do compile }
+// { dg-options "-O2 -Wall -fsanitize=undefined" }
+
+struct C { void foo (int); void bar (); int c[16]; };
+typedef void (C::*P) ();
+struct D { P d; };
+static D e[1] = { { &C::bar } };
+
+void
+C::foo (int x)
+{
+  (this->*e[c[x]].d) ();
+}

Reply via email to