diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2023-11-22 18:29:40 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2023-11-23 15:50:35 +0100 |
commit | ba1e8d8dca515aac101404ec1e2247d2992a24c7 (patch) | |
tree | d27f8f084fd272f99022f403f3adb84586d720f8 /lib | |
parent | dde1c3e59b88a74a2eb85e9874384936ae20cb57 (diff) | |
download | barebox-ba1e8d8dca515aac101404ec1e2247d2992a24c7.tar.gz barebox-ba1e8d8dca515aac101404ec1e2247d2992a24c7.tar.xz |
string: implement strscpy
strscpy is meant to be a safer alternative to strscpy, which always
terminates the destination string and returns an error code if
truncation happens. To enable porting kernel code using it, import the
definition.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Link: https://lore.barebox.org/20231122172951.376531-10-a.fatoum@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/string.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/lib/string.c b/lib/string.c index 166ef190d6..bf0f0455ab 100644 --- a/lib/string.c +++ b/lib/string.c @@ -22,6 +22,7 @@ #include <linux/types.h> #include <string.h> #include <linux/ctype.h> +#include <asm/word-at-a-time.h> #include <malloc.h> #ifndef __HAVE_ARCH_STRCASECMP @@ -87,6 +88,76 @@ char * strcpy(char * dest,const char *src) #endif EXPORT_SYMBOL(strcpy); +#ifndef __HAVE_ARCH_STRSCPY +ssize_t strscpy(char *dest, const char *src, size_t count) +{ + const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; + size_t max = count; + long res = 0; + + if (count == 0 || WARN_ON_ONCE(count > INT_MAX)) + return -E2BIG; + +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + /* + * If src is unaligned, don't cross a page boundary, + * since we don't know if the next page is mapped. + */ + if ((long)src & (sizeof(long) - 1)) { + size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)); + if (limit < max) + max = limit; + } +#else + /* If src or dest is unaligned, don't do word-at-a-time. */ + if (((long) dest | (long) src) & (sizeof(long) - 1)) + max = 0; +#endif + + /* + * read_word_at_a_time() below may read uninitialized bytes after the + * trailing zero and use them in comparisons. Disable this optimization + * under KMSAN to prevent false positive reports. + */ + if (IS_ENABLED(CONFIG_KMSAN)) + max = 0; + + while (max >= sizeof(unsigned long)) { + unsigned long c, data; + + c = read_word_at_a_time(src+res); + if (has_zero(c, &data, &constants)) { + data = prep_zero_mask(c, data, &constants); + data = create_zero_mask(data); + *(unsigned long *)(dest+res) = c & zero_bytemask(data); + return res + find_zero(data); + } + *(unsigned long *)(dest+res) = c; + res += sizeof(unsigned long); + count -= sizeof(unsigned long); + max -= sizeof(unsigned long); + } + + while (count) { + char c; + + c = src[res]; + dest[res] = c; + if (!c) + return res; + res++; + count--; + } + + /* Hit buffer length without finding a NUL; force NUL-termination. */ + if (res) + dest[res-1] = '\0'; + + return -E2BIG; +} +EXPORT_SYMBOL(strscpy); +#endif + /** * stpcpy - Copy a %NUL terminated string, but return pointer to %NUL * @dest: Where to copy the string to |