From ca1c292d2ee6bcb06be71400d25ae37e9dc2c1aa Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:31 +0200 Subject: [PATCH 1/9] string: fix prototype of memdup() It doesn't make sense to restrict memdup() to only return char* pointers, especially when it is already defined to accept void*. This makes it uglier to use to e.g. duplicate a struct. Make it return void*, just as kmemdup() does in the kernel (and which our kmemdup() in fact also does). While in here, make a small optimization: memcpy() is defined to return the destination register, so we write this in a way that the compiler may do a tail call. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- include/linux/string.h | 2 +- lib/string.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/linux/string.h b/include/linux/string.h index d943fcce690..9e47fe01c16 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -142,7 +142,7 @@ void *memchr_inv(const void *, int, size_t); * memory is available * */ -char *memdup(const void *src, size_t len); +void *memdup(const void *src, size_t len); unsigned long ustrtoul(const char *cp, char **endp, unsigned int base); unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base); diff --git a/lib/string.c b/lib/string.c index d56f88d4a84..c2813e0f854 100644 --- a/lib/string.c +++ b/lib/string.c @@ -667,17 +667,15 @@ void * memscan(void * addr, int c, size_t size) } #endif -char *memdup(const void *src, size_t len) +void *memdup(const void *src, size_t len) { - char *p; + void *p; p = malloc(len); if (!p) return NULL; - memcpy(p, src, len); - - return p; + return memcpy(p, src, len); } #ifndef __HAVE_ARCH_STRNSTR From 719cacb92e039308e23cbd6b653275e939a5aca5 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:32 +0200 Subject: [PATCH 2/9] stdio: drop stdio_clone The helper stdio_clone only has a single caller, so it certainly doesn't need to be public. But in fact, it is merely an open-coded memdup() - which for some reason uses calloc() even if the whole allocation is obviously immediately overwritten. Drop it and just use memdup() directly. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- common/stdio.c | 18 +----------------- include/stdio_dev.h | 1 - 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/common/stdio.c b/common/stdio.c index fc965944209..038e576147b 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -217,27 +217,11 @@ struct stdio_dev *stdio_get_by_name(const char *name) return NULL; } -struct stdio_dev *stdio_clone(struct stdio_dev *dev) -{ - struct stdio_dev *_dev; - - if (!dev) - return NULL; - - _dev = calloc(1, sizeof(struct stdio_dev)); - if (!_dev) - return NULL; - - memcpy(_dev, dev, sizeof(struct stdio_dev)); - - return _dev; -} - int stdio_register_dev(struct stdio_dev *dev, struct stdio_dev **devp) { struct stdio_dev *_dev; - _dev = stdio_clone(dev); + _dev = memdup(dev, sizeof(*dev)); if (!_dev) return -ENODEV; list_add_tail(&_dev->list, &devs.list); diff --git a/include/stdio_dev.h b/include/stdio_dev.h index f7f9c10199e..d93604331ff 100644 --- a/include/stdio_dev.h +++ b/include/stdio_dev.h @@ -96,7 +96,6 @@ int stdio_add_devices(void); int stdio_deregister_dev(struct stdio_dev *dev, int force); struct list_head *stdio_get_list(void); struct stdio_dev *stdio_get_by_name(const char *name); -struct stdio_dev *stdio_clone(struct stdio_dev *dev); int drv_lcd_init(void); int drv_video_init(void); From 349d148f16d83da3b1e3475be0e43bfda4f4ab71 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:33 +0200 Subject: [PATCH 3/9] lib/string.c: drop pointless __HAVE_ARCH_STRDUP There has never been an arch-specific optimized implementation of str[n]dup, nor is there likely to ever be one, because unlike their cousins strlen(), strcpy() and similar that simply read/write the src/dst, the dup functions by definition involve memory allocation. So drop this irrelevant cpp guard. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- include/linux/string.h | 3 +-- lib/string.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/linux/string.h b/include/linux/string.h index 9e47fe01c16..a28150fa578 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -101,10 +101,9 @@ size_t strcspn(const char *s, const char *reject); # define strndup sandbox_strndup #endif -#ifndef __HAVE_ARCH_STRDUP extern char * strdup(const char *); extern char * strndup(const char *, size_t); -#endif + #ifndef __HAVE_ARCH_STRSWAB extern char * strswab(const char *); #endif diff --git a/lib/string.c b/lib/string.c index c2813e0f854..2c1baa568b9 100644 --- a/lib/string.c +++ b/lib/string.c @@ -343,7 +343,6 @@ size_t strcspn(const char *s, const char *reject) } #endif -#ifndef __HAVE_ARCH_STRDUP char * strdup(const char *s) { char *new; @@ -379,7 +378,6 @@ char * strndup(const char *s, size_t n) return new; } -#endif #ifndef __HAVE_ARCH_STRSPN /** From 8c664d2135723a110a60b792a8614c4864ad82a3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:34 +0200 Subject: [PATCH 4/9] lib/string.c: introduce memdup_nul() helper This is completely analogous to the linux kernel's kmemdup_nul() helper, apart from the lack of the gfp_t argument: Allocate a buffer of size {len}+1, copy {len} bytes from the given buffer, and add a final nul byte. This pattern exists in a number of places, so this helper can reduce some boilerplate code. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- include/linux/string.h | 13 +++++++++++++ lib/string.c | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/linux/string.h b/include/linux/string.h index a28150fa578..b2e38ecf26e 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -143,6 +143,19 @@ void *memchr_inv(const void *, int, size_t); */ void *memdup(const void *src, size_t len); +/** + * memdup_nul() - allocate a buffer and copy in the contents, appending a nul byte + * + * Note that this returns a valid pointer even if @len is 0 + * + * @src: data to copy in + * @len: number of bytes to copy + * Return: allocated buffer with the copied contents and an extra nul byte, + * or NULL if not enough memory is available + * + */ +void *memdup_nul(const void *src, size_t len); + unsigned long ustrtoul(const char *cp, char **endp, unsigned int base); unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base); diff --git a/lib/string.c b/lib/string.c index 2c1baa568b9..3923cce5561 100644 --- a/lib/string.c +++ b/lib/string.c @@ -343,6 +343,21 @@ size_t strcspn(const char *s, const char *reject) } #endif +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) { char *new; From ee8be5d4a1035de232b3497563fe9f6773775f96 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:35 +0200 Subject: [PATCH 5/9] lib/string.c: implement strdup() and strndup() in terms of memdup_nul() 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. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- lib/string.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/lib/string.c b/lib/string.c index 3923cce5561..5ccd1011ab5 100644 --- a/lib/string.c +++ b/lib/string.c @@ -360,38 +360,12 @@ void *memdup_nul(const void *src, size_t len) char * strdup(const char *s) { - char *new; - - if ((s == NULL) || - ((new = malloc (strlen(s) + 1)) == NULL) ) { - return NULL; - } - - strcpy (new, s); - return new; + return s ? memdup_nul(s, strlen(s)) : NULL; } char * strndup(const char *s, size_t n) { - size_t len; - char *new; - - if (s == NULL) - return NULL; - - len = strlen(s); - - if (n < len) - len = n; - - new = malloc(len + 1); - if (new == NULL) - return NULL; - - strncpy(new, s, len); - new[len] = '\0'; - - return new; + return s ? memdup_nul(s, strnlen(s, n)) : NULL; } #ifndef __HAVE_ARCH_STRSPN From b87ff4878d081103c45f0ebea7528609a0b173f3 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:36 +0200 Subject: [PATCH 6/9] lib/hashtable.c: use memdup_nul() in himport_r We have memdup_nul() for exactly this pattern of duplicating a block of memory and ensuring there's a nul byte after the copy. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- lib/hashtable.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/hashtable.c b/lib/hashtable.c index 75c263b5053..f96a8e686f6 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -821,13 +821,12 @@ int himport_r(struct hsearch_data *htab, } /* we allocate new space to make sure we can write to the array */ - if ((data = malloc(size + 1)) == NULL) { - debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1); + data = memdup_nul(env, size); + if (data == NULL) { + debug("himport_r: can't duplicate env block\n"); __set_errno(ENOMEM); return 0; } - memcpy(data, env, size); - data[size] = '\0'; dp = data; /* make a local copy of the list of variables */ From 11168813bf9c088e1fce8a96f4b493ee815c966b Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:37 +0200 Subject: [PATCH 7/9] common/cli.c: use memdup_nul() in run_command_list() Use memdup_nul() instead of open-coding it. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- common/cli.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/cli.c b/common/cli.c index 4694a35cd0e..ccf7e26e821 100644 --- a/common/cli.c +++ b/common/cli.c @@ -138,11 +138,9 @@ int run_command_list(const char *cmd, int len, int flag) #endif } if (need_buff) { - buff = malloc(len + 1); + buff = memdup_nul(cmd, len); if (!buff) return 1; - memcpy(buff, cmd, len); - buff[len] = '\0'; } #ifdef CONFIG_HUSH_PARSER if (use_hush_old()) { From 4ef201e607ebed2432ee929446e3fb9b57c53a54 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:38 +0200 Subject: [PATCH 8/9] drivers/core: use memdup() instead of malloc()+memcpy() Use memdup() instead of open-coding it. In the dm_setup_inst() case, there was never any reason to use calloc(), as the whole allocation is definitely initialized via the immediately following memcpy(). Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes --- drivers/core/acpi.c | 3 +-- drivers/core/ofnode.c | 3 +-- drivers/core/root.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/core/acpi.c b/drivers/core/acpi.c index 4763963914b..6a431171c8d 100644 --- a/drivers/core/acpi.c +++ b/drivers/core/acpi.c @@ -154,10 +154,9 @@ static int add_item(struct acpi_ctx *ctx, struct udevice *dev, if (!item->size) return 0; if (type != TYPE_OTHER) { - item->buf = malloc(item->size); + item->buf = memdup(start, item->size); if (!item->buf) return log_msg_ret("mem", -ENOMEM); - memcpy(item->buf, start, item->size); } item_count++; log_debug("* %s: Added type %d, %p, size %x\n", diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 3a36b6fdd03..12511f10aa9 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -1750,10 +1750,9 @@ int ofnode_write_prop(ofnode node, const char *propname, const void *value, void *newval; if (copy) { - newval = malloc(len); + newval = memdup(value, len); if (!newval) return log_ret(-ENOMEM); - memcpy(newval, value, len); value = newval; } ret = of_write_prop(ofnode_to_np(node), propname, len, value); diff --git a/drivers/core/root.c b/drivers/core/root.c index d43645f34dd..1f32f33b295 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -81,10 +81,9 @@ static int dm_setup_inst(void) /* Now allocate space for the priv/plat data, and copy it in */ size = __priv_data_end - __priv_data_start; - base = calloc(1, size); + base = memdup(__priv_data_start, size); if (!base) return log_msg_ret("priv", -ENOMEM); - memcpy(base, __priv_data_start, size); gd_set_dm_priv_base(base); } From 8d209186a1e4aca4ec44745d05d51de7e80f7e3e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Tue, 21 Apr 2026 09:54:39 +0200 Subject: [PATCH 9/9] test: lib: add test of memdup_nul() Add a very basic test of the new memdup_nul() helper. Signed-off-by: Rasmus Villemoes Reviewed-by: Simon Glass --- test/lib/string.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/lib/string.c b/test/lib/string.c index f56c2e4c946..db6f28dbfdf 100644 --- a/test/lib/string.c +++ b/test/lib/string.c @@ -223,6 +223,40 @@ static int lib_memdup(struct unit_test_state *uts) } LIB_TEST(lib_memdup, 0); +/** lib_memdup_nul() - unit test for memdup_nul() */ +static int lib_memdup_nul(struct unit_test_state *uts) +{ + char buf[BUFLEN]; + size_t len; + char *p, *q; + + /* Zero size should return a buffer containing a single nul byte */ + p = memdup_nul(NULL, 0); + ut_assertnonnull(p); + ut_assert(p[0] == '\0'); + free(p); + + p = memdup_nul(buf, 0); + ut_assertnonnull(p); + ut_assert(p[0] == '\0'); + free(p); + + strcpy(buf, TEST_STR); + len = sizeof(TEST_STR); + p = memdup_nul(buf, len); + ut_asserteq_mem(p, buf, len); + ut_assert(p[len] == '\0'); + + q = memdup_nul(p, len); + ut_asserteq_mem(q, buf, len); + ut_assert(q[len] == '\0'); + free(q); + free(p); + + return 0; +} +LIB_TEST(lib_memdup_nul, 0); + /** lib_strnstr() - unit test for strnstr() */ static int lib_strnstr(struct unit_test_state *uts) {