1  /* Copyright (C) 2004, 2005  Free Software Foundation.
       2  
       3     Ensure builtin __strncat_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 *strcat (char *, const char *);
      10  extern char *strncat (char *, const char *, size_t);
      11  extern int memcmp (const void *, const void *, size_t);
      12  extern char *strcpy (char *, const char *);
      13  extern int strcmp (const char *, const char *);
      14  extern void *memset (void *, int, size_t);
      15  
      16  #include "chk.h"
      17  
      18  const char s1[] = "123";
      19  char p[32] = "";
      20  char *s2 = "defg";
      21  char *s3 = "FGH";
      22  char *s4;
      23  size_t l1 = 1;
      24  char *s5;
      25  int x = 123;
      26  
      27  void
      28  __attribute__((noinline))
      29  test1 (void)
      30  {
      31    const char *const s1 = "hello world";
      32    const char *const s2 = "";
      33    const char *s3;
      34    char dst[64], *d2;
      35  
      36    /* Following strncat calls should be all optimized out.  */
      37    chk_calls = 0;
      38    strncat_disallowed = 1;
      39    strcat_disallowed = 1;
      40    strcpy (dst, s1);
      41    if (strncat (dst, "", 100) != dst || strcmp (dst, s1))
      42      abort ();
      43    strcpy (dst, s1);
      44    if (strncat (dst, s2, 100) != dst || strcmp (dst, s1))
      45      abort ();
      46    strcpy (dst, s1); d2 = dst;
      47    if (strncat (++d2, s2, 100) != dst+1 || d2 != dst+1 || strcmp (dst, s1))
      48      abort ();
      49    strcpy (dst, s1); d2 = dst;
      50    if (strncat (++d2+5, s2, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
      51      abort ();
      52    strcpy (dst, s1); d2 = dst;
      53    if (strncat (++d2+5, s1+11, 100) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
      54      abort ();
      55    strcpy (dst, s1); d2 = dst;
      56    if (strncat (++d2+5, s1, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1))
      57      abort ();
      58    strcpy (dst, s1); d2 = dst; s3 = s1;
      59    if (strncat (++d2+5, ++s3, 0) != dst+6 || d2 != dst+1 || strcmp (dst, s1)
      60        || s3 != s1 + 1)
      61      abort ();
      62    strcpy (dst, s1); d2 = dst;
      63    if (strncat (++d2+5, "", ++x) != dst+6 || d2 != dst+1 || x != 124
      64        || strcmp (dst, s1))
      65      abort ();
      66    if (chk_calls)
      67      abort ();
      68    strcat_disallowed = 0;
      69  
      70    /* These __strncat_chk calls should be optimized into __strcat_chk,
      71       as strlen (src) <= len.  */
      72    strcpy (dst, s1);
      73    if (strncat (dst, "foo", 3) != dst || strcmp (dst, "hello worldfoo"))
      74      abort ();
      75    strcpy (dst, s1);
      76    if (strncat (dst, "foo", 100) != dst || strcmp (dst, "hello worldfoo"))
      77      abort ();
      78    strcpy (dst, s1);
      79    if (strncat (dst, s1, 100) != dst || strcmp (dst, "hello worldhello world"))
      80      abort ();
      81    if (chk_calls != 3)
      82      abort ();
      83  
      84    chk_calls = 0;
      85    /* The following calls have side-effects in dest, so are not checked.  */
      86    strcpy (dst, s1); d2 = dst;
      87    if (__builtin___strncat_chk (++d2, s1, 100, os (++d2)) != dst+1
      88        || d2 != dst+1 || strcmp (dst, "hello worldhello world"))
      89      abort ();
      90    strcpy (dst, s1); d2 = dst;
      91    if (__builtin___strncat_chk (++d2+5, s1, 100, os (++d2+5)) != dst+6
      92        || d2 != dst+1 || strcmp (dst, "hello worldhello world"))
      93      abort ();
      94    strcpy (dst, s1); d2 = dst;
      95    if (__builtin___strncat_chk (++d2+5, s1+5, 100, os (++d2+5)) != dst+6
      96        || d2 != dst+1 || strcmp (dst, "hello world world"))
      97      abort ();
      98    if (chk_calls)
      99      abort ();
     100  
     101    chk_calls = 0;
     102    strcat_disallowed = 1;
     103  
     104    /* Test at least one instance of the __builtin_ style.  We do this
     105       to ensure that it works and that the prototype is correct.  */
     106    strcpy (dst, s1);
     107    if (__builtin_strncat (dst, "", 100) != dst || strcmp (dst, s1))
     108      abort ();
     109  
     110    if (chk_calls)
     111      abort ();
     112    strncat_disallowed = 0;
     113    strcat_disallowed = 0;
     114  }
     115  
     116  /* Test whether compile time checking is done where it should
     117     and so is runtime object size checking.  */
     118  void
     119  __attribute__((noinline))
     120  test2 (void)
     121  {
     122    struct A { char buf1[10]; char buf2[10]; } a;
     123    char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
     124    char buf3[20];
     125    int i;
     126  
     127    /* The following calls should do runtime checking.  */
     128    memset (&a, '\0', sizeof (a));
     129    s5 = (char *) &a;
     130    __asm __volatile ("" : : "r" (s5) : "memory");
     131    chk_calls = 0;
     132    strncat (a.buf1 + 2, s3 + 3, l1 - 1);
     133    strncat (r, s3 + 2, l1);
     134    r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     135    memset (r, '\0', 3);
     136    __asm __volatile ("" : : "r" (r) : "memory");
     137    strncat (r, s2 + 2, l1 + 1);
     138    strncat (r + 2, s3 + 3, l1 - 1);
     139    r = buf3;
     140    for (i = 0; i < 4; ++i)
     141      {
     142        if (i == l1 - 1)
     143  	r = &a.buf1[1];
     144        else if (i == l1)
     145  	r = &a.buf2[7];
     146        else if (i == l1 + 1)
     147  	r = &buf3[5];
     148        else if (i == l1 + 2)
     149  	r = &a.buf1[9];
     150      }
     151    strncat (r, s2 + 4, l1);
     152    if (chk_calls != 5)
     153      abort ();
     154  
     155    /* Following have known destination and known source length,
     156       but we don't know the length of dest string, so runtime checking
     157       is needed too.  */
     158    memset (&a, '\0', sizeof (a));
     159    chk_calls = 0;
     160    s5 = (char *) &a;
     161    __asm __volatile ("" : : "r" (s5) : "memory");
     162    strncat (a.buf1 + 2, "a", 5);
     163    strncat (r, "def", 0);
     164    r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     165    memset (r, '\0', 3);
     166    __asm __volatile ("" : : "r" (r) : "memory");
     167    strncat (r, s1 + 1, 2);
     168    if (chk_calls != 2)
     169      abort ();
     170    chk_calls = 0;
     171    strcat_disallowed = 1;
     172    /* Unknown destination and source, no checking.  */
     173    strncat (s4, s3, l1 + 1);
     174    strcat_disallowed = 0;
     175    if (chk_calls)
     176      abort ();
     177  }
     178  
     179  /* Test whether runtime and/or compile time checking catches
     180     buffer overflows.  */
     181  void
     182  __attribute__((noinline))
     183  test3 (void)
     184  {
     185    struct A { char buf1[10]; char buf2[10]; } a;
     186    char buf3[20];
     187  
     188    memset (&a, '\0', sizeof (a));
     189    memset (buf3, '\0', sizeof (buf3));
     190    s5 = (char *) &a;
     191    __asm __volatile ("" : : "r" (s5) : "memory");
     192    s5 = buf3;
     193    __asm __volatile ("" : : "r" (s5) : "memory");
     194    chk_fail_allowed = 1;
     195    /* Runtime checks.  */
     196    if (__builtin_setjmp (chk_fail_buf) == 0)
     197      {
     198        strncat (&a.buf2[9], s2 + 3, 4);
     199        abort ();
     200      }
     201    if (__builtin_setjmp (chk_fail_buf) == 0)
     202      {
     203        strncat (&a.buf2[7], s3 + strlen (s3) - 3, 3);
     204        abort ();
     205      }
     206    if (__builtin_setjmp (chk_fail_buf) == 0)
     207      {
     208        strncat (&buf3[19], "abcde", 1);
     209        abort ();
     210      }
     211    chk_fail_allowed = 0;
     212  }
     213  
     214  void
     215  main_test (void)
     216  {
     217  #ifndef __OPTIMIZE__
     218    /* Object size checking is only intended for -O[s123].  */
     219    return;
     220  #endif
     221    __asm ("" : "=r" (s2) : "0" (s2));
     222    __asm ("" : "=r" (s3) : "0" (s3));
     223    __asm ("" : "=r" (l1) : "0" (l1));
     224    s4 = p;
     225    test1 ();
     226    memset (p, '\0', sizeof (p));
     227    test2 ();
     228    test3 ();
     229  }