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