summaryrefslogtreecommitdiff
path: root/lib/string.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/string.c')
-rw-r--r--lib/string.c38
1 files changed, 28 insertions, 10 deletions
diff --git a/lib/string.c b/lib/string.c
index 966da44bfc86..b632c71df1a5 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -27,7 +27,7 @@
#include <asm/page.h>
#include <asm/rwonce.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/word-at-a-time.h>
#ifndef __HAVE_ARCH_STRNCASECMP
@@ -104,6 +104,12 @@ char *strncpy(char *dest, const char *src, size_t count)
EXPORT_SYMBOL(strncpy);
#endif
+#ifdef __BIG_ENDIAN
+# define ALLBUTLAST_BYTE_MASK (~255ul)
+#else
+# define ALLBUTLAST_BYTE_MASK (~0ul >> 8)
+#endif
+
ssize_t sized_strscpy(char *dest, const char *src, size_t count)
{
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
@@ -113,6 +119,7 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
if (count == 0 || WARN_ON_ONCE(count > INT_MAX))
return -E2BIG;
+#ifndef CONFIG_DCACHE_WORD_ACCESS
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
/*
* If src is unaligned, don't cross a page boundary,
@@ -128,11 +135,13 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
if (((long) dest | (long) src) & (sizeof(long) - 1))
max = 0;
#endif
+#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.
+ * load_unaligned_zeropad() or 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;
@@ -140,20 +149,29 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
while (max >= sizeof(unsigned long)) {
unsigned long c, data;
+#ifdef CONFIG_DCACHE_WORD_ACCESS
+ c = load_unaligned_zeropad(src+res);
+#else
c = read_word_at_a_time(src+res);
+#endif
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);
}
+ count -= sizeof(unsigned long);
+ if (unlikely(!count)) {
+ c &= ALLBUTLAST_BYTE_MASK;
+ *(unsigned long *)(dest+res) = c;
+ return -E2BIG;
+ }
*(unsigned long *)(dest+res) = c;
res += sizeof(unsigned long);
- count -= sizeof(unsigned long);
max -= sizeof(unsigned long);
}
- while (count) {
+ while (count > 1) {
char c;
c = src[res];
@@ -164,11 +182,11 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count)
count--;
}
- /* Hit buffer length without finding a NUL; force NUL-termination. */
- if (res)
- dest[res-1] = '\0';
+ /* Force NUL-termination. */
+ dest[res] = '\0';
- return -E2BIG;
+ /* Return E2BIG if the source didn't stop */
+ return src[res] ? -E2BIG : res;
}
EXPORT_SYMBOL(sized_strscpy);