Hi Paul,

> Unfortunately Vincent Lefevre is correct that the C Standard allows calloc
> (SIZE_MAX, 2) to succeed on a hypothetical machine that actually can allocate
> that amount of memory. This could in theory occur on a machine that doesn't 
> have
> a flat address space.
> 
> A program like the following should defeat Clang's optimization, though (at
> least, if Clang is not buggy). And it would also detect the hypothetical 
> machine
> that Vincent alluded to, which would make it more-accurate than the 'volatile'
> workaround. How about if we switch calloc.m4 to use this test instead?
> 
>   #include <stdlib.h>
>   int main ()
>   {
>     struct { char c[8]; } *s;
>     size_t sn = (size_t) -1 / sizeof *s + 2;
>     int result = 0;
>     char *p = calloc (0, 0);
>     if (!p)
>       result |= 1;
>     free (p);
>     s = calloc (sn, sizeof *s);
>     if (s)
>       {
>       s[0].c[0] = 1;
>       if (s[sn - 1].c[0])
>         result |= 2;
>       }
>     free (s);
>     return result;
>   }

OK, I'm adding this to calloc.m4. Patch below. However, the 'volatile' is still
needed. And even if it wasn't, it would be good to keep two arrows in our
quiver.

Now, this looks really like a bug that you could report: With clang 10.0.0
on x86_64 (Ubuntu 18.04), the following program

================================= foo.c ============================
#include <stdlib.h>
int main ()
{
  int result;
  typedef struct { char c[8]; } S8;
  size_t n = (size_t) -1 / sizeof (S8) + 2;
  S8 *s = calloc (n, sizeof (S8));
  if (s)
    {
      s[0].c[0] = 1;
      if (s[n - 1].c[0])
        result = 0;
      else
        result = 2;
    }
  else
    result = 3;
  free (s);
  return result;
}
====================================================================
returns with exit code 0, when optimization is enabled:

$ clang -O2 foo.c
$ ./a.out; echo $?
0

When no optimization is enabled, it returns with exit code 3, as
expected.

Does this look like a reportable bug? :-)


2020-05-23  Bruno Haible  <br...@clisp.org>

        calloc-gnu: Make test work in non-flat address spaces.
        Uses code by Paul Eggert.
        * m4/calloc.m4 (_AC_FUNC_CALLOC_IF): Allow a calloc() implementation
        to return more than SIZE_MAX bytes, but only without wrap-around bugs.

diff --git a/m4/calloc.m4 b/m4/calloc.m4
index a93439e..f179a8a 100644
--- a/m4/calloc.m4
+++ b/m4/calloc.m4
@@ -1,4 +1,4 @@
-# calloc.m4 serial 22
+# calloc.m4 serial 23
 
 # Copyright (C) 2004-2020 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
@@ -38,16 +38,27 @@ AC_DEFUN([_AC_FUNC_CALLOC_IF],
        AC_RUN_IFELSE(
          [AC_LANG_PROGRAM(
             [AC_INCLUDES_DEFAULT],
-            [[int result = 0;
-              char * volatile p = calloc ((size_t) -1 / 8 + 1, 8);
-              if (!p)
-                result |= 2;
-              free (p);
+            [[int result;
+              typedef struct { char c[8]; } S8;
+              size_t n = (size_t) -1 / sizeof (S8) + 2;
+              S8 * volatile s = calloc (n, sizeof (S8));
+              if (s)
+                {
+                  s[0].c[0] = 1;
+                  if (s[n - 1].c[0])
+                    result = 0;
+                  else
+                    result = 2;
+                }
+              else
+                result = 3;
+              free (s);
               return result;
             ]])],
-         dnl The exit code of this program is 0 if calloc() succeeded (which
-         dnl it shouldn't), 2 if calloc() failed, or 1 if some leak sanitizer
-         dnl terminated the program as a result of the calloc() call.
+         dnl The exit code of this program is 0 if calloc() succeeded with a
+         dnl wrap-around bug (which it shouldn't), 2 if calloc() succeeded in
+         dnl a non-flat address space, 3 if calloc() failed, or 1 if some leak
+         dnl sanitizer terminated the program as a result of the calloc() call.
          [ac_cv_func_calloc_0_nonnull=no],
          [])
      else




Reply via email to