1  /* Copyright (C) 2004, 2005  Free Software Foundation.
       2  
       3     Ensure builtin __vsnprintf_chk performs correctly.  */
       4  
       5  #include <stdarg.h>
       6  
       7  extern void abort (void);
       8  typedef __SIZE_TYPE__ size_t;
       9  extern size_t strlen(const char *);
      10  extern void *memcpy (void *, const void *, size_t);
      11  extern char *strcpy (char *, const char *);
      12  extern int memcmp (const void *, const void *, size_t);
      13  extern void *memset (void *, int, size_t);
      14  extern int vsnprintf (char *, size_t, const char *, va_list);
      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  static char buffer[32];
      25  char * volatile ptr = "barf";  /* prevent constant propagation to happen when whole program assumptions are made.  */
      26  
      27  int
      28  __attribute__((noinline))
      29  test1_sub (int i, ...)
      30  {
      31    int ret = 0;
      32    va_list ap;
      33    va_start (ap, i);
      34    switch (i)
      35      {
      36      case 0:
      37        vsnprintf (buffer, 4, "foo", ap);
      38        break;
      39      case 1:
      40        ret = vsnprintf (buffer, 4, "foo bar", ap);
      41        break;
      42      case 2:
      43        vsnprintf (buffer, 32, "%s", ap);
      44        break;
      45      case 3:
      46        ret = vsnprintf (buffer, 21, "%s", ap);
      47        break;
      48      case 4:
      49        ret = vsnprintf (buffer, 4, "%d%d%d", ap);
      50        break;
      51      case 5:
      52        ret = vsnprintf (buffer, 32, "%d%d%d", ap);
      53        break;
      54      case 6:
      55        ret = vsnprintf (buffer, strlen (ptr) + 1, "%s", ap);
      56        break;
      57      case 7:
      58        vsnprintf (buffer, l1 + 31, "%d - %c", ap);
      59        break;
      60      case 8:
      61        vsnprintf (s4, l1 + 6, "%d - %c", ap);
      62        break;
      63      }
      64    va_end (ap);
      65    return ret;
      66  }
      67  
      68  void
      69  __attribute__((noinline))
      70  test1 (void)
      71  {
      72    chk_calls = 0;
      73    /* vsnprintf_disallowed = 1; */
      74  
      75    memset (buffer, 'A', 32);
      76    test1_sub (0);
      77    if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
      78      abort ();
      79  
      80    memset (buffer, 'A', 32);
      81    if (test1_sub (1) != 7)
      82      abort ();
      83    if (memcmp (buffer, "foo", 4) || buffer[4] != 'A')
      84      abort ();
      85  
      86    vsnprintf_disallowed = 0;
      87  
      88    memset (buffer, 'A', 32);
      89    test1_sub (2, "bar");
      90    if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
      91      abort ();
      92  
      93    memset (buffer, 'A', 32);
      94    if (test1_sub (3, "bar") != 3)
      95      abort ();
      96    if (memcmp (buffer, "bar", 4) || buffer[4] != 'A')
      97      abort ();
      98  
      99    memset (buffer, 'A', 32);
     100    if (test1_sub (4, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4)
     101      abort ();
     102    if (memcmp (buffer, "121", 4) || buffer[4] != 'A')
     103      abort ();
     104  
     105    memset (buffer, 'A', 32);
     106    if (test1_sub (5, (int) l1, (int) l1 + 1, (int) l1 + 12) != 4)
     107      abort ();
     108    if (memcmp (buffer, "1213", 5) || buffer[5] != 'A')
     109      abort ();
     110  
     111    if (chk_calls)
     112      abort ();
     113  
     114    memset (buffer, 'A', 32);
     115    test1_sub (6, ptr);
     116    if (memcmp (buffer, "barf", 5) || buffer[5] != 'A')
     117      abort ();
     118  
     119    memset (buffer, 'A', 32);
     120    test1_sub (7, (int) l1 + 27, *ptr);
     121    if (memcmp (buffer, "28 - b\0AAAAA", 12))
     122      abort ();
     123  
     124    if (chk_calls != 2)
     125      abort ();
     126    chk_calls = 0;
     127  
     128    memset (s4, 'A', 32);
     129    test1_sub (8, (int) l1 - 17, ptr[1]);
     130    if (memcmp (s4, "-16 - \0AAA", 10))
     131      abort ();
     132    if (chk_calls)
     133      abort ();
     134  }
     135  
     136  void
     137  __attribute__((noinline))
     138  test2_sub (int i, ...)
     139  {
     140    va_list ap;
     141    struct A { char buf1[10]; char buf2[10]; } a;
     142    char *r = l1 == 1 ? &a.buf1[5] : &a.buf2[4];
     143    char buf3[20];
     144    int j;
     145  
     146    va_start (ap, i);
     147    /* The following calls should do runtime checking
     148       - length is not known, but destination is.  */
     149    switch (i)
     150      {
     151      case 0:
     152        vsnprintf (a.buf1 + 2, l1, "%s", ap);
     153        break;
     154      case 1:
     155        vsnprintf (r, l1 + 4, "%s%c", ap);
     156        break;
     157      case 2:
     158        r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     159        vsnprintf (r, strlen (s2) - 2, "%c %s", ap);
     160        break;
     161      case 3:
     162        r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     163        vsnprintf (r + 2, l1, s3 + 3, ap);
     164        break;
     165      case 4:
     166      case 7:
     167        r = buf3;
     168        for (j = 0; j < 4; ++j)
     169  	{
     170  	  if (j == l1 - 1)
     171  	    r = &a.buf1[1];
     172  	  else if (j == l1)
     173  	    r = &a.buf2[7];
     174  	  else if (j == l1 + 1)
     175  	    r = &buf3[5];
     176  	  else if (j == l1 + 2)
     177  	    r = &a.buf1[9];
     178  	}
     179        if (i == 4)
     180  	vsnprintf (r, l1, s2 + 4, ap);
     181        else
     182  	vsnprintf (r, 1, "a", ap);
     183        break;
     184      case 5:
     185        r = l1 == 1 ? __builtin_alloca (4) : &a.buf2[7];
     186        vsnprintf (r, l1 + 3, "%s", ap);
     187        break;
     188      case 6:
     189        vsnprintf (a.buf1 + 2, 4, "", ap);
     190        break;
     191      case 8:
     192        vsnprintf (s4, 3, "%s %d", ap);
     193        break;
     194      }
     195    va_end (ap);
     196  }
     197  
     198  /* Test whether compile time checking is done where it should
     199     and so is runtime object size checking.  */
     200  void
     201  __attribute__((noinline))
     202  test2 (void)
     203  {
     204    /* The following calls should do runtime checking
     205       - length is not known, but destination is.  */
     206    chk_calls = 0;
     207    test2_sub (0, s3 + 3);
     208    test2_sub (1, s3 + 3, s3[3]);
     209    test2_sub (2, s2[2], s2 + 4);
     210    test2_sub (3);
     211    test2_sub (4);
     212    test2_sub (5, s1 + 1);
     213    if (chk_calls != 6)
     214      abort ();
     215  
     216    /* Following have known destination and known source length,
     217       so if optimizing certainly shouldn't result in the checking
     218       variants.  */
     219    chk_calls = 0;
     220    /* vsnprintf_disallowed = 1; */
     221    test2_sub (6);
     222    test2_sub (7);
     223    vsnprintf_disallowed = 0;
     224    /* Unknown destination and source, no checking.  */
     225    test2_sub (8, s3, 0);
     226    if (chk_calls)
     227      abort ();
     228  }
     229  
     230  void
     231  __attribute__((noinline))
     232  test3_sub (int i, ...)
     233  {
     234    va_list ap;
     235    struct A { char buf1[10]; char buf2[10]; } a;
     236    char buf3[20];
     237  
     238    va_start (ap, i);
     239    /* The following calls should do runtime checking
     240       - source length is not known, but destination is.  */
     241    switch (i)
     242      {
     243      case 0:
     244        vsnprintf (&a.buf2[9], l1 + 1, "%c%s", ap);
     245        break;
     246      case 1:
     247        vsnprintf (&a.buf2[7], l1 + 30, "%s%c", ap);
     248        break;
     249      case 2:
     250        vsnprintf (&a.buf2[7], l1 + 3, "%d", ap);
     251        break;
     252      case 3:
     253        vsnprintf (&buf3[17], l1 + 3, "%s", ap);
     254        break;
     255      case 4:
     256        vsnprintf (&buf3[19], 2, "a", ap);
     257        break;
     258      case 5:
     259        vsnprintf (&buf3[16], 5, "a", ap);
     260        break;
     261      }
     262    va_end (ap);
     263  }
     264  
     265  /* Test whether runtime and/or compile time checking catches
     266     buffer overflows.  */
     267  void
     268  __attribute__((noinline))
     269  test3 (void)
     270  {
     271    chk_fail_allowed = 1;
     272    /* Runtime checks.  */
     273    if (__builtin_setjmp (chk_fail_buf) == 0)
     274      {
     275        test3_sub (0, s2[3], s2 + 4);
     276        abort ();
     277      }
     278    if (__builtin_setjmp (chk_fail_buf) == 0)
     279      {
     280        test3_sub (1, s3 + strlen (s3) - 2, *s3);
     281        abort ();
     282      }
     283    if (__builtin_setjmp (chk_fail_buf) == 0)
     284      {
     285        test3_sub (2, (int) l1 + 9999);
     286        abort ();
     287      }
     288    if (__builtin_setjmp (chk_fail_buf) == 0)
     289      {
     290        test3_sub (3, "abc");
     291        abort ();
     292      }
     293    /* This should be detectable at compile time already.  */
     294    if (__builtin_setjmp (chk_fail_buf) == 0)
     295      {
     296        test3_sub (4);
     297        abort ();
     298      }
     299    if (__builtin_setjmp (chk_fail_buf) == 0)
     300      {
     301        test3_sub (5);
     302        abort ();
     303      }
     304    chk_fail_allowed = 0;
     305  }
     306  
     307  void
     308  main_test (void)
     309  {
     310  #ifndef __OPTIMIZE__
     311    /* Object size checking is only intended for -O[s123].  */
     312    return;
     313  #endif
     314    __asm ("" : "=r" (s2) : "0" (s2));
     315    __asm ("" : "=r" (s3) : "0" (s3));
     316    __asm ("" : "=r" (l1) : "0" (l1));
     317    s4 = p;
     318    test1 ();
     319    test2 ();
     320    test3 ();
     321  }