]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/efi-string.c
tree-wide: use ASSERT_PTR more
[thirdparty/systemd.git] / src / boot / efi / efi-string.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdbool.h>
4 #include <stdint.h>
5
6 #include "efi-string.h"
7
8 #ifdef SD_BOOT
9 # include "util.h"
10 #else
11 # include <stdlib.h>
12 # include "macro.h"
13 # define xmalloc(n) ASSERT_SE_PTR(malloc(n))
14 #endif
15
16 /* String functions for both char and char16_t that should behave the same way as their respective
17 * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas
18 * userspace does not allow for this (strlen8(NULL) returns 0 like strlen_ptr(NULL) for example). To make it
19 * easier to tell in code which kind of string they work on, we use 8/16 suffixes. This also makes is easier
20 * to unit test them. */
21
22 #define DEFINE_STRNLEN(type, name) \
23 size_t name(const type *s, size_t n) { \
24 if (!s) \
25 return 0; \
26 \
27 size_t len = 0; \
28 while (len < n && *s) { \
29 s++; \
30 len++; \
31 } \
32 \
33 return len; \
34 }
35
36 DEFINE_STRNLEN(char, strnlen8);
37 DEFINE_STRNLEN(char16_t, strnlen16);
38
39 #define TOLOWER(c) \
40 ({ \
41 typeof(c) _c = (c); \
42 (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \
43 })
44
45 #define DEFINE_STRTOLOWER(type, name) \
46 void name(type *s) { \
47 if (!s) \
48 return; \
49 for (; *s; s++) \
50 *s = TOLOWER(*s); \
51 }
52
53 DEFINE_STRTOLOWER(char, strtolower8);
54 DEFINE_STRTOLOWER(char16_t, strtolower16);
55
56 #define DEFINE_STRNCASECMP(type, name, tolower) \
57 int name(const type *s1, const type *s2, size_t n) { \
58 if (!s1 || !s2) \
59 return CMP(s1, s2); \
60 \
61 while (n > 0) { \
62 type c1 = *s1, c2 = *s2; \
63 if (tolower) { \
64 c1 = TOLOWER(c1); \
65 c2 = TOLOWER(c2); \
66 } \
67 if (!c1 || c1 != c2) \
68 return CMP(c1, c2); \
69 \
70 s1++; \
71 s2++; \
72 n--; \
73 } \
74 \
75 return 0; \
76 }
77
78 DEFINE_STRNCASECMP(char, strncmp8, false);
79 DEFINE_STRNCASECMP(char16_t, strncmp16, false);
80 DEFINE_STRNCASECMP(char, strncasecmp8, true);
81 DEFINE_STRNCASECMP(char16_t, strncasecmp16, true);
82
83 #define DEFINE_STRCPY(type, name) \
84 type *name(type * restrict dest, const type * restrict src) { \
85 type *ret = ASSERT_PTR(dest); \
86 \
87 if (!src) { \
88 *dest = '\0'; \
89 return ret; \
90 } \
91 \
92 while (*src) { \
93 *dest = *src; \
94 dest++; \
95 src++; \
96 } \
97 \
98 *dest = '\0'; \
99 return ret; \
100 }
101
102 DEFINE_STRCPY(char, strcpy8);
103 DEFINE_STRCPY(char16_t, strcpy16);
104
105 #define DEFINE_STRCHR(type, name) \
106 type *name(const type *s, type c) { \
107 if (!s) \
108 return NULL; \
109 \
110 while (*s) { \
111 if (*s == c) \
112 return (type *) s; \
113 s++; \
114 } \
115 \
116 return NULL; \
117 }
118
119 DEFINE_STRCHR(char, strchr8);
120 DEFINE_STRCHR(char16_t, strchr16);
121
122 #define DEFINE_STRNDUP(type, name, len_func) \
123 type *name(const type *s, size_t n) { \
124 if (!s) \
125 return NULL; \
126 \
127 size_t len = len_func(s, n); \
128 size_t size = len * sizeof(type); \
129 \
130 type *dup = xmalloc(size + sizeof(type)); \
131 efi_memcpy(dup, s, size); \
132 dup[len] = '\0'; \
133 \
134 return dup; \
135 }
136
137 DEFINE_STRNDUP(char, xstrndup8, strnlen8);
138 DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
139
140 /* Patterns are fnmatch-compatible (with reduced feature support). */
141 static bool efi_fnmatch_internal(const char16_t *p, const char16_t *h, int max_depth) {
142 assert(p);
143 assert(h);
144
145 if (max_depth == 0)
146 return false;
147
148 for (;; p++, h++)
149 switch (*p) {
150 case '\0':
151 /* End of pattern. Check that haystack is now empty. */
152 return *h == '\0';
153
154 case '\\':
155 p++;
156 if (*p == '\0' || *p != *h)
157 /* Trailing escape or no match. */
158 return false;
159 break;
160
161 case '?':
162 if (*h == '\0')
163 /* Early end of haystack. */
164 return false;
165 break;
166
167 case '*':
168 /* No need to recurse for consecutive '*'. */
169 while (*p == '*')
170 p++;
171
172 for (; *h != '\0'; h++)
173 /* Try matching haystack with remaining pattern. */
174 if (efi_fnmatch_internal(p, h, max_depth - 1))
175 return true;
176
177 /* End of haystack. Pattern needs to be empty too for a match. */
178 return *p == '\0';
179
180 case '[':
181 if (*h == '\0')
182 /* Early end of haystack. */
183 return false;
184
185 bool first = true, can_range = true, match = false;
186 for (;; first = false) {
187 p++;
188 if (*p == '\0')
189 return false;
190
191 if (*p == '\\') {
192 p++;
193 if (*p == '\0')
194 return false;
195 if (*p == *h)
196 match = true;
197 can_range = true;
198 continue;
199 }
200
201 /* End of set unless it's the first char. */
202 if (*p == ']' && !first)
203 break;
204
205 /* Range pattern if '-' is not first or last in set. */
206 if (*p == '-' && can_range && !first && *(p + 1) != ']') {
207 char16_t low = *(p - 1);
208 p++;
209 if (*p == '\\')
210 p++;
211 if (*p == '\0')
212 return false;
213
214 if (low <= *h && *h <= *p)
215 match = true;
216
217 /* Ranges cannot be chained: [a-c-f] == [-abcf] */
218 can_range = false;
219 continue;
220 }
221
222 if (*p == *h)
223 match = true;
224 can_range = true;
225 }
226
227 if (!match)
228 return false;
229 break;
230
231 default:
232 if (*p != *h)
233 /* Single char mismatch. */
234 return false;
235 }
236 }
237
238 bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
239 return efi_fnmatch_internal(pattern, haystack, 32);
240 }
241
242 #define DEFINE_PARSE_NUMBER(type, name) \
243 bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
244 assert(ret_u); \
245 \
246 if (!s) \
247 return false; \
248 \
249 /* Need at least one digit. */ \
250 if (*s < '0' || *s > '9') \
251 return false; \
252 \
253 uint64_t u = 0; \
254 while (*s >= '0' && *s <= '9') { \
255 if (__builtin_mul_overflow(u, 10, &u)) \
256 return false; \
257 if (__builtin_add_overflow(u, *s - '0', &u)) \
258 return false; \
259 s++; \
260 } \
261 \
262 if (!ret_tail && *s != '\0') \
263 return false; \
264 \
265 *ret_u = u; \
266 if (ret_tail) \
267 *ret_tail = s; \
268 return true; \
269 }
270
271 DEFINE_PARSE_NUMBER(char, parse_number8);
272 DEFINE_PARSE_NUMBER(char16_t, parse_number16);
273
274 int efi_memcmp(const void *p1, const void *p2, size_t n) {
275 const uint8_t *up1 = p1, *up2 = p2;
276 int r;
277
278 if (!p1 || !p2)
279 return CMP(p1, p2);
280
281 while (n > 0) {
282 r = CMP(*up1, *up2);
283 if (r != 0)
284 return r;
285
286 up1++;
287 up2++;
288 n--;
289 }
290
291 return 0;
292 }
293
294 void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n) {
295 if (!dest || !src || n == 0)
296 return dest;
297
298 #ifdef SD_BOOT
299 /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
300 * available by the UEFI spec. We still make it depend on the boot services pointer being set just in
301 * case the compiler emits a call before it is available. */
302 if (_likely_(BS)) {
303 BS->CopyMem(dest, (void *) src, n);
304 return dest;
305 }
306 #endif
307
308 uint8_t *d = dest;
309 const uint8_t *s = src;
310
311 while (n > 0) {
312 *d = *s;
313 d++;
314 s++;
315 n--;
316 }
317
318 return dest;
319 }
320
321 void *efi_memset(void *p, int c, size_t n) {
322 if (!p || n == 0)
323 return p;
324
325 #ifdef SD_BOOT
326 /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
327 if (_likely_(BS)) {
328 BS->SetMem(p, n, c);
329 return p;
330 }
331 #endif
332
333 uint8_t *q = p;
334 while (n > 0) {
335 *q = c;
336 q++;
337 n--;
338 }
339
340 return p;
341 }
342
343 #ifdef SD_BOOT
344 # undef memcmp
345 # undef memcpy
346 # undef memset
347 /* Provide the actual implementation for the builtins by providing aliases. These need to be marked as used,
348 * as otherwise the compiler might remove them but still emit calls, which would break when linking.
349 * To prevent a different linker error, we mark memcpy/memset as weak, because gnu-efi is currently
350 * providing them. */
351 __attribute__((used, alias("efi_memcmp"))) int memcmp(const void *p1, const void *p2, size_t n);
352 __attribute__((used, weak, alias("efi_memcpy"))) void *memcpy(void * restrict dest, const void * restrict src, size_t n);
353 __attribute__((used, weak, alias("efi_memset"))) void *memset(void *p, int c, size_t n);
354 #endif