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