Lasse Collin wrote:
> These sanitizer errors mean that the program cannot be compiled with
> strict forms of control flow integrity checking. It can be done in
> software (clang -flto -fsanitize=cfi -fvisibility=hidden)

Thanks for mentioning this; I did not know about this kind of sanitize
combined with optimization.

Indeed, I see a few test failures in the test cases, still (despite
the no_sanitize ("function") attributes). Fixing them through the attached
patch.

But some other test failures will remain:
  - jit/test-cache,
  - test-list-c++, test-map-c++, test-omap-c++, test-oset-c++.

The reason is that C is not only used for application programming —
for which the above options "-flto -fsanitize=cfi -fvisibility=hidden"
make sense [except for GNOME / Qt and their "signals"] — but also for
system programming, where treating functions of different prototypes
in the same way is commonplace (think of libffi or libffcall), or for
generics (where C++ programmers like to have 10 different code
instantiations for 10 different pointer types, but C programmers don't
like that).

Bruno


2026-08-15  Bruno Haible  <[email protected]>

        tests: Support CC="clang -flto -fsanitize=cfi -fvisibility=hidden".
        Reported by Lasse Collin in
        <https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00190.html>.
        * tests/test-array_map.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-array_omap.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-avltree_omap.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-rbtree_omap.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-array_set.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-array_oset.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-avltree_oset.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-rbtree_oset.c (string_compare): New function.
        (main): Use instead of casting strcmp to a different function prototype.
        * tests/test-oset-update.h (test_update): Likewise.

>From 9388edc18a2c1de1ae9f58cef2446e1c1116bf36 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email protected]>
Date: Sat, 15 Aug 2026 17:46:48 +0200
Subject: [PATCH] tests: Support CC="clang -flto -fsanitize=cfi
 -fvisibility=hidden".

Reported by Lasse Collin in
<https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00190.html>.

* tests/test-array_map.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-array_omap.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-avltree_omap.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-rbtree_omap.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-array_set.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-array_oset.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-avltree_oset.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-rbtree_oset.c (string_compare): New function.
(main): Use instead of casting strcmp to a different function prototype.
* tests/test-oset-update.h (test_update): Likewise.
---
 ChangeLog                 | 23 +++++++++++++++++++++++
 tests/test-array_map.c    | 24 ++++++++++++++++--------
 tests/test-array_omap.c   | 26 +++++++++++++++++---------
 tests/test-array_oset.c   | 26 +++++++++++++++++---------
 tests/test-array_set.c    | 10 +++++++++-
 tests/test-avltree_omap.c | 12 ++++++++++--
 tests/test-avltree_oset.c | 16 ++++++++++++----
 tests/test-oset-update.h  |  2 +-
 tests/test-rbtree_omap.c  | 12 ++++++++++--
 tests/test-rbtree_oset.c  | 16 ++++++++++++----
 10 files changed, 127 insertions(+), 40 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f6b4603910..19acb732d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2026-08-15  Bruno Haible  <[email protected]>
+
+	tests: Support CC="clang -flto -fsanitize=cfi -fvisibility=hidden".
+	Reported by Lasse Collin in
+	<https://lists.gnu.org/archive/html/bug-gnulib/2026-08/msg00190.html>.
+	* tests/test-array_map.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-array_omap.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-avltree_omap.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-rbtree_omap.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-array_set.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-array_oset.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-avltree_oset.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-rbtree_oset.c (string_compare): New function.
+	(main): Use instead of casting strcmp to a different function prototype.
+	* tests/test-oset-update.h (test_update): Likewise.
+
 2026-08-15  Bruno Haible  <[email protected]>
 
 	memmove: Avoid undefined behaviour upon memmove (NULL, ptr, 0).
diff --git a/tests/test-array_map.c b/tests/test-array_map.c
index 5e93d4b74c..265debd550 100644
--- a/tests/test-array_map.c
+++ b/tests/test-array_map.c
@@ -34,6 +34,14 @@ static const char *objects[30] =
     "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "<", ">", "[", "]"
   };
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 static bool
 string_equals (const void *x1, const void *x2)
 {
@@ -143,12 +151,12 @@ main (int argc, char *argv[])
         const char *key = RANDOM_OBJECT ();
         const char *value = RANDOM_OBJECT ();
         bool added = gl_map_nx_put (map1, key, value);
-        size_t index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+        size_t index = gl_sortedlist_indexof (keys, string_compare, key);
         ASSERT (added == (index == (size_t)(-1)));
         if (added)
           {
-            gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key);
-            index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+            gl_sortedlist_add (keys, string_compare, key);
+            index = gl_sortedlist_indexof (keys, string_compare, key);
             gl_list_add_at (values, index, value);
           }
         else
