1 #define _FORTIFY_SOURCE 0
2 #define __arch_memcmp_zero_ptr_aligned
3
4 /* must include first because otherwise header guard conflicts with SDK's
5 * string.h (quite reasonably)
6 */
7 #include "../osfmk/libsa/string.h"
8
9 char *strerror(int);
10 char *itoa(int, char *);
11
12 #include <darwintest.h>
13 #include <darwintest_utils.h>
14
15 #pragma clang diagnostic ignored "-Wlanguage-extension-token"
16 #pragma clang diagnostic ignored "-Wformat-pedantic"
17
18 #define DEVELOPMENT 0
19 #define DEBUG 0
20 #define XNU_KERNEL_PRIVATE 1
21
22 __printflike(1, 2) __attribute__((noreturn))
23 static void
panic(const char * fmt,...)24 panic(const char *fmt, ...)
25 {
26 va_list ap;
27 va_start(ap, fmt);
28 vfprintf(stderr, fmt, ap);
29 abort();
30 }
31
32 #include "../libkern/libkern/section_keywords.h"
33 #include "../osfmk/machine/string.h"
34 #include "../osfmk/device/subrs.c"
35
36 #pragma clang diagnostic ignored "-Wdeclaration-after-statement"
37 #pragma clang diagnostic ignored "-Wgnu-designator"
38
39 T_DECL(strbufcmp, "strbufcmp") {
40 #define T_COMPARE(A, AS, B, BS, EQ) T_ASSERT_EQ(strbufcmp_impl((A), (AS), (B), (BS)), (EQ), "compare '%s'.%zu, '%s'.%zu", (A), (AS), (B), (BS))
41 // two identical strings
42 char a[] = "hello";
43 char b[] = "hello";
44 T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
45 T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
46
47 // the same string
48 T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
49 T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
50
51 // two different strings
52 char c[] = "world";
53 T_COMPARE(a, sizeof(a), c, sizeof(c), a[0] - c[0]);
54 T_COMPARE(c, sizeof(c), a, sizeof(a), c[0] - a[0]);
55 char d[] = "hellp";
56 T_COMPARE(a, sizeof(a), d, sizeof(d), 'o' - 'p');
57 T_COMPARE(d, sizeof(d), a, sizeof(a), 'p' - 'o');
58
59 // strings of different size
60 char e[] = "aaaa";
61 char f[] = "aaaab";
62 T_COMPARE(e, sizeof(e), f, sizeof(f), 0 - 'b');
63 T_COMPARE(f, sizeof(f), e, sizeof(e), 'b' - 0);
64
65 // strings that are not NUL-terminated
66 T_COMPARE(a, sizeof(a) - 1, b, sizeof(b) - 1, 0);
67 T_COMPARE(b, sizeof(b) - 1, a, sizeof(a) - 1, 0);
68 T_COMPARE(a, sizeof(a) - 1, d, sizeof(d) - 1, 'o' - 'p');
69 T_COMPARE(d, sizeof(d) - 1, a, sizeof(a) - 1, 'p' - 'o');
70 T_COMPARE(e, sizeof(e) - 1, f, sizeof(f) - 1, 0 - 'b');
71 T_COMPARE(f, sizeof(f) - 1, e, sizeof(e) - 1, 'b' - 0);
72 #undef T_COMPARE
73 }
74
75 T_DECL(strlcmp, "strlcmp") {
76 #define T_COMPARE(A, AS, B, EQ) T_ASSERT_EQ(strlcmp_impl((A), (B), (AS)), (EQ), "compare '%s'.%zu, '%s'", (A), (AS), (B))
77 // two identical strings
78 char a[] = "hello";
79 char b[] = "hello";
80 T_COMPARE(a, sizeof(a), b, 0);
81 T_COMPARE(b, sizeof(b), a, 0);
82
83 // the same string
84 T_COMPARE(a, sizeof(a), b, 0);
85 T_COMPARE(b, sizeof(b), a, 0);
86
87 // two different strings
88 char c[] = "world";
89 T_COMPARE(a, sizeof(a), c, a[0] - c[0]);
90 T_COMPARE(c, sizeof(c), a, c[0] - a[0]);
91 char d[] = "hellp";
92 T_COMPARE(a, sizeof(a), d, 'o' - 'p');
93 T_COMPARE(d, sizeof(d), a, 'p' - 'o');
94
95 // strings of different size
96 char e[] = "aaaa";
97 char f[] = "aaaab";
98 T_COMPARE(e, sizeof(e), f, 0 - 'b');
99 T_COMPARE(f, sizeof(f), e, 'b' - 0);
100
101 // strings that are not NUL-terminated
102 T_COMPARE(a, sizeof(a) - 1, b, 0);
103 T_COMPARE(b, sizeof(b) - 1, a, 0);
104 T_COMPARE(a, sizeof(a) - 1, d, 'o' - 'p');
105 T_COMPARE(d, sizeof(d) - 1, a, 'p' - 'o');
106 T_COMPARE(e, sizeof(e) - 1, f, 0 - 'b');
107 T_COMPARE(f, sizeof(f) - 1, e, 'b' - 0);
108 #undef T_COMPARE
109 }
110
111 T_DECL(strbufcasecmp, "strbufcasecmp") {
112 #define T_COMPARE(A, AS, B, BS, EQ) T_ASSERT_EQ(strbufcasecmp_impl((A), (AS), (B), (BS)), (EQ), "case-insensitive compare '%s'.%zu, '%s'.%zu", (A), (AS), (B), (BS))
113 // same tests as strcasecmp, then tests with individual characters
114 // two identical strings
115 char a[] = "hElLo";
116 char b[] = "HeLlO";
117 T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
118 T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
119
120 // the same string
121 T_COMPARE(a, sizeof(a), b, sizeof(b), 0);
122 T_COMPARE(b, sizeof(b), a, sizeof(a), 0);
123
124 // two different strings
125 char c[] = "world";
126 T_COMPARE(a, sizeof(a), c, sizeof(c), a[0] - c[0]);
127 T_COMPARE(c, sizeof(c), a, sizeof(a), c[0] - a[0]);
128 char d[] = "hellp";
129 T_COMPARE(a, sizeof(a), d, sizeof(d), 'o' - 'p');
130 T_COMPARE(d, sizeof(d), a, sizeof(a), 'p' - 'o');
131
132 // strings of different size
133 char e[] = "aAaA";
134 char f[] = "AaAaB";
135 T_COMPARE(e, sizeof(e), f, sizeof(f), 0 - 'b');
136 T_COMPARE(f, sizeof(f), e, sizeof(e), 'b' - 0);
137
138 // strings that are not NUL-terminated
139 T_COMPARE(a, sizeof(a) - 1, b, sizeof(b) - 1, 0);
140 T_COMPARE(b, sizeof(b) - 1, a, sizeof(a) - 1, 0);
141 T_COMPARE(a, sizeof(a) - 1, d, sizeof(d) - 1, 'o' - 'p');
142 T_COMPARE(d, sizeof(d) - 1, a, sizeof(a) - 1, 'p' - 'o');
143 T_COMPARE(e, sizeof(e) - 1, f, sizeof(f) - 1, 0 - 'b');
144 T_COMPARE(f, sizeof(f) - 1, e, sizeof(e) - 1, 'b' - 0);
145 #undef T_COMPARE
146 }
147
148 T_DECL(strlcasecmp, "strlcasecmp") {
149 #define T_COMPARE(A, AS, B, EQ) T_ASSERT_EQ(strlcasecmp_impl((A), (B), (AS)), (EQ), "case-insensitive compare '%s'.%zu, '%s'", (A), (AS), (B))
150 // same tests as strcasecmp, then tests with individual characters
151 // two identical strings
152 char a[] = "hElLo";
153 char b[] = "HeLlO";
154 T_COMPARE(a, sizeof(a), b, 0);
155 T_COMPARE(b, sizeof(b), a, 0);
156
157 // the same string
158 T_COMPARE(a, sizeof(a), b, 0);
159 T_COMPARE(b, sizeof(b), a, 0);
160
161 // two different strings
162 char c[] = "world";
163 T_COMPARE(a, sizeof(a), c, a[0] - c[0]);
164 T_COMPARE(c, sizeof(c), a, c[0] - a[0]);
165 char d[] = "hellp";
166 T_COMPARE(a, sizeof(a), d, 'o' - 'p');
167 T_COMPARE(d, sizeof(d), a, 'p' - 'o');
168
169 // strings of different size
170 char e[] = "aAaA";
171 char f[] = "AaAaB";
172 T_COMPARE(e, sizeof(e), f, 0 - 'b');
173 T_COMPARE(f, sizeof(f), e, 'b' - 0);
174
175 // strings that are not NUL-terminated
176 T_COMPARE(a, sizeof(a) - 1, b, 0);
177 T_COMPARE(b, sizeof(b) - 1, a, 0);
178 T_COMPARE(a, sizeof(a) - 1, d, 'o' - 'p');
179 T_COMPARE(d, sizeof(d) - 1, a, 'p' - 'o');
180 T_COMPARE(e, sizeof(e) - 1, f, 0 - 'b');
181 T_COMPARE(f, sizeof(f) - 1, e, 'b' - 0);
182 #undef T_COMPARE
183 }
184
185 T_DECL(strbufcasecmp_all, "strbufcasecmp_all") {
186 #define T_CHAR_COMPARE(A, AS, B, BS, EQ) do { \
187 int r = strbufcasecmp_impl((A), (AS), (B), (BS)); \
188 if (r != (EQ)) T_FAIL("case-insensitive compare '0x%02hhx' to '0x%02hhx' was %i instead of %i", *(A), *(B), r, (EQ)); \
189 } while (0)
190 // test each character
191 char ga, gb, ha, hb;
192 char nul = 0;
193 for (int i = 0; i < 256; ++i) {
194 ga = (char)(i);
195 gb = (i >= 'A' && i <= 'Z') ? (char)(i - 'A' + 'a') : ga;
196 T_CHAR_COMPARE(&ga, 1, &nul, 0, gb);
197 T_CHAR_COMPARE(&nul, 0, &ga, 1, -gb);
198
199 for (int j = 0; j < 256; ++j) {
200 ha = (char)(j);
201 hb = (j >= 'A' && j <= 'Z') ? (char)(j - 'A' + 'a') : ha;
202 T_CHAR_COMPARE(&ga, 1, &ha, 1, gb - hb);
203 T_CHAR_COMPARE(&ha, 1, &ga, 1, hb - gb);
204 }
205 }
206 T_PASS("ASCII character case insensitivity");
207 }
208
209 T_DECL(strbufcpy, "strbufcpy") {
210 char dst[32];
211 // empty dest
212 T_ASSERT_EQ(strbufcpy_impl(NULL, 0, "hello", 5), NULL, "0-length destination");
213
214 #define T_CPY(A, AS, B, BS) T_ASSERT_EQ(strbufcpy_impl((A), (AS), (B), (BS)), (char *)(A), "copy '%.*s'.%zu to dst.%zu", (int)(BS), (B), (size_t)(BS), (AS))
215 // copy NUL terminated string that fits in dst
216 char hello[] = "hello";
217 memset(dst, 0, sizeof(dst));
218 T_CPY(dst, sizeof(dst), hello, sizeof(hello));
219 T_ASSERT_EQ(memcmp_impl(dst, (char[32]){"hello"}, sizeof(dst)), 0, "check result is 'hello'");
220
221 // copy NUL terminated string that does not fit in dst
222 char aaa[40] = {[0 ... 38] = 'a' };
223 memset(dst, 0, sizeof(dst));
224 T_CPY(dst, sizeof(dst), aaa, sizeof(aaa));
225 T_ASSERT_EQ(memcmp_impl(aaa, dst, 31), 0, "check result is 'aaaaaa...'");
226 T_ASSERT_EQ(dst[31], 0, "check result is NUL-terminated");
227
228 // copy non-terminated string
229 memset(dst, 0xff, sizeof(dst));
230 T_CPY(dst, sizeof(dst), "bbb", 3);
231 T_ASSERT_EQ(strcmp_impl(dst, "bbb"), 0, "check result is 'bbb'");
232
233 // copy string over itself
234 char hw1[32] = "hello world";
235 T_CPY(hw1 + 6, sizeof(hw1) - 6, hw1, sizeof(hw1));
236 T_ASSERT_EQ(strcmp_impl(hw1, "hello hello world"), 0, "check copy over self is 'hello hello world'");
237
238 char hw2[32] = "hello world";
239 T_CPY(hw2, sizeof(hw2), hw2 + 6, sizeof(hw2) - 6);
240 T_ASSERT_EQ(strcmp_impl(hw2, "world"), 0, "check copy over self is 'world'");
241 #undef T_CPY
242 }
243
244 T_DECL(strbufcat, "strbufcat") {
245 char dst[32] = {0};
246
247 // empty dst
248 T_ASSERT_EQ(strbufcat_impl(NULL, 0, "hello", 5), NULL, "check concatenation to 0-length destination");
249
250 #define T_CAT_RESULT(RESULT) \
251 T_ASSERT_EQ(strcmp_impl(dst, (RESULT)), 0, "check result of concatenation is '%s'", (RESULT)); \
252
253 #define T_CAT(TO_CAT, RESULT) do { \
254 T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), (TO_CAT), sizeof(TO_CAT)), (char *)dst, "check concatenation of '%s'", (TO_CAT)); \
255 T_CAT_RESULT(RESULT); \
256 } while (0)
257
258 // append "hello "
259 T_CAT("hello ", "hello ");
260
261 // append "world!"
262 T_CAT("world!", "hello world!");
263
264 // append itself
265 T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), dst, sizeof(dst)), (char *)dst, "check concatenating self");
266 T_CAT_RESULT("hello world!hello world!");
267
268 // append bunch of 'a's
269 T_ASSERT_EQ(strbufcat_impl(dst, sizeof(dst), "aaaaaaaaaa", 10), (char *)dst, "check concatenating 'aaaa...'");
270 T_CAT_RESULT("hello world!hello world!aaaaaaa");
271
272 #undef T_CAT
273 #undef T_CAT_RESULT
274 }
275
276 T_DECL(libsa_overloads, "libsa_overloads") {
277 char buf[32] = "hello, world";
278 char buf2[32] = "world, hello";
279
280 T_ASSERT_EQ(strbuflen(buf), (size_t)12, "strbuflen one argument");
281 T_ASSERT_EQ(strbuflen(buf, sizeof(buf)), (size_t)12, "strbuflen two arguments");
282
283 T_ASSERT_LT(strbufcmp(buf, buf2), 0, "strbufcmp two arguments");
284 T_ASSERT_LT(strbufcmp(buf, sizeof(buf), buf2, sizeof(buf2)), 0, "strbufcmp four arguments");
285
286 T_ASSERT_LT(strbufcasecmp(buf, buf2), 0, "strbufcasecmp two arguments");
287 T_ASSERT_LT(strbufcasecmp(buf, sizeof(buf), buf2, sizeof(buf2)), 0, "strbufcasecmp four arguments");
288
289 T_ASSERT_NE(strbufcpy(buf, buf2), NULL, "strbufcpy two arguments");
290 T_ASSERT_NE(strbufcpy(buf, sizeof(buf), buf2, sizeof(buf2)), NULL, "strbufcpy four arguments");
291
292 T_ASSERT_NE(strbufcat(buf, buf2), NULL, "strbufcat two arguments");
293 T_ASSERT_NE(strbufcat(buf, sizeof(buf), buf2, sizeof(buf2)), NULL, "strbufcat four arguments");
294 }
295