]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/boot/efi/efi-string.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "efi-string.h"
13 # define xmalloc(n) ASSERT_SE_PTR(malloc(n))
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. */
22 #define DEFINE_STRNLEN(type, name) \
23 size_t name(const type *s, size_t n) { \
28 while (len < n && *s) { \
36 DEFINE_STRNLEN(char, strnlen8
);
37 DEFINE_STRNLEN(char16_t
, strnlen16
);
42 (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \
45 #define DEFINE_STRTOLOWER(type, name) \
46 void name(type *s) { \
53 DEFINE_STRTOLOWER(char, strtolower8
);
54 DEFINE_STRTOLOWER(char16_t
, strtolower16
);
56 #define DEFINE_STRNCASECMP(type, name, tolower) \
57 int name(const type *s1, const type *s2, size_t n) { \
62 type c1 = *s1, c2 = *s2; \
67 if (!c1 || c1 != c2) \
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);
83 #define DEFINE_STRCPY(type, name) \
84 type *name(type * restrict dest, const type * restrict src) { \
103 DEFINE_STRCPY(char, strcpy8
);
104 DEFINE_STRCPY(char16_t
, strcpy16
);
106 #define DEFINE_STRCHR(type, name) \
107 type *name(const type *s, type c) { \
120 DEFINE_STRCHR(char, strchr8
);
121 DEFINE_STRCHR(char16_t
, strchr16
);
123 #define DEFINE_STRNDUP(type, name, len_func) \
124 type *name(const type *s, size_t n) { \
128 size_t len = len_func(s, n); \
129 size_t size = len * sizeof(type); \
131 type *dup = xmalloc(size + sizeof(type)); \
132 efi_memcpy(dup, s, size); \
138 DEFINE_STRNDUP(char, xstrndup8
, strnlen8
);
139 DEFINE_STRNDUP(char16_t
, xstrndup16
, strnlen16
);
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
) {
152 /* End of pattern. Check that haystack is now empty. */
157 if (*p
== '\0' || *p
!= *h
)
158 /* Trailing escape or no match. */
164 /* Early end of haystack. */
169 /* No need to recurse for consecutive '*'. */
173 for (; *h
!= '\0'; h
++)
174 /* Try matching haystack with remaining pattern. */
175 if (efi_fnmatch_internal(p
, h
, max_depth
- 1))
178 /* End of haystack. Pattern needs to be empty too for a match. */
183 /* Early end of haystack. */
186 bool first
= true, can_range
= true, match
= false;
187 for (;; first
= false) {
202 /* End of set unless it's the first char. */
203 if (*p
== ']' && !first
)
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);
215 if (low
<= *h
&& *h
<= *p
)
218 /* Ranges cannot be chained: [a-c-f] == [-abcf] */
234 /* Single char mismatch. */
239 bool efi_fnmatch(const char16_t
*pattern
, const char16_t
*haystack
) {
240 return efi_fnmatch_internal(pattern
, haystack
, 32);
243 #define DEFINE_PARSE_NUMBER(type, name) \
244 bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
250 /* Need at least one digit. */ \
251 if (*s < '0' || *s > '9') \
255 while (*s >= '0' && *s <= '9') { \
256 if (__builtin_mul_overflow(u, 10, &u)) \
258 if (__builtin_add_overflow(u, *s - '0', &u)) \
263 if (!ret_tail && *s != '\0') \
272 DEFINE_PARSE_NUMBER(char, parse_number8
);
273 DEFINE_PARSE_NUMBER(char16_t
, parse_number16
);
275 int efi_memcmp(const void *p1
, const void *p2
, size_t n
) {
276 const uint8_t *up1
= p1
, *up2
= p2
;
295 void *efi_memcpy(void * restrict dest
, const void * restrict src
, size_t n
) {
296 if (!dest
|| !src
|| n
== 0)
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. */
304 BS
->CopyMem(dest
, (void *) src
, n
);
310 const uint8_t *s
= src
;
322 void *efi_memset(void *p
, int c
, size_t n
) {
327 /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
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
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
);