@@ -166,7 +174,7 @@ main (int argc, char *argv[])
               const char *key = RANDOM_OBJECT ();
               const void *ret = gl_map_get (map1, key);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (ret
                       == (index != (size_t)(-1) ? gl_list_get_at (values, index) : NULL));
             }
@@ -177,12 +185,12 @@ main (int argc, char *argv[])
               const char *value = RANDOM_OBJECT ();
               bool added = gl_map_nx_put (map1, key, value);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (added == (index == (size_t)(-1)));
               if (added)
                 {
-                  gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key);
-                  index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                  gl_sortedlist_add (keys, string_compare, key);
+                  index = gl_sortedlist_indexof (keys, string_compare, key);
                   gl_list_add_at (values, index, value);
                 }
               else
@@ -194,7 +202,7 @@ main (int argc, char *argv[])
               const char *key = RANDOM_OBJECT ();
               bool removed = gl_map_remove (map1, key);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (removed == (index != (size_t)(-1)));
               if (removed)
                 {
diff --git a/tests/test-array_omap.c b/tests/test-array_omap.c
index 44c4e8d18e..b04ab30467 100644
--- a/tests/test-array_omap.c
+++ b/tests/test-array_omap.c
@@ -73,6 +73,14 @@ check_all (gl_omap_t map1, gl_list_t keys, gl_list_t values)
   check_equals (map1, keys, values);
 }
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -88,7 +96,7 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create map1.  */
-    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, (gl_mapkey_compar_fn) strcmp, NULL, NULL);
+    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, string_compare, NULL, NULL);
     ASSERT (map1 != NULL);
 
     /* Create keys and values.  */
@@ -103,12 +111,12 @@ main (int argc, char *argv[])
         const char *key = RANDOM_OBJECT ();
         const char *value = RANDOM_OBJECT ();
         bool added = gl_omap_nx_put (map1, key, value);
-        size_t index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+        size_t index = gl_sortedlist_indexof (keys, string_compare, key);
         ASSERT (added == (index == (size_t)(-1)));
         if (added)
           {
-            gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key);
-            index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+            gl_sortedlist_add (keys, string_compare, key);
+            index = gl_sortedlist_indexof (keys, string_compare, key);
             gl_list_add_at (values, index, value);
           }
         else
@@ -126,7 +134,7 @@ main (int argc, char *argv[])
               const char *key = RANDOM_OBJECT ();
               const void *ret = gl_omap_get (map1, key);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (ret
                       == (index != (size_t)(-1) ? gl_list_get_at (values, index) : NULL));
             }
@@ -137,12 +145,12 @@ main (int argc, char *argv[])
               const char *value = RANDOM_OBJECT ();
               bool added = gl_omap_nx_put (map1, key, value);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (added == (index == (size_t)(-1)));
               if (added)
                 {
-                  gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key);
-                  index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                  gl_sortedlist_add (keys, string_compare, key);
+                  index = gl_sortedlist_indexof (keys, string_compare, key);
                   gl_list_add_at (values, index, value);
                 }
               else
@@ -154,7 +162,7 @@ main (int argc, char *argv[])
               const char *key = RANDOM_OBJECT ();
               bool removed = gl_omap_remove (map1, key);
               size_t index =
