Paul Eggert wrote: > > On the contrary, I will try to find and eliminate such alarms. > > Please don't complicate and/or slow down shared Gnulib code just to > pacify this particular false alarm from Clang. The obstack fix was fine, > because it made obstack clearer and no slower. But we shouldn't have to > insert unnecessary "is the size zero?" checks merely to pacify the false > alarms.
Building a gnulib testdir with these settings: CC="clang -fsanitize=address,undefined,signed-integer-overflow,shift,integer-divide-by-zero" produced no relevant findings (other than 'argp' things). But building coreutils with these settings gives two findings: lib/linebuffer.c:65:22: runtime error: applying zero offset to null pointer lib/mpsort.c:155:34: runtime error: applying zero offset to null pointer In both cases, the ability to add NULL + 0 unifies code paths for empty arrays and non-empty arrays. Such unified code paths are desirable: they make the code look simpler. Marc Nieper-Wißkirchen wrote: > I would suggest writing macros that encapsulate code like "NULL + 0" and > replace the code with macro invocations. On legacy systems, the macro > would expand into the legacy code; on modern systems where checks like "is > this zero size?" should be eliminated by the compiler, the macro would > expand into the correct ISO C code. That's an interesting idea. It applies to both lib/linebuffer.c and lib/mpsort.c. Paul, is the attached patch OK with you? It allows coreutils to build and "make check" with clang's ASAN+UBSAN, without taking out some categories of UBSAN. The downside is an additional macro. Which I could move to a separate include file. The macro does not produce slower code: - clang optimizes the conditional expression nicely. - With gcc, which does not yet optimize the conditional expression well, we continue to use the simple but not ISO C compliant code. It is quite possible that newer GCCs will, in the future, trigger the same runtime error: applying zero offset to null pointer as clang does. For this reason, I would report a GCC bug, asking them to optimize ((n) ? (array) + (n) : (array)) into identical code as ((array) + (n)) Bruno
diff --git a/lib/linebuffer.c b/lib/linebuffer.c index 4124bbcb37..9c8e12ab32 100644 --- a/lib/linebuffer.c +++ b/lib/linebuffer.c @@ -31,6 +31,19 @@ # include "unlocked-io.h" #endif +/* END_OF_ARRAY(array,n) returns a pointer past the last element of the array + ARRAY that has N elements. + For clang, it uses a conditional expression that avoids adding 0 to a null + pointer, which is undefined according to ISO C 23 § 6.5.6.(9) and which + triggers an error in clang's undefined-behaviour sanitizer. When no + sanitizer is in effect, clang optimizes this conditional expression to + exactly the same code. */ +#if defined __clang__ +# define END_OF_ARRAY(array,n) ((n) ? (array) + (n) : (array)) +#else +# define END_OF_ARRAY(array,n) ((array) + (n)) +#endif + /* Initialize linebuffer LINEBUFFER for use. */ void @@ -62,7 +75,7 @@ readlinebuffer_delim (struct linebuffer *linebuffer, FILE *stream, int c; char *buffer = linebuffer->buffer; char *p = linebuffer->buffer; - char *end = buffer + linebuffer->size; /* Sentinel. */ + char *end = END_OF_ARRAY (buffer, linebuffer->size); /* Sentinel. */ if (feof (stream)) return NULL; diff --git a/lib/mpsort.c b/lib/mpsort.c index 32cc5e1f82..2741d7199d 100644 --- a/lib/mpsort.c +++ b/lib/mpsort.c @@ -23,6 +23,19 @@ #include <string.h> +/* END_OF_ARRAY(array,n) returns a pointer past the last element of the array + ARRAY that has N elements. + For clang, it uses a conditional expression that avoids adding 0 to a null + pointer, which is undefined according to ISO C 23 § 6.5.6.(9) and which + triggers an error in clang's undefined-behaviour sanitizer. When no + sanitizer is in effect, clang optimizes this conditional expression to + exactly the same code. */ +#if defined __clang__ +# define END_OF_ARRAY(array,n) ((n) ? (array) + (n) : (array)) +#else +# define END_OF_ARRAY(array,n) ((array) + (n)) +#endif + /* The type of qsort-style comparison functions. */ typedef int (*comparison_function) (void const *, void const *); @@ -152,5 +165,5 @@ mpsort_with_tmp (void const **restrict base, size_t n, void mpsort (void const **base, size_t n, comparison_function cmp) { - mpsort_with_tmp (base, n, base + n, cmp); + mpsort_with_tmp (base, n, END_OF_ARRAY (base, n), cmp); }