Since IFUNC resolvers run early before the sanitizer run-time libraries
have a chance to set up the sanitizer infrastructure, disable sanitizer
in IFUNC resolver when the sanitizer infrastructure initialization is
needed.

gcc/

        PR sanitizer/110442
        * asan.h (sanitize_flags_p): Return false if the function is
        called by IFUNC resolver.
        (sanitize_coverage_p): Likewise.
        * cgraph.cc (called_by_ifunc_resolver_p): New.
        * internal-fn.cc (expand_TSAN_FUNC_EXIT): Return in function
        called by IFUNC resolver.
        * tree.h (called_by_ifunc_resolver_p): Likewise.

gcc/testsuite/

        PR sanitizer/110442
        * gcc.dg/asan/ifunc-1.c: New test.
        * gcc.dg/tsan/ifunc-1.c: Likewise.
        * gcc.dg/ubsan/ifunc-shift-1.c: Likewise.

Signed-off-by: H.J. Lu <[email protected]>
---
 gcc/asan.h                                 |  9 ++++-
 gcc/cgraph.cc                              |  9 +++++
 gcc/internal-fn.cc                         |  6 ++-
 gcc/testsuite/gcc.dg/asan/ifunc-1.c        | 37 ++++++++++++++++++
 gcc/testsuite/gcc.dg/tsan/ifunc-1.c        | 38 +++++++++++++++++++
 gcc/testsuite/gcc.dg/ubsan/ifunc-shift-1.c | 44 ++++++++++++++++++++++
 gcc/tree.h                                 |  5 +++
 7 files changed, 144 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/asan/ifunc-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tsan/ifunc-1.c
 create mode 100644 gcc/testsuite/gcc.dg/ubsan/ifunc-shift-1.c

diff --git a/gcc/asan.h b/gcc/asan.h
index a24562f67a2..999ed0dd818 100644
--- a/gcc/asan.h
+++ b/gcc/asan.h
@@ -251,6 +251,10 @@ sanitize_flags_p (sanitize_code_type flag,
 
   if (fn != NULL_TREE)
     {
+      if (TREE_CODE (fn) == FUNCTION_DECL
+         && called_by_ifunc_resolver_p (fn))
+       return false;
+
       tree value = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (fn));
       if (value)
        result_flags &= ~tree_to_uhwi (TREE_VALUE (value));
@@ -266,8 +270,9 @@ sanitize_coverage_p (const_tree fn = current_function_decl)
 {
   return (flag_sanitize_coverage
          && (fn == NULL_TREE
-             || lookup_attribute ("no_sanitize_coverage",
-                                  DECL_ATTRIBUTES (fn)) == NULL_TREE));
+             || (!called_by_ifunc_resolver_p (fn)
+                 && !lookup_attribute ("no_sanitize_coverage",
+                                       DECL_ATTRIBUTES (fn)))));
 }
 
 #endif /* TREE_ASAN */
diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index 32071a84bac..820c57695c2 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -4494,6 +4494,15 @@ cgraph_node::former_thunk_p (void)
   return true;
 }
 
+/* Return true if FN is called by an IFUNC resolver.  */
+
+bool
+called_by_ifunc_resolver_p (const_tree fn)
+{
+  struct cgraph_node *c_node = cgraph_node::get (fn);
+  return c_node && c_node->called_by_ifunc_resolver;
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
index bf2fac81807..f4cc0ce745d 100644
--- a/gcc/internal-fn.cc
+++ b/gcc/internal-fn.cc
@@ -877,12 +877,14 @@ expand_ASAN_POISON_USE (internal_fn, gcall *)
   gcc_unreachable ();
 }
 
-/* This should get expanded in the tsan pass.  */
+/* This should get expanded in the tsan pass unless it is in a function
+   called by IFUNC resolver.  */
 
 static void
 expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
 {
-  gcc_unreachable ();
+  if (!called_by_ifunc_resolver_p (current_function_decl))
+    gcc_unreachable ();
 }
 
 /* This should get expanded in the lower pass.  */
