1  /* { dg-do compile } */
       2  /* { dg-options "-O2 -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */
       3  
       4  typedef __SIZE_TYPE__ size_t;
       5  
       6  #define INT_MAX   __INT_MAX__
       7  #define INT_MIN   (-INT_MAX - 1)
       8  
       9  #ifndef LINE
      10  #  define LINE 0
      11  #endif
      12  
      13  int dummy_sprintf (char*, const char*, ...);
      14  void sink (void*);
      15  
      16  char buffer[4096];
      17  char *ptr;
      18  
      19  /* Helper to expand function to either __builtin_f or dummy_f to
      20     make debugging GCC easy.  */
      21  #define FUNC(f)							\
      22    ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f)
      23  
      24  /* Evaluate to an array of SIZE characters when non-negative, or to
      25     a pointer to an unknown object otherwise.  */
      26  #define buffer(size)					\
      27    ((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
      28  
      29  #define T(bufsize, fmt, ...)						\
      30    do {									\
      31      char *buf = buffer (bufsize);					\
      32      FUNC (sprintf)(buf, fmt, __VA_ARGS__);				\
      33      sink (buf);								\
      34    } while (0)
      35  
      36  
      37  /* Identity function to verify that the checker figures out the value
      38     of the operand even when it's not constant (i.e., makes use of
      39     inlining and constant propagation information).  */
      40  
      41  static int i (int x) { return x; }
      42  static const char* s (const char *str) { return str; }
      43  
      44  /* Function to "generate" a unique unknown number (as far as GCC can
      45     tell) each time it's called.  It prevents the optimizer from being
      46     able to narrow down the ranges of possible values in test functions
      47     with repeated references to the same variable.  */
      48  extern int value (void);
      49  
      50  /* Return a value in the range [MIN, MAX].  */
      51  static int range (int min, int max)
      52  {
      53    int val = value ();
      54    return val < min || max < val ? min : val;
      55  }
      56  
      57  #define R(min, max) range (min, max)
      58  
      59  /* Verify that the checker can detect buffer overflow when the "%s"
      60     argument is in a known range of lengths and one or both of which
      61     exceed the size of the destination.  */
      62  
      63  void test_sprintf_chk_string (const char *s)
      64  {
      65    T (1, "%*s", R (0, 1), "");     /* { dg-warning "may write a terminating nul" } */
      66    T (1, "%*s", R (-2, -1), "");   /* { dg-warning "writing up to 2 bytes" } */
      67    T (1, "%*s", R (-3,  2), "");   /* { dg-warning "writing up to 3 bytes" } */
      68    T (1, "%*s", R (-4,  5), "");   /* { dg-warning "writing up to 5 bytes" } */
      69  
      70    T (1, "%*s", R ( -5, 6), "1");  /* { dg-warning "writing between 1 and 6 bytes" } */
      71    T (1, "%*s", R ( -6, 7), "12"); /* { dg-warning "writing between 2 and 7 bytes" } */
      72  
      73    T (1, "%.*s", R (0, 1), "");
      74    T (1, "%.*s", R (0, 1), s);     /* { dg-warning "may write a terminating nul" } */
      75    T (1, "%.*s", R (-2, -1), "");
      76    T (1, "%.*s", R (-2, -1), s);   /* { dg-warning "may write a terminating nul" } */
      77    T (1, "%.*s", R (-3,  2), "");
      78    T (1, "%.*s", R (-4,  5), "");
      79  
      80    T (1, "%.*s", R ( -5, 6), "1");  /* { dg-warning "may write a terminating nul" } */
      81    T (1, "%.*s", R ( -6, 7), "12"); /* { dg-warning "writing up to 2 bytes " } */
      82    T (1, "%.*s", R (  1, 7), "12"); /* { dg-warning "writing between 1 and 2 bytes " } */
      83    T (1, "%.*s", R (  2, 7), "12"); /* { dg-warning "writing 2 bytes " } */
      84  
      85    T (1, "%*.*s", R (0, 1), R (0, 1), "");     /* { dg-warning "may write a terminating nul" } */
      86    T (1, "%*.*s", R (0, 2), R (0, 1), "");     /* { dg-warning "directive writing up to 2 bytes into a region of size 1" } */
      87    T (1, "%*.*s", R (0, 3), R (0, 1), "");     /* { dg-warning "writing up to 3 bytes" } */
      88  
      89    T (1, "%*.*s", R (0, 1), R (0, 1), "1");    /* { dg-warning "may write a terminating nul" } */
      90    T (1, "%*.*s", R (0, 2), R (0, 1), "1");    /* { dg-warning "writing up to 2 bytes" } */
      91    T (1, "%*.*s", R (0, 3), R (0, 1), "1");    /* { dg-warning "writing up to 3 bytes" } */
      92  
      93    T (1, "%*.*s", R (0, 1), R (0, 1), "12");   /* { dg-warning "may write a terminating nul" } */
      94    T (1, "%*.*s", R (0, 2), R (0, 1), "12");   /* { dg-warning "writing up to 2 bytes" } */
      95    T (1, "%*.*s", R (0, 3), R (0, 1), "12");   /* { dg-warning "writing up to 3 bytes" } */
      96  
      97    T (1, "%*.*s", R (0, 1), R (0, 1), "123");  /* { dg-warning "may write a terminating nul" } */
      98    T (1, "%*.*s", R (0, 2), R (0, 1), "123");  /* { dg-warning "writing up to 2 bytes" } */
      99    T (1, "%*.*s", R (0, 3), R (0, 1), "123");  /* { dg-warning "writing up to 3 bytes" } */
     100    T (1, "%*.*s", R (0, 3), R (0, 1), s);      /* { dg-warning "writing up to 3 bytes" } */
     101  
     102    T (1, "%*.*s", R (0, 1), R (0, 2), "123");  /* { dg-warning "writing up to 2 bytes" } */
     103    T (1, "%*.*s", R (0, 2), R (0, 2), "123");  /* { dg-warning "writing up to 2 bytes" } */
     104    T (1, "%*.*s", R (0, 3), R (0, 2), "123");  /* { dg-warning "writing up to 3 bytes" } */
     105    T (1, "%*.*s", R (0, 3), R (0, 2), s);      /* { dg-warning "writing up to 3 bytes" } */
     106  
     107    T (1, "%*.*s", R (0, 1), R (0, 3), "123");  /* { dg-warning "writing up to 3 bytes" } */
     108    T (1, "%*.*s", R (0, 2), R (0, 3), "123");  /* { dg-warning "writing up to 3 bytes" } */
     109    T (1, "%*.*s", R (0, 3), R (0, 3), "123");  /* { dg-warning "writing up to 3 bytes" } */
     110    T (1, "%*.*s", R (0, 3), R (0, 3), s);      /* { dg-warning "writing up to 3 bytes" } */
     111  
     112    T (1, "%*.*s", R (1, 1), R (0, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     113    T (1, "%*.*s", R (1, 2), R (0, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     114    T (1, "%*.*s", R (1, 3), R (0, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     115    T (1, "%*.*s", R (1, 3), R (0, 3), s);      /* { dg-warning "writing between 1 and 3 bytes" } */
     116  
     117    T (1, "%*.*s", R (1, 1), R (1, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     118    T (1, "%*.*s", R (1, 2), R (1, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     119    T (1, "%*.*s", R (1, 3), R (1, 3), "123");  /* { dg-warning "writing between 1 and 3 bytes" } */
     120    T (1, "%*.*s", R (1, 3), R (1, 3), s);      /* { dg-warning "writing between 1 and 3 bytes" } */
     121  
     122    T (1, "%*.*s", R (2, 3), R (1, 3), "123");  /* { dg-warning "writing between 2 and 3 bytes" } */
     123    T (1, "%*.*s", R (3, 4), R (1, 3), "123");  /* { dg-warning "writing between 3 and 4 bytes" } */
     124    T (1, "%*.*s", R (4, 5), R (1, 3), "123");  /* { dg-warning "writing between 4 and 5 bytes" } */
     125    T (1, "%*.*s", R (2, 3), R (1, 3), s);      /* { dg-warning "writing between 2 and 3 bytes" } */
     126  }
     127  
     128  void test_sprintf_chk_int (int w, int p, int i)
     129  {
     130    T (1, "%*d", w, 0);             /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
     131    T (1, "%*d", w, i);             /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
     132  
     133    T (1, "%*d", R (-1, 1), 0);     /* { dg-warning "writing a terminating nul" } */
     134    T (1, "%*d", R ( 0, 1), 0);     /* { dg-warning "writing a terminating nul" } */
     135    T (1, "%+*d", R ( 0, 1), 0);    /* { dg-warning "directive writing 2 bytes" } */
     136    T (1, "%+*u", R ( 0, 1), 0);    /* { dg-warning "writing a terminating nul" } */
     137    T (2, "%*d", R (-3, -2), 0);     /* { dg-warning "directive writing between 1 and 3 bytes" } */
     138    T (2, "%*d", R (-3, -1), 0);     /* { dg-warning "directive writing between 1 and 3 bytes" } */
     139    T (2, "%*d", R (-3,  0), 0);     /* { dg-warning "directive writing between 1 and 3 bytes" } */
     140    T (2, "%*d", R (-2, -1), 0);     /* { dg-warning "may write a terminating nul" } */
     141    T (2, "%*d", R (-2,  2), 0);     /* { dg-warning "may write a terminating nul" } */
     142    T (2, "%*d", R (-1,  2), 0);     /* { dg-warning "may write a terminating nul" } */
     143    T (2, "%*d", R ( 0,  2), 0);     /* { dg-warning "may write a terminating nul" } */
     144    T (2, "%*d", R ( 1,  2), 0);     /* { dg-warning "may write a terminating nul" } */
     145  
     146    T (1, "%.*d", p, 0);             /* { dg-warning "may write a terminating nul|directive writing up to \[0-9\]+ bytes" } */
     147    T (1, "%.*d", p, i);             /* { dg-warning "may write a terminating nul||directive writing up to \[0-9\]+ bytes" } */
     148    T (1, "%.*d", R (INT_MIN, -1), 0);     /* { dg-warning "writing a terminating nul" } */
     149    T (1, "%.*d", R (INT_MIN,  0), 0);     /* { dg-warning "may write a terminating nul" } */
     150    T (1, "%.*d", R (-2, -1), 0);     /* { dg-warning "writing a terminating nul" } */
     151    T (1, "%.*d", R (-1,  1), 0);     /* { dg-warning "may write a terminating nul" } */
     152    T (1, "%.*d", R ( 0,  1), 0);     /* { dg-warning "may write a terminating nul" } */
     153    T (1, "%.*d", R ( 0,  2), 0);     /* { dg-warning "directive writing up to 2 bytes" } */
     154    T (1, "%.*d", R ( 0,  INT_MAX - 1), 0);     /* { dg-warning "directive writing up to \[0-9\]+ bytes" } */
     155    T (1, "%.*d", R ( 1,  INT_MAX - 1), 0);     /* { dg-warning "directive writing between 1 and \[0-9\]+ bytes" } */
     156  }
     157  
     158  /* { dg-prune-output "flag used with .%.. gnu_printf format" } */