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

Reply via email to