]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/efi-string.c
Merge pull request #15205 from jlebon/pr/preset-all-firstboot
[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 assert(dest); \
86 type *ret = dest; \
87 \
88 if (!src) { \
89 *dest = '\0'; \
90 return ret; \
91 } \
92 \
93 while (*src) { \
94 *dest = *src; \
95 dest++; \
96 src++; \
97 } \
98 \
99 *dest = '\0'; \
100 return ret; \
101 }
102
103 DEFINE_STRCPY(char, strcpy8);
104 DEFINE_STRCPY(char16_t, strcpy16);
105
106 #define DEFINE_STRCHR(type, name) \
107 type *name(const type *s, type c) { \
108 if (!s) \
109 return NULL; \
110 \
111 while (*s) { \
112 if (*s == c) \
113 return (type *) s; \
114 s++; \
115 } \
116 \
117 return NULL; \
118 }
119
120 DEFINE_STRCHR(char, strchr8);
121 DEFINE_STRCHR(char16_t, strchr16);
122
123 #define DEFINE_STRNDUP(type, name, len_func) \
124 type *name(const type *s, size_t n) { \
125 if (!s) \
126 return NULL; \
127 \
128 size_t len = len_func(s, n); \
129 size_t size = len * sizeof(type); \
130 \
131 type *dup = xmalloc(size + sizeof(type)); \
132 efi_memcpy(dup, s, size); \
133 dup[len] = '\0'; \
134 \
135 return dup; \
136 }
137
138 DEFINE_STRNDUP(char, xstrndup8, strnlen8);
139 DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
140
141 /* Patterns are fnmatch-compatible (with reduced feature support). */
142 static bool efi_fnmatch_internal(const char16_t *p, const char16_t *h, int max_depth) {
143 assert(p);
144 assert(h);
145
146 if (max_depth == 0)
147 return false;
148
149 for (;; p++, h++)
150 switch (*p) {
151 case '\0':
152 /* End of pattern. Check that haystack is now empty. */
153 return *h == '\0';
154
155 case '\\':
156 p++;
157 if (*p == '\0' || *p != *h)
158 /* Trailing escape or no match. */
159 return false;
160 break;
161
162 case '?':
163 if (*h == '\0')
164 /* Early end of haystack. */
165 return false;
166 break;
167
168 case '*':
169 /* No need to recurse for consecutive '*'. */
170 while (*p == '*')
171 p++;
172
173 for (; *h != '\0'; h++)
174 /* Try matching haystack with remaining pattern. */
175 if (efi_fnmatch_internal(p, h, max_depth - 1))
176 return true;
177
178 /* End of haystack. Pattern needs to be empty too for a match. */
179 return *p == '\0';
180
181 case '[':
182 if (*h == '\0')
183 /* Early end of haystack. */
184 return false;
185
186 bool first = true, can_range = true, match = false;
187 for (;; first = false) {
188 p++;
189 if (*p == '\0')
190 return false;
191
192 if (*p == '\\') {
193 p++;
194 if (*p == '\0')
195 return false;
196 if (*p == *h)
197 match = true;
198 can_range = true;
199 continue;
200 }
201
202 /* End of set unless it's the first char. */
203 if (*p == ']' && !first)
204 break;
205
206 /* Range pattern if '-' is not first or last in set. */
207 if (*p == '-' && can_range && !first && *(p + 1) != ']') {
208 char16_t low = *(p - 1);
209 p++;
210 if (*p == '\\')
211 p++;
212 if (*p == '\0')
213 return false;
214
215 if (low <= *h && *h <= *p)
216 match = true;
217
218 /* Ranges cannot be chained: [a-c-f] == [-abcf] */
219 can_range = false;
220 continue;
221 }
222
223 if (*p == *h)
224 match = true;
225 can_range = true;
226 }
227
228 if (!match)
229 return false;
230 break;
231
232 default:
233 if (*p != *h)
234 /* Single char mismatch. */
235 return false;
236 }
237 }
238
239 bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
240 return efi_fnmatch_internal(pattern, haystack, 32);
241 }
242
243 #define DEFINE_PARSE_NUMBER(type, name) \
244 bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
245 assert(ret_u); \
246 \
247 if (!s) \
248 return false; \
249 \
250 /* Need at least one digit. */ \
251 if (*s < '0' || *s > '9') \
252 return false; \
253 \
254 uint64_t u = 0; \
255 while (*s >= '0' && *s <= '9') { \
256 if (__builtin_mul_overflow(u, 10, &u)) \
257 return false; \
258 if (__builtin_add_overflow(u, *s - '0', &u)) \
259 return false; \
260 s++; \
261 } \
262 \
263 if (!ret_tail && *s != '\0') \
264 return false; \
265 \
266 *ret_u = u; \
267 if (ret_tail) \
268 *ret_tail = s; \
269 return true; \
270 }
271
272 DEFINE_PARSE_NUMBER(char, parse_number8);
273 DEFINE_PARSE_NUMBER(char16_t, parse_number16);
274
275 int efi_memcmp(const void *p1, const void *p2, size_t n) {
276 const uint8_t *up1 = p1, *up2 = p2;
277 int r;
278
279 if (!p1 || !p2)
280 return CMP(p1, p2);
281
282 while (n > 0) {
283 r = CMP(*up1, *up2);
284 if (r != 0)
285 return r;
286
287 up1++;
288 up2++;
289 n--;
290 }
291
292 return 0;
293 }
294
295 void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n) {
296 if (!dest || !src || n == 0)
297 return dest;
298
299 #ifdef SD_BOOT
300 /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
301 * available by the UEFI spec. We still make it depend on the boot services pointer being set just in
302 * case the compiler emits a call before it is available. */
303 if (_likely_(BS)) {
304 BS->CopyMem(dest, (void *) src, n);
305 return dest;
306 }
307 #endif
308
309 uint8_t *d = dest;
310 const uint8_t *s = src;
311
312 while (n > 0) {
313 *d = *s;
314 d++;
315 s++;
316 n--;
317 }
318
319 return dest;
320 }
321
322 void *efi_memset(void *p, int c, size_t n) {
323 if (!p || n == 0)
324 return p;
325
326 #ifdef SD_BOOT
327 /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
328 if (_likely_(BS)) {
329 BS->SetMem(p, n, c);
330 return p;
331 }
332 #endif
333
334 uint8_t *q = p;
335 while (n > 0) {
336 *q = c;
337 q++;
338 n--;
339 }
340
341 return p;
342 }
343
344 #ifdef SD_BOOT
345 # undef memcmp
346 # undef memcpy
347 # undef memset
348 /* Provide the actual implementation for the builtins by providing aliases. These need to be marked as used,
349 * as otherwise the compiler might remove them but still emit calls, which would break when linking.
350 * To prevent a different linker error, we mark memcpy/memset as weak, because gnu-efi is currently
351 * providing them. */
352 __attribute__((used, alias("efi_memcmp"))) int memcmp(const void *p1, const void *p2, size_t n);
353 __attribute__((used, weak, alias("efi_memcpy"))) void *memcpy(void * restrict dest, const void * restrict src, size_t n);
354 __attribute__((used, weak, alias("efi_memset"))) void *memset(void *p, int c, size_t n);
355 #endif