1 /* Copyright (C) 2004, 2005 Free Software Foundation.
2
3 Ensure builtin __strcat_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 int memcmp (const void *, const void *, size_t);
11 extern char *strcpy (char *, const char *);
12 extern int strcmp (const char *, const char *);
13 extern 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
19 const char s1[] = "123";
20 char p[32] = "";
21 char *s2 = "defg";
22 char *s3 = "FGH";
23 char *s4;
24 size_t l1 = 1;
25 char *s5;
26
27 void
28 __attribute__((noinline))
test1(void)29 test1 (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. */
93 void
94 __attribute__((noinline))
test2(void)95 test2 (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. */
156 void
157 __attribute__((noinline))
test3(void)158 test3 (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
189 void
main_test(void)190 main_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