1  /* Copyright (C) 2004, 2005  Free Software Foundation.
       2  
       3     Ensure builtin __strncpy_chk performs correctly.  */
       4  
       5  extern void abort (void);
       6  typedef __SIZE_TYPE__ size_t;
       7  extern size_t strlen(const char *);
       8  extern void *memcpy (void *, const void *, size_t);
       9  extern char *strncpy (char *, const char *, size_t);
      10  extern int memcmp (const void *, const void *, size_t);
      11  extern int strcmp (const char *, const char *);
      12  extern int strncmp (const char *, const char *, size_t);
      13  extern void *memset (void *, int, size_t);
      14  
      15  #include "chk.h"
      16  
      17  const char s1[] = "123";
      18  char p[32] = "";
      19  char * volatile s2 = "defg";  /* prevent constant propagation to happen when whole program assumptions are made.  */
      20  char * volatile s3 = "FGH";  /* prevent constant propagation to happen when whole program assumptions are made.  */
      21  char *s4;
      22  volatile size_t l1 = 1;  /* prevent constant propagation to happen when whole program assumptions are made.  */
      23  int i;
      24  
      25  void
      26  __attribute__((noinline))
      27  test1 (void)
      28  {
      29    const char *const src = "hello world";
      30    const char *src2;
      31    char dst[64], *dst2;
      32  
      33    strncpy_disallowed = 1;
      34    chk_calls = 0;
      35  
      36    memset (dst, 0, sizeof (dst));
      37    if (strncpy (dst, src, 4) != dst || strncmp (dst, src, 4))
      38      abort();
      39  
      40    memset (dst, 0, sizeof (dst));
      41    if (strncpy (dst+16, src, 4) != dst+16 || strncmp (dst+16, src, 4))
      42      abort();
      43  
      44    memset (dst, 0, sizeof (dst));
      45    if (strncpy (dst+32, src+5, 4) != dst+32 || strncmp (dst+32, src+5, 4))
      46      abort();
      47  
      48    memset (dst, 0, sizeof (dst));
      49    dst2 = dst;
      50    if (strncpy (++dst2, src+5, 4) != dst+1 || strncmp (dst2, src+5, 4)
      51        || dst2 != dst+1)
      52      abort();
      53  
      54    memset (dst, 0, sizeof (dst));
      55    if (strncpy (dst, src, 0) != dst || strcmp (dst, ""))
      56      abort();
      57    
      58    memset (dst, 0, sizeof (dst));
      59    dst2 = dst; src2 = src;
      60    if (strncpy (++dst2, ++src2, 0) != dst+1 || strcmp (dst2, "")
      61        || dst2 != dst+1 || src2 != src+1)
      62      abort();
      63  
      64    memset (dst, 0, sizeof (dst));
      65    dst2 = dst; src2 = src;
      66    if (strncpy (++dst2+5, ++src2+5, 0) != dst+6 || strcmp (dst2+5, "")
      67        || dst2 != dst+1 || src2 != src+1)
      68      abort();
      69  
      70    memset (dst, 0, sizeof (dst));
      71    if (strncpy (dst, src, 12) != dst || strcmp (dst, src))
      72      abort();
      73  
      74    /* Test at least one instance of the __builtin_ style.  We do this
      75       to ensure that it works and that the prototype is correct.  */
      76    memset (dst, 0, sizeof (dst));
      77    if (__builtin_strncpy (dst, src, 4) != dst || strncmp (dst, src, 4))
      78      abort();
      79  
      80    memset (dst, 0, sizeof (dst));
      81    if (strncpy (dst, i++ ? "xfoo" + 1 : "bar", 4) != dst
      82        || strcmp (dst, "bar")
      83        || i != 1)
      84      abort ();
      85  
      86    if (chk_calls)
      87      abort ();
      88    strncpy_disallowed = 0;
      89  }
      90  
      91  void
      92  __attribute__((noinline))
      93  test2 (void)
      94  {
      95    chk_calls = 0;
      96    /* No runtime checking should be done here, both destination
      97       and length are unknown.  */
      98    strncpy (s4, "abcd", l1 + 1);
      99    if (chk_calls)
     100      abort ();
     101  }
     102  
     103  /* Test whether compile time checking is done where it should
     104     and so is runtime object size checking.  */
     105  void
     106  __attribute__((noinline))
     107  test3 (void)
     108  {
     109    struct A { char buf1[10]; char buf2[10]; } a;
     110    char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
     111    char buf3[20];
     112    int i;
     113    const char *l;
     114    size_t l2;
     115  
     116    /* The following calls should do runtime checking
     117       - source length is not known, but destination is.  */
     118    chk_calls = 0;
     119    strncpy (a.buf1 + 2, s3 + 3, l1);
     120    strncpy (r, s3 + 2, l1 + 2);
     121    r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     122    strncpy (r, s2 + 2, l1 + 2);
     123    strncpy (r + 2, s3 + 3, l1);
     124    r = buf3;
     125    for (i = 0; i < 4; ++i)
     126      {
     127        if (i == l1 - 1)
     128  	r = &a.buf1[1];
     129        else if (i == l1)
     130  	r = &a.buf2[7];
     131        else if (i == l1 + 1)
     132  	r = &buf3[5];
     133        else if (i == l1 + 2)
     134  	r = &a.buf1[9];
     135      }
     136    strncpy (r, s2 + 4, l1);
     137    if (chk_calls != 5)
     138      abort ();
     139  
     140    /* Following have known destination and known length,
     141       so if optimizing certainly shouldn't result in the checking
     142       variants.  */
     143    chk_calls = 0;
     144    strncpy (a.buf1 + 2, "", 3);
     145    strncpy (a.buf1 + 2, "", 0);
     146    strncpy (r, "a", 1);
     147    strncpy (r, "a", 3);
     148    r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     149    strncpy (r, s1 + 1, 3);
     150    strncpy (r, s1 + 1, 2);
     151    r = buf3;
     152    l = "abc";
     153    l2 = 4;
     154    for (i = 0; i < 4; ++i)
     155      {
     156        if (i == l1 - 1)
     157  	r = &a.buf1[1], l = "e", l2 = 2;
     158        else if (i == l1)
     159  	r = &a.buf2[7], l = "gh", l2 = 3;
     160        else if (i == l1 + 1)
     161  	r = &buf3[5], l = "jkl", l2 = 4;
     162        else if (i == l1 + 2)
     163  	r = &a.buf1[9], l = "", l2 = 1;
     164      }
     165    strncpy (r, "", 1);
     166    /* Here, strlen (l) + 1 is known to be at most 4 and
     167       __builtin_object_size (&buf3[16], 0) is 4, so this doesn't need
     168       runtime checking.  */
     169    strncpy (&buf3[16], l, l2);
     170    strncpy (&buf3[15], "abc", l2);
     171    strncpy (&buf3[10], "fghij", l2);
     172    if (chk_calls)
     173      abort ();
     174    chk_calls = 0;
     175  }
     176  
     177  /* Test whether runtime and/or compile time checking catches
     178     buffer overflows.  */
     179  void
     180  __attribute__((noinline))
     181  test4 (void)
     182  {
     183    struct A { char buf1[10]; char buf2[10]; } a;
     184    char buf3[20];
     185  
     186    chk_fail_allowed = 1;
     187    /* Runtime checks.  */
     188    if (__builtin_setjmp (chk_fail_buf) == 0)
     189      {
     190        strncpy (&a.buf2[9], s2 + 4, l1 + 1);
     191        abort ();
     192      }
     193    if (__builtin_setjmp (chk_fail_buf) == 0)
     194      {
     195        strncpy (&a.buf2[7], s3, l1 + 4);
     196        abort ();
     197      }
     198    /* This should be detectable at compile time already.  */
     199    if (__builtin_setjmp (chk_fail_buf) == 0)
     200      {
     201        strncpy (&buf3[19], "abc", 2);
     202        abort ();
     203      }
     204    if (__builtin_setjmp (chk_fail_buf) == 0)
     205      {
     206        strncpy (&buf3[18], "", 3);
     207        abort ();
     208      }
     209    chk_fail_allowed = 0;
     210  }
     211  
     212  void
     213  main_test (void)
     214  {
     215  #ifndef __OPTIMIZE__
     216    /* Object size checking is only intended for -O[s123].  */
     217    return;
     218  #endif
     219    __asm ("" : "=r" (s2) : "0" (s2));
     220    __asm ("" : "=r" (s3) : "0" (s3));
     221    __asm ("" : "=r" (l1) : "0" (l1));
     222    test1 ();
     223    s4 = p;
     224    test2 ();
     225    test3 ();
     226    test4 ();
     227  }