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