Skip to content

Commit b005ea6

Browse files
authored
Merge branch 'microsoft:3.0-dev' into 3.0-dev
2 parents 2e66305 + 1cd7743 commit b005ea6

47 files changed

Lines changed: 3330 additions & 90 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SPECS/busybox/CVE-2026-26157.patch

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
From 77a3ee825c430d054492b0101750b1c7ee8edf1c Mon Sep 17 00:00:00 2001
2+
From: AllSpark <allspark@microsoft.com>
3+
Date: Mon, 16 Feb 2026 08:13:03 +0000
4+
Subject: [PATCH] tar: strip unsafe hardlink components - GNU tar does the same
5+
6+
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
7+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
8+
Upstream-reference: AI Backport of https://github.com/mirror/busybox/commit/3fb6b31c716669e12f75a2accd31bb7685b1a1cb.patch
9+
---
10+
archival/libarchive/data_extract_all.c | 7 +++--
11+
archival/libarchive/get_header_tar.c | 11 ++++++--
12+
archival/libarchive/unsafe_prefix.c | 30 +++++++++++++++++----
13+
archival/libarchive/unsafe_symlink_target.c | 1 +
14+
archival/tar.c | 2 +-
15+
archival/unzip.c | 2 +-
16+
include/bb_archive.h | 3 ++-
17+
7 files changed, 42 insertions(+), 14 deletions(-)
18+
19+
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
20+
index 8a69711..b84b960 100644
21+
--- a/archival/libarchive/data_extract_all.c
22+
+++ b/archival/libarchive/data_extract_all.c
23+
@@ -66,8 +66,8 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
24+
}
25+
#endif
26+
#if ENABLE_FEATURE_PATH_TRAVERSAL_PROTECTION
27+
- /* Strip leading "/" and up to last "/../" path component */
28+
- dst_name = (char *)strip_unsafe_prefix(dst_name);
29+
+ /* Skip leading "/" and past last ".." path component */
30+
+ dst_name = (char *)skip_unsafe_prefix(dst_name);
31+
#endif
32+
// ^^^ This may be a problem if some applets do need to extract absolute names.
33+
// (Probably will need to invent ARCHIVE_ALLOW_UNSAFE_NAME flag).
34+
@@ -185,8 +185,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
35+
36+
/* To avoid a directory traversal attack via symlinks,
37+
* do not restore symlinks with ".." components
38+
- * or symlinks starting with "/", unless a magic
39+
- * envvar is set.
40+
+ * or symlinks starting with "/"
41+
*
42+
* For example, consider a .tar created via:
43+
* $ tar cvf bug.tar anything.txt
44+
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
45+
index cc6f3f0..1c40ece 100644
46+
--- a/archival/libarchive/get_header_tar.c
47+
+++ b/archival/libarchive/get_header_tar.c
48+
@@ -454,8 +454,15 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
49+
#endif
50+
51+
/* Everything up to and including last ".." component is stripped */
52+
- overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name));
53+
-//TODO: do the same for file_header->link_target?
54+
+ strip_unsafe_prefix(file_header->name);
55+
+ if (file_header->link_target) {
56+
+ /* GNU tar 1.34 examples:
57+
+ * tar: Removing leading '/' from hard link targets
58+
+ * tar: Removing leading '../' from hard link targets
59+
+ * tar: Removing leading 'etc/../' from hard link targets
60+
+ */
61+
+ strip_unsafe_prefix(file_header->link_target);
62+
+ }
63+
64+
/* Strip trailing '/' in directories */
65+
/* Must be done after mode is set as '/' is used to check if it's a directory */
66+
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
67+
index 6670811..89a371a 100644
68+
--- a/archival/libarchive/unsafe_prefix.c
69+
+++ b/archival/libarchive/unsafe_prefix.c
70+
@@ -5,11 +5,11 @@
71+
#include "libbb.h"
72+
#include "bb_archive.h"
73+
74+
-const char* FAST_FUNC strip_unsafe_prefix(const char *str)
75+
+const char* FAST_FUNC skip_unsafe_prefix(const char *str)
76+
{
77+
const char *cp = str;
78+
while (1) {
79+
- char *cp2;
80+
+ const char *cp2;
81+
if (*cp == '/') {
82+
cp++;
83+
continue;
84+
@@ -22,10 +22,25 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str)
85+
cp += 3;
86+
continue;
87+
}
88+
- cp2 = strstr(cp, "/../");
89+
+ cp2 = cp;
90+
+ find_dotdot:
91+
+ cp2 = strstr(cp2, "/..");
92+
if (!cp2)
93+
- break;
94+
- cp = cp2 + 4;
95+
+ break; /* No (more) malicious components */
96+
+
97+
+ /* We found "/..something" */
98+
+ cp2 += 3;
99+
+ if (*cp2 != '/') {
100+
+ if (*cp2 == '\0') {
101+
+ /* Trailing "/..": malicious, return "" */
102+
+ /* (causes harmless errors trying to create or hardlink a file named "") */
103+
+ return cp2;
104+
+ }
105+
+ /* "/..name" is not malicious, look for next "/.." */
106+
+ goto find_dotdot;
107+
+ }
108+
+ /* Found "/../": malicious, advance past it */
109+
+ cp = cp2 + 1;
110+
}
111+
if (cp != str) {
112+
static smallint warned = 0;
113+
@@ -37,3 +52,8 @@ const char* FAST_FUNC strip_unsafe_prefix(const char *str)
114+
}
115+
return cp;
116+
}
117+
+
118+
+void FAST_FUNC strip_unsafe_prefix(char *str)
119+
+{
120+
+ overlapping_strcpy(str, skip_unsafe_prefix(str));
121+
+}
122+
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
123+
index f8dc803..d764c89 100644
124+
--- a/archival/libarchive/unsafe_symlink_target.c
125+
+++ b/archival/libarchive/unsafe_symlink_target.c
126+
@@ -36,6 +36,7 @@ void FAST_FUNC create_links_from_list(llist_t *list)
127+
*list->data ? "hard" : "sym",
128+
list->data + 1, target
129+
);
130+
+ /* Note: GNU tar 1.34 errors out only _after_ all links are (attempted to be) created */
131+
}
132+
list = list->link;
133+
}
134+
diff --git a/archival/tar.c b/archival/tar.c
135+
index 9de3759..cf8c2d1 100644
136+
--- a/archival/tar.c
137+
+++ b/archival/tar.c
138+
@@ -475,7 +475,7 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
139+
DBG("writeFileToTarball('%s')", fileName);
140+
141+
/* Strip leading '/' and such (must be before memorizing hardlink's name) */
142+
- header_name = strip_unsafe_prefix(fileName);
143+
+ header_name = skip_unsafe_prefix(fileName);
144+
145+
if (header_name[0] == '\0')
146+
return TRUE;
147+
diff --git a/archival/unzip.c b/archival/unzip.c
148+
index 691a2d8..5844215 100644
149+
--- a/archival/unzip.c
150+
+++ b/archival/unzip.c
151+
@@ -853,7 +853,7 @@ int unzip_main(int argc, char **argv)
152+
unzip_skip(zip.fmt.extra_len);
153+
154+
/* Guard against "/abspath", "/../" and similar attacks */
155+
- overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn));
156+
+ strip_unsafe_prefix(dst_fn);
157+
158+
/* Filter zip entries */
159+
if (find_list_entry(zreject, dst_fn)
160+
diff --git a/include/bb_archive.h b/include/bb_archive.h
161+
index e0ef8fc..1dc77f3 100644
162+
--- a/include/bb_archive.h
163+
+++ b/include/bb_archive.h
164+
@@ -202,7 +202,8 @@ char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
165+
void seek_by_jump(int fd, off_t amount) FAST_FUNC;
166+
void seek_by_read(int fd, off_t amount) FAST_FUNC;
167+
168+
-const char *strip_unsafe_prefix(const char *str) FAST_FUNC;
169+
+const char *skip_unsafe_prefix(const char *str) FAST_FUNC;
170+
+void strip_unsafe_prefix(char *str) FAST_FUNC;
171+
void create_or_remember_link(llist_t **link_placeholders,
172+
const char *target,
173+
const char *linkname,
174+
--
175+
2.45.4
176+

SPECS/busybox/busybox.spec

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Summary: Statically linked binary providing simplified versions of system commands
22
Name: busybox
33
Version: 1.36.1
4-
Release: 21%{?dist}
4+
Release: 22%{?dist}
55
License: GPLv2
66
Vendor: Microsoft Corporation
77
Distribution: Azure Linux
@@ -18,6 +18,7 @@ Patch4: CVE-2023-42365.patch
1818
Patch5: CVE-2023-42366.patch
1919
Patch6: CVE-2023-39810.patch
2020
Patch7: CVE-2022-48174.patch
21+
Patch8: CVE-2026-26157.patch
2122
BuildRequires: gcc
2223
BuildRequires: glibc-static >= 2.38-18%{?dist}
2324
BuildRequires: libselinux-devel >= 1.27.7-2
@@ -91,7 +92,10 @@ install -m 644 docs/busybox.petitboot.1 %{buildroot}/%{_mandir}/man1/busybox.pet
9192

9293
%check
9394
cd testsuite
94-
SKIP_KNOWN_BUGS=1 ./runtest
95+
96+
# CVE-2026-26157: hardened tar extraction blocks symlink + hardlink write attacks
97+
# These tests validate insecure legacy behavior and are expected to fail
98+
./runtest --skip "tar-symlink-attack,tar-symlink-hardlink-coexist"
9599

96100
%files
97101
%license LICENSE
@@ -106,6 +110,9 @@ SKIP_KNOWN_BUGS=1 ./runtest
106110
%{_mandir}/man1/busybox.petitboot.1.gz
107111

108112
%changelog
113+
* Mon Feb 16 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 1.36.1-22
114+
- Patch for CVE-2026-26157
115+
109116
* Thu Jan 22 2026 Kanishk Bansal <kanbansal@microsoft.com> - 1.36.1-21
110117
- Bump to rebuild with updated glibc
111118

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
From 2e6abd9ec21d45a3faef6cc8d8c4a6b165ff4b64 Mon Sep 17 00:00:00 2001
2+
From: Roland Shoemaker <roland@golang.org>
3+
Date: Mon, 29 Sep 2025 16:33:18 -0700
4+
Subject: [PATCH] html: impose open element stack size limit
5+
6+
The HTML specification contains a number of algorithms which are
7+
quadratic in complexity by design. Instead of adding complicated
8+
workarounds to prevent these cases from becoming extremely expensive in
9+
pathological cases, we impose a limit of 512 to the size of the stack of
10+
open elements. It is extremely unlikely that non-adversarial HTML
11+
documents will ever hit this limit (but if we see cases of this, we may
12+
want to make the limit configurable via a ParseOption).
13+
14+
Thanks to Guido Vranken and Jakub Ciolek for both independently
15+
reporting this issue.
16+
17+
Fixes CVE-2025-47911
18+
Fixes golang/go#75682
19+
20+
Change-Id: I890517b189af4ffbf427d25d3fde7ad7ec3509ad
21+
Reviewed-on: https://go-review.googlesource.com/c/net/+/709876
22+
Reviewed-by: Damien Neil <dneil@google.com>
23+
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
24+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
25+
Upstream-reference: https://github.com/golang/net/commit/59706cdaa8f95502fdec64b67b4c61d6ca58727d.patch
26+
---
27+
.../vendor/golang.org/x/net/html/escape.go | 2 +-
28+
cmd/ctl/vendor/golang.org/x/net/html/parse.go | 21 +++++++++++++++----
29+
2 files changed, 18 insertions(+), 5 deletions(-)
30+
31+
diff --git a/cmd/ctl/vendor/golang.org/x/net/html/escape.go b/cmd/ctl/vendor/golang.org/x/net/html/escape.go
32+
index 04c6bec..12f2273 100644
33+
--- a/cmd/ctl/vendor/golang.org/x/net/html/escape.go
34+
+++ b/cmd/ctl/vendor/golang.org/x/net/html/escape.go
35+
@@ -299,7 +299,7 @@ func escape(w writer, s string) error {
36+
case '\r':
37+
esc = "&#13;"
38+
default:
39+
- panic("unrecognized escape character")
40+
+ panic("html: unrecognized escape character")
41+
}
42+
s = s[i+1:]
43+
if _, err := w.WriteString(esc); err != nil {
44+
diff --git a/cmd/ctl/vendor/golang.org/x/net/html/parse.go b/cmd/ctl/vendor/golang.org/x/net/html/parse.go
45+
index 979ef17..4d12a1c 100644
46+
--- a/cmd/ctl/vendor/golang.org/x/net/html/parse.go
47+
+++ b/cmd/ctl/vendor/golang.org/x/net/html/parse.go
48+
@@ -231,7 +231,14 @@ func (p *parser) addChild(n *Node) {
49+
}
50+
51+
if n.Type == ElementNode {
52+
- p.oe = append(p.oe, n)
53+
+ p.insertOpenElement(n)
54+
+ }
55+
+}
56+
+
57+
+func (p *parser) insertOpenElement(n *Node) {
58+
+ p.oe = append(p.oe, n)
59+
+ if len(p.oe) > 512 {
60+
+ panic("html: open stack of elements exceeds 512 nodes")
61+
}
62+
}
63+
64+
@@ -810,7 +817,7 @@ func afterHeadIM(p *parser) bool {
65+
p.im = inFramesetIM
66+
return true
67+
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
68+
- p.oe = append(p.oe, p.head)
69+
+ p.insertOpenElement(p.head)
70+
defer p.oe.remove(p.head)
71+
return inHeadIM(p)
72+
case a.Head:
73+
@@ -2320,9 +2327,13 @@ func (p *parser) parseCurrentToken() {
74+
}
75+
}
76+
77+
-func (p *parser) parse() error {
78+
+func (p *parser) parse() (err error) {
79+
+ defer func() {
80+
+ if panicErr := recover(); panicErr != nil {
81+
+ err = fmt.Errorf("%s", panicErr)
82+
+ }
83+
+ }()
84+
// Iterate until EOF. Any other error will cause an early return.
85+
- var err error
86+
for err != io.EOF {
87+
// CDATA sections are allowed only in foreign content.
88+
n := p.oe.top()
89+
@@ -2351,6 +2362,8 @@ func (p *parser) parse() error {
90+
// <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
91+
// with no corresponding node in the resulting tree.
92+
//
93+
+// Parse will reject HTML that is nested deeper than 512 elements.
94+
+//
95+
// The input is assumed to be UTF-8 encoded.
96+
func Parse(r io.Reader) (*Node, error) {
97+
return ParseWithOptions(r)
98+
--
99+
2.45.4
100+

0 commit comments

Comments
 (0)