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