(root)/
gcc-13.2.0/
gcc/
testsuite/
gcc.dg/
builtin-stringop-chk-5.c
       1  /* Test exercising -Wstringop-overflow warnings.  */
       2  /* { dg-do compile } */
       3  /* { dg-options "-O2 -Wstringop-overflow=1 -Wno-array-bounds" } */
       4  
       5  #define offsetof(type, mem)   __builtin_offsetof (type, mem)
       6  
       7  /* Return the number of bytes from member MEM of TYPE to the end
       8     of object OBJ.  */
       9  #define offsetfrom(type, obj, mem) (sizeof (obj) - offsetof (type, mem))
      10  
      11  
      12  typedef __SIZE_TYPE__ size_t;
      13  extern void* memcpy (void*, const void*, size_t);
      14  extern void* memset (void*, int, __SIZE_TYPE__);
      15  
      16  
      17  struct A { char a, b; };
      18  struct B { struct A a; char c, d; };
      19  
      20  /* Function to call to "escape" pointers from tests below to prevent
      21     GCC from assuming the values of the objects they point to stay
      22     the unchanged.  */
      23  void escape (void*, ...);
      24  
      25  /* Function to "generate" a random number each time it's called.  Declared
      26     (but not defined) and used to prevent GCC from making assumptions about
      27     their values based on the variables uses in the tested expressions.  */
      28  size_t random_unsigned_value (void);
      29  
      30  /* Return a random unsigned value between MIN and MAX.  */
      31  
      32  static inline size_t
      33  range (size_t min, size_t max)
      34  {
      35    const size_t val = random_unsigned_value ();
      36    return val < min || max < val ? min : val;
      37  }
      38  
      39  /* Verify that writing past the end of a local array is diagnosed.  */
      40  
      41  void test_memop_warn_local (const void *src)
      42  {
      43    size_t n;
      44  
      45    n = range (8, 32);
      46  
      47    struct A a[2];
      48  
      49    memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
      50    escape (a, src);
      51  
      52    /* At -Wstringop-overflow=1 the destination is considered to be
      53       the whole array and its size is therefore sizeof a.  */
      54    memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
      55    escape (a, src);
      56  
      57    /* Verify the same as above but by writing into the first mmeber
      58       of the first element of the array.  */
      59    memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" } */
      60    escape (a, src);
      61  
      62    n = range (12, 32);
      63  
      64    struct B b[2];
      65  
      66    memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 overflows the destination" } */
      67    escape (b);
      68  
      69    /* The following idiom of clearing multiple members of a struct is
      70       used in a few places in the Linux kernel.  Verify that a warning
      71       is issued for it when it writes past the end of the array object.  */
      72    memset (&b[0].a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" } */
      73    escape (b);
      74  
      75    memset (&b->a.b, 0, offsetfrom (struct B, b, a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size 7" } */
      76    escape (b);
      77  
      78    memset (&b[0].c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" } */
      79    escape (b);
      80  
      81    memset (&b->c, 0, offsetfrom (struct B, b, c) + 1);   /* { dg-warning "writing 7 bytes into a region of size 6" } */
      82    escape (b);
      83  
      84    memset (&b[0].d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" } */
      85    escape (b);
      86  
      87    memset (&b->d, 0, offsetfrom (struct B, b, d) + 1);   /* { dg-warning "writing 6 bytes into a region of size 5" } */
      88    escape (b);
      89  
      90    /* Same as above but clearing just members of the second element
      91       of the array.  */
      92    memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size 3" } */
      93    escape (b);
      94  
      95    memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size 2" } */
      96    escape (b);
      97  
      98    memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" } */
      99    escape (b);
     100  }
     101  
     102  /* Verify that writing past the end of a dynamically allocated array
     103     of known size is diagnosed.  */
     104  
     105  void test_memop_warn_alloc (const void *src)
     106  {
     107    size_t n;
     108  
     109    n = range (8, 32);
     110  
     111    struct A *a = __builtin_malloc (sizeof *a * 2);
     112  
     113    memcpy (a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 " "memcpy into allocated" } */
     114    escape (a, src);
     115  
     116    /* At -Wstringop-overflow=1 the destination is considered to be
     117       the whole array and its size is therefore sizeof a.  */
     118    memcpy (&a[0], src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size 4 overflows the destination" "memcpy into allocated" } */
     119    escape (a, src);
     120  
     121    /* Verify the same as above but by writing into the first mmeber
     122       of the first element of the array.  */
     123    memcpy (&a[0].a, src, n);   /* { dg-warning "writing between 8 and 32 bytes into a region of size " "memcpy into allocated" } */
     124    escape (a, src);
     125  
     126    n = range (12, 32);
     127  
     128    struct B *b = __builtin_malloc (sizeof (struct B[2]));
     129  
     130    memcpy (&b[0], src, n);   /* { dg-warning "writing between 12 and 32 bytes into a region of size 8 " "memcpy into allocated" } */
     131    escape (b);
     132  
     133    /* The following idiom of clearing multiple members of a struct is
     134       used in a few places in the Linux kernel.  Verify that a warning
     135       is issued for it when it writes past the end of the array object.  */
     136    memset (&b[0].a.b, 0, offsetfrom (struct B, struct B[2], a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
     137    escape (b);
     138  
     139    memset (&b->a.b, 0, offsetfrom (struct B, struct B[2], a.b) + 1);   /* { dg-warning "writing 8 bytes into a region of size " "memcpy into allocated" } */
     140    escape (b);
     141  
     142    memset (&b[0].c, 0, offsetfrom (struct B, struct B[2], c) + 1);   /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
     143    escape (b);
     144  
     145    memset (&b->c, 0, offsetfrom (struct B, struct B[2], c) + 1);   /* { dg-warning "writing 7 bytes into a region of size " "memcpy into allocated" } */
     146    escape (b);
     147  
     148    memset (&b[0].d, 0, offsetfrom (struct B, struct B[2], d) + 1);   /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
     149    escape (b);
     150  
     151    memset (&b->d, 0, offsetfrom (struct B, struct B[2], d) + 1);   /* { dg-warning "writing 6 bytes into a region of size " "memcpy into allocated" } */
     152    escape (b);
     153  
     154    /* Same as above but clearing just elements of the second element
     155       of the array.  */
     156    memset (&b[1].a.b, 0, offsetfrom (struct B, b[1], a.b) + 1);   /* { dg-warning "writing 4 bytes into a region of size " "memcpy into allocated" } */
     157    escape (b);
     158  
     159    memset (&b[1].c, 0, offsetfrom (struct B, b[1], c) + 1);   /* { dg-warning "writing 3 bytes into a region of size " "memcpy into allocated" } */
     160    escape (b);
     161  
     162    memset (&b[1].d, 0, offsetfrom (struct B, b[1], d) + 1);   /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy into allocated" } */
     163    escape (b);
     164  }
     165  
     166  
     167  void test_memop_nowarn (const void *src)
     168  {
     169    struct B b[2];
     170  
     171    size_t n = range (sizeof b, 32);
     172  
     173    /* Verify that clearing the whole array is not diagnosed regardless
     174       of whether the expression pointing to its beginning is obtained
     175       from the array itself or its first member(s).  */
     176    memcpy (b, src, n);
     177    escape (b);
     178  
     179    memcpy (&b[0], src, n);
     180    escape (b);
     181  
     182    memcpy (&b[0].a, src, n);
     183    escape (b, src);
     184  
     185    memcpy (&b[0].a.a, src, n);
     186    escape (b, src);
     187  
     188    /* Clearing multiple elements of an array of structs.  */
     189    memset (&b[0].a.b, 0, sizeof b - offsetof (struct B, a.b));
     190    escape (b);
     191  
     192    memset (&b->a.b, 0, sizeof b - offsetof (struct B, a.b));
     193    escape (b);
     194  
     195    memset (&b[0].c, 0, sizeof b - offsetof (struct B, c));
     196    escape (b);
     197  
     198    memset (&b->c, 0, sizeof b - offsetof (struct B, c));
     199    escape (b);
     200  
     201    memset (&b[0].d, 0, sizeof b - offsetof (struct B, d));
     202    escape (b);
     203  
     204    memset (&b->d, 0, sizeof b - offsetof (struct B, d));
     205    escape (b);
     206  
     207    /* Same as above but clearing just elements of the second element
     208       of the array.  */
     209    memset (&b[1].a.b, 0, sizeof b[1] - offsetof (struct B, a.b));
     210    escape (b);
     211  
     212    memset (&b[1].c, 0, sizeof b[1] - offsetof (struct B, c));
     213    escape (b);
     214  
     215    memset (&b[1].d, 0, sizeof b[1] - offsetof (struct B, d));
     216    escape (b);
     217  }
     218  
     219  
     220  /* The foollowing function could specify in its API that it takes
     221     an array of exactly two elements, as shown below.  Verify that
     222     writing into both elements is not diagnosed.  */
     223  void test_memop_nowarn_arg (struct A[2], const void*);
     224  
     225  void test_memop_nowarn_arg (struct A *a, const void *src)
     226  {
     227    memcpy (a, src, 2 * sizeof *a);
     228    escape (a, src);
     229  
     230    memcpy (a, src, range (2 * sizeof *a, 123));
     231    escape (a, src);
     232  }
     233  
     234  
     235  struct C { char a[3], b; };
     236  struct D { struct C c; char d, e; };
     237  
     238  extern char* strncpy (char*, const char*, __SIZE_TYPE__);
     239  
     240  void test_stringop_warn (void)
     241  {
     242    size_t n = range (2 * sizeof (struct D) + 1, 33);
     243  
     244    struct C c[2];
     245  
     246    /* Similarly, at -Wstringop-overflow=1 the destination is considered
     247       to be the whole array and its size is therefore sizeof c.  */
     248    strncpy (c[0].a, "123", n);   /* { dg-warning "writing between 13 and 33 bytes into a region of size 8 overflows the destination" } */
     249  
     250    escape (c);
     251  }
     252  
     253  
     254  void test_stringop_nowarn (void)
     255  {
     256    struct D d[2];
     257  
     258    strncpy (d[0].c.a, "123", range (sizeof d, 32));
     259    escape (d);
     260  }