-                gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key);
+                gl_sortedlist_indexof (keys, string_compare, key);
               ASSERT (removed == (index != (size_t)(-1)));
               if (removed)
                 {
diff --git a/tests/test-array_oset.c b/tests/test-array_oset.c
index 59aae8be9c..99c095a2bb 100644
--- a/tests/test-array_oset.c
+++ b/tests/test-array_oset.c
@@ -27,8 +27,6 @@
 #include "gl_array_list.h"
 #include "macros.h"
 
-#include "test-oset-update.h"
-
 static const char *objects[30] =
   {
     "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
@@ -68,6 +66,14 @@ check_all (gl_oset_t set1, gl_list_t set2)
   check_equals (set1, set2);
 }
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 static bool
 is_at_least (const void *elt, const void *threshold)
 {
@@ -88,6 +94,8 @@ gl_sortedlist_indexof_atleast (gl_list_t set,
   return (size_t)(-1);
 }
 
+#include "test-oset-update.h"
+
 int
 main (int argc, char *argv[])
 {
@@ -102,7 +110,7 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create set1.  */
-    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, string_compare, NULL);
     ASSERT (set1 != NULL);
 
     /* Create set2.  */
@@ -115,9 +123,9 @@ main (int argc, char *argv[])
       {
         const char *obj = RANDOM_OBJECT ();
         ASSERT (gl_oset_nx_add (set1, obj)
-                == (gl_sortedlist_search (set2, (gl_listelement_compar_fn)strcmp, obj) != NULL
+                == (gl_sortedlist_search (set2, string_compare, obj) != NULL
                     ? false
-                    : (gl_sortedlist_add (set2, (gl_listelement_compar_fn)strcmp, obj), true)));
+                    : (gl_sortedlist_add (set2, string_compare, obj), true)));
         check_all (set1, set2);
       }
 
@@ -130,23 +138,23 @@ main (int argc, char *argv[])
             {
               const char *obj = RANDOM_OBJECT ();
               ASSERT (gl_oset_search (set1, obj)
-                      == (gl_sortedlist_search (set2, (gl_listelement_compar_fn)strcmp, obj) != NULL));
+                      == (gl_sortedlist_search (set2, string_compare, obj) != NULL));
             }
             break;
           case 1:
             {
               const char *obj = RANDOM_OBJECT ();
               ASSERT (gl_oset_nx_add (set1, obj)
-                      == (gl_sortedlist_search (set2, (gl_listelement_compar_fn)strcmp, obj) != NULL
+                      == (gl_sortedlist_search (set2, string_compare, obj) != NULL
                           ? false
-                          : (gl_sortedlist_add (set2, (gl_listelement_compar_fn)strcmp, obj), true)));
+                          : (gl_sortedlist_add (set2, string_compare, obj), true)));
             }
             break;
           case 2:
             {
               const char *obj = RANDOM_OBJECT ();
               ASSERT (gl_oset_remove (set1, obj)
-                      == gl_sortedlist_remove (set2, (gl_listelement_compar_fn)strcmp, obj));
+                      == gl_sortedlist_remove (set2, string_compare, obj));
             }
             break;
           case 3:
diff --git a/tests/test-array_set.c b/tests/test-array_set.c
index 4d18ecd1c7..1d02238fa7 100644
--- a/tests/test-array_set.c
+++ b/tests/test-array_set.c
@@ -88,6 +88,14 @@ check_all (gl_set_t set1, gl_oset_t set2)
   check_equals (set1, set2);
 }
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -106,7 +114,7 @@ main (int argc, char *argv[])
     ASSERT (set1 != NULL);
 
     /* Create set2.  */
-    set2 = gl_oset_create_empty (GL_ARRAY_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set2 = gl_oset_create_empty (GL_ARRAY_OSET, string_compare, NULL);
 
     check_all (set1, set2);
 
diff --git a/tests/test-avltree_omap.c b/tests/test-avltree_omap.c
index d116971c4e..0a9bb3a616 100644
--- a/tests/test-avltree_omap.c
+++ b/tests/test-avltree_omap.c
@@ -69,6 +69,14 @@ check_all (gl_omap_t map1, gl_omap_t map2)
   check_equals (map1, map2);
 }
 
+static int
+string_compare (const void *key1, const void *key2)
+{
+  const char *s1 = key1;
+  const char *s2 = key2;
+  return strcmp (s1, s2);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -82,11 +90,11 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create map1.  */
-    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, (gl_mapkey_compar_fn) strcmp, NULL, NULL);
+    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, string_compare, NULL, NULL);
     ASSERT (map1 != NULL);
 
     /* Create map2.  */
-    map2 = gl_omap_nx_create_empty (GL_AVLTREE_OMAP, (gl_mapkey_compar_fn) strcmp, NULL, NULL);
+    map2 = gl_omap_nx_create_empty (GL_AVLTREE_OMAP, string_compare, NULL, NULL);
     ASSERT (map2 != NULL);
 
     check_all (map1, map2);
diff --git a/tests/test-avltree_oset.c b/tests/test-avltree_oset.c
index 2a68aca332..9b1f2e32a1 100644
--- a/tests/test-avltree_oset.c
+++ b/tests/test-avltree_oset.c
@@ -26,8 +26,6 @@
 #include "gl_array_oset.h"
 #include "macros.h"
 
-#include "test-oset-update.h"
-
 extern void gl_avltree_oset_check_invariants (gl_oset_t set);
 
 static const char *objects[30] =
