]>
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) { \
85 type *ret = ASSERT_PTR(dest); \
102 DEFINE_STRCPY(char, strcpy8
);
103 DEFINE_STRCPY(char16_t
, strcpy16
);
105 #define DEFINE_STRCHR(type, name) \
106 type *name(const type *s, type c) { \
119 DEFINE_STRCHR(char, strchr8
);
120 DEFINE_STRCHR(char16_t
, strchr16
);
122 #define DEFINE_STRNDUP(type, name, len_func) \
123 type *name(const type *s, size_t n) { \
127 size_t len = len_func(s, n); \
128 size_t size = len * sizeof(type); \
130 type *dup = xmalloc(size + sizeof(type)); \
131 efi_memcpy(dup, s, size); \
137 DEFINE_STRNDUP(char, xstrndup8
, strnlen8
);
138 DEFINE_STRNDUP(char16_t
, xstrndup16
, strnlen16
);
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
) {
151 /* End of pattern. Check that haystack is now empty. */
156 if (*p
== '\0' || *p
!= *h
)
157 /* Trailing escape or no match. */
163 /* Early end of haystack. */
168 /* No need to recurse for consecutive '*'. */
172 for (; *h
!= '\0'; h
++)
173 /* Try matching haystack with remaining pattern. */
174 if (efi_fnmatch_internal(p
, h
, max_depth
- 1))
177 /* End of haystack. Pattern needs to be empty too for a match. */
182 /* Early end of haystack. */
185 bool first
= true, can_range
= true, match
= false;
186 for (;; first
= false) {
201 /* End of set unless it's the first char. */
202 if (*p
== ']' && !first
)
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);
214 if (low
<= *h
&& *h
<= *p
)
217 /* Ranges cannot be chained: [a-c-f] == [-abcf] */
233 /* Single char mismatch. */
238 bool efi_fnmatch(const char16_t
*pattern
, const char16_t
*haystack
) {
239 return efi_fnmatch_internal(pattern
, haystack
, 32);
242 #define DEFINE_PARSE_NUMBER(type, name) \
243 bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
249 /* Need at least one digit. */ \
250 if (*s < '0' || *s > '9') \
254 while (*s >= '0' && *s <= '9') { \
255 if (__builtin_mul_overflow(u, 10, &u)) \
257 if (__builtin_add_overflow(u, *s - '0', &u)) \
262 if (!ret_tail && *s != '\0') \
271 DEFINE_PARSE_NUMBER(char, parse_number8
);
272 DEFINE_PARSE_NUMBER(char16_t
, parse_number16
);
274 int efi_memcmp(const void *p1
, const void *p2
, size_t n
) {
275 const uint8_t *up1
= p1
, *up2
= p2
;
294 void *efi_memcpy(void * restrict dest
, const void * restrict src
, size_t n
) {
295 if (!dest
|| !src
|| n
== 0)
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. */
303 BS
->CopyMem(dest
, (void *) src
, n
);
309 const uint8_t *s
= src
;
321 void *efi_memset(void *p
, int c
, size_t n
) {
326 /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
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
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
);