[packages/busybox] - release 2; backport CVE fixes: 2023-42366, 2024-58251, 2025-46394, 2025-60876, 2026-38752..38755

arekm arekm at pld-linux.org
Mon Oct 19 16:32:53 CEST 2026


commit 168af84b55992613f8030537edae977674860da4
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Mon Oct 19 16:31:48 2026 +0200

    - release 2; backport CVE fixes: 2023-42366, 2024-58251, 2025-46394,
      2025-60876, 2026-38752..38755, 2026-88830..88842, 2026-108119
      (CVE-2026-88833 and CVE-2026-88838 are own fixes - no upstream or
      distro patch exists)
    - enable CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION (CVE-2023-39810,
      CVE-2026-26157)
    - CVE-2026-88829 and CVE-2026-90954 remain unfixed (no fix available)

 busybox-CVE-2023-42366.patch  |  32 +++
 busybox-CVE-2024-58251.patch  |  47 ++++
 busybox-CVE-2025-46394.patch  |  19 ++
 busybox-CVE-2025-60876.patch  |  38 ++++
 busybox-CVE-2026-108119.patch | 200 +++++++++++++++++
 busybox-CVE-2026-38752.patch  |  80 +++++++
 busybox-CVE-2026-38753.patch  |  51 +++++
 busybox-CVE-2026-38754.patch  | 154 +++++++++++++
 busybox-CVE-2026-38755.patch  |  69 ++++++
 busybox-CVE-2026-88830.patch  |  55 +++++
 busybox-CVE-2026-88831.patch  | 134 ++++++++++++
 busybox-CVE-2026-88832.patch  |  48 +++++
 busybox-CVE-2026-88833.patch  |  25 +++
 busybox-CVE-2026-88834.patch  |  55 +++++
 busybox-CVE-2026-88835.patch  | 492 ++++++++++++++++++++++++++++++++++++++++++
 busybox-CVE-2026-88837.patch  |  40 ++++
 busybox-CVE-2026-88838.patch  | 110 ++++++++++
 busybox-CVE-2026-88839.patch  |  30 +++
 busybox-CVE-2026-88840.patch  |  38 ++++
 busybox-CVE-2026-88842.patch  |  82 +++++++
 busybox.config                |   3 +-
 busybox.spec                  |  49 ++++-
 22 files changed, 1846 insertions(+), 5 deletions(-)
---
diff --git a/busybox.spec b/busybox.spec
index b2f8f99..bb25ce1 100644
--- a/busybox.spec
+++ b/busybox.spec
@@ -1,6 +1,4 @@
 # TODO:
-# - review patch 3. Updated to 1.17.3, but the code changed so much it's unclear
-#   if it still serves a purpose
 # - sparc64 modules support in sparc(32), x86_64 modules support in i386 version
 # - make internal commands work even if busybox is not in /bin/busybox (initrd)
 #   or when /proc is not mounted (static / normal)
@@ -36,9 +34,8 @@ Summary:	Set of common Unix utilities for embeded systems
 Summary(pl.UTF-8):	Zestaw narzędzi uniksowych dla systemów wbudowanych
 Summary(pt_BR.UTF-8):	BusyBox é um conjunto de utilitários UNIX em um único binário
 Name:		busybox
-# stable line only
 Version:	1.38.0
-Release:	1
+Release:	2
 License:	GPL v2
 Group:		Applications
 Source0:	https://www.busybox.net/downloads/%{name}-%{version}.tar.bz2
@@ -48,6 +45,7 @@ Source2:	%{name}-initrd.config
 %{?with_altconfig:Source3:	%{cfgfile}}
 Patch1:		%{name}-logconsole.patch
 Patch2:		%{name}-printf-gettext.patch
+# needed with Linux >= 6.8 headers, which removed the CBQ uapi definitions
 Patch3:		busybox-1.36.1-kernel-6.8.patch
 Patch4:		%{name}-kernel_headers.patch
 Patch5:		%{name}-insmod-morearchs.patch
@@ -56,6 +54,29 @@ Patch7:		%{name}-fix_64_archs.patch
 Patch8:		busybox-1.31.1-stime-fix.patch
 Patch9:		%{name}-ash-export-PATH.patch
 Patch12:	%{name}-inetd-rpc-portno.patch
+Patch13:	%{name}-CVE-2023-42366.patch
+Patch14:	%{name}-CVE-2025-46394.patch
+Patch15:	%{name}-CVE-2024-58251.patch
+Patch16:	%{name}-CVE-2025-60876.patch
+Patch17:	%{name}-CVE-2026-38752.patch
+Patch18:	%{name}-CVE-2026-38753.patch
+Patch19:	%{name}-CVE-2026-38754.patch
+Patch20:	%{name}-CVE-2026-38755.patch
+Patch21:	%{name}-CVE-2026-88830.patch
+Patch22:	%{name}-CVE-2026-88831.patch
+Patch23:	%{name}-CVE-2026-88832.patch
+Patch24:	%{name}-CVE-2026-88835.patch
+Patch25:	%{name}-CVE-2026-88837.patch
+Patch26:	%{name}-CVE-2026-88834.patch
+Patch27:	%{name}-CVE-2026-88839.patch
+Patch28:	%{name}-CVE-2026-88840.patch
+Patch29:	%{name}-CVE-2026-88842.patch
+Patch30:	%{name}-CVE-2026-108119.patch
+# local backports, no upstream or distro fix available
+Patch31:	%{name}-CVE-2026-88833.patch
+Patch32:	%{name}-CVE-2026-88838.patch
+# No fix available (upstream, Debian, SUSE, Fedora) for CVE-2026-88829
+# (httpd proxy auth and pipelining) and CVE-2026-90954 (dpkg-deb ar link flush).
 URL:		https://www.busybox.net/
 BuildRequires:	gcc >= 3.2
 BuildRequires:	perl-tools-pod
@@ -203,6 +224,26 @@ Statycznie skonsolidowany busybox dla initrd.
 %patch -P8 -p1
 %patch -P9 -p1
 %patch -P12 -p1
+%patch -P13 -p1
+%patch -P14 -p1
+%patch -P15 -p1
+%patch -P16 -p1
+%patch -P17 -p1
+%patch -P18 -p1
+%patch -P19 -p1
+%patch -P20 -p1
+%patch -P21 -p1
+%patch -P22 -p1
+%patch -P23 -p1
+%patch -P24 -p1
+%patch -P25 -p1
+%patch -P26 -p1
+%patch -P27 -p1
+%patch -P28 -p1
+%patch -P29 -p1
+%patch -P30 -p1
+%patch -P31 -p1
+%patch -P32 -p1
 
 %build
 install -d built
