On Sat, Oct 10 2026, "Marcel Ziswiler" <[email protected]> wrote:
> Hi Rasmus, et. al. > > On Tue, 2026-04-21 at 09:54 +0200, Rasmus Villemoes wrote: >> With the addition of memdup_nul(), strdup() and strndup() can be >> implemented as one-liners. >> >> While not required by POSIX or C, do keep the behaviour of gracefully >> accepting a NULL source and simply return NULL. > > I bisected this commit to regress on the LicheePi 4A in the sense that the > SPL is no longer able to > load/execute OpenSBI/U-Boot proper: > > U-Boot SPL 2026.10 (Oct 10 2026 - 21:45:55 +0200) > Trying to boot from RAM > => hangs > > My fix reverting this and a second patch [1] got accepted in meta-riscv, see: > > https://github.com/riscv/meta-riscv/pull/711 > > Before I dig deeper, I would welcome any insight anybody could have. Thanks! That is quite odd. I really can't see how that commit itself should be buggy. For reference, here are the complete current implementations of strdup(), strndup() and the memdup_nul() helper: void *memdup_nul(const void *src, size_t len) { char *dst; if (len + 1 < len) return NULL; dst = malloc(len + 1); if (!dst) return NULL; dst[len] = '\0'; return memcpy(dst, src, len); } char * strdup(const char *s) { return s ? memdup_nul(s, strlen(s)) : NULL; } char * strndup(const char *s, size_t n) { return s ? memdup_nul(s, strnlen(s, n)) : NULL; } And here are the corresponding functions from the linux kernel, without kdoc of comments: static __always_inline char *__kmemdup_nul(const char *s, size_t len, gfp_t gfp) { char *buf; /* '+1' for the NUL terminator */ buf = kmalloc_track_caller(len + 1, gfp); if (!buf) return NULL; memcpy(buf, s, len); /* Ensure the buf is always NUL-terminated, regardless of @s. */ buf[len] = '\0'; return buf; } char *kstrdup(const char *s, gfp_t gfp) { return s ? __kmemdup_nul(s, strlen(s), gfp) : NULL; } char *kstrndup(const char *s, size_t max, gfp_t gfp) { return s ? __kmemdup_nul(s, strnlen(s, max), gfp) : NULL; } Ignoring the k prefixes and gfp arguments, there are very few differences. The U-Boot version has an overflow check on the length argument - it's slightly odd that the kernel does not, but that's clearly irrelevant. The only other difference I can see is the U-Boot version doing a tail call return memcpy(dst, src, len); which relies on memcpy() being defined to return the dst argument. Now, usually nobody uses memcpy's return value, but it can make code slightly more compact, and any custom memcpy() implementation (e.g. in asm) _must_ honour this, as the compiler might transform memcpy(a, b, c); return a; into return memcpy(a, b, c); [I know this because I played around with this in godbolt a few years ago, and I think found that gcc did this but clang did not, or something like that]. So my best bet is that your platform uses a custom memcpy() which does not have to right return value. Can you try changing that last return memcpy(dst, src, len); into memcpy(dst, src, len); return dst; and verify in the disassembly that the compiler hasn't changed it back, and then see if your board boots? Rasmus