diff --git a/gcc/testsuite/gcc.dg/asan/ifunc-1.c 
b/gcc/testsuite/gcc.dg/asan/ifunc-1.c
new file mode 100644
index 00000000000..3e944c4fb67
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/asan/ifunc-1.c
@@ -0,0 +1,37 @@
+/* PR sanitizer/110442 */
+/* Don't use dg-require-ifunc to avoid marking this test as UNSUPPORTED
+   due to ICE caused by compiler bug during IFUNC check.  */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+
+#include <stdio.h>
+ 
+void
+f1 (void)
+{
+  puts ("f1");
+}
+ 
+void
+f2 (void)
+{
+  puts ("f2");
+}
+ 
+void *
+resolve (void)
+{
+  __builtin_cpu_init ();
+  if (__builtin_cpu_supports ("f16c"))
+    return f1;
+  else
+    return f2;
+}
+ 
+void f (void) __attribute__ ((ifunc ("resolve")));
+ 
+int
+main (void)
+{
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/tsan/ifunc-1.c 
b/gcc/testsuite/gcc.dg/tsan/ifunc-1.c
new file mode 100644
index 00000000000..da477e33983
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tsan/ifunc-1.c
@@ -0,0 +1,38 @@
+/* PR sanitizer/110442 */
+/* Don't use dg-require-ifunc to avoid marking this test as UNSUPPORTED
+   due to ICE caused by compiler bug during IFUNC check.  */
+/* { dg-do run { target { x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=thread" } */
+
+#include <stdio.h>
+ 
+void
+f1 (void)
+{
+  puts ("f1");
+}
+ 
+void
+f2 (void)
+{
+  puts ("f2");
+}
+ 
+void *
+resolve (void)
+{
+  __builtin_cpu_init ();
+  if (__builtin_cpu_supports ("f16c"))
+    return f1;
+  else
+    return f2;
+}
+ 
+void f (void) __attribute__ ((ifunc ("resolve")));
+ 
+int
+main (void)
+{
+  f ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ubsan/ifunc-shift-1.c 
b/gcc/testsuite/gcc.dg/ubsan/ifunc-shift-1.c
new file mode 100644
index 00000000000..973097cf098
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ubsan/ifunc-shift-1.c
@@ -0,0 +1,44 @@
+/* PR sanitizer/110442 */
+/* Don't use dg-require-ifunc to avoid marking this test as UNSUPPORTED
+   due to ICE caused by compiler bug during IFUNC check.  */
+/* { dg-do run { target { i?86-*-linux* x86_64-*-linux* } } } */
+/* { dg-options "-fsanitize=shift-exponent -Wno-shift-count-overflow" } */
+
+#include <stdio.h>
+ 
+int n = 30;
+
+void
+f1 (void)
+{
+  puts ("f1");
+}
+ 
+void
+f2 (void)
+{
+  puts ("f2");
+}
+ 
+void *
+resolve (void)
+{
+  if ((n << 32) != 0)
+    return f1;
+  __builtin_cpu_init ();
+  if (__builtin_cpu_supports ("f16c"))
+    return f1;
+  else
+    return f2;
+}
+ 
+void f (void) __attribute__ ((ifunc ("resolve")));
+ 
+int
+main (void)
+{
+  f ();
+  return 0;
+}
+
+/* { dg-output "shift exponent 32 is too large for 32-bit type 
'int'\[^\n\r]*(\n|\r\n|\r)" } */
diff --git a/gcc/tree.h b/gcc/tree.h
index ce8c778087f..706e704b2e2 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7113,4 +7113,9 @@ extern auto_vec<string_slice> get_clone_versions (const 
tree, int * = NULL);
    directly.  */
 extern auto_vec<string_slice> get_clone_attr_versions (const tree, int *);
 
+/* In cgraph.cc */
+
+/* Return true if the function is called by an IFUNC resolver.  */
+extern bool called_by_ifunc_resolver_p (const_tree);
+
 #endif  /* GCC_TREE_H  */
-- 
2.51.0

Reply via email to