The m68 hand-written assembler version of strcmp() has always been broken: it returns the difference between the first non-matching byte done as a 8-bit subtraction. That is _almost_ right, but is broken for the overflow case. The strcmp() function should indeed return the sign of the difference between the first byte that differs, but the subtraction needs to be done in a wider type than 'char'. Otherwise the ordering isn't actually stable. This went unnoticed for basically forever, because nobody ever cares about non-US-ASCII orderings in the kernel (in fact, most users only care about "exact match or not"), so overflows don't really happen in practice, even if it was very very wrong. But that mostly unnoticeable bug becomes very noticeable by the recent change to make 'char' be unsigned in the kernel across all architectures (commit 3bc753c06dd0: "kbuild: treat char as always unsigned"). Because the code not only did the subtraction in the wrong type width, it also used 'char' to then make the compiler expand the result from an 8-bit difference to the 'int' return value. So now with an unsigned char that incorrect arithmetic width was then not even sign-expanded, and always returned just a positive integer. We could re-instate the old broken code by just turning the 'char' into 'signed char' as has been done elsewhere where people depended on the signedness of 'char', but since the whole function was broken to begin with, and we have a non-broken default fallback implementation, let's just remove this broken function entirely. Reported-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/lkml/20221221145332.GA2399037@roeck-us.net/ Cc: Jason Donenfeld <Jason@zx2c4.com> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
55 lines
1.2 KiB
C
55 lines
1.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _M68K_STRING_H_
|
|
#define _M68K_STRING_H_
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/compiler.h>
|
|
|
|
#define __HAVE_ARCH_STRNLEN
|
|
static inline size_t strnlen(const char *s, size_t count)
|
|
{
|
|
const char *sc = s;
|
|
|
|
asm volatile ("\n"
|
|
"1: subq.l #1,%1\n"
|
|
" jcs 2f\n"
|
|
" tst.b (%0)+\n"
|
|
" jne 1b\n"
|
|
" subq.l #1,%0\n"
|
|
"2:"
|
|
: "+a" (sc), "+d" (count));
|
|
return sc - s;
|
|
}
|
|
|
|
#define __HAVE_ARCH_STRNCPY
|
|
static inline char *strncpy(char *dest, const char *src, size_t n)
|
|
{
|
|
char *xdest = dest;
|
|
|
|
asm volatile ("\n"
|
|
" jra 2f\n"
|
|
"1: move.b (%1),(%0)+\n"
|
|
" jeq 2f\n"
|
|
" addq.l #1,%1\n"
|
|
"2: subq.l #1,%2\n"
|
|
" jcc 1b\n"
|
|
: "+a" (dest), "+a" (src), "+d" (n)
|
|
: : "memory");
|
|
return xdest;
|
|
}
|
|
|
|
#define __HAVE_ARCH_MEMMOVE
|
|
extern void *memmove(void *, const void *, __kernel_size_t);
|
|
|
|
#define memcmp(d, s, n) __builtin_memcmp(d, s, n)
|
|
|
|
#define __HAVE_ARCH_MEMSET
|
|
extern void *memset(void *, int, __kernel_size_t);
|
|
#define memset(d, c, n) __builtin_memset(d, c, n)
|
|
|
|
#define __HAVE_ARCH_MEMCPY
|
|
extern void *memcpy(void *, const void *, __kernel_size_t);
|
|
#define memcpy(d, s, n) __builtin_memcpy(d, s, n)
|
|
|
|
#endif /* _M68K_STRING_H_ */
|