@@ -68,12 +66,22 @@ check_all (gl_oset_t set1, gl_oset_t set2)
   check_equals (set1, set2);
 }
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 static bool
 is_at_least (const void *elt, const void *threshold)
 {
   return strcmp ((const char *) elt, (const char *) threshold) >= 0;
 }
 
+#include "test-oset-update.h"
+
 int
 main (int argc, char *argv[])
 {
@@ -87,11 +95,11 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create set1.  */
-    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, string_compare, NULL);
     ASSERT (set1 != NULL);
 
     /* Create set2.  */
-    set2 = gl_oset_nx_create_empty (GL_AVLTREE_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set2 = gl_oset_nx_create_empty (GL_AVLTREE_OSET, string_compare, NULL);
     ASSERT (set2 != NULL);
 
     check_all (set1, set2);
diff --git a/tests/test-oset-update.h b/tests/test-oset-update.h
index 973b6258a8..e301eb218e 100644
--- a/tests/test-oset-update.h
+++ b/tests/test-oset-update.h
@@ -30,7 +30,7 @@ test_update (gl_oset_implementation_t implementation)
   char D[2] = "D";
 
   gl_oset_t set1 =
-    gl_oset_nx_create_empty (implementation, (gl_setelement_compar_fn) strcmp, NULL);
+    gl_oset_nx_create_empty (implementation, string_compare, NULL);
   ASSERT (set1 != NULL);
 
   /* Fill the set.  */
diff --git a/tests/test-rbtree_omap.c b/tests/test-rbtree_omap.c
index 4d0781ca5e..777170fba8 100644
--- a/tests/test-rbtree_omap.c
+++ b/tests/test-rbtree_omap.c
@@ -69,6 +69,14 @@ check_all (gl_omap_t map1, gl_omap_t map2)
   check_equals (map1, map2);
 }
 
+static int
+string_compare (const void *key1, const void *key2)
+{
+  const char *s1 = key1;
+  const char *s2 = key2;
+  return strcmp (s1, s2);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -82,11 +90,11 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create map1.  */
-    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, (gl_mapkey_compar_fn) strcmp, NULL, NULL);
+    map1 = gl_omap_nx_create_empty (GL_ARRAY_OMAP, string_compare, NULL, NULL);
     ASSERT (map1 != NULL);
 
     /* Create map2.  */
-    map2 = gl_omap_nx_create_empty (GL_RBTREE_OMAP, (gl_mapkey_compar_fn) strcmp, NULL, NULL);
+    map2 = gl_omap_nx_create_empty (GL_RBTREE_OMAP, string_compare, NULL, NULL);
     ASSERT (map2 != NULL);
 
     check_all (map1, map2);
diff --git a/tests/test-rbtree_oset.c b/tests/test-rbtree_oset.c
index 6edb6e72e1..385a216cd3 100644
--- a/tests/test-rbtree_oset.c
+++ b/tests/test-rbtree_oset.c
@@ -26,8 +26,6 @@
 #include "gl_array_oset.h"
 #include "macros.h"
 
-#include "test-oset-update.h"
-
 extern void gl_rbtree_oset_check_invariants (gl_oset_t set);
 
 static const char *objects[30] =
@@ -68,12 +66,22 @@ check_all (gl_oset_t set1, gl_oset_t set2)
   check_equals (set1, set2);
 }
 
+static int
+string_compare (const void *elt1, const void *elt2)
+{
+  const char *s1 = elt1;
+  const char *s2 = elt2;
+  return strcmp (s1, s2);
+}
+
 static bool
 is_at_least (const void *elt, const void *threshold)
 {
   return strcmp ((const char *) elt, (const char *) threshold) >= 0;
 }
 
+#include "test-oset-update.h"
+
 int
 main (int argc, char *argv[])
 {
@@ -87,11 +95,11 @@ main (int argc, char *argv[])
     size_t initial_size = RANDOM (20);
 
     /* Create set1.  */
-    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set1 = gl_oset_nx_create_empty (GL_ARRAY_OSET, string_compare, NULL);
     ASSERT (set1 != NULL);
 
     /* Create set2.  */
-    set2 = gl_oset_nx_create_empty (GL_RBTREE_OSET, (gl_setelement_compar_fn) strcmp, NULL);
+    set2 = gl_oset_nx_create_empty (GL_RBTREE_OSET, string_compare, NULL);
     ASSERT (set2 != NULL);
 
     check_all (set1, set2);
-- 
2.53.0

Reply via email to