diff --git a/busybox-CVE-2023-42366.patch b/busybox-CVE-2023-42366.patch
new file mode 100644
index 0000000..a129c90
--- /dev/null
+++ b/busybox-CVE-2023-42366.patch
@@ -0,0 +1,32 @@
+From 5cf8b332429a1dd9afef3337bae92aeddaeff993 Mon Sep 17 00:00:00 2001
+From: Valery Ushakov <uwe at stderr.spb.ru>
+Date: Wed, 24 Jan 2024 22:24:41 +0300
+Subject: [PATCH] awk.c: fix CVE-2023-42366 (bug #15874)
+
+Make sure we don't read past the end of the string in next_token()
+when backslash is the last character in an (invalid) regexp.
+---
+ editors/awk.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/editors/awk.c b/editors/awk.c
+index 728ee8685..be48df7c7 100644
+--- a/editors/awk.c
++++ b/editors/awk.c
+@@ -1165,9 +1165,11 @@ static uint32_t next_token(uint32_t expected)
+ 					s[-1] = bb_process_escape_sequence((const char **)&pp);
+ 					if (*p == '\\')
+ 						*s++ = '\\';
+-					if (pp == p)
++					if (pp == p) {
++						if (*p == '\0')
++							syntax_error(EMSG_UNEXP_EOS);
+ 						*s++ = *p++;
+-					else
++					} else
+ 						p = pp;
+ 				}
+ 			}
+-- 
+2.34.1
+
diff --git a/busybox-CVE-2024-58251.patch b/busybox-CVE-2024-58251.patch
new file mode 100644
index 0000000..12ff357
--- /dev/null
+++ b/busybox-CVE-2024-58251.patch
@@ -0,0 +1,47 @@
+From: Valery Ushakov <valery.ushakov at bell-sw.com>
+Date: Thu, 21 Aug 2025 12:31:53 +0000
+Subject: netstat: CVE-2024-58251 - sanitize argv0 for -p
+Bug-Debian: https://bugs.debian.org/1104009
+
+Signed-off-by: Valery Ushakov <valery.ushakov at bell-sw.com>
+---
+ networking/netstat.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/networking/netstat.c b/networking/netstat.c
+index 807800a62..d979f6079 100644
+--- a/networking/netstat.c
++++ b/networking/netstat.c
+@@ -41,6 +41,7 @@
+ 
+ #include "libbb.h"
+ #include "inet_common.h"
++#include "unicode.h"
+ 
+ //usage:#define netstat_trivial_usage
+ //usage:       "[-"IF_ROUTE("r")"al] [-tuwx] [-en"IF_FEATURE_NETSTAT_WIDE("W")IF_FEATURE_NETSTAT_PRG("p")"]"
+@@ -314,9 +315,12 @@ static int FAST_FUNC dir_act(struct recursive_state *state,
+ 		return FALSE;
+ 	cmdline_buf[n] = '\0';
+ 
++	/* don't write process-controlled argv[0] to the user's terminal as-is */
++	const char *argv0base = printable_string(bb_basename(cmdline_buf));
++
+ 	/* go through all files in /proc/PID/fd and check whether they are sockets */
+ 	strcpy(proc_pid_fname + len - (sizeof("cmdline")-1), "fd");
+-	pid_slash_progname = concat_path_file(pid, bb_basename(cmdline_buf)); /* "PID/argv0" */
++	pid_slash_progname = concat_path_file(pid, argv0base); /* "PID/argv0" */
+ 	n = recursive_action(proc_pid_fname,
+ 			ACTION_RECURSE | ACTION_QUIET,
+ 			add_to_prg_cache_if_socket,
+@@ -686,6 +690,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
+ 	unsigned opt;
+ 
+ 	INIT_G();
++	init_unicode();
+ 
+ 	/* Option string must match NETSTAT_xxx constants */
+ 	opt = getopt32(argv, NETSTAT_OPTS);
+-- 
+2.34.1
+
diff --git a/busybox-CVE-2025-46394.patch b/busybox-CVE-2025-46394.patch
new file mode 100644
index 0000000..2db8130
--- /dev/null
+++ b/busybox-CVE-2025-46394.patch
@@ -0,0 +1,19 @@
+From: Michael Tokarev <mjt at tls.msk.ru>
+Subject: missing part of CVE-2025-46394 fix
+Date: Sun, 01 Feb 2026 19:21:37 +0300
+Bug-Debian: https://bugs.debian.org/1104008
+Forwarded: yes, https://lists.busybox.net/pipermail/busybox/2026-February/091910.html
+
+diff --git a/archival/libarchive/header_verbose_list.c b/archival/libarchive/header_verbose_list.c
+index a575a08a0..e4ee939c0 100644
+--- a/archival/libarchive/header_verbose_list.c
++++ b/archival/libarchive/header_verbose_list.c
+@@ -40,7 +40,7 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header)
+ 		ptm->tm_hour,
+ 		ptm->tm_min,
+ 		ptm->tm_sec,
+-		file_header->name);
++		printable_string(file_header->name));
+ 
+ #else /* !FEATURE_TAR_UNAME_GNAME */
+ 
diff --git a/busybox-CVE-2025-60876.patch b/busybox-CVE-2025-60876.patch
new file mode 100644
index 0000000..639cb37
--- /dev/null
+++ b/busybox-CVE-2025-60876.patch
@@ -0,0 +1,38 @@
+From 9904185c589bb43f0c7f129bd16e885b140ae931 Mon Sep 17 00:00:00 2001
+From: Radoslav Kolev <radoslav.kolev at suse.com>
+Date: Wed, 12 Nov 2025 19:46:15 +0200
+Subject: [PATCH v2 1/1] wget: don't allow control characters or spaces in the
+ URL
+
+Fixes CVE-2025-60876 malicious URL can be used to inject
+HTTP headers in the request.
+
+Signed-off-by: Radoslav Kolev <radoslav.kolev at suse.com>
+Reviewed-by: Emmanuel Deloget <logout at free.fr>
+---
+ networking/wget.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/networking/wget.c b/networking/wget.c
+index ec3767793..fa555427b 100644
+--- a/networking/wget.c
++++ b/networking/wget.c
+@@ -536,6 +536,15 @@ static void parse_url(const char *src_url, struct host_info *h)
+ {
+ 	char *url, *p, *sp;
+ 
++	/* Fix for CVE-2025-60876 - don't allow control characters or spaces in the URL */
++	/* otherwise a malicious URL can be used to inject HTTP headers in the request */
++	const unsigned char *u = (void *) src_url;
++	while (*u) {
++		if (*u <= ' ')
++			bb_simple_error_msg_and_die("Unencoded control character found in the URL!");
++		u++;
++	}
++
+ 	free(h->allocated);
+ 	h->allocated = url = xstrdup(src_url);
+ 
+-- 
+2.51.1
+
diff --git a/busybox-CVE-2026-108119.patch b/busybox-CVE-2026-108119.patch
new file mode 100644
index 0000000..a9dc1d8
--- /dev/null
+++ b/busybox-CVE-2026-108119.patch
@@ -0,0 +1,200 @@
+From 5da20102b5db249c76edddd2cf608394a1ad34dc Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Fri, 9 Oct 2026 15:09:26 +0200
+Subject: [PATCH] tar: ".." is also an unsafe filename, treat as such
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ archival/libarchive/unsafe_prefix.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
+index 89a371a7f..ab70ff816 100644
+--- a/archival/libarchive/unsafe_prefix.c
++++ b/archival/libarchive/unsafe_prefix.c
+@@ -8,6 +8,12 @@
+ const char* FAST_FUNC skip_unsafe_prefix(const char *str)
+ {
+ 	const char *cp = str;
++
++	/* if "..": malicious, return "" */
++	/* (causes harmless errors trying to create or hardlink a file named "") */
++	if (cp[0] == '.' && cp[1] == '.' && cp[2] == '\0')
++		return cp + 2;
++
+ 	while (1) {
+ 		const char *cp2;
+ 		if (*cp == '/') {
+-- 
+2.56.0
+
+From 382008b3f07d78e3abbc50d00bb857622dbbfac6 Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Fri, 9 Oct 2026 16:37:30 +0200
+Subject: [PATCH] tar: protect against Symlink+Hardlink attack
+
+function                                             old     new   delta
+create_links_from_list                                93     190     +97
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ archival/libarchive/unsafe_symlink_target.c | 54 ++++++++++++++++++---
+ testsuite/tar.tests                         | 39 +++++++++++++--
+ 2 files changed, 81 insertions(+), 12 deletions(-)
+
+diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
+index d764c89ab..65c782bdd 100644
+--- a/archival/libarchive/unsafe_symlink_target.c
++++ b/archival/libarchive/unsafe_symlink_target.c
+@@ -17,26 +17,64 @@ void FAST_FUNC create_or_remember_link(llist_t **link_placeholders,
+ 		return;
+ 	}
+ 	if (symlink(target, linkname) != 0) {
+-		/* shared message */
++		// shared message
+ 		bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
+ 			"sym", linkname, target
+ 		);
+ 	}
+ }
+ 
++static int all_dir_components_are_dirs(char *path)
++{
++	struct stat sb;
++	char *slash;
++
++	slash = path;
++	while ((slash = strchr(slash, '/')) != NULL) {
++		int ok;
++		*slash = '\0';
++		ok = (lstat(path, &sb) == 0 && S_ISDIR(sb.st_mode));
++		*slash++ = '/';
++		if (!ok)
++			return 0;
++	}
++	return 1;
++}
++
+ void FAST_FUNC create_links_from_list(llist_t *list)
+ {
++	// This idea sounds better (fewer file ops):
++        //  Create hardlinks first, then symlinks.
++	// ^^^ but it breaks the "hardlink to symlink" case:
++	//  mkdir dir
++	//  >dir/a
++	//  ln -s ../dir/a dir/b
++	//  ln dir/b dir/c
++	//  mkdir new; tar cf - dir/a dir/b dir/c | tar -C new -xvf -
++	// On extract, 'c' will have no 'b' to be a hardlink to.
++
+ 	while (list) {
+ 		char *target;
++		char *linkname = list->data + 1;
+ 
+-		target = list->data + 1 + strlen(list->data + 1) + 1;
+-		if ((*list->data ? link : symlink) (target, list->data + 1)) {
+-			/* shared message */
+-			bb_error_msg_and_die("can't create %slink '%s' to '%s'",
+-				*list->data ? "hard" : "sym",
+-				list->data + 1, target
++		target = linkname + strlen(linkname) + 1;
++
++		// Check whether the linkname has suspicious components:
++		// SYMLINK_OUTSIDE_EXTRACT_DIR/etc/passwd sort of thing.
++		// (Our delayed creation of symlinks is not defending against it,
++		// we could create SYMLINK_OUTSIDE_EXTRACT_DIR ourself!)
++		if (!all_dir_components_are_dirs(linkname)) {
++			errno = ENOTDIR;
++			goto err;
++		}
++		if ((list->data[0] ? link : symlink) (target, linkname)) {
++			// shared message
++ err:
++			bb_perror_msg_and_die("can't create %slink '%s' to '%s'",
++				list->data[0] ? "hard" : "sym",
++				linkname, target
+ 			);
+-			/* Note: GNU tar 1.34 errors out only _after_ all links are (attempted to be) created */
++			// Note: GNU tar 1.34 errors out only _after_ all links are (attempted to be) created
+ 		}
+ 		list = list->link;
+ 	}
+diff --git a/testsuite/tar.tests b/testsuite/tar.tests
+index 83c8c2edf..efd8f4855 100755
+--- a/testsuite/tar.tests
++++ b/testsuite/tar.tests
+@@ -304,7 +304,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
+ testing "tar does not extract into symlinks" "\
+ >>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
+ " "\
+-tar: can't create symlink 'passwd' to '/tmp/passwd'
++tar: can't create symlink 'passwd' to '/tmp/passwd': File exists
+ 0
+ " \
+ "" "\
+@@ -324,7 +324,7 @@ optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
+ testing "tar -k does not extract into symlinks" "\
+ >>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
+ " "\
+-tar: can't create symlink 'passwd' to '/tmp/passwd'
++tar: can't create symlink 'passwd' to '/tmp/passwd': File exists
+ 0
+ " \
+ "" "\
+@@ -374,7 +374,7 @@ ls symlink/bb_test_evilfile
+ anything.txt
+ symlink
+ symlink/bb_test_evilfile
+-tar: can't create symlink 'symlink' to '/tmp'
++tar: can't create symlink 'symlink' to '/tmp': File exists
+ 1
+ ls: /tmp/bb_test_evilfile: No such file or directory
+ ls: bb_test_evilfile: No such file or directory
+@@ -400,7 +400,7 @@ mkdir dir
+ ln -s ../dir/a dir/b
+ ln dir/b dir/c
+ mkdir new
+-tar cf - dir/* | tar -C new -xvf - 2>&1
++tar cf - dir/a dir/b dir/c | tar -C new -xvf - 2>&1
+ ' "\
+ dir/a
+ dir/b
+@@ -410,4 +410,35 @@ dir/c
+ SKIP=
+ cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
+ 
++mkdir tar.tempdir && cd tar.tempdir || exit 1
++testing "tar Symlink+Hardlink attack: create symlink-to-outside and then create a hardlink in it" '\
++exec 2>&1
++mkdir 2tar || exit 1
++(
++cd 2tar || exit 1
++ln -s ..    gate
++ln -s gate  alias
++ln -s alias portal
++          > seed
++ln     seed portal/ESCAPED
++#^^^ the ESCAPED file is actually in ..!
++tar cf 2tar.tar \
++        alias \
++        gate \
++        portal \
++        seed \
++        portal/ESCAPED
++rm portal/ESCAPED alias gate portal seed
++tar xf 2tar.tar
++echo $?
++)
++test -e ESCAPED && echo "ESCAPED!"
++' "\
++tar: can't create directory 'portal': No such file or directory
++tar: can't create hardlink 'portal/ESCAPED' to 'seed': Not a directory
++1
++" \
++"" ""
++cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
++
+ exit $FAILCOUNT
+-- 
+2.56.0
+
diff --git a/busybox-CVE-2026-38752.patch b/busybox-CVE-2026-38752.patch
new file mode 100644
index 0000000..baee6d8
--- /dev/null
+++ b/busybox-CVE-2026-38752.patch
@@ -0,0 +1,80 @@
+From sanghyun.park.cnu at gmail.com  Tue Jun 16 03:16:05 2026
+From: sanghyun.park.cnu at gmail.com (Sanghyun Park)
+Date: Tue, 16 Jun 2026 12:16:05 +0900
+Subject: [PATCH] awk: fix stack overflow in evaluate()
+Message-ID: <20260616031605.1178760-3-sanghyun.park.cnu at gmail.com>
+
+Recursive awk functions can keep re-entering evaluate() until the
+process stack is exhausted. Track active function-call depth and report a
+normal awk error once the limit is reached instead of overflowing the
+stack.
+
+Signed-off-by: Sanghyun Park <sanghyun.park.cnu at gmail.com>
+---
+ editors/awk.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/editors/awk.c b/editors/awk.c
+index dd8f4ac..f15832b 100644
+--- a/editors/awk.c
++++ b/editors/awk.c
+@@ -633,6 +633,7 @@ struct globals {
+ 	smalluint exitcode;
+ 
+ 	unsigned evaluate__seed;
++	unsigned evaluate__func_depth;
+ 	var *evaluate__fnargs;
+ 	regex_t evaluate__sreg;
+ 
+@@ -703,6 +704,7 @@ static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token";
+ static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero";
+ static const char EMSG_INV_FMT[] ALIGN1 = "Invalid format specifier";
+ static const char EMSG_TOO_FEW_ARGS[] ALIGN1 = "Too few arguments";
++static const char EMSG_RECURSION[] ALIGN1 = "Recursion limit exceeded";
+ static const char EMSG_NOT_ARRAY[] ALIGN1 = "Not an array";
+ static const char EMSG_POSSIBLE_ERROR[] ALIGN1 = "Possible syntax error";
+ static const char EMSG_UNDEF_FUNC[] ALIGN1 = "Call to undefined function";
+@@ -2988,6 +2990,7 @@ static var *evaluate(node *op, var *res)
+ #define fnargs (G.evaluate__fnargs)
+ /* seed is initialized to 1 */
+ #define seed   (G.evaluate__seed)
++#define func_depth (G.evaluate__func_depth)
+ #define sreg   (G.evaluate__sreg)
+ 
+ 	var *tmpvars;
+@@ -3299,13 +3302,17 @@ static var *evaluate(node *op, var *res)
+ 			var *argvars, *sv_fnargs;
+ 			const char *sv_progname;
+ 			int nargs, i;
++			enum { MAX_AWK_FUNC_DEPTH = 1000 };
+ 
+ 			debug_printf_eval("FUNC\n");
+ 
+ 			if (!op->r.f->defined)
+ 				syntax_error(EMSG_UNDEF_FUNC);
++			if (func_depth >= MAX_AWK_FUNC_DEPTH)
++				syntax_error(EMSG_RECURSION);
+ 
+ 			/* The body might be empty, still has to eval the args */
++			func_depth++;
+ 			nargs = op->r.f->nargs;
+ 			argvars = nvalloc(nargs);
+ 			i = 0;
+@@ -3333,6 +3340,7 @@ static var *evaluate(node *op, var *res)
+ 
+ 			g_progname = sv_progname;
+ 			fnargs = sv_fnargs;
++			func_depth--;
+ 
+ 			break;
+ 		}
+@@ -3690,6 +3698,7 @@ static var *evaluate(node *op, var *res)
+ 	return res;
+ #undef fnargs
+ #undef seed
++#undef func_depth
+ #undef sreg
+ }
+ 
+-- 
+2.48.1
diff --git a/busybox-CVE-2026-38753.patch b/busybox-CVE-2026-38753.patch
new file mode 100644
index 0000000..4ef3b42
--- /dev/null
+++ b/busybox-CVE-2026-38753.patch
@@ -0,0 +1,51 @@
+From sanghyun.park.cnu at gmail.com  Tue Jun 16 03:18:43 2026
+From: sanghyun.park.cnu at gmail.com (Sanghyun Park)
+Date: Tue, 16 Jun 2026 12:18:43 +0900
+Subject: [PATCH] awk: fix use-after-free in awk_sub()
+Message-ID: <20260616031845.1181463-1-sanghyun.park.cnu at gmail.com>
+
+awk_sub() receives the replacement text as a pointer into awk variable
+storage. Evaluating the regular expression argument can change that
+storage through as_regex(), leaving the replacement pointer dangling
+before strlen() and the replacement loop use it.
+
+Copy the replacement string before calling as_regex() so substitution
+uses stable storage for the duration of the operation.
+
+Signed-off-by: Sanghyun Park <sanghyun.park.cnu at gmail.com>
+---
+ editors/awk.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/editors/awk.c b/editors/awk.c
+index f15832b..8a51827 100644
+--- a/editors/awk.c
++++ b/editors/awk.c
+@@ -2555,6 +2555,7 @@ static char *awk_printf(node *n, size_t *len)
+ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest /*,int subexp*/)
+ {
+ 	char *resbuf;
++	char *repl_copy;
+ 	const char *sp;
+ 	int match_no, residx, replen, resbufsize;
+ 	int regexec_flags;
+@@ -2572,7 +2573,9 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest /*,in
+ 	resbuf = NULL;
+ 	residx = 0;
+ 	match_no = 0;
++	repl_copy = xstrdup(repl);
+ 	regex = as_regex(rn, &sreg);
++	repl = repl_copy;
+ 	sp = getvar_s(src ? src : intvar[F0]);
+ #if defined(REG_STARTEND)
+ 	src_string = sp;
+@@ -2662,6 +2665,7 @@ static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest /*,in
+ 	setvar_p(dest ? dest : intvar[F0], resbuf);
+ 	if (regex == &sreg)
+ 		regfree(regex);
++	free(repl_copy);
+ 	return match_no;
+ }
+ 
+-- 
+2.48.1
diff --git a/busybox-CVE-2026-38754.patch b/busybox-CVE-2026-38754.patch
new file mode 100644
index 0000000..8bfe71a
--- /dev/null
+++ b/busybox-CVE-2026-38754.patch
@@ -0,0 +1,154 @@
+From a448b6d5b21e5b21249391389b6f0551d9bea136 Mon Sep 17 00:00:00 2001
+From: Sanghyun Park <sanghyun.park.cnu at gmail.com>
+Date: Thu, 18 Jun 2026 17:04:20 +0900
+Subject: [PATCH] ash: fix out-of-bounds read in ifsbreakup()
+
+ifsfree() does not only release allocated ifsregion nodes; it also clears
+the global IFS region state used by ifsbreakup(). If argstr() raises an
+error while expanding an argument, ash longjmps out of expandarg() before
+that cleanup runs, leaving stale IFS split offsets behind.
+
+A later expansion can reuse the stack for a shorter string. ifsbreakup()
+then sees the stale IFS state, trusts the old offsets, and can walk past
+the current stack block before dereferencing p.
+
+Follow dash's root-cause fix: when an expansion-related handler catches
+EXERROR and continues, restore the handler and call ifsfree(). Apply
+the cleanup to redirectsafe(), expandstr(), and evaltree().
+
+Upstream commit:
+
+    Date:   Mon Dec 5 23:02:01 2022 +0800
+    expand: Add ifsfree to expand to fix a logic error that causes a buffer over-read
+
+    On Mon, Jun 20, 2022 at 02:27:10PM -0400, Alex Gorinson wrote:
+    > Due to a logic error in the ifsbreakup function in expand.c if a
+    > heredoc and normal command is run one after the other by means of a
+    > semi-colon, when the second command drops into ifsbreakup the command
+    > will be evaluated with the ifslastp/ifsfirst struct that was set when
+    > the here doc was evaluated. This results in a buffer over-read that
+    > can leak the program's heap, stack, and arena addresses which can be
+    > used to beat ASLR.
+    >
+    > Steps to Reproduce:
+    > First bug:
+    > cmd args: ~/exampleDir/example> dash
+    > $ M='AAAAAAAAAAAAAAAAA'    <note: 17 A's>
+    > $ q00(){
+    > $ <<000;echo
+    > $ ${D?$M$M$M$M$M$M}        <note: 6 $M's>
+    > $ 000
+    > $ }
+    > $ q00                      <note: After the q00 is typed in, the leak
+    > should be echo'd out; this works with ash, busybox ash, and dash and
+    > with all option args.>
+    >
+    > Patch:
+    > Adding the following to expand.c will fix both bugs in one go.
+    > (Thank you to Harald van Dijk and Michael Greenberg for doing the
+    > heavy lifting for this patch!)
+    > ==========================
+    > --- a/src/expand.c
+    > +++ b/src/expand.c
+    > @@ -859,6 +859,7 @@
+    > if (discard)
+    > return -1;
+    >
+    > +ifsfree();
+    > sh_error("Bad substitution");
+    > }
+    >
+    > @@ -1739,6 +1740,7 @@
+    > } else
+    > msg = umsg;
+    > }
+    > +ifsfree();
+    > sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
+    >  }
+    > ==========================
+
+    Thanks for the report!
+
+    I think it's better to add the ifsfree() call to the exception
+    handling path as other sh_error calls may trigger this too.
+
+function                                             old     new   delta
+restore_handler_expandarg                              -      33     +33
+evaltree                                             725     711     -14
+static.redirectsafe                                  141     124     -17
+expandstr                                            262     242     -20
+------------------------------------------------------------------------------
+(add/remove: 1/0 grow/shrink: 0/3 up/down: 36/-45)            Total: -18 bytes
+
+Signed-off-by: Sanghyun Park <sanghyun.park.cnu at gmail.com>
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ shell/ash.c | 24 +++++++++++++++---------
+ 1 file changed, 15 insertions(+), 9 deletions(-)
+
+diff --git a/shell/ash.c b/shell/ash.c
+index fb887f31b..b8ff67b16 100644
+--- a/shell/ash.c
++++ b/shell/ash.c
+@@ -5574,6 +5574,7 @@ write2pipe(int pip[2], const char *p, size_t len)
+ 
+ /* openhere needs this forward reference */
+ static void expandhere(union node *arg);
++static void ifsfree(void);
+ static int
+ openhere(union node *redir)
+ {
+@@ -5998,6 +5999,17 @@ redirect(union node *redir, int flags)
+ 	//	preverrout_fd = copied_fd2;
+ }
+ 
++static void
++restore_handler_expandarg(struct jmploc *savehandler, int err)
++{
++	exception_handler = savehandler;
++	if (err) {
++		if (exception_type != EXERROR)
++			longjmp(exception_handler->loc, 1);
++		ifsfree();
++	}
++}
++
+ static int
+ redirectsafe(union node *redir, int flags)
+ {
+@@ -6013,9 +6025,7 @@ redirectsafe(union node *redir, int flags)
+ 		exception_handler = &jmploc;
+ 		redirect(redir, flags);
+ 	}
+-	exception_handler = savehandler;
+-	if (err && exception_type != EXERROR)
+-		longjmp(exception_handler->loc, 1);
++	restore_handler_expandarg(savehandler, err);
+ 	RESTORE_INT(saveint);
+ 	return err;
+ }
+@@ -9792,9 +9802,7 @@ evaltree(union node *n, int flags)
+ 			trap_depth--;
+ 			in_trap_ERR = 0;
+ 
+-			exception_handler = savehandler;
+-			if (err && exception_type != EXERROR)
+-				longjmp(exception_handler->loc, 1);
++			restore_handler_expandarg(savehandler, err);
+ 
+ 			exitstatus = savestatus;
+ 		}
+@@ -14009,9 +14017,7 @@ expandstr(const char *ps, int syntax_type)
+ 	result = stackblock();
+ 
+ out:
+-	exception_handler = savehandler;
+-	if (err && exception_type != EXERROR)
+-		longjmp(exception_handler->loc, 1);
++	restore_handler_expandarg(savehandler, err);
+ 
+ 	doprompt = saveprompt;
+ 	/* Try: PS1='`xxx(`' */
+-- 
+2.52.0
+
diff --git a/busybox-CVE-2026-38755.patch b/busybox-CVE-2026-38755.patch
new file mode 100644
index 0000000..75022c6
--- /dev/null
+++ b/busybox-CVE-2026-38755.patch
@@ -0,0 +1,69 @@
+From sanghyun.park.cnu at gmail.com  Tue Jun 16 03:19:27 2026
+From: sanghyun.park.cnu at gmail.com (Sanghyun Park)
+Date: Tue, 16 Jun 2026 12:19:27 +0900
+Subject: [PATCH] ash: fix stack overflow in evalfun()
+Message-ID: <20260616031928.1182292-1-sanghyun.park.cnu at gmail.com>
+
+Recursive shell functions can repeatedly enter evalfun() until the
+process stack is exhausted. Track active shell function calls and raise a
+normal shell error when the recursion limit is reached.
+
+The counter is decremented through the existing funcdone cleanup path so
+errors raised from inside the function body unwind it correctly.
+
+Signed-off-by: Sanghyun Park <sanghyun.park.cnu at gmail.com>
+---
+ shell/ash.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/shell/ash.c b/shell/ash.c
+index cda6c85..2e319cc 100644
+--- a/shell/ash.c
++++ b/shell/ash.c
+@@ -481,6 +481,7 @@ struct globals_misc {
+ 	int shlvl;
+ #define rootshell (!shlvl)
+ 	int errlinno;
++	unsigned func_depth;
+ 
+ 	char *minusc;  /* argument to -c option */
+ 
+@@ -588,6 +589,7 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
+ #define rootpid     (G_misc.rootpid    )
+ #define shlvl       (G_misc.shlvl      )
+ #define errlinno    (G_misc.errlinno   )
++#define func_depth  (G_misc.func_depth )
+ #define minusc      (G_misc.minusc     )
+ #define curdir      (G_misc.curdir     )
+ #define physdir     (G_misc.physdir    )
+@@ -10321,6 +10323,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
+ 	int savefuncline;
+ 	char *savefuncname;
+ 	char *savetrap = NULL;
++	enum { MAX_ASH_FUNC_DEPTH = 1000 };
++
++	if (func_depth >= MAX_ASH_FUNC_DEPTH)
++		ash_msg_and_raise_error("function recursion limit exceeded");
+ 
+ 	if (!Eflag) {
+ 		savetrap = trap[NTRAP_ERR];
+@@ -10339,6 +10345,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
+ 	exception_handler = &jmploc;
+ 	shellparam.malloced = 0;
+ 	func->count++;
++	func_depth++;
+ 	funcname = func->n.ndefun.text;
+ 	funcline = func->n.ndefun.linno;
+ 	INTON;
+@@ -10360,6 +10367,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
+ 	}
+ 	funcline = savefuncline;
+ 	lineno = savelineno;
++	func_depth--;
+ 	freefunc(func);
+ 	freeparam(&shellparam);
+ 	shellparam = saveparam;
+-- 
+2.48.1
+
+
diff --git a/busybox-CVE-2026-88830.patch b/busybox-CVE-2026-88830.patch
new file mode 100644
index 0000000..9a71fb1
--- /dev/null
+++ b/busybox-CVE-2026-88830.patch
@@ -0,0 +1,55 @@
+From 89ac82774f90e270d04ed5299e5cf05dfbc314c1 Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 03:19:28 +0200
+Subject: [PATCH] tls: fix undersized buffer calculation
+
+All callers of pstm_montgomery_reduce() are currently in pstm_exptmod()
+and they all pass a preallocated temporary buffer, so the incorrect code
+is unreachable. But fix it anyway.
+
+function                                             old     new   delta
+pstm_montgomery_reduce                               431     430      -1
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ networking/tls_pstm_montgomery_reduce.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+diff --git a/networking/tls_pstm_montgomery_reduce.c b/networking/tls_pstm_montgomery_reduce.c
+index e63e590db..36882425a 100644
+--- a/networking/tls_pstm_montgomery_reduce.c
++++ b/networking/tls_pstm_montgomery_reduce.c
+@@ -370,6 +370,7 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m,
+ 	pstm_digit	*c, *_c, *tmpm, mu;
+ 	int32		oldused, x, y;
+ 	int		pa; //bbox: was int16
++	uint32          cSize;
+ 
+ 	pa = m->used;
+ 	if (pa > a->alloc) {
+@@ -377,11 +378,13 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m,
+ 		return PS_LIMIT_FAIL;
+ 	}
+ 
+-	if (paD && paDlen >= (uint32)2*pa+1) {
++	cSize = (2 * pa + 1) * sizeof(pstm_digit);
++	if (paD && paDlen >= cSize) {
+ 		c = paD;
+ 		memset(c, 0x0, paDlen);
+ 	} else {
+-		c = xzalloc(2*pa+1);//bbox
++		//bbox: dead code, all callers always pass paD:
++		c = xzalloc(cSize); //bbox
+ 	}
+ 	/* copy the input */
+ 	oldused = a->used;
+@@ -441,7 +444,8 @@ int32 FAST_FUNC pstm_montgomery_reduce(psPool_t *pool, pstm_int *a, pstm_int *m,
+ 			x = PS_MEM_FAIL;
+ 		}
+ 	}
+-	if (paDlen < (uint32)2*pa+1) {
++	//bbox: dead code, all callers always pass paD:
++	if (c != paD) {
+ 		psFree(c, pool);
+ 	}
+ 	return x;
diff --git a/busybox-CVE-2026-88831.patch b/busybox-CVE-2026-88831.patch
new file mode 100644
index 0000000..37052f6
--- /dev/null
+++ b/busybox-CVE-2026-88831.patch
@@ -0,0 +1,134 @@
+From 339e3c62f5bcf252839c8abca5325f48a22f21aa Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 04:40:37 +0200
+Subject: [PATCH] httpd: fix handling of "D:1.2.3/999"
+
+An error in allow/deny rule should be interpreted as D:0/0
+("deny all") but was interpreted as D:IP/0. Fix this.
+
+function                                             old     new   delta
+parse_conf                                          1323    1330      +7
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ networking/httpd.c | 32 ++++++++++++++++++--------------
+ 1 file changed, 18 insertions(+), 14 deletions(-)
+
+diff --git a/networking/httpd.c b/networking/httpd.c
+index ad59493d7..bc446a838 100644
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -41,7 +41,7 @@
+  * H:/serverroot     # define the server root. It will override -h
+  * A:172.20.         # Allow address from 172.20.0.0/16
+  * A:10.0.0.0/25     # Allow any address from 10.0.0.0-10.0.0.127
+- * A:10.0.0.0/255.255.255.128  # Allow any address that previous set
++ * A:10.0.0.0/255.255.255.128  # Same as the previous
+  * A:127.0.0.1       # Allow local loopback connections
+  * D:*               # Deny from other IP connections
+  * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
+@@ -63,7 +63,7 @@
+  * Deny/Allow IP logic:
+  *  - Default is to allow all (Allow all (A:*) is a no-op).
+  *  - Deny rules take precedence over allow rules.
+- *  - "Deny all" rule (D:*) is applied last.
++ *  - However, "Deny all" rule (D:*) is applied last.
+  *
+  * Example:
+  *   1. Allow only specified addresses
+@@ -381,8 +381,8 @@ typedef struct Htaccess {
+ /* Must have "next" as a first member */
+ typedef struct Htaccess_IP {
+ 	struct Htaccess_IP *next;
+-	unsigned ip;
+-	unsigned mask;
++	uint32_t ip;   /* host-endian */
++	uint32_t mask; /* host-endian */
+ 	int allow_deny;
+ } Htaccess_IP;
+ #endif
+@@ -653,11 +653,11 @@ static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
+ #if ENABLE_FEATURE_HTTPD_ACL_IP
+ /* Returns presumed mask width in bits or < 0 on error.
+  * Updates strp, stores IP at provided pointer */
+-static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
++static int scan_ip(const char **strp, uint32_t *ipp, unsigned char endc)
+ {
+ 	const char *p = *strp;
+ 	int auto_mask = 8;
+-	unsigned ip = 0;
++	uint32_t ip = 0;
+ 	int j;
+ 
+ 	if (*p == '/')
+@@ -695,10 +695,10 @@ static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
+ }
+ 
+ /* Returns 0 on success. Stores IP and mask at provided pointers */
+-static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
++static int scan_ip_mask(const char *str, uint32_t *ipp, uint32_t *maskp)
+ {
+ 	int i;
+-	unsigned mask;
++	uint32_t mask;
+ 	char *p;
+ 
+ 	i = scan_ip(&str, ipp, '/');
+@@ -713,15 +713,16 @@ static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
+ 			/* (return 0 (success) only if it has N.N.N.N form) */
+ 			return scan_ip(&str, maskp, '\0') - 32;
+ 		}
+-		if (*p)
++		if (*p) /* 'xxx' had something apart from just digits */
+ 			return -1;
+ 	}
++	//else: i = "automask" (the count of explicit IP components: "10.0[.]" = 16)
+ 
+ 	if (i > 32)
+ 		return -1;
+ 
+-	if (sizeof(unsigned) == 4 && i == 32) {
+-		/* mask >>= 32 below may not work */
++	if (i == 32) {
++		/* mask >>= 32 below may not work (according to C standard) */
+ 		mask = 0;
+ 	} else {
+ 		mask = 0xffffffff;
+@@ -890,15 +891,18 @@ static int parse_conf(const char *path, int flag)
+ 			if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
+ 				/* IP{/mask} syntax error detected, protect all */
+ 				ch = 'D';
++				//bb_error_msg("ERR");
++				pip->ip = 0; /* could be set before error is detected - zero it (again) */
+ 				pip->mask = 0;
+ 			}
++			//bb_error_msg_and_die("ip:0x%08x mask:0x%08x", pip->ip, pip->mask);
+ 			pip->allow_deny = ch;
+ 			if (ch == 'D') {
+ 				/* Deny:from_IP - prepend */
+ 				pip->next = G.ip_a_d;
+ 				G.ip_a_d = pip;
+ 			} else {
+-				/* A:from_IP - append (thus all D's precedes A's) */
++				/* A:from_IP - append (thus all D's precede A's) */
+ 				Htaccess_IP *prev_IP = G.ip_a_d;
+ 				if (prev_IP == NULL) {
+ 					G.ip_a_d = pip;
+@@ -2045,7 +2049,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
+ }
+ 
+ #if ENABLE_FEATURE_HTTPD_ACL_IP
+-static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
++static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(uint32_t remote_ip)
+ {
+ 	Htaccess_IP *cur;
+ 
+@@ -2337,7 +2341,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
+ 	char *urlp;
+ 	char *tptr;
+ #if ENABLE_FEATURE_HTTPD_ACL_IP
+-	unsigned remote_ip;
++	uint32_t remote_ip;
+ #endif
+ #if ENABLE_FEATURE_HTTPD_CGI
+ 	unsigned total_headers_len;
diff --git a/busybox-CVE-2026-88832.patch b/busybox-CVE-2026-88832.patch
new file mode 100644
index 0000000..a8cdc75
--- /dev/null
+++ b/busybox-CVE-2026-88832.patch
@@ -0,0 +1,48 @@
+From 9073c1d15c9953c6f1e39145815fc5dccfa6081c Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 05:34:19 +0200
+Subject: [PATCH] volume_id/romfs: limit the maximum size of label to
+ VOLUME_ID_LABEL_SIZE
+
+function                                             old     new   delta
+volume_id_set_label_string                            71      81     +10
+volume_id_probe_romfs                                 94      97      +3
+------------------------------------------------------------------------------
+(add/remove: 0/0 grow/shrink: 2/0 up/down: 13/0)               Total: 13 bytes
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ util-linux/volume_id/romfs.c | 4 ++--
+ util-linux/volume_id/util.c  | 4 ++++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/util-linux/volume_id/romfs.c b/util-linux/volume_id/romfs.c
+index bd74fda8d..475144c43 100644
+--- a/util-linux/volume_id/romfs.c
++++ b/util-linux/volume_id/romfs.c
+@@ -44,8 +44,8 @@ int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/)
+ 	if (rfs == NULL)
+ 		return -1;
+ 
+-	if (memcmp(rfs->magic, "-rom1fs-", 4) == 0) {
+-		size_t len = strlen((char *)rfs->name);
++	if (memcmp(rfs->magic, "-rom1fs-", 8) == 0) {
++		size_t len = strnlen((char *)rfs->name, VOLUME_ID_LABEL_SIZE);
+ 
+ 		if (len) {
+ //			volume_id_set_label_raw(id, rfs->name, len);
+diff --git a/util-linux/volume_id/util.c b/util-linux/volume_id/util.c
+index b59aa99b2..a2bee28ab 100644
+--- a/util-linux/volume_id/util.c
++++ b/util-linux/volume_id/util.c
+@@ -116,6 +116,10 @@ void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t
+ {
+ 	unsigned i;
+ 
++	/* Do not overflow label[VOLUME_ID_LABEL_SIZE+1] */
++	if (count > VOLUME_ID_LABEL_SIZE)
++		count = VOLUME_ID_LABEL_SIZE;
++
+ 	memcpy(id->label, buf, count);
+ 
+ 	/* remove trailing whitespace */
diff --git a/busybox-CVE-2026-88833.patch b/busybox-CVE-2026-88833.patch
new file mode 100644
index 0000000..58c37a4
--- /dev/null
+++ b/busybox-CVE-2026-88833.patch
@@ -0,0 +1,25 @@
+udhcpc: reject packets whose IP total length is below the header size (CVE-2026-88833)
+
+d4_recv_raw_packet() replaces the received byte count with the
+attacker-controlled ip.tot_len and only checks it against the received
+size. A tot_len smaller than sizeof(ip)+sizeof(udp) makes the following
+"bytes -= sizeof(packet.ip) + sizeof(packet.udp)" underflow, and the
+memcpy() at the end then copies a huge amount of memory, crashing udhcpc.
+Validate the minimum length before switching to ip.tot_len.
+
+diff -ruN a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
+--- a/networking/udhcp/dhcpc.c	2026-10-19 16:19:14.050304928 +0200
++++ b/networking/udhcp/dhcpc.c	2026-10-19 16:19:14.051130457 +0200
+@@ -913,6 +913,12 @@
+ 
+ 	/* ignore any extra garbage bytes */
+ 	bytes = ntohs(packet.ip.tot_len);
++	if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
++		/* ip.tot_len must at least cover the IP and UDP headers,
++		 * otherwise the subtraction below underflows */
++		log1s("packet is too short, ignoring");
++		return -2;
++	}
+ 
+ 	/* make sure its the right packet for us, and that it passes sanity checks */
+ 	if (packet.ip.protocol != IPPROTO_UDP
diff --git a/busybox-CVE-2026-88834.patch b/busybox-CVE-2026-88834.patch
new file mode 100644
index 0000000..6e9c987
--- /dev/null
+++ b/busybox-CVE-2026-88834.patch
@@ -0,0 +1,55 @@
+From b829bba95095b87834628adb38ea14b5885c0fbc Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 5 Oct 2026 22:59:11 +0200
+Subject: [PATCH] httpd: do not try to use obviously bogus password from
+ /etc/passwd
+
+If password is 'x' or '*' and yet, /etc/shadow has no entry,
+do not run the crypt machinery - go to the next entry.
+
+function                                             old     new   delta
+check_user_passwd                                    444     462     +18
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ networking/httpd.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+diff --git a/networking/httpd.c b/networking/httpd.c
+index 7469de1de..a6daba044 100644
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -2181,7 +2181,6 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
+ # if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
+ 			char sp_buf[256];
+ # endif
+-
+ 			colon_after_user = strchr(user_and_passwd, ':');
+ 			if (!colon_after_user)
+ 				goto bad_input;
+@@ -2236,8 +2235,9 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
+ 					 * At least glibc 2.4 does this. Be extra paranoid here. */
+ 					struct spwd *result = NULL;
+ 					r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
+-					if (r == 0 && result)
+-						passwd = result->sp_pwdp;
++					if (r != 0 || !result)
++						continue; /* no shadow, and passwd entry is bogus one-char string */
++					passwd = result->sp_pwdp;
+ 				}
+ #  endif
+ 				/* In this case, passwd is ALWAYS encrypted:
+@@ -2250,9 +2250,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
+ 
+ 			if (passwd[0] == '$' && (isdigit(passwd[1]) || passwd[1] == 'y')) {
+ 				char *encrypted;
+-# if !ENABLE_PAM
+- check_encrypted:
+-# endif
++ IF_NOT_PAM(check_encrypted:)
+ 				/* encrypt pwd from peer and check match with local one */
+ 				encrypted = pw_encrypt(
+ 					/* pwd (from peer): */  colon_after_user + 1,
+-- 
+2.56.0
+
diff --git a/busybox-CVE-2026-88835.patch b/busybox-CVE-2026-88835.patch
new file mode 100644
index 0000000..82db94c
--- /dev/null
+++ b/busybox-CVE-2026-88835.patch
@@ -0,0 +1,492 @@
+From e2c5cc04206008acc82d28dac4fcb1963e8f0acf Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 16:16:47 +0200
+Subject: [PATCH] dpkg: free results of read_package_field(), preliminary
+ cleanups in read_package_field()
+
+function                                             old     new   delta
+dpkg_main                                           3910    3943     +33
+write_buffer_no_status                                92     114     +22
+read_package_field                                   204     184     -20
+------------------------------------------------------------------------------
+(add/remove: 0/0 grow/shrink: 2/1 up/down: 55/-20)             Total: 35 bytes
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ archival/dpkg.c | 80 +++++++++++++++++++++++++++----------------------
+ 1 file changed, 45 insertions(+), 35 deletions(-)
+
+diff --git a/archival/dpkg.c b/archival/dpkg.c
+index eda5ec7eb..4ca1daf9b 100644
+--- a/archival/dpkg.c
++++ b/archival/dpkg.c
+@@ -531,26 +531,31 @@ static void free_package(common_node_t *node)
+ }
+ 
+ /*
+- * Gets the next package field from package_buffer, separated into the field name
+- * and field value, it returns the int offset to the first character of the next field
++ * Gets the next package field from package_buffer:
++ * "Name:<whitespace>VALUE{\n|NUL}"
++ * "Name:<whitespace>\n
++ * " VALUE{\n|NUL}"
++ * separated into "NAME" and "VALUE", both strdup()ed.
++ * Returns the int offset to the first character of the next field.
++ * The package_buffer parameter is NUL-terminated.
+  */
+ static int read_package_field(const char *package_buffer, char **field_name, char **field_value)
+ {
+ 	int offset_name_start = 0;
+ 	int offset_name_end = 0;
+ 	int offset_value_start = 0;
+-	int offset_value_end = 0;
+ 	int offset = 0;
+ 	int next_offset;
+ 	int name_length;
+ 	int value_length;
+ 	int exit_flag = FALSE;
+ 
++	*field_name = NULL;
++	*field_value = NULL;
+ 	if (package_buffer == NULL) {
+-		*field_name = NULL;
+-		*field_value = NULL;
+ 		return -1;
+ 	}
++
+ 	while (1) {
+ 		next_offset = offset + 1;
+ 		switch (package_buffer[offset]) {
+@@ -566,14 +571,14 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 				 * immediately after name */
+ 				break;
+ 			case '\n':
+-				/* TODO: The char next_offset may be out of bounds */
+ 				if (package_buffer[next_offset] != ' ') {
+ 					exit_flag = TRUE;
+ 					break;
+ 				}
++				/* fall through */
+ 			case '\t':
+ 			case ' ':
+-				/* increment the value start point if its a just filler */
++				/* increment start points if it is just a filler */
+ 				if (offset_name_start == offset) {
+ 					offset_name_start++;
+ 				}
+@@ -584,9 +589,8 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 		}
+ 		if (exit_flag) {
+ 			/* Check that the names are valid */
+-			offset_value_end = offset;
+ 			name_length = offset_name_end - offset_name_start;
+-			value_length = offset_value_end - offset_value_start;
++			value_length = offset - offset_value_start;
+ 			if (name_length == 0) {
+ 				break;
+ 			}
+@@ -594,27 +598,26 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 				break;
+ 			}
+ 
+-			/* If not valid, start fresh with next field */
++			/* Not valid: start fresh with next field */
+ 			exit_flag = FALSE;
+ 			offset_name_start = offset + 1;
+ 			offset_name_end = 0;
+ 			offset_value_start = offset + 1;
+-			offset_value_end = offset + 1;
+ 			offset++;
+ 		}
+ 		offset++;
+-	}
+-	*field_name = NULL;
+-	if (name_length) {
++	} /* while (1) */
++
++	if (name_length > 0) {
+ 		*field_name = xstrndup(&package_buffer[offset_name_start], name_length);
+ 	}
+-	*field_value = NULL;
+ 	if (value_length > 0) {
+ 		*field_value = xstrndup(&package_buffer[offset_value_start], value_length);
+ 	}
+ 	return next_offset;
+ }
+ 
++/* The parameter is NUL-terminated */
+ static unsigned fill_package_struct(char *control_buffer)
+ {
+ 	static const char field_names[] ALIGN1 =
+@@ -623,14 +626,14 @@ static unsigned fill_package_struct(char *control_buffer)
+ 		"Conflicts\0""Suggests\0""Recommends\0""Enhances\0";
+ 
+ 	common_node_t *new_node = xzalloc(sizeof(common_node_t));
+-	char *field_name;
+-	char *field_value;
+ 	int field_start = 0;
+-	int num = -1;
+ 	int buffer_length = strlen(control_buffer);
++	int num;
+ 
+ 	new_node->version = search_name_hashtable("unknown");
+ 	while (field_start < buffer_length) {
++		char *field_name;
++		char *field_value;
+ 		unsigned field_num;
+ 
+ 		field_start += read_package_field(&control_buffer[field_start],
+@@ -799,7 +802,7 @@ static void write_buffer_no_status(FILE *new_status_file, const char *control_bu
+ 	char *name;
+ 	char *value;
+ 	int start = 0;
+-	while (1) {
++	while (control_buffer[start]) {
+ 		start += read_package_field(&control_buffer[start], &name, &value);
+ 		if (name == NULL) {
+ 			break;
+@@ -807,6 +810,8 @@ static void write_buffer_no_status(FILE *new_status_file, const char *control_bu
+ 		if (strcmp(name, "Status") != 0) {
+ 			fprintf(new_status_file, "%s: %s\n", name, value);
+ 		}
++		free(name);
++		free(value);
+ 	}
+ }
+ 
+@@ -815,34 +820,35 @@ static void write_status_file(deb_file_t **deb_file)
+ {
+ 	FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status");
+ 	FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb");
+-	char *package_name;
+-	char *status_from_file;
+-	char *control_buffer = NULL;
+-	char *tmp_string;
+-	int status_num;
++	char *control_buffer;
+ 	int field_start = 0;
+-	int write_flag;
+-	int i = 0;
++	int status_num;
++	int i;
+ 
+ 	/* Update previously known packages */
+ 	while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) {
++		char *package_name;
++		char *status_from_file;
++		char *tmp_string;
++		int write_flag;
++//FIXME: "int field_start = 0;" should be _here_, right?
++
+ 		tmp_string = strstr(control_buffer, "Package:");
+ 		if (tmp_string == NULL) {
++			free(control_buffer);
+ 			continue;
+ 		}
+-
+ 		tmp_string += 8;
+ 		tmp_string += strspn(tmp_string, " \n\t");
+ 		package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
++
+ 		write_flag = FALSE;
++		status_from_file = NULL;
+ 		tmp_string = strstr(control_buffer, "Status:");
+ 		if (tmp_string != NULL) {
+-			/* Separate the status value from the control buffer */
+ 			tmp_string += 7;
+ 			tmp_string += strspn(tmp_string, " \n\t");
+ 			status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+-		} else {
+-			status_from_file = NULL;
+ 		}
+ 
+ 		/* Find this package in the status hashtable */
+@@ -870,7 +876,7 @@ static void write_status_file(deb_file_t **deb_file)
+ 						}
+ 						i++;
+ 					}
+-					/* This is temperary, debugging only */
++					/* This is temporary, debugging only */
+ 					if (deb_file[i] == NULL) {
+ 						bb_error_msg_and_die("ALERT: cannot find a control file, "
+ 							"your status file may be broken, status may be "
+@@ -882,7 +888,7 @@ static void write_status_file(deb_file_t **deb_file)
+ 					fprintf(new_status_file, "Package: %s\n", package_name);
+ 					fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+ 
+-					while (1) {
++					while (control_buffer[field_start]) {
+ 						char *field_name;
+ 						char *field_value;
+ 						field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+@@ -894,13 +900,15 @@ static void write_status_file(deb_file_t **deb_file)
+ 						) {
+ 							fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+ 						}
++						free(field_name);
++						free(field_value);
+ 					}
+ 					write_flag = TRUE;
+ 					fputs("\n", new_status_file);
+ 				}
+ 				else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
+ 					/* only change the status line */
+-					while (1) {
++					while (control_buffer[field_start]) {
+ 						char *field_name;
+ 						char *field_value;
+ 						field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+@@ -913,13 +921,15 @@ static void write_status_file(deb_file_t **deb_file)
+ 						} else {
+ 							fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+ 						}
++						free(field_name);
++						free(field_value);
+ 					}
+ 					write_flag = TRUE;
+ 					fputs("\n", new_status_file);
+ 				}
+ 			}
+ 		}
+-		/* If the package from the status file wasn't handle above, do it now*/
++		/* If the package from the status file wasn't handled above, do it now */
+ 		if (!write_flag) {
+ 			fprintf(new_status_file, "%s\n\n", control_buffer);
+ 		}
+@@ -927,7 +937,7 @@ static void write_status_file(deb_file_t **deb_file)
+ 		free(status_from_file);
+ 		free(package_name);
+ 		free(control_buffer);
+-	}
++	} /* while (control_buffer) */
+ 
+ 	/* Write any new packages */
+ 	for (i = 0; deb_file[i] != NULL; i++) {
+From 43a9d7d31560ad4e9d6a341207b1aa191cb9b0db Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 16:32:43 +0200
+Subject: [PATCH] dpkg: reformat code in read_package_field(), exposing why
+ it's buggy
+
+function                                             old     new   delta
+read_package_field                                   184     169     -15
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ archival/dpkg.c | 56 +++++++++++++++++++------------------------------
+ 1 file changed, 22 insertions(+), 34 deletions(-)
+
+diff --git a/archival/dpkg.c b/archival/dpkg.c
+index 4ca1daf9b..860256f0e 100644
+--- a/archival/dpkg.c
++++ b/archival/dpkg.c
+@@ -548,20 +548,13 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 	int next_offset;
+ 	int name_length;
+ 	int value_length;
+-	int exit_flag = FALSE;
+ 
+ 	*field_name = NULL;
+ 	*field_value = NULL;
+-	if (package_buffer == NULL) {
+-		return -1;
+-	}
+ 
+ 	while (1) {
+ 		next_offset = offset + 1;
+ 		switch (package_buffer[offset]) {
+-			case '\0':
+-				exit_flag = TRUE;
+-				break;
+ 			case ':':
+ 				if (offset_name_end == 0) {
+ 					offset_name_end = offset;
+@@ -572,8 +565,7 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 				break;
+ 			case '\n':
+ 				if (package_buffer[next_offset] != ' ') {
+-					exit_flag = TRUE;
+-					break;
++					goto end_of_value;
+ 				}
+ 				/* fall through */
+ 			case '\t':
+@@ -586,35 +578,31 @@ static int read_package_field(const char *package_buffer, char **field_name, cha
+ 					offset_value_start++;
+ 				}
+ 				break;
+-		}
+-		if (exit_flag) {
+-			/* Check that the names are valid */
+-			name_length = offset_name_end - offset_name_start;
+-			value_length = offset - offset_value_start;
+-			if (name_length == 0) {
+-				break;
+-			}
+-			if ((name_length > 0) && (value_length > 0)) {
++			case '\0':
++ end_of_value:
++				/* Check that the names are valid */
++				name_length = offset_name_end - offset_name_start;
++				value_length = offset - offset_value_start;
++				if ((name_length > 0) && (value_length > 0)) {
++					if (name_length > 0) {
++						*field_name = xstrndup(&package_buffer[offset_name_start], name_length);
++					}
++					if (value_length > 0) {
++						*field_value = xstrndup(&package_buffer[offset_value_start], value_length);
++					}
++					return next_offset;
++				}
++				if (!package_buffer[offset])
++					return next_offset; //BUG! past NUL
++				/* Not valid: start fresh with next field */
++				offset_name_start = offset + 1;
++				offset_name_end = 0;
++				offset_value_start = offset + 1;
++				offset++; //BUG?
+ 				break;
+-			}
+-
+-			/* Not valid: start fresh with next field */
+-			exit_flag = FALSE;
+-			offset_name_start = offset + 1;
+-			offset_name_end = 0;
+-			offset_value_start = offset + 1;
+-			offset++;
+ 		}
+ 		offset++;
+ 	} /* while (1) */
+-
+-	if (name_length > 0) {
+-		*field_name = xstrndup(&package_buffer[offset_name_start], name_length);
+-	}
+-	if (value_length > 0) {
+-		*field_value = xstrndup(&package_buffer[offset_value_start], value_length);
+-	}
+-	return next_offset;
+ }
+ 
+ /* The parameter is NUL-terminated */
+From f5a4a02a157e250d28976e2ba4ea61a2fa7eb51f Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Mon, 21 Sep 2026 16:59:56 +0200
+Subject: [PATCH] dpkg: fix cases where read_package_field() returns offset
+ past NUL
+
+function                                             old     new   delta
+read_package_field                                   169     211     +42
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ archival/dpkg.c | 63 +++++++++++++++++++++++--------------------------
+ 1 file changed, 30 insertions(+), 33 deletions(-)
+
+diff --git a/archival/dpkg.c b/archival/dpkg.c
+index 860256f0e..b8d8eb648 100644
+--- a/archival/dpkg.c
++++ b/archival/dpkg.c
+@@ -532,8 +532,8 @@ static void free_package(common_node_t *node)
+ 
+ /*
+  * Gets the next package field from package_buffer:
+- * "Name:<whitespace>VALUE{\n|NUL}"
+- * "Name:<whitespace>\n
++ * "<whitespace>Name:<whitespace>VALUE{\n|NUL}"
++ * "<whitespace>Name:<whitespace>\n
+  * " VALUE{\n|NUL}"
+  * separated into "NAME" and "VALUE", both strdup()ed.
+  * Returns the int offset to the first character of the next field.
+@@ -541,64 +541,61 @@ static void free_package(common_node_t *node)
+  */
+ static int read_package_field(const char *package_buffer, char **field_name, char **field_value)
+ {
+-	int offset_name_start = 0;
+-	int offset_name_end = 0;
+-	int offset_value_start = 0;
+-	int offset = 0;
+-	int next_offset;
+-	int name_length;
+-	int value_length;
++	int offset_name_start;
++	int offset_name_end;
++	int offset_value_start = offset_value_start;
++	int offset;
+ 
+ 	*field_name = NULL;
+ 	*field_value = NULL;
+ 
++	offset = 0;
++	offset_name_start = 0;
++	offset_name_end = 0; /* "we did not see ':' yet" */
+ 	while (1) {
+-		next_offset = offset + 1;
+-		switch (package_buffer[offset]) {
++		char ch = package_buffer[offset];
++		switch (ch) {
+ 			case ':':
+ 				if (offset_name_end == 0) {
+-					offset_name_end = offset;
+-					offset_value_start = next_offset;
++					offset_name_end = offset + 1; /* points AFTER ':' - think of empty NAME case */
++					offset_value_start = offset + 1;
+ 				}
+-				/* TODO: Name might still have trailing spaces if ':' isn't
++				/* TODO: NAME might still have trailing spaces if ':' isn't
+ 				 * immediately after name */
+ 				break;
+ 			case '\n':
+-				if (package_buffer[next_offset] != ' ') {
++				if (package_buffer[offset + 1] != ' ')
+ 					goto end_of_value;
+-				}
+ 				/* fall through */
+ 			case '\t':
+ 			case ' ':
+ 				/* increment start points if it is just a filler */
+-				if (offset_name_start == offset) {
++				if (offset_name_start == offset)
+ 					offset_name_start++;
+-				}
+-				if (offset_value_start == offset) {
++				if (offset_value_start == offset)
+ 					offset_value_start++;
+-				}
+ 				break;
+ 			case '\0':
+  end_of_value:
+-				/* Check that the names are valid */
+-				name_length = offset_name_end - offset_name_start;
+-				value_length = offset - offset_value_start;
+-				if ((name_length > 0) && (value_length > 0)) {
+-					if (name_length > 0) {
++				/* Did we see the ':'? */
++				if (offset_name_end != 0) {
++					/* Yes. Check that NAME and VALUE exist and not empty */
++					int name_length = (offset_name_end - 1) - offset_name_start;
++					int value_length = offset - offset_value_start;
++					if ((name_length > 0) && (value_length > 0)) {
+ 						*field_name = xstrndup(&package_buffer[offset_name_start], name_length);
+-					}
+-					if (value_length > 0) {
+ 						*field_value = xstrndup(&package_buffer[offset_value_start], value_length);
++						if (ch)
++							offset++; /* skip '\n' */
++						return offset;
+ 					}
+-					return next_offset;
+ 				}
+-				if (!package_buffer[offset])
+-					return next_offset; //BUG! past NUL
++				if (!ch)
++					return offset; /* stop at NUL */
++
+ 				/* Not valid: start fresh with next field */
+ 				offset_name_start = offset + 1;
+-				offset_name_end = 0;
+-				offset_value_start = offset + 1;
+-				offset++; //BUG?
++				offset_name_end = 0; /* "we did not see ':' yet" */
+ 				break;
+ 		}
+ 		offset++;
diff --git a/busybox-CVE-2026-88837.patch b/busybox-CVE-2026-88837.patch
new file mode 100644
index 0000000..d46815a
--- /dev/null
+++ b/busybox-CVE-2026-88837.patch
@@ -0,0 +1,40 @@
+From 9f5cbdcaa5972d58503c59cd0da13da1e5bb272b Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Tue, 22 Sep 2026 01:14:47 +0200
+Subject: [PATCH] httpd: allow yescrypt passwords ("$y$....")
+
+function                                             old     new   delta
+check_user_passwd                                    439     444      +5
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ loginutils/Config.src | 2 +-
+ networking/httpd.c    | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/loginutils/Config.src b/loginutils/Config.src
+index a7812bd32..5559992da 100644
+--- a/loginutils/Config.src
++++ b/loginutils/Config.src
+@@ -96,7 +96,7 @@ config USE_BB_CRYPT_YES
+ 	default y
+ 	depends on USE_BB_CRYPT
+ 	help
+-	Enable this if you have passwords starting with "$y$" or
++	Enable this if you have passwords starting with "$y$"
+ 	in your /etc/passwd or /etc/shadow files. These passwords
+ 	are hashed using yescrypt algorithms.
+ 	With this option off, login will fail password check for any
+diff --git a/networking/httpd.c b/networking/httpd.c
+index a5dfb38af..842574e0e 100644
+--- a/networking/httpd.c
++++ b/networking/httpd.c
+@@ -2247,7 +2247,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
+ 			}
+ 			/* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
+ 
+-			if (passwd[0] == '$' && isdigit(passwd[1])) {
++			if (passwd[0] == '$' && (isdigit(passwd[1]) || passwd[1] == 'y')) {
+ 				char *encrypted;
+ # if !ENABLE_PAM
+  check_encrypted:
diff --git a/busybox-CVE-2026-88838.patch b/busybox-CVE-2026-88838.patch
new file mode 100644
index 0000000..b293dc9
--- /dev/null
+++ b/busybox-CVE-2026-88838.patch
@@ -0,0 +1,110 @@
+mount: reject clearing options for non-root users (CVE-2026-88838)
+
+Options such as suid/dev/exec/rw only clear flag bits and never set any,
+so parse_mount_options() returns just MS_SILENT for them and the non-root
+"cannot specify flags" check misses them. They are merged with the fstab
+options as a string; when the combined string is re-parsed they clear the
+nosuid/nodev/noexec the administrator set. Report the clearing options too
+and reject them for non-root.
+
+diff -ruN a/util-linux/mount.c b/util-linux/mount.c
+--- a/util-linux/mount.c	2026-10-19 16:19:14.065346691 +0200
++++ b/util-linux/mount.c	2026-10-19 16:19:14.066286273 +0200
+@@ -595,7 +595,7 @@
+ 
+ // Use the mount_options list to parse options into flags.
+ // Also update list of unrecognized options if unrecognized != NULL
+-static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt)
++static unsigned long parse_mount_options(char *options, char **unrecognized, uint32_t *opt, unsigned long *inverted)
+ {
+ 	unsigned long flags = MS_SILENT;
+ 
+@@ -619,9 +619,11 @@
+ 			    )
+ 			) {
+ 				unsigned long fl = mount_options[i];
+-				if (fl & BB_MS_INVERTED_VALUE)
++				if (fl & BB_MS_INVERTED_VALUE) {
+ 					flags &= fl;
+-				else
++					if (inverted)
++						*inverted |= fl;
++				} else
+ 					flags |= fl;
+ 				/* If we see "-o rw" on command line, it's the same as -w:
+ 				 * "do not try to fall back to RO mounts"
+@@ -1984,7 +1986,7 @@
+ 
+ 	errno = 0;
+ 
+-	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL);
++	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts, NULL, NULL);
+ 
+ 	// Treat fstype "auto" as unspecified
+ 	if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
+@@ -2058,7 +2060,7 @@
+ 				len, share,
+ 				share + len + 1  /* "dir1/dir2" */
+ 			);
+-			parse_mount_options(unc, &filteropts, NULL);
++			parse_mount_options(unc, &filteropts, NULL, NULL);
+ 			if (ENABLE_FEATURE_CLEAN_UP) free(unc);
+ 		}
+ 
+@@ -2083,7 +2085,7 @@
+ // (instead of _numeric_ iface_id) with glibc.
+ // This probably should be fixed in glibc, not here.
+ // The workaround is to manually specify correct "ip=ADDR%n" option.
+-			parse_mount_options(ip, &filteropts, NULL);
++			parse_mount_options(ip, &filteropts, NULL, NULL);
+ 			if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+ 		}
+ 
+@@ -2365,9 +2367,17 @@
+ 	// Past this point, we are handling either "mount -a [opts]"
+ 	// or "mount [opts] single_param"
+ 
+-	cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32);
+-	if (nonroot && (cmdopt_flags & ~MS_SILENT)) // Non-root users cannot specify flags
+-		bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
++	{
++		unsigned long inverted_flags = 0;
++
++		cmdopt_flags = parse_mount_options(cmdopts, NULL, &option_mask32, &inverted_flags);
++		// Non-root users cannot specify flags. Clearing options ("suid", "dev",
++		// "exec", "rw", "atime", ...) do not set any flag, they are reported
++		// via inverted_flags; without this they could undo nosuid/nodev/noexec
++		// set in fstab when the combined options are re-parsed.
++		if (nonroot && ((cmdopt_flags & ~MS_SILENT) || inverted_flags))
++			bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
++	}
+ 
+ 	// If we have a shared subtree flag, don't worry about fstab or mtab.
+ 	if (ENABLE_FEATURE_MOUNT_FLAGS
+@@ -2439,7 +2449,7 @@
+ 				continue;
+ 
+ 			// Skip noauto and swap anyway
+-			if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
++			if ((parse_mount_options(mtcur->mnt_opts, NULL, NULL, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
+ 			// swap is bogus "fstype", parse_mount_options can't check fstypes
+ 			 || strcasecmp(mtcur->mnt_type, "swap") == 0
+ 			) {
+@@ -2500,7 +2510,7 @@
+ 		// exit_group(32)                          = ?
+ #if 0
+ 		// In case we want to simply skip swap partitions:
+-		l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
++		l = parse_mount_options(mtcur->mnt_opts, NULL, NULL, NULL);
+ 		if ((l & MOUNT_SWAP)
+ 		// swap is bogus "fstype", parse_mount_options can't check fstypes
+ 		 || strcasecmp(mtcur->mnt_type, "swap") == 0
+@@ -2510,7 +2520,7 @@
+ #endif
+ 		if (nonroot) {
+ 			// fstab must have "users" or "user"
+-			l = parse_mount_options(mtcur->mnt_opts, NULL, NULL);
++			l = parse_mount_options(mtcur->mnt_opts, NULL, NULL, NULL);
+ 			if (!(l & MOUNT_USERS))
+ 				bb_simple_error_msg_and_die(bb_msg_you_must_be_root);
+ 		}
diff --git a/busybox-CVE-2026-88839.patch b/busybox-CVE-2026-88839.patch
new file mode 100644
index 0000000..db94095
--- /dev/null
+++ b/busybox-CVE-2026-88839.patch
@@ -0,0 +1,30 @@
+From c2dec52d37380d27d89827cf01a770f6b3b04e3a Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Tue, 22 Sep 2026 02:19:06 +0200
+Subject: [PATCH] libpwdgrp: tokenizer fix for trailing whitespace removeal.
+
+The tokenize() function trims trailing whitespace via
+overlapping_strcpy() but continues to reference the stale *end
+pointer. Fixing this.
+
+function                                             old     new   delta
+tokenize                                             120     119      -1
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ libpwdgrp/pwd_grp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
+index 10debbcdb..48a49fc24 100644
+--- a/libpwdgrp/pwd_grp.c
++++ b/libpwdgrp/pwd_grp.c
+@@ -171,7 +171,7 @@ static int tokenize(char *buffer, int ch)
+ 			if (p != end)
+ 				overlapping_strcpy(p, end);
+ 			num_fields++;
+-			if (*end == '\0') {
++			if (*p == '\0') {
+ 				S.tokenize_end = p + 1;
+ 				return num_fields;
+ 			}
diff --git a/busybox-CVE-2026-88840.patch b/busybox-CVE-2026-88840.patch
new file mode 100644
index 0000000..e179691
--- /dev/null
+++ b/busybox-CVE-2026-88840.patch
@@ -0,0 +1,38 @@
+From e97224f8fbad11a4d00feaec3391c27ed773f147 Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Tue, 22 Sep 2026 02:38:38 +0200
+Subject: [PATCH] tls: fix off-by-one access past the end of the received
+ packet.
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+ networking/tls.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/networking/tls.c b/networking/tls.c
+index 91172f8bc..05dda491a 100644
+--- a/networking/tls.c
++++ b/networking/tls.c
+@@ -2467,7 +2467,9 @@ static void get_client_hello(tls_state_t *tls)
+ 	p += 2;
+ 	len -= 2;
+ 
+-	if (len < cipher_list_len) {
++	if (len <= cipher_list_len) {
++		/* The "<=" is subtle: it aborts even if we do have cipher list,
++		 * but have no compression size byte after it */
+ 		bb_simple_error_msg_and_die("malformed ClientHello");
+ 	}
+ 
+@@ -2522,7 +2524,7 @@ static void get_client_hello(tls_state_t *tls)
+ 	len -= cipher_list_len;
+ 
+ 	/* Skip compression methods */
+-	len -= 1 + p[0];
++	len -= 1 + p[0]; /* touching p[0] is allowed: len was > cipher_list_len */
+ 	p += 1 + p[0];
+ 
+ 	/* Parse extensions if present */
+-- 
+2.56.0
+
diff --git a/busybox-CVE-2026-88842.patch b/busybox-CVE-2026-88842.patch
new file mode 100644
index 0000000..44bfae7
--- /dev/null
+++ b/busybox-CVE-2026-88842.patch
@@ -0,0 +1,82 @@
+From d5cc94063b2bb5b54fe1021e54600cbcbce6a350 Mon Sep 17 00:00:00 2001
+From: Denys Vlasenko <vda.linux at googlemail.com>
+Date: Tue, 22 Sep 2026 01:04:32 +0200
+Subject: [PATCH] dpkg: fix field handling in write_status_file()
+
+function                                             old     new   delta
+dpkg_main                                           3943    3934      -9
+
+Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
+---
+[Backport note: dropped the upstream comment-only hunk that renames
+"Name" to "NAME" in the read_package_field() description, as it depends
+on the unrelated variable-renaming commit 7c611860d.]
+---
+ archival/dpkg.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/archival/dpkg.c b/archival/dpkg.c
+index 3abc810c8..6a65f16a4 100644
+--- a/archival/dpkg.c
++++ b/archival/dpkg.c
+@@ -809,7 +809,6 @@ static void write_status_file(deb_file_t **deb_file)
+ 	FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status");
+ 	FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb");
+ 	char *control_buffer;
+-	int field_start = 0;
+ 	int status_num;
+ 	int i;
+ 
+@@ -819,23 +818,20 @@ static void write_status_file(deb_file_t **deb_file)
+ 		char *status_from_file;
+ 		char *tmp_string;
+ 		int write_flag;
+-//FIXME: "int field_start = 0;" should be _here_, right?
+ 
+ 		tmp_string = strstr(control_buffer, "Package:");
+ 		if (tmp_string == NULL) {
+ 			free(control_buffer);
+ 			continue;
+ 		}
+-		tmp_string += 8;
+-		tmp_string += strspn(tmp_string, " \n\t");
++		tmp_string = skip_whitespace(tmp_string + 8);
+ 		package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+ 
+ 		write_flag = FALSE;
+ 		status_from_file = NULL;
+ 		tmp_string = strstr(control_buffer, "Status:");
+ 		if (tmp_string != NULL) {
+-			tmp_string += 7;
+-			tmp_string += strspn(tmp_string, " \n\t");
++			tmp_string = skip_whitespace(tmp_string + 7);
+ 			status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+ 		}
+ 
+@@ -872,6 +868,8 @@ static void write_status_file(deb_file_t **deb_file)
+ 					}
+ 				}
+ 				else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
++					int field_start = 0;
++
+ 					/* Only write the Package, Status, Priority and Section lines */
+ 					fprintf(new_status_file, "Package: %s\n", package_name);
+ 					fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+@@ -880,6 +878,8 @@ static void write_status_file(deb_file_t **deb_file)
+ 						char *field_name;
+ 						char *field_value;
+ 						field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
++//FIXME: the questionable ": VALUE" lines (empty NAME)
++//probably should not stop parsing of the entire file?
+ 						if (field_name == NULL) {
+ 							break;
+ 						}
+@@ -895,6 +895,8 @@ static void write_status_file(deb_file_t **deb_file)
+ 					fputs("\n", new_status_file);
+ 				}
+ 				else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
++					int field_start = 0;
++
+ 					/* only change the status line */
+ 					while (control_buffer[field_start]) {
+ 						char *field_name;
diff --git a/busybox.config b/busybox.config
index 010d612..c5f98db 100644
--- a/busybox.config
+++ b/busybox.config
@@ -195,7 +195,8 @@ CONFIG_UNXZ=y
 CONFIG_XZCAT=y
 CONFIG_XZ=y
 # CONFIG_FEATURE_LZMA_FAST is not set
-# CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION is not set
+# strip unsafe paths from cpio/ar/rpm extraction (CVE-2023-39810, CVE-2026-26157)
+CONFIG_FEATURE_PATH_TRAVERSAL_PROTECTION=y
 CONFIG_LZMA=y
 CONFIG_UNZIP=y
 CONFIG_FEATURE_UNZIP_CDF=y
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/busybox.git/commitdiff/168af84b55992613f8030537edae977674860da4



More information about the pld-cvs-commit mailing list