* [oe][meta-oe][wrynose][PATCH 2/33] libnfs: patch CVE-2026-57918
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 3/33] libssh: ignore CVE-2025-14821 ankur.tyagi85
` (30 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-57918
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../libnfs/libnfs/CVE-2026-57918.patch | 34 +++++++++++++++++++
.../libnfs/libnfs_6.0.2.bb | 1 +
2 files changed, 35 insertions(+)
create mode 100644 meta-oe/recipes-connectivity/libnfs/libnfs/CVE-2026-57918.patch
diff --git a/meta-oe/recipes-connectivity/libnfs/libnfs/CVE-2026-57918.patch b/meta-oe/recipes-connectivity/libnfs/libnfs/CVE-2026-57918.patch
new file mode 100644
index 0000000000..27c99b5704
--- /dev/null
+++ b/meta-oe/recipes-connectivity/libnfs/libnfs/CVE-2026-57918.patch
@@ -0,0 +1,34 @@
+From 589568b88a06d92b298ecce563ae57b5fad91238 Mon Sep 17 00:00:00 2001
+From: Ronnie Sahlberg <ronniesahlberg@gmail.com>
+Date: Wed, 10 Jun 2026 12:21:58 +1000
+Subject: [PATCH] socket: prevent an underflow in xid
+
+if the expected pdu-size is larger than the absolute pdu size
+from the xid/record-marker.
+
+Reported-by: Nick Hummel <nickhummel@google.com>
+Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>
+(cherry picked from commit 935b8db712b3c6649bc57ddc276526c4a31680de)
+
+CVE: CVE-2026-57918
+Upstream-Status: Backport [https://github.com/sahlberg/libnfs/commit/935b8db712b3c6649bc57ddc276526c4a31680de]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ lib/socket.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/socket.c b/lib/socket.c
+index f51ca4d..8e38eb8 100644
+--- a/lib/socket.c
++++ b/lib/socket.c
+@@ -929,6 +929,9 @@ rpc_read_from_socket(struct rpc_context *rpc)
+ break;
+ case READ_IOVEC:
+ rpc->pdu->read_count -= rpc->pdu_size;
++ if (rpc->rm_xid[0] < rpc->pdu_size) {
++ return -1;
++ }
+ rpc->rm_xid[0] -= rpc->pdu_size;
+ if (!rpc->rm_xid[0]) {
+ rpc_finished_pdu(rpc);
diff --git a/meta-oe/recipes-connectivity/libnfs/libnfs_6.0.2.bb b/meta-oe/recipes-connectivity/libnfs/libnfs_6.0.2.bb
index 403bae9465..36676708cb 100644
--- a/meta-oe/recipes-connectivity/libnfs/libnfs_6.0.2.bb
+++ b/meta-oe/recipes-connectivity/libnfs/libnfs_6.0.2.bb
@@ -6,6 +6,7 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=825301ba17efc9d188ee0abd4b924ada"
SRC_URI = "git://github.com/sahlberg/libnfs.git;protocol=https;branch=master \
file://0001-CMakeLists.txt-respect-CMAKE_INSTALL_LIBDIR-for-mult.patch \
file://CVE-2026-53689.patch \
+ file://CVE-2026-57918.patch \
"
SRCREV = "18c5c73ee88bb7dc8da0d55dc95164bb77e49dc6"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 3/33] libssh: ignore CVE-2025-14821
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 2/33] libnfs: patch CVE-2026-57918 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 4/33] libssh: mark CVEs patched ankur.tyagi85
` (29 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2025-14821
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-oe/recipes-support/libssh/libssh_0.11.5.bb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
index c28a417244..612cfdbb19 100644
--- a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
+++ b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
@@ -47,3 +47,5 @@ do_install_ptest () {
}
BBCLASSEXTEND = "native nativesdk"
+
+CVE_STATUS[CVE-2025-14821] = "not-applicable-platform: issue only applies on Windows"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 4/33] libssh: mark CVEs patched
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 2/33] libnfs: patch CVE-2026-57918 ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 3/33] libssh: ignore CVE-2025-14821 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842 ankur.tyagi85
` (28 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Changelog[1] also confirms the fix.
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-15370
https://nvd.nist.gov/vuln/detail/cve-2026-59843
https://nvd.nist.gov/vuln/detail/cve-2026-59844
https://nvd.nist.gov/vuln/detail/cve-2026-59845
https://nvd.nist.gov/vuln/detail/cve-2026-59846
https://nvd.nist.gov/vuln/detail/cve-2026-59847
https://nvd.nist.gov/vuln/detail/cve-2026-59848
https://nvd.nist.gov/vuln/detail/cve-2026-59849
https://nvd.nist.gov/vuln/detail/cve-2026-59850
[1]https://gitlab.com/libssh/libssh-mirror/-/blob/libssh-0.11.5/CHANGELOG?ref_type=tags
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-oe/recipes-support/libssh/libssh_0.11.5.bb | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
index 612cfdbb19..752bded528 100644
--- a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
+++ b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
@@ -49,3 +49,12 @@ do_install_ptest () {
BBCLASSEXTEND = "native nativesdk"
CVE_STATUS[CVE-2025-14821] = "not-applicable-platform: issue only applies on Windows"
+CVE_STATUS[CVE-2026-15370] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59843] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59844] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59845] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59846] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59847] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59848] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59849] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2026-59850] = "fixed-version: fixed in v0.11.5"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (2 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 4/33] libssh: mark CVEs patched ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-15 1:29 ` Anuj Mittal
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 6/33] miniupnpd: patch CVE-2026-5720 ankur.tyagi85
` (27 subsequent siblings)
31 siblings, 1 reply; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
The vulnerability exists in GSSAPI key exchange introduced in v0.12.0[1]
Also confirmed by libssh security advisory[2]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-59842
[1]https://gitlab.com/libssh/libssh-mirror/-/commit/88c2ea6752fab7b3da9cc4c51eaf632361a44080
[2]https://www.libssh.org/security/advisories/CVE-2026-59842.txt
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-oe/recipes-support/libssh/libssh_0.11.5.bb | 1 +
1 file changed, 1 insertion(+)
diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
index 752bded528..5cc270477e 100644
--- a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
+++ b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
@@ -58,3 +58,4 @@ CVE_STATUS[CVE-2026-59847] = "fixed-version: fixed in v0.11.5"
CVE_STATUS[CVE-2026-59848] = "fixed-version: fixed in v0.11.5"
CVE_STATUS[CVE-2026-59849] = "fixed-version: fixed in v0.11.5"
CVE_STATUS[CVE-2026-59850] = "fixed-version: fixed in v0.11.5"
+CVE_STATUS[CVE-2025-59842] = "cpe-incorrect: the current version (0.11.5) is not affected"
^ permalink raw reply related [flat|nested] 37+ messages in thread* Re: [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842 ankur.tyagi85
@ 2026-09-15 1:29 ` Anuj Mittal
2026-09-15 1:54 ` Ankur Tyagi
0 siblings, 1 reply; 37+ messages in thread
From: Anuj Mittal @ 2026-09-15 1:29 UTC (permalink / raw)
To: ankur.tyagi85; +Cc: openembedded-devel
On Mon, Sep 7, 2026 at 6:24 PM Ankur Tyagi via lists.openembedded.org
<ankur.tyagi85=gmail.com@lists.openembedded.org> wrote:
>
> From: Ankur Tyagi <ankur.tyagi85@gmail.com>
>
> The vulnerability exists in GSSAPI key exchange introduced in v0.12.0[1]
> Also confirmed by libssh security advisory[2]
>
> Details:
> https://nvd.nist.gov/vuln/detail/cve-2026-59842
>
> [1]https://gitlab.com/libssh/libssh-mirror/-/commit/88c2ea6752fab7b3da9cc4c51eaf632361a44080
> [2]https://www.libssh.org/security/advisories/CVE-2026-59842.txt
>
> Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
> ---
> meta-oe/recipes-support/libssh/libssh_0.11.5.bb | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> index 752bded528..5cc270477e 100644
> --- a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> +++ b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> @@ -58,3 +58,4 @@ CVE_STATUS[CVE-2026-59847] = "fixed-version: fixed in v0.11.5"
> CVE_STATUS[CVE-2026-59848] = "fixed-version: fixed in v0.11.5"
> CVE_STATUS[CVE-2026-59849] = "fixed-version: fixed in v0.11.5"
> CVE_STATUS[CVE-2026-59850] = "fixed-version: fixed in v0.11.5"
> +CVE_STATUS[CVE-2025-59842] = "cpe-incorrect: the current version (0.11.5) is not affected"
Should this be CVE-2026-59842?
Thanks,
Anuj
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842
2026-09-15 1:29 ` Anuj Mittal
@ 2026-09-15 1:54 ` Ankur Tyagi
0 siblings, 0 replies; 37+ messages in thread
From: Ankur Tyagi @ 2026-09-15 1:54 UTC (permalink / raw)
To: Anuj Mittal; +Cc: openembedded-devel
On Tue, Sep 15, 2026 at 1:29 PM Anuj Mittal
<anuj.mittal@oss.qualcomm.com> wrote:
>
> On Mon, Sep 7, 2026 at 6:24 PM Ankur Tyagi via lists.openembedded.org
> <ankur.tyagi85=gmail.com@lists.openembedded.org> wrote:
> >
> > From: Ankur Tyagi <ankur.tyagi85@gmail.com>
> >
> > The vulnerability exists in GSSAPI key exchange introduced in v0.12.0[1]
> > Also confirmed by libssh security advisory[2]
> >
> > Details:
> > https://nvd.nist.gov/vuln/detail/cve-2026-59842
> >
> > [1]https://gitlab.com/libssh/libssh-mirror/-/commit/88c2ea6752fab7b3da9cc4c51eaf632361a44080
> > [2]https://www.libssh.org/security/advisories/CVE-2026-59842.txt
> >
> > Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
> > ---
> > meta-oe/recipes-support/libssh/libssh_0.11.5.bb | 1 +
> > 1 file changed, 1 insertion(+)
> >
> > diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> > index 752bded528..5cc270477e 100644
> > --- a/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> > +++ b/meta-oe/recipes-support/libssh/libssh_0.11.5.bb
> > @@ -58,3 +58,4 @@ CVE_STATUS[CVE-2026-59847] = "fixed-version: fixed in v0.11.5"
> > CVE_STATUS[CVE-2026-59848] = "fixed-version: fixed in v0.11.5"
> > CVE_STATUS[CVE-2026-59849] = "fixed-version: fixed in v0.11.5"
> > CVE_STATUS[CVE-2026-59850] = "fixed-version: fixed in v0.11.5"
> > +CVE_STATUS[CVE-2025-59842] = "cpe-incorrect: the current version (0.11.5) is not affected"
>
> Should this be CVE-2026-59842?
yep, I'll send v2
>
> Thanks,
>
> Anuj
^ permalink raw reply [flat|nested] 37+ messages in thread
* [oe][meta-networking][wrynose][PATCH 6/33] miniupnpd: patch CVE-2026-5720
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (3 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 5/33] libssh: ignore CVE-2025-59842 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 7/33] python3-zeroconf: patch CVE-2026-47180 ankur.tyagi85
` (26 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-5720
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../miniupnpd/files/CVE-2026-5720.patch | 35 +++++++++++++++++++
.../miniupnpd/miniupnpd_2.1.20191006.bb | 1 +
2 files changed, 36 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/miniupnpd/files/CVE-2026-5720.patch
diff --git a/meta-networking/recipes-connectivity/miniupnpd/files/CVE-2026-5720.patch b/meta-networking/recipes-connectivity/miniupnpd/files/CVE-2026-5720.patch
new file mode 100644
index 0000000000..902e8321a5
--- /dev/null
+++ b/meta-networking/recipes-connectivity/miniupnpd/files/CVE-2026-5720.patch
@@ -0,0 +1,35 @@
+From d1c82ab65fd685ceed28a1a0de29510df7c3c959 Mon Sep 17 00:00:00 2001
+From: Thomas Bernard <miniupnp@free.fr>
+Date: Mon, 23 Mar 2026 02:37:02 +0100
+Subject: [PATCH] upnphttp.c: fix removal of quotes in ParseHttpHeaders()
+
+the length of the string including the quotes must be at
+least 2 for the string to contain the 2 enclosing quotes !
+
+(cherry picked from commit f56bd09b2f2650126b832c5f30a65a09e28167fa)
+
+CVE: CVE-2026-5720
+Upstream-Status: Backport [https://github.com/miniupnp/miniupnp/commit/f56bd09b2f2650126b832c5f30a65a09e28167fa]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ upnphttp.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/upnphttp.c b/upnphttp.c
+index fc8ccb6..2d9a2a6 100644
+--- a/upnphttp.c
++++ b/upnphttp.c
+@@ -269,9 +269,10 @@ ParseHttpHeaders(struct upnphttp * h)
+ p++;
+ while(p[n]>=' ')
+ n++;
+- if((p[0] == '"' && p[n-1] == '"')
+- || (p[0] == '\'' && p[n-1] == '\''))
++ if((n >= 2) && ((p[0] == '"' && p[n-1] == '"')
++ || (p[0] == '\'' && p[n-1] == '\'')))
+ {
++ /* remove the quotes */
+ p++; n -= 2;
+ }
+ h->req_soapActionOff = p - h->req_buf;
diff --git a/meta-networking/recipes-connectivity/miniupnpd/miniupnpd_2.1.20191006.bb b/meta-networking/recipes-connectivity/miniupnpd/miniupnpd_2.1.20191006.bb
index b7ba37f290..0aadfe9cf6 100644
--- a/meta-networking/recipes-connectivity/miniupnpd/miniupnpd_2.1.20191006.bb
+++ b/meta-networking/recipes-connectivity/miniupnpd/miniupnpd_2.1.20191006.bb
@@ -14,6 +14,7 @@ DEPENDS += "iptables net-tools util-linux libmnl libnetfilter-conntrack openssl"
SRC_URI = "http://miniupnp.tuxfamily.org/files/download.php?file=${BP}.tar.gz;downloadfilename=${BP}.tar.gz \
file://miniupnpd.service \
file://0001-Add-OpenEmbedded-cross-compile-case.patch \
+ file://CVE-2026-5720.patch \
"
SRC_URI[sha256sum] = "218fad7af31f3c22fb4c9db28a55a2a8b5067d41f5b38f52008a057a00d2206d"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-python][wrynose][PATCH 7/33] python3-zeroconf: patch CVE-2026-47180
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (4 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 6/33] miniupnpd: patch CVE-2026-5720 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 8/33] python3-zeroconf: patch CVE-2026-47183 ankur.tyagi85
` (25 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-47180
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../python3-zeroconf/CVE-2026-47180.patch | 110 ++++++++++++++++++
.../python/python3-zeroconf_0.148.0.bb | 2 +
2 files changed, 112 insertions(+)
create mode 100644 meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch
new file mode 100644
index 0000000000..7d27066d6f
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47180.patch
@@ -0,0 +1,110 @@
+From 7ffd3a90230cb99bcae4350fcb8498c48044f6a6 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <nick@koston.org>
+Date: Sun, 17 May 2026 19:48:44 -0700
+Subject: [PATCH] fix: bound DNS compression-pointer chain depth in DNSIncoming
+ (#1719)
+
+(cherry picked from commit f9e23592137f30fdf7ef710dba065da31c79b1cf)
+
+CVE: CVE-2026-47180
+Upstream-Status: Backport [https://github.com/python-zeroconf/python-zeroconf/commit/f9e23592137f30fdf7ef710dba065da31c79b1cf]
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/zeroconf/_protocol/incoming.pxd | 2 +-
+ src/zeroconf/_protocol/incoming.py | 14 ++++++++++----
+ tests/test_protocol.py | 22 ++++++++++++++++++++++
+ 3 files changed, 33 insertions(+), 5 deletions(-)
+
+diff --git a/src/zeroconf/_protocol/incoming.pxd b/src/zeroconf/_protocol/incoming.pxd
+index feaa2a0..eface8d 100644
+--- a/src/zeroconf/_protocol/incoming.pxd
++++ b/src/zeroconf/_protocol/incoming.pxd
+@@ -83,7 +83,7 @@ cdef class DNSIncoming:
+ link_py_int=object,
+ linked_labels=cython.list
+ )
+- cdef unsigned int _decode_labels_at_offset(self, unsigned int off, cython.list labels, cython.set seen_pointers)
++ cdef unsigned int _decode_labels_at_offset(self, unsigned int off, cython.list labels, cython.set seen_pointers, unsigned int depth)
+
+ @cython.locals(offset="unsigned int")
+ cdef void _read_header(self)
+diff --git a/src/zeroconf/_protocol/incoming.py b/src/zeroconf/_protocol/incoming.py
+index 2d977b6..d772f47 100644
+--- a/src/zeroconf/_protocol/incoming.py
++++ b/src/zeroconf/_protocol/incoming.py
+@@ -60,7 +60,7 @@ DNS_COMPRESSION_POINTER_LEN = 2
+ MAX_DNS_LABELS = 128
+ MAX_NAME_LENGTH = 253
+
+-DECODE_EXCEPTIONS = (IndexError, struct.error, IncomingDecodeError)
++DECODE_EXCEPTIONS = (IndexError, struct.error, IncomingDecodeError, RecursionError)
+
+
+ _seen_logs: dict[str, int | tuple] = {}
+@@ -409,7 +409,7 @@ class DNSIncoming:
+ labels: list[str] = []
+ seen_pointers: set[int] = set()
+ original_offset = self.offset
+- self.offset = self._decode_labels_at_offset(original_offset, labels, seen_pointers)
++ self.offset = self._decode_labels_at_offset(original_offset, labels, seen_pointers, 0)
+ self._name_cache[original_offset] = labels
+ name = ".".join(labels) + "."
+ if len(name) > MAX_NAME_LENGTH:
+@@ -418,8 +418,14 @@ class DNSIncoming:
+ )
+ return name
+
+- def _decode_labels_at_offset(self, off: _int, labels: list[str], seen_pointers: set[int]) -> int:
++ def _decode_labels_at_offset(
++ self, off: _int, labels: list[str], seen_pointers: set[int], depth: _int
++ ) -> int:
+ # This is a tight loop that is called frequently, small optimizations can make a difference.
++ if depth > MAX_DNS_LABELS:
++ raise IncomingDecodeError(
++ f"DNS compression pointer chain exceeds {MAX_DNS_LABELS} at {off} from {self.source}"
++ )
+ view = self.view
+ while off < self._data_len:
+ length = view[off]
+@@ -457,7 +463,7 @@ class DNSIncoming:
+ if not linked_labels:
+ linked_labels = []
+ seen_pointers.add(link_py_int)
+- self._decode_labels_at_offset(link, linked_labels, seen_pointers)
++ self._decode_labels_at_offset(link, linked_labels, seen_pointers, depth + 1)
+ self._name_cache[link_py_int] = linked_labels
+ labels.extend(linked_labels)
+ if len(labels) > MAX_DNS_LABELS:
+diff --git a/tests/test_protocol.py b/tests/test_protocol.py
+index edd87c2..bac2b44 100644
+--- a/tests/test_protocol.py
++++ b/tests/test_protocol.py
+@@ -1011,6 +1011,28 @@ def test_label_compression_attack():
+ assert len(parsed.answers()) == 1
+
+
++def test_dns_compression_pointer_chain_depth_attack() -> None:
++ """Test our wire parser rejects deeply chained compression pointers without recursing."""
++ # Build a packet with one question whose name is a 1500-deep chain of forward
++ # compression pointers, ending in a root label. Each pointer is 2 bytes,
++ # so chain length easily exceeds CPython's default recursion limit.
++ header = b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
++ # Question at offset 12: pointer to offset 18 (past the question's type/class).
++ question_name = bytes([0xC0, 18])
++ question_type_class = b"\x00\x01\x00\x01"
++ chain_depth = 1500
++ chain = bytearray()
++ for i in range(chain_depth):
++ target = 18 + 2 * (i + 1)
++ chain.append(0xC0 | (target >> 8))
++ chain.append(target & 0xFF)
++ chain.append(0x00)
++ packet = header + question_name + question_type_class + bytes(chain)
++ parsed = r.DNSIncoming(packet, ("1.2.3.4", 5353))
++ assert parsed.valid is False
++ assert parsed.questions == []
++
++
+ def test_dns_compression_loop_attack():
+ """Test our wire parser does not loop forever when dns compression is in a loop."""
+ packet = (
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
index fd083d6ee8..c405e83b8c 100644
--- a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
+++ b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
@@ -5,6 +5,8 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=9fe712b1bc27c5c4e9ecd7f31d208900"
SRC_URI[sha256sum] = "03fcca123df3652e23d945112d683d2f605f313637611b7d4adf31056f681702"
+SRC_URI += "file://CVE-2026-47180.patch"
+
inherit pypi python_poetry_core cython
RDEPENDS:${PN} += " \
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-python][wrynose][PATCH 8/33] python3-zeroconf: patch CVE-2026-47183
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (5 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 7/33] python3-zeroconf: patch CVE-2026-47180 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 9/33] python3-zeroconf: patch CVE-2026-47184 ankur.tyagi85
` (24 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-47183
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../python3-zeroconf/CVE-2026-47183.patch | 401 ++++++++++++++++++
.../python/python3-zeroconf_0.148.0.bb | 4 +-
2 files changed, 404 insertions(+), 1 deletion(-)
create mode 100644 meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47183.patch
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47183.patch b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47183.patch
new file mode 100644
index 0000000000..a1ab4cbfe6
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47183.patch
@@ -0,0 +1,401 @@
+From 831dda6b9d17d6abd6e9501b45ad8b3573a8fab5 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <nick@koston.org>
+Date: Sun, 17 May 2026 20:58:18 -0700
+Subject: [PATCH] fix: bound _seen_logs and stop retaining exc_info (#1717)
+
+(cherry picked from commit 95561e28b24922358f1991e38e3a86d70d72dcec)
+
+CVE: CVE-2026-47183
+Upstream-Status: Backport [https://github.com/python-zeroconf/python-zeroconf/commit/95561e28b24922358f1991e38e3a86d70d72dcec]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/zeroconf/_logger.py | 89 +++++++++++++++++++-----------
+ src/zeroconf/_protocol/incoming.py | 12 +---
+ tests/benchmarks/test_mark_seen.py | 39 +++++++++++++
+ tests/test_logger.py | 87 +++++++++++++++++++++++++++--
+ tests/test_protocol.py | 34 ++++++++++++
+ 5 files changed, 214 insertions(+), 47 deletions(-)
+ create mode 100644 tests/benchmarks/test_mark_seen.py
+
+diff --git a/src/zeroconf/_logger.py b/src/zeroconf/_logger.py
+index 0d734df..99990cf 100644
+--- a/src/zeroconf/_logger.py
++++ b/src/zeroconf/_logger.py
+@@ -25,7 +25,7 @@ from __future__ import annotations
+
+ import logging
+ import sys
+-from typing import Any, ClassVar, cast
++from typing import Any
+
+ log = logging.getLogger(__name__.split(".", maxsplit=1)[0])
+ log.addHandler(logging.NullHandler())
+@@ -39,50 +39,73 @@ def set_logger_level_if_unset() -> None:
+ set_logger_level_if_unset()
+
+
+-class QuietLogger:
+- _seen_logs: ClassVar[dict[str, int | tuple]] = {}
++_MAX_SEEN_LOGS = 512
++_seen_logs: dict[str, None] = {}
++
++
++def _evict_oldest(seen: dict[str, None]) -> bool:
++ """Pop the oldest entry from ``seen``; return False if it raced.
++
++ Individual dict ops (``pop`` with a default, ``next``) are atomic
++ on the free-threaded build, but the compound ``iter`` → ``next``
++ used to pick the FIFO victim can raise ``RuntimeError`` if
++ another thread mutates the dict between the two ops. The caller
++ breaks its drain loop on False so concurrent mutation can't make
++ it spin.
++ """
++ try:
++ seen.pop(next(iter(seen)), None)
++ except (RuntimeError, StopIteration):
++ return False
++ return True
++
++
++def _mark_seen(seen: dict[str, None], key: str) -> bool:
++ """Record ``key`` in ``seen`` and return True if it was newly added.
++
++ Bounds the dict so callers passing attacker-influenced keys (peer
++ addresses, packet offsets) cannot grow it without bound. Evicts
++ the oldest entries on overflow (dict preserves insertion order on
++ Python 3.7+), so ``_MAX_SEEN_LOGS`` is a recency window.
++
++ The dict is shared across all ``Zeroconf`` instances in the
++ process; on the free-threaded build (3.14t) and under multi-
++ instance sync use, callers can race the ``len < cap`` check and
++ both insert, leaving the dict transiently above the cap. The
++ drain loop runs on every call (steady-state-at-cap hits are a
++ single ``len`` + compare past the membership check because the
++ helper short-circuits) so a contention burst is corrected by the
++ next caller regardless of whether it's a hit or a miss.
++ """
++ inserting = key not in seen
++ # Hit (``inserting`` is False): drain only if drifted above cap.
++ # Miss (``inserting`` is True): drain to ``cap - 1`` to make room
++ # for the new key. Bool subtracts as 0/1 to pick the right limit.
++ while len(seen) > _MAX_SEEN_LOGS - inserting and _evict_oldest(seen):
++ pass
++ if inserting:
++ seen[key] = None
++ return inserting
++
+
++class QuietLogger:
+ @classmethod
+ def log_exception_warning(cls, *logger_data: Any) -> None:
+- exc_info = sys.exc_info()
+- exc_str = str(exc_info[1])
+- if exc_str not in cls._seen_logs:
+- # log at warning level the first time this is seen
+- cls._seen_logs[exc_str] = exc_info
+- logger = log.warning
+- else:
+- logger = log.debug
++ first_time = _mark_seen(_seen_logs, str(sys.exc_info()[1]))
++ logger = log.warning if first_time else log.debug
+ logger(*(logger_data or ["Exception occurred"]), exc_info=True)
+
+ @classmethod
+ def log_exception_debug(cls, *logger_data: Any) -> None:
+- log_exc_info = False
+- exc_info = sys.exc_info()
+- exc_str = str(exc_info[1])
+- if exc_str not in cls._seen_logs:
+- # log the trace only on the first time
+- cls._seen_logs[exc_str] = exc_info
+- log_exc_info = True
+- log.debug(*(logger_data or ["Exception occurred"]), exc_info=log_exc_info)
++ first_time = _mark_seen(_seen_logs, str(sys.exc_info()[1]))
++ log.debug(*(logger_data or ["Exception occurred"]), exc_info=first_time)
+
+ @classmethod
+ def log_warning_once(cls, *args: Any) -> None:
+- msg_str = args[0]
+- if msg_str not in cls._seen_logs:
+- cls._seen_logs[msg_str] = 0
+- logger = log.warning
+- else:
+- logger = log.debug
+- cls._seen_logs[msg_str] = cast(int, cls._seen_logs[msg_str]) + 1
++ logger = log.warning if _mark_seen(_seen_logs, args[0]) else log.debug
+ logger(*args)
+
+ @classmethod
+ def log_exception_once(cls, exc: Exception, *args: Any) -> None:
+- msg_str = args[0]
+- if msg_str not in cls._seen_logs:
+- cls._seen_logs[msg_str] = 0
+- logger = log.warning
+- else:
+- logger = log.debug
+- cls._seen_logs[msg_str] = cast(int, cls._seen_logs[msg_str]) + 1
++ logger = log.warning if _mark_seen(_seen_logs, args[0]) else log.debug
+ logger(*args, exc_info=exc)
+diff --git a/src/zeroconf/_protocol/incoming.py b/src/zeroconf/_protocol/incoming.py
+index d772f47..ffbbb59 100644
+--- a/src/zeroconf/_protocol/incoming.py
++++ b/src/zeroconf/_protocol/incoming.py
+@@ -37,7 +37,7 @@ from .._dns import (
+ DNSText,
+ )
+ from .._exceptions import IncomingDecodeError
+-from .._logger import log
++from .._logger import _mark_seen, log
+ from .._utils.time import current_time_millis
+ from ..const import (
+ _FLAGS_QR_MASK,
+@@ -63,7 +63,7 @@ MAX_NAME_LENGTH = 253
+ DECODE_EXCEPTIONS = (IndexError, struct.error, IncomingDecodeError, RecursionError)
+
+
+-_seen_logs: dict[str, int | tuple] = {}
++_seen_logs: dict[str, None] = {}
+ _str = str
+ _int = int
+
+@@ -182,13 +182,7 @@ class DNSIncoming:
+
+ @classmethod
+ def _log_exception_debug(cls, *logger_data: Any) -> None:
+- log_exc_info = False
+- exc_info = sys.exc_info()
+- exc_str = str(exc_info[1])
+- if exc_str not in _seen_logs:
+- # log the trace only on the first time
+- _seen_logs[exc_str] = exc_info
+- log_exc_info = True
++ log_exc_info = _mark_seen(_seen_logs, str(sys.exc_info()[1]))
+ log.debug(*(logger_data or ["Exception occurred"]), exc_info=log_exc_info)
+
+ def answers(self) -> list[DNSRecord]:
+diff --git a/tests/benchmarks/test_mark_seen.py b/tests/benchmarks/test_mark_seen.py
+new file mode 100644
+index 0000000..4f82da8
+--- /dev/null
++++ b/tests/benchmarks/test_mark_seen.py
+@@ -0,0 +1,39 @@
++"""Benchmark for _logger._mark_seen."""
++
++from __future__ import annotations
++
++from pytest_codspeed import BenchmarkFixture
++
++from zeroconf._logger import _MAX_SEEN_LOGS, _mark_seen
++
++
++def test_mark_seen_hit(benchmark: BenchmarkFixture) -> None:
++ """Benchmark the cache-hit path (same key repeated)."""
++ seen: dict[str, None] = {"warm": None}
++
++ @benchmark
++ def _hit() -> None:
++ for _ in range(1000):
++ _mark_seen(seen, "warm")
++
++
++def test_mark_seen_fill(benchmark: BenchmarkFixture) -> None:
++ """Benchmark filling from empty up to the cap (no evictions)."""
++ keys = [f"key-{i}" for i in range(_MAX_SEEN_LOGS)]
++
++ @benchmark
++ def _fill() -> None:
++ seen: dict[str, None] = {}
++ for k in keys:
++ _mark_seen(seen, k)
++
++
++def test_mark_seen_churn(benchmark: BenchmarkFixture) -> None:
++ """Benchmark sustained eviction (every call past the cap drops oldest)."""
++ keys = [f"churn-{i}" for i in range(_MAX_SEEN_LOGS * 4)]
++
++ @benchmark
++ def _churn() -> None:
++ seen: dict[str, None] = {}
++ for k in keys:
++ _mark_seen(seen, k)
+diff --git a/tests/test_logger.py b/tests/test_logger.py
+index 4e09aa3..8042e49 100644
+--- a/tests/test_logger.py
++++ b/tests/test_logger.py
+@@ -5,7 +5,8 @@ from __future__ import annotations
+ import logging
+ from unittest.mock import call, patch
+
+-from zeroconf._logger import QuietLogger, set_logger_level_if_unset
++from zeroconf import _logger
++from zeroconf._logger import _MAX_SEEN_LOGS, QuietLogger, _mark_seen, set_logger_level_if_unset
+
+
+ def test_loading_logger():
+@@ -25,7 +26,7 @@ def test_loading_logger():
+
+ def test_log_warning_once():
+ """Test we only log with warning level once."""
+- QuietLogger._seen_logs = {}
++ _logger._seen_logs.clear()
+ quiet_logger = QuietLogger()
+ with (
+ patch("zeroconf._logger.log.warning") as mock_log_warning,
+@@ -48,7 +49,7 @@ def test_log_warning_once():
+
+ def test_log_exception_warning():
+ """Test we only log with warning level once."""
+- QuietLogger._seen_logs = {}
++ _logger._seen_logs.clear()
+ quiet_logger = QuietLogger()
+ with (
+ patch("zeroconf._logger.log.warning") as mock_log_warning,
+@@ -71,7 +72,7 @@ def test_log_exception_warning():
+
+ def test_llog_exception_debug():
+ """Test we only log with a trace once."""
+- QuietLogger._seen_logs = {}
++ _logger._seen_logs.clear()
+ quiet_logger = QuietLogger()
+ with patch("zeroconf._logger.log.debug") as mock_log_debug:
+ quiet_logger.log_exception_debug("the exception")
+@@ -84,9 +85,85 @@ def test_llog_exception_debug():
+ assert mock_log_debug.mock_calls == [call("the exception", exc_info=False)]
+
+
++def test_mark_seen_absorbs_runtime_error_during_eviction() -> None:
++ """Concurrent mutation can make ``iter(seen)`` raise ``RuntimeError``.
++
++ Free-threaded (3.14t) and multi-instance sync callers share
++ ``_seen_logs``; if another thread mutates it between ``iter()``
++ and ``next()`` the iterator raises ``RuntimeError``.
++ ``_mark_seen`` must absorb that and still insert the new key.
++ """
++
++ class RacyDict(dict[str, None]):
++ def __iter__(self): # type: ignore[override]
++ raise RuntimeError("dictionary changed size during iteration")
++
++ seen: dict[str, None] = RacyDict()
++ for i in range(_MAX_SEEN_LOGS):
++ seen[f"k-{i}"] = None
++ assert _mark_seen(seen, "new-key") is True
++ assert "new-key" in seen
++
++
++def test_mark_seen_drains_drift_above_cap() -> None:
++ """``_mark_seen`` drains a drifted-over-cap dict back to the cap.
++
++ Concurrent inserts on the free-threaded build can leave the dict
++ transiently above ``_MAX_SEEN_LOGS`` (e.g. two threads both passed
++ the ``len < cap`` check and both inserted). The next non-racing
++ call must drain the accumulated overshoot, not just evict one
++ entry — otherwise the cap silently inflates with thread count.
++ """
++ seen: dict[str, None] = {}
++ drift = 10
++ for i in range(_MAX_SEEN_LOGS + drift):
++ seen[f"k-{i}"] = None
++ assert len(seen) == _MAX_SEEN_LOGS + drift
++ assert _mark_seen(seen, "new-key") is True
++ assert len(seen) == _MAX_SEEN_LOGS
++ assert "new-key" in seen
++ for i in range(drift + 1):
++ assert f"k-{i}" not in seen
++
++
++def test_mark_seen_drains_drift_on_hit_path() -> None:
++ """``_mark_seen`` drains drift even when ``key`` is already cached.
++
++ A hit-heavy workload after a contention burst (e.g. the same
++ exception text deduplicated repeatedly) must still correct the
++ overshoot — otherwise the dict can sit permanently above the cap
++ until a miss happens to come along.
++ """
++ seen: dict[str, None] = {}
++ drift = 10
++ for i in range(_MAX_SEEN_LOGS + drift):
++ seen[f"k-{i}"] = None
++ # Hit on a non-oldest key — survives the drift drain.
++ hit_key = f"k-{_MAX_SEEN_LOGS}"
++ assert _mark_seen(seen, hit_key) is False
++ assert len(seen) == _MAX_SEEN_LOGS
++ assert hit_key in seen
++ for i in range(drift):
++ assert f"k-{i}" not in seen
++
++
++def test_seen_logs_is_bounded() -> None:
++ """``_seen_logs`` stays at the cap and evicts oldest-first (FIFO)."""
++ _logger._seen_logs.clear()
++ overflow = 5
++ with patch("zeroconf._logger.log.warning"), patch("zeroconf._logger.log.debug"):
++ for i in range(_MAX_SEEN_LOGS + overflow):
++ QuietLogger.log_warning_once(f"warning-{i}")
++ assert len(_logger._seen_logs) == _MAX_SEEN_LOGS
++ for i in range(overflow):
++ assert f"warning-{i}" not in _logger._seen_logs
++ for i in range(_MAX_SEEN_LOGS, _MAX_SEEN_LOGS + overflow):
++ assert f"warning-{i}" in _logger._seen_logs
++
++
+ def test_log_exception_once():
+ """Test we only log with warning level once."""
+- QuietLogger._seen_logs = {}
++ _logger._seen_logs.clear()
+ quiet_logger = QuietLogger()
+ exc = Exception()
+ with (
+diff --git a/tests/test_protocol.py b/tests/test_protocol.py
+index bac2b44..782b77a 100644
+--- a/tests/test_protocol.py
++++ b/tests/test_protocol.py
+@@ -14,6 +14,8 @@ import pytest
+
+ import zeroconf as r
+ from zeroconf import DNSHinfo, DNSIncoming, DNSText, const, current_time_millis
++from zeroconf._logger import _MAX_SEEN_LOGS
++from zeroconf._protocol import incoming as _incoming_module
+
+ from . import has_working_ipv6
+
+@@ -962,6 +964,38 @@ def test_dns_compression_generic_failure(caplog):
+ assert "Received invalid packet from ('1.2.3.4', 5353)" in caplog.text
+
+
++def test_seen_logs_is_bounded():
++ """Corrupt packets from varying peers fill ``_seen_logs`` exactly to the cap."""
++ packet = (
++ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06domain\x05local\x00\x00\x01"
++ b"\x80\x01\x00\x00\x00\x01\x00\x04\xc0\xa8\xd0\x05-\x0c\x00\x01\x80\x01\x00\x00"
++ b"\x00\x01\x00\x04\xc0\xa8\xd0\x06"
++ )
++ overflow = 5
++ _incoming_module._seen_logs.clear()
++ # Snapshot the actual key the parser inserted per port. This is whatever
++ # ``str(exc_info()[1])`` produces today — the test stays agnostic to the
++ # exception text format so a future normalization of the message (see
++ # the discussion on #1714) doesn't break the assertions, while still
++ # pinning that the parser exception path actually entered the dict.
++ keys_per_port: list[str] = []
++ for port in range(_MAX_SEEN_LOGS + overflow):
++ r.DNSIncoming(packet, ("1.2.3.4", port))
++ keys_per_port.append(next(reversed(_incoming_module._seen_logs)))
++ # Bound is hit exactly.
++ assert len(_incoming_module._seen_logs) == _MAX_SEEN_LOGS
++ # Each port produced a distinct dedup key — a regression that dropped
++ # the per-packet-varying component (e.g. self.source) from the exception
++ # text would collapse all 517 calls to one key and fail this.
++ assert len(set(keys_per_port)) == _MAX_SEEN_LOGS + overflow
++ # FIFO eviction by key identity (no substring matching on the message
++ # format): the earliest ports' keys are gone, the latest ports' remain.
++ for port in range(overflow):
++ assert keys_per_port[port] not in _incoming_module._seen_logs
++ for port in range(_MAX_SEEN_LOGS, _MAX_SEEN_LOGS + overflow):
++ assert keys_per_port[port] in _incoming_module._seen_logs
++
++
+ def test_label_length_attack():
+ """Test our wire parser does not loop forever when the name exceeds 253 chars."""
+ packet = (
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
index c405e83b8c..aa1bb11065 100644
--- a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
+++ b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
@@ -5,7 +5,9 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=9fe712b1bc27c5c4e9ecd7f31d208900"
SRC_URI[sha256sum] = "03fcca123df3652e23d945112d683d2f605f313637611b7d4adf31056f681702"
-SRC_URI += "file://CVE-2026-47180.patch"
+SRC_URI += "file://CVE-2026-47180.patch \
+ file://CVE-2026-47183.patch \
+"
inherit pypi python_poetry_core cython
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-python][wrynose][PATCH 9/33] python3-zeroconf: patch CVE-2026-47184
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (6 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 8/33] python3-zeroconf: patch CVE-2026-47183 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 10/33] valkey: mark CVE-2026-56684 and CVE-2026-63639 patched ankur.tyagi85
` (23 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-47184
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../python3-zeroconf/CVE-2026-47184.patch | 558 ++++++++++++++++++
.../python/python3-zeroconf_0.148.0.bb | 1 +
2 files changed, 559 insertions(+)
create mode 100644 meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47184.patch
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47184.patch b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47184.patch
new file mode 100644
index 0000000000..73d4c2bd9c
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-zeroconf/CVE-2026-47184.patch
@@ -0,0 +1,558 @@
+From 61d9e147da48ffb5f660e639f6d457e5cda008d4 Mon Sep 17 00:00:00 2001
+From: "J. Nick Koston" <nick@koston.org>
+Date: Sun, 17 May 2026 21:48:40 -0700
+Subject: [PATCH] fix: bound DNSCache record count to prevent unbounded
+ LAN-driven growth (#1718)
+
+(cherry picked from commit 0ad3f37b5b852b8f614d322283d148efb2cef6e4)
+
+CVE: CVE-2026-47184
+Upstream-Status: Backport [https://github.com/python-zeroconf/python-zeroconf/commit/0ad3f37b5b852b8f614d322283d148efb2cef6e4]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/zeroconf/_cache.pxd | 11 +-
+ src/zeroconf/_cache.py | 79 +++++----
+ src/zeroconf/const.py | 6 +
+ tests/benchmarks/test_cache_bound.py | 68 ++++++++
+ tests/test_cache.py | 241 ++++++++++++++++++++++++++-
+ 5 files changed, 374 insertions(+), 31 deletions(-)
+ create mode 100644 tests/benchmarks/test_cache_bound.py
+
+diff --git a/src/zeroconf/_cache.pxd b/src/zeroconf/_cache.pxd
+index 05a40c0..023304b 100644
+--- a/src/zeroconf/_cache.pxd
++++ b/src/zeroconf/_cache.pxd
+@@ -19,6 +19,7 @@ cdef object _UNIQUE_RECORD_TYPES
+ cdef unsigned int _TYPE_PTR
+ cdef cython.uint _ONE_SECOND
+ cdef unsigned int _MIN_SCHEDULED_RECORD_EXPIRATION
++cdef unsigned int _MAX_CACHE_RECORDS
+
+
+ @cython.locals(record_cache=dict)
+@@ -31,6 +32,7 @@ cdef class DNSCache:
+ cdef public cython.dict service_cache
+ cdef public list _expire_heap
+ cdef public dict _expirations
++ cdef public unsigned int _total_records
+
+ cpdef bint async_add_records(self, object entries)
+
+@@ -60,10 +62,17 @@ cdef class DNSCache:
+ service_store=cython.dict,
+ service_record=DNSService,
+ when=object,
+- new=bint
++ new=bint,
++ is_new=bint
+ )
+ cdef bint _async_add(self, DNSRecord record)
+
++ @cython.locals(record=DNSRecord, when_record=tuple)
++ cdef void _async_evict_oldest(self)
++
++ @cython.locals(expire_heap_len="unsigned int")
++ cdef void _maybe_rebuild_heap(self)
++
+ @cython.locals(service_record=DNSService)
+ cdef void _async_remove(self, DNSRecord record)
+
+diff --git a/src/zeroconf/_cache.py b/src/zeroconf/_cache.py
+index c7ca847..d323a6a 100644
+--- a/src/zeroconf/_cache.py
++++ b/src/zeroconf/_cache.py
+@@ -37,7 +37,7 @@ from ._dns import (
+ DNSText,
+ )
+ from ._utils.time import current_time_millis
+-from .const import _ONE_SECOND, _TYPE_PTR
++from .const import _MAX_CACHE_RECORDS, _ONE_SECOND, _TYPE_PTR
+
+ _UNIQUE_RECORD_TYPES = (DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService)
+ _UniqueRecordsType = Union[DNSAddress, DNSHinfo, DNSPointer, DNSText, DNSService]
+@@ -72,6 +72,7 @@ class DNSCache:
+ self._expire_heap: list[tuple[float, DNSRecord]] = []
+ self._expirations: dict[DNSRecord, float] = {}
+ self.service_cache: _DNSRecordCacheType = {}
++ self._total_records: int = 0
+
+ # Functions prefixed with async_ are NOT threadsafe and must
+ # be run in the event loop.
+@@ -89,15 +90,34 @@ class DNSCache:
+ # replaces any existing records that are __eq__ to each other which
+ # removes the risk that accessing the cache from the wrong
+ # direction would return the old incorrect entry.
+- if (store := self.cache.get(record.key)) is None:
++ store = self.cache.get(record.key)
++ is_new = store is None or record not in store
++ # Bound total cache size; evict closest-to-expiration entry to
++ # make room before inserting a new record. Prevents a LAN-local
++ # flood of unique-name records from growing the cache without
++ # bound (RFC 6762 §10 advisory caching, defense-in-depth).
++ if is_new and self._total_records >= _MAX_CACHE_RECORDS:
++ self._async_evict_oldest()
++ # The victim may have been the last record under
++ # ``record.key``, in which case ``_remove_key`` deleted
++ # the bucket. Re-fetch before creating below.
++ store = self.cache.get(record.key)
++ if store is None:
+ store = self.cache[record.key] = {}
+- new = record not in store and not isinstance(record, DNSNsec)
++ new = is_new and not isinstance(record, DNSNsec)
++ if is_new:
++ self._total_records += 1
+ store[record] = record
+ when = record.created + (record.ttl * 1000)
+ if self._expirations.get(record) != when:
+- # Avoid adding duplicates to the heap
+ heappush(self._expire_heap, (when, record))
+ self._expirations[record] = when
++ # Re-adds of an existing record with a new TTL push a fresh
++ # entry but leave the prior tuple behind as stale, so a peer
++ # that just replays cached records can grow ``_expire_heap``
++ # without ever tripping the cap. Rebuild when stale entries
++ # dominate.
++ self._maybe_rebuild_heap()
+
+ if isinstance(record, DNSService):
+ service_record = record
+@@ -106,6 +126,28 @@ class DNSCache:
+ service_store[service_record] = service_record
+ return new
+
++ def _async_evict_oldest(self) -> None:
++ """Drop the closest-to-expiration record to make room for a new one."""
++ while self._expire_heap:
++ when_record = heappop(self._expire_heap)
++ record = when_record[1]
++ if self._expirations.get(record) != when_record[0]:
++ continue
++ self._async_remove(record)
++ return
++
++ def _maybe_rebuild_heap(self) -> None:
++ """Rebuild ``_expire_heap`` when stale entries dominate live ones."""
++ expire_heap_len = len(self._expire_heap)
++ if (
++ expire_heap_len > _MIN_SCHEDULED_RECORD_EXPIRATION
++ and expire_heap_len > len(self._expirations) * 2
++ ):
++ self._expire_heap = [
++ entry for entry in self._expire_heap if self._expirations.get(entry[1]) == entry[0]
++ ]
++ heapify(self._expire_heap)
++
+ def async_add_records(self, entries: Iterable[DNSRecord]) -> bool:
+ """Add multiple records.
+
+@@ -129,6 +171,7 @@ class DNSCache:
+ _remove_key(self.service_cache, service_record.server_key, service_record)
+ _remove_key(self.cache, record.key, record)
+ self._expirations.pop(record, None)
++ self._total_records -= 1
+
+ def async_remove_records(self, entries: Iterable[DNSRecord]) -> None:
+ """Remove multiple records.
+@@ -145,43 +188,23 @@ class DNSCache:
+
+ :param now: The current time in milliseconds.
+ """
+- if not (expire_heap_len := len(self._expire_heap)):
++ if not self._expire_heap:
+ return []
+
+ expired: list[DNSRecord] = []
+- # Find any expired records and add them to the to-delete list
+ while self._expire_heap:
+ when_record = self._expire_heap[0]
+ when = when_record[0]
+ if when > now:
+ break
+ heappop(self._expire_heap)
+- # Check if the record hasn't been re-added to the heap
+- # with a different expiration time as it will be removed
+- # later when it reaches the top of the heap and its
+- # expiration time is met.
++ # Skip entries left behind by a TTL re-add; the live tuple is
++ # later in the heap and will be removed when it reaches the top.
+ record = when_record[1]
+ if self._expirations.get(record) == when:
+ expired.append(record)
+
+- # If the expiration heap grows larger than the number expirations
+- # times two, we clean it up to avoid keeping expired entries in
+- # the heap and consuming memory. We guard this with a minimum
+- # threshold to avoid cleaning up the heap too often when there are
+- # only a few scheduled expirations.
+- if (
+- expire_heap_len > _MIN_SCHEDULED_RECORD_EXPIRATION
+- and expire_heap_len > len(self._expirations) * 2
+- ):
+- # Remove any expired entries from the expiration heap
+- # that do not match the expiration time in the expirations
+- # as it means the record has been re-added to the heap
+- # with a different expiration time.
+- self._expire_heap = [
+- entry for entry in self._expire_heap if self._expirations.get(entry[1]) == entry[0]
+- ]
+- heapify(self._expire_heap)
+-
++ self._maybe_rebuild_heap()
+ self.async_remove_records(expired)
+ return expired
+
+diff --git a/src/zeroconf/const.py b/src/zeroconf/const.py
+index 1db39a4..a17e468 100644
+--- a/src/zeroconf/const.py
++++ b/src/zeroconf/const.py
+@@ -59,6 +59,12 @@ _DNS_OTHER_TTL = 4500 # 75 minutes for non-host records (PTR, TXT etc) as-per R
+ # level of rate limit and safe guards so we use 1/4 of the recommended value
+ _DNS_PTR_MIN_TTL = 1125
+
++# Upper bound on the number of records the DNSCache will hold before it
++# starts evicting the closest-to-expiration entry to make room for new
++# arrivals. Bounds the memory a malicious LAN peer can force the cache
++# to retain by multicasting many unique-name records.
++_MAX_CACHE_RECORDS = 10000
++
+ _DNS_PACKET_HEADER_LEN = 12
+
+ _MAX_MSG_TYPICAL = 1460 # unused
+diff --git a/tests/benchmarks/test_cache_bound.py b/tests/benchmarks/test_cache_bound.py
+new file mode 100644
+index 0000000..774129e
+--- /dev/null
++++ b/tests/benchmarks/test_cache_bound.py
+@@ -0,0 +1,68 @@
++"""Benchmark for the DNSCache record-count bound + overflow eviction."""
++
++from __future__ import annotations
++
++from collections.abc import Iterator
++from itertools import count
++
++from pytest_codspeed import BenchmarkFixture
++
++from zeroconf import DNSAddress, DNSCache, current_time_millis
++from zeroconf.const import _CLASS_IN, _MAX_CACHE_RECORDS, _TYPE_A
++
++
++def _make_records(count_: int, now: float, prefix: str = "bench") -> list[DNSAddress]:
++ return [
++ DNSAddress(
++ f"{prefix}-{i}.local.",
++ _TYPE_A,
++ _CLASS_IN,
++ 120,
++ bytes(((i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF)),
++ created=now + i,
++ )
++ for i in range(count_)
++ ]
++
++
++def _unbounded_records(now: float, prefix: str = "evict") -> Iterator[DNSAddress]:
++ """Unbounded generator of unique-name DNSAddress records."""
++ for i in count():
++ yield DNSAddress(
++ f"{prefix}-{i}.local.",
++ _TYPE_A,
++ _CLASS_IN,
++ 120,
++ bytes(((i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF)),
++ created=now + i,
++ )
++
++
++def test_cache_add_below_cap(benchmark: BenchmarkFixture) -> None:
++ """Adding records while the cache is well below the cap (no eviction)."""
++ now = current_time_millis()
++ records = _make_records(1000, now)
++
++ @benchmark
++ def _add() -> None:
++ cache = DNSCache()
++ cache.async_add_records(records)
++
++
++def test_cache_add_at_cap_evicts(benchmark: BenchmarkFixture) -> None:
++ """Steady-state add at the cap: every measured insert forces one eviction.
++
++ Pre-fills the cache to ``_MAX_CACHE_RECORDS`` outside the timed body so
++ only the eviction-path adds are measured. Each benchmark iteration
++ pulls one fresh unique record from an unbounded generator, keeping the
++ cache permanently at the cap. The generator avoids the iteration-count
++ cap that a pre-built pool would impose for very fast operations.
++ """
++ now = current_time_millis()
++ cache = DNSCache()
++ cache.async_add_records(_make_records(_MAX_CACHE_RECORDS, now, prefix="fill"))
++ pool = _unbounded_records(now + _MAX_CACHE_RECORDS)
++
++ @benchmark
++ def _evict_one() -> None:
++ cache.async_add_records([next(pool)])
+diff --git a/tests/test_cache.py b/tests/test_cache.py
+index 9d55435..aeb3a2a 100644
+--- a/tests/test_cache.py
++++ b/tests/test_cache.py
+@@ -439,7 +439,9 @@ async def test_cache_heap_multi_name_cleanup() -> None:
+ )
+ cache.async_add_records([record])
+
+- assert len(cache._expire_heap) == min_records_to_cleanup + 5
++ # ``_async_add`` rebuilds ``_expire_heap`` proactively when stale entries
++ # dominate (heap > 2x expirations), so the heap is already capped at
++ # ~one entry per unique record long before ``async_expire`` is called.
+ assert len(cache.async_entries_with_name(name)) == 1
+ assert len(cache.async_entries_with_name(name2)) == 5
+
+@@ -473,7 +475,8 @@ async def test_cache_heap_pops_order() -> None:
+ )
+ cache.async_add_records([record])
+
+- assert len(cache._expire_heap) == min_records_to_cleanup + 5
++ # ``_async_add`` proactively rebuilds the heap when stale entries dominate,
++ # so the heap holds only one entry per unique record by this point.
+ assert len(cache.async_entries_with_name(name)) == 1
+ assert len(cache.async_entries_with_name(name2)) == 5
+
+@@ -482,3 +485,237 @@ async def test_cache_heap_pops_order() -> None:
+ ts, _ = heappop(cache._expire_heap)
+ assert ts >= start_ts
+ start_ts = ts
++
++
++def _addr(name: str, idx: int, *, ttl: int = 120, created: float | None = None) -> r.DNSAddress:
++ """Build a DNSAddress with idx-derived payload for the bound/eviction tests."""
++ return r.DNSAddress(
++ name,
++ const._TYPE_A,
++ const._CLASS_IN,
++ ttl,
++ bytes((idx & 0xFF, (idx >> 8) & 0xFF, 0, 1)),
++ created=r.current_time_millis() if created is None else created,
++ )
++
++
++def test_cache_size_is_bounded() -> None:
++ """A flood of unique-name records is capped at ``_MAX_CACHE_RECORDS``."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ overflow = 1000
++ flood_size = const._MAX_CACHE_RECORDS + overflow
++
++ cache.async_add_records(_addr(f"flood-{i}.local.", i, created=now + i) for i in range(flood_size))
++
++ total = sum(len(store) for store in cache.cache.values())
++ assert total == const._MAX_CACHE_RECORDS
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++ # FIFO-ish: the earliest-created records (closest to expiration) get
++ # evicted first, so the names that remain are from the tail.
++ for i in range(overflow):
++ assert f"flood-{i}.local." not in cache.cache
++ for i in range(flood_size - overflow, flood_size):
++ assert f"flood-{i}.local." in cache.cache
++
++
++def test_cache_eviction_empty_heap_returns_without_evicting() -> None:
++ """Eviction tolerates an empty ``_expire_heap`` (invariant-violation safety net)."""
++ cache = r.DNSCache()
++ # By the cache invariant every record in ``_total_records`` has a heap
++ # entry, so eviction should never see an empty heap. Force the broken
++ # state directly to pin the defensive behaviour: ``_async_evict_oldest``
++ # returns without raising and the subsequent insert still lands. Since
++ # eviction can't free space, the counter is allowed to drift past the
++ # cap by exactly one — pinned so a future change to the recovery
++ # semantics (e.g., refusing the add or clamping) fails this test.
++ cache._total_records = const._MAX_CACHE_RECORDS
++ cache._expire_heap = []
++ cache.async_add_records([_addr("post-empty.local.", 0)])
++ assert "post-empty.local." in cache.cache
++ assert cache._total_records == const._MAX_CACHE_RECORDS + 1
++
++
++def test_cache_eviction_skips_stale_heap_entries() -> None:
++ """Eviction skips stale heap entries left by TTL re-adds."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ cache.async_add_records(
++ _addr(f"stale-{i}.local.", i, created=now + i) for i in range(const._MAX_CACHE_RECORDS)
++ )
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++
++ # Re-add the closest-to-expiration record with a longer TTL; the prior
++ # ``(when, record)`` tuple stays as stale, eviction must skip it.
++ victim_name = "stale-0.local."
++ cache.async_add_records([_addr(victim_name, 0, ttl=7200, created=now)])
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++
++ cache.async_add_records([_addr("trigger.local.", 0xFFFF, created=now + const._MAX_CACHE_RECORDS)])
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++ assert victim_name in cache.cache
++ assert "stale-1.local." not in cache.cache
++
++
++def test_cache_eviction_victim_shares_key_with_new_record() -> None:
++ """Inserting a record whose key collides with the eviction victim keeps it reachable."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ cache.async_add_records(
++ _addr(f"filler-{i}.local.", i, created=now + 1000 + i) for i in range(const._MAX_CACHE_RECORDS - 1)
++ )
++
++ # Insert at "shared.local." with the earliest expiration so eviction
++ # picks it. ``_remove_key`` then deletes ``cache["shared.local."]``.
++ shared_key = "shared.local."
++ cache.async_add_records([_addr(shared_key, 0x0102, created=now)])
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++
++ # Adding a new record under the SAME key: a pre-eviction-captured
++ # ``store`` would write into an orphaned dict; the fix re-resolves.
++ new_shared = _addr(shared_key, 0x0506, created=now + 999)
++ cache.async_add_records([new_shared])
++
++ assert shared_key in cache.cache, "new record orphaned: cache bucket missing"
++ assert new_shared in cache.cache[shared_key]
++ assert cache.async_get_unique(new_shared) == new_shared
++ total = sum(len(store) for store in cache.cache.values())
++ assert total == cache._total_records
++
++
++def test_cache_dnsnsec_at_cap_evicts_prior_record() -> None:
++ """A single DNSNsec arriving at the cap evicts one prior record and stays reachable."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ cache.async_add_records(
++ _addr(f"fill-{i}.local.", i, created=now + i) for i in range(const._MAX_CACHE_RECORDS)
++ )
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++
++ nsec = r.DNSNsec(
++ "nsec-arrival.local.",
++ const._TYPE_NSEC,
++ const._CLASS_IN,
++ 120,
++ "nsec-arrival.local.",
++ [const._TYPE_A],
++ )
++ cache.async_add_records([nsec])
++
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++ assert nsec in cache.cache[nsec.key]
++ # The earliest-created fill record is gone (FIFO-ish eviction).
++ assert "fill-0.local." not in cache.cache
++
++
++def test_cache_dnsnsec_flood_is_bounded() -> None:
++ """DNSNsec records honour ``_MAX_CACHE_RECORDS`` (no bypass via the ``new`` flag)."""
++ cache = r.DNSCache()
++ overflow = 100
++ cache.async_add_records(
++ r.DNSNsec(
++ f"nsec-{i}.local.",
++ const._TYPE_NSEC,
++ const._CLASS_IN,
++ 120,
++ f"nsec-{i}.local.",
++ [const._TYPE_A],
++ )
++ for i in range(const._MAX_CACHE_RECORDS + overflow)
++ )
++ assert cache._total_records == const._MAX_CACHE_RECORDS
++ total = sum(len(store) for store in cache.cache.values())
++ assert total == const._MAX_CACHE_RECORDS
++
++
++def test_cache_re_add_flood_does_not_grow_heap_unbounded() -> None:
++ """Replaying cached records with shifting TTLs cannot grow ``_expire_heap`` unbounded."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ # Stay below the cache cap so eviction never fires; the attack here is
++ # heap growth via re-add, not cap saturation. Clear the
++ # ``_MIN_SCHEDULED_RECORD_EXPIRATION`` floor so the rebuild engages.
++ record_count = 200
++ cache.async_add_records(_addr(f"flood-{i}.local.", i, created=now) for i in range(record_count))
++ assert cache._total_records == record_count
++
++ # 10 cycles x ``record_count`` stale pushes each. Without
++ # ``_maybe_rebuild_heap`` firing inside ``_async_add``, the heap would
++ # grow to ~11 x record_count.
++ for cycle in range(10):
++ cache.async_add_records(
++ _addr(f"flood-{i}.local.", i, ttl=7200 + cycle, created=now) for i in range(record_count)
++ )
++
++ # Heap is bounded near the rebuild threshold; ``+ record_count`` of slack
++ # to stay resilient to where in a re-add cycle the rebuild last fired.
++ assert len(cache._expire_heap) <= 2 * len(cache._expirations) + record_count
++ assert cache._total_records == record_count
++
++
++def test_cache_eviction_decrements_total_records() -> None:
++ """Natural removal (goodbyes, expirations) keeps ``_total_records`` in sync."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++ records = [_addr(f"sync-{i}.local.", i, created=now) for i in range(50)]
++ cache.async_add_records(records)
++ assert cache._total_records == 50
++
++ cache.async_remove_records(records[:20])
++ assert cache._total_records == 30
++
++ cache.async_expire(now + (200 * 1000))
++ assert cache._total_records == 0
++ assert not cache.cache
++
++
++def test_cache_total_records_invariant_under_mixed_ops() -> None:
++ """``_total_records`` stays equal to the sum of bucket sizes across all touched paths."""
++ cache = r.DNSCache()
++ now = r.current_time_millis()
++
++ def actual() -> int:
++ return sum(len(store) for store in cache.cache.values())
++
++ addrs = [_addr(f"mix-{i}.local.", i, created=now + i) for i in range(20)]
++ cache.async_add_records(addrs)
++ assert cache._total_records == actual() == 20
++
++ # Re-add of an identical record: no increment.
++ cache.async_add_records([addrs[0]])
++ assert cache._total_records == actual() == 20
++
++ # DNSService writes service_cache too — counter still matches cache size.
++ svc = r.DNSService("svc.local.", const._TYPE_SRV, const._CLASS_IN, 120, 0, 0, 80, "host.local.")
++ cache.async_add_records([svc])
++ assert cache._total_records == actual() == 21
++ cache.async_remove_records([svc])
++ assert cache._total_records == actual() == 20
++
++ # DNSNsec is stored but excluded from the "new" return; counter tracks it anyway.
++ nsec = r.DNSNsec("nsec.local.", const._TYPE_NSEC, const._CLASS_IN, 120, "nsec.local.", [const._TYPE_A])
++ cache.async_add_records([nsec])
++ assert cache._total_records == actual() == 21
++ cache.async_remove_records([nsec])
++ assert cache._total_records == actual() == 20
++
++ # Shared-key insert/remove: emptying the bucket drops the cache key but
++ # counter decrements only by the records that left.
++ shared_a = _addr("shared.local.", 0x0101, created=now)
++ shared_b = _addr("shared.local.", 0x0202, created=now)
++ cache.async_add_records([shared_a, shared_b])
++ assert cache._total_records == actual() == 22
++ cache.async_remove_records([shared_a, shared_b])
++ assert cache._total_records == actual() == 20
++ assert "shared.local." not in cache.cache
++
++ cache.async_expire(now + (200 * 1000))
++ assert cache._total_records == actual() == 0
++ assert not cache.cache
++
++ # Full-cap eviction loop: counter never grows past the cap, never drifts.
++ cap_records = [_addr(f"cap-{i}.local.", i, created=now + i) for i in range(const._MAX_CACHE_RECORDS + 50)]
++ for rec in cap_records:
++ cache.async_add_records([rec])
++ assert cache._total_records == actual()
++ assert cache._total_records == const._MAX_CACHE_RECORDS
diff --git a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
index aa1bb11065..cc68ffbf96 100644
--- a/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
+++ b/meta-python/recipes-devtools/python/python3-zeroconf_0.148.0.bb
@@ -7,6 +7,7 @@ SRC_URI[sha256sum] = "03fcca123df3652e23d945112d683d2f605f313637611b7d4adf31056f
SRC_URI += "file://CVE-2026-47180.patch \
file://CVE-2026-47183.patch \
+ file://CVE-2026-47184.patch \
"
inherit pypi python_poetry_core cython
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 10/33] valkey: mark CVE-2026-56684 and CVE-2026-63639 patched
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (7 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-python][wrynose][PATCH 9/33] python3-zeroconf: patch CVE-2026-47184 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 11/33] libyang: patch CVE-2026-41401 ankur.tyagi85
` (22 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Also mentioned in the release notes[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-56684
https://nvd.nist.gov/vuln/detail/cve-2026-63639
[1]https://github.com/valkey-io/valkey/releases/tag/9.0.5
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-oe/recipes-extended/valkey/valkey_9.0.5.bb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/meta-oe/recipes-extended/valkey/valkey_9.0.5.bb b/meta-oe/recipes-extended/valkey/valkey_9.0.5.bb
index d57aa6e1b4..d7ce19ce72 100644
--- a/meta-oe/recipes-extended/valkey/valkey_9.0.5.bb
+++ b/meta-oe/recipes-extended/valkey/valkey_9.0.5.bb
@@ -74,3 +74,5 @@ INITSCRIPT_PARAMS = "defaults 87"
SYSTEMD_SERVICE:${PN} = "valkey.service"
CVE_STATUS[CVE-2022-3734] = "not-applicable-platform: CVE only applies for Windows."
+CVE_STATUS[CVE-2026-56684] = "fixed-version: fixed in v9.0.5"
+CVE_STATUS[CVE-2026-63639] = "fixed-version: fixed in v9.0.5"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 11/33] libyang: patch CVE-2026-41401
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (8 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 10/33] valkey: mark CVE-2026-56684 and CVE-2026-63639 patched ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 12/33] tinyproxy: patch CVE-2026-31842 ankur.tyagi85
` (21 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Backport patch identified by Debian[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-41401
[1]https://security-tracker.debian.org/tracker/CVE-2026-41401
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../libyang/libyang/CVE-2026-41401.patch | 43 +++++++++++++++++++
.../libyang/libyang_3.13.6.bb | 1 +
2 files changed, 44 insertions(+)
create mode 100644 meta-oe/recipes-extended/libyang/libyang/CVE-2026-41401.patch
diff --git a/meta-oe/recipes-extended/libyang/libyang/CVE-2026-41401.patch b/meta-oe/recipes-extended/libyang/libyang/CVE-2026-41401.patch
new file mode 100644
index 0000000000..06537acbb8
--- /dev/null
+++ b/meta-oe/recipes-extended/libyang/libyang/CVE-2026-41401.patch
@@ -0,0 +1,43 @@
+From 34b2a26ee86716e2b593c44e51e38ee15d4e8434 Mon Sep 17 00:00:00 2001
+From: Michal Vasko <mvasko@cesnet.cz>
+Date: Thu, 26 Mar 2026 08:33:44 +0100
+Subject: [PATCH] parser common BUGFIX invalid metadata removal
+
+(cherry picked from commit 54c3276d871023da266d4ed3ceaee7e8d71d0b04)
+
+CVE: CVE-2026-41401
+Upstream-Status: Backport [https://github.com/CESNET/libyang/commit/54c3276d871023da266d4ed3ceaee7e8d71d0b04]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/parser_common.c | 4 ++--
+ tests/fuzz/corpus/lyd_parse_mem_xml/advisory2026_03_26 | 5 +++++
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+ create mode 100644 tests/fuzz/corpus/lyd_parse_mem_xml/advisory2026_03_26
+
+diff --git a/src/parser_common.c b/src/parser_common.c
+index 0b221af72..fefe723eb 100644
+--- a/src/parser_common.c
++++ b/src/parser_common.c
+@@ -413,8 +413,8 @@ lyd_parser_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct
+ next_meta = meta2->next;
+
+ /* delete the metadata */
+- if (meta != &node->meta) {
+- *meta = (*meta)->next;
++ if ((meta != &node->meta) && (meta2 == *meta)) {
++ *meta = next_meta;
+ }
+ lyd_free_meta_single(meta2);
+ if (prev_meta) {
+diff --git a/tests/fuzz/corpus/lyd_parse_mem_xml/advisory2026_03_26 b/tests/fuzz/corpus/lyd_parse_mem_xml/advisory2026_03_26
+new file mode 100644
+index 000000000..7e3b6c39e
+--- /dev/null
++++ b/tests/fuzz/corpus/lyd_parse_mem_xml/advisory2026_03_26
+@@ -0,0 +1,5 @@
++<int32 xmlns="urn:tests:types"
++ xmlns:yang="urn:ietf:params:xml:ns:yang:1"
++ xmlns:dflt="urn:ietf:params:xml:ns:netconf:default:1.0"
++ yang:operation="create"
++ dflt:default="true">5</int32>
diff --git a/meta-oe/recipes-extended/libyang/libyang_3.13.6.bb b/meta-oe/recipes-extended/libyang/libyang_3.13.6.bb
index 822c30ba24..7724b4c218 100644
--- a/meta-oe/recipes-extended/libyang/libyang_3.13.6.bb
+++ b/meta-oe/recipes-extended/libyang/libyang_3.13.6.bb
@@ -11,6 +11,7 @@ SRCREV = "c2ddd01b9b810a30d6a7d6749a3bc9adeb7b01fb"
SRC_URI = "git://github.com/CESNET/libyang.git;branch=master;protocol=https;tag=v${PV} \
file://0001-test_context-skip-test-case-test_searchdirs.patch \
file://run-ptest \
+ file://CVE-2026-41401.patch \
"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 12/33] tinyproxy: patch CVE-2026-31842
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (9 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-oe][wrynose][PATCH 11/33] libyang: patch CVE-2026-41401 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 13/33] tinyproxy: patch CVE-2026-54387 ankur.tyagi85
` (20 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Backport commit identified by Debian[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-31842
[1]https://security-tracker.debian.org/tracker/CVE-2026-31842
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tinyproxy/tinyproxy/CVE-2026-31842.patch | 33 +++++++++++++++++++
.../tinyproxy/tinyproxy_1.11.3.bb | 1 +
2 files changed, 34 insertions(+)
create mode 100644 meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-31842.patch
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-31842.patch b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-31842.patch
new file mode 100644
index 0000000000..6b8c02d629
--- /dev/null
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-31842.patch
@@ -0,0 +1,33 @@
+From ed5252d213a68546feaff68e2b40e401d2c2afd4 Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Fri, 3 Apr 2026 11:21:23 +0200
+Subject: [PATCH] reqs: fix case-sensitive matching of "chunked" (#605)
+
+the chunked transfer encoding needs to be matched in a case-
+insensitive manner.
+
+closes #604
+
+(cherry picked from commit 879bf844abffa0bf5fae6aff0c73179024dd9f98)
+
+CVE: CVE-2026-31842
+Upstream-Status: Backport [https://github.com/tinyproxy/tinyproxy/commit/879bf844abffa0bf5fae6aff0c73179024dd9f98]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/reqs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/reqs.c b/src/reqs.c
+index 7aacfd3..8b68538 100644
+--- a/src/reqs.c
++++ b/src/reqs.c
+@@ -850,7 +850,7 @@ static int is_chunked_transfer (pseudomap *hashofheaders)
+ {
+ char *data;
+ data = pseudomap_find (hashofheaders, "transfer-encoding");
+- return data ? !strcmp (data, "chunked") : 0;
++ return data ? !strcasecmp (data, "chunked") : 0;
+ }
+
+ /*
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
index 56e3296066..4ce766b40b 100644
--- a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
@@ -9,6 +9,7 @@ SRC_URI = "https://github.com/${BPN}/${BPN}/releases/download/${PV}/${BP}.tar.gz
file://run-ptest \
file://CVE-2026-3945-1.patch \
file://CVE-2026-3945-2.patch \
+ file://CVE-2026-31842.patch \
"
SRC_URI[sha256sum] = "9bcf46db1a2375ff3e3d27a41982f1efec4706cce8899ff9f33323a8218f7592"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 13/33] tinyproxy: patch CVE-2026-54387
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (10 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 12/33] tinyproxy: patch CVE-2026-31842 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 14/33] tinyproxy: patch CVE-2026-55202 ankur.tyagi85
` (19 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Backport commit identified by Debian[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-54387
[1]https://security-tracker.debian.org/tracker/CVE-2026-54387
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tinyproxy/tinyproxy/CVE-2026-54387.patch | 37 +++++++++++++++++++
.../tinyproxy/tinyproxy_1.11.3.bb | 1 +
2 files changed, 38 insertions(+)
create mode 100644 meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-54387.patch
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-54387.patch b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-54387.patch
new file mode 100644
index 0000000000..b12572f0ad
--- /dev/null
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-54387.patch
@@ -0,0 +1,37 @@
+From a9602e492cedd5d5d18dd3675d9be90e9b0d9e66 Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Thu, 7 May 2026 16:33:11 +0000
+Subject: [PATCH] reqs: prevent request smuggling via both content-length and
+ chunked
+
+addressing point 1 of #609
+
+(cherry picked from commit 623bfc093df009296f0b85d40bc677ef9d5c09bb)
+
+CVE: CVE-2026-54387
+Upstream-Statys: Backport [https://github.com/tinyproxy/tinyproxy/commit/623bfc093df009296f0b85d40bc677ef9d5c09bb]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/reqs.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/src/reqs.c b/src/reqs.c
+index 8b68538..e3cfe76 100644
+--- a/src/reqs.c
++++ b/src/reqs.c
+@@ -942,8 +942,13 @@ process_client_headers (struct conn_s *connptr, pseudomap *hashofheaders)
+ connptr->content_length.client = get_content_length (hashofheaders);
+
+ /* Check whether client sends chunked data. */
+- if (connptr->content_length.client == -1 && is_chunked_transfer (hashofheaders))
++ if (is_chunked_transfer (hashofheaders)) {
++ if (connptr->content_length.client != -1)
++ /* request smuggling, see GH issue #609 */
++ pseudomap_remove (hashofheaders, "content-length");
++
+ connptr->content_length.client = -2;
++ }
+
+ /*
+ * See if there is a "Connection" header. If so, we need to do a bit
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
index 4ce766b40b..b8a508d8d4 100644
--- a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
@@ -10,6 +10,7 @@ SRC_URI = "https://github.com/${BPN}/${BPN}/releases/download/${PV}/${BP}.tar.gz
file://CVE-2026-3945-1.patch \
file://CVE-2026-3945-2.patch \
file://CVE-2026-31842.patch \
+ file://CVE-2026-54387.patch \
"
SRC_URI[sha256sum] = "9bcf46db1a2375ff3e3d27a41982f1efec4706cce8899ff9f33323a8218f7592"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 14/33] tinyproxy: patch CVE-2026-55202
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (11 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 13/33] tinyproxy: patch CVE-2026-54387 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:22 ` [oe][meta-webserver][wrynose][PATCH 15/33] nginx: mark CVE-2026-1642 patched ankur.tyagi85
` (18 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Backport commit identified by Debian[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-55202
[1]https://security-tracker.debian.org/tracker/CVE-2026-55202
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tinyproxy/tinyproxy/CVE-2026-55202.patch | 107 ++++++++++++++++++
.../tinyproxy/tinyproxy_1.11.3.bb | 1 +
2 files changed, 108 insertions(+)
create mode 100644 meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-55202.patch
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-55202.patch b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-55202.patch
new file mode 100644
index 0000000000..4e446542e2
--- /dev/null
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy/CVE-2026-55202.patch
@@ -0,0 +1,107 @@
+From 04b34f814076d790db26925d7df7c30798910c99 Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Sat, 18 Apr 2026 00:03:15 +0200
+Subject: [PATCH] reqs: improve stathost detection (#606)
+
+until now, only the basicauth code checked the host header, regular connections didn't.
+
+- add a new helper function to compare a hostname with optional
+ trailingcolon/port against the stathost.
+- add stathost check via host header before transparent proxy check,
+ else stathost might be misdetected as a trans host request.
+- refactor existing stathost checks to use the new helper
+
+this should make it easier to access the stathost, for example by
+injecting a host header into a curl command line with -H:
+
+ $ curl -H "Host: tinyproxy.stats" 127.0.0.1:8080
+
+the stathost can also be specified as an ip address, e.g.
+Stathost "127.0.0.10" + a separate Listen statement for that ip.
+in such a case e.g.
+
+ $ curl http://127.0.0.10:8080
+
+would work too, even if curl didn't add a Host header (but it does anyway).
+
+(cherry picked from commit 09312a185ae25cc486b4ff5987638a7917a48bce)
+
+CVE: CVE-2026-55202
+Upstream-Status: Backport [https://github.com/tinyproxy/tinyproxy/commit/09312a185ae25cc486b4ff5987638a7917a48bce]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/reqs.c | 37 +++++++++++++++++++++++++------------
+ 1 file changed, 25 insertions(+), 12 deletions(-)
+
+diff --git a/src/reqs.c b/src/reqs.c
+index e3cfe76..d5037cf 100644
+--- a/src/reqs.c
++++ b/src/reqs.c
+@@ -316,6 +316,17 @@ static int send_connect_method_response (struct conn_s *connptr)
+ connptr->protocol.minor);
+ }
+
++/* determine whether a hostname with optional trailing colon/port is the
++ stathost */
++static int is_stathost (const char* host)
++{
++ const char *p = config->stathost;
++ const char *q = host;
++ if (!p || !q) return 0;
++ while (*p && *(p++) == *(q++));
++ return *p == 0 && (*q == 0 || *q == ':');
++}
++
+ /*
+ * Break the request line apart and figure out where to connect and
+ * build a new request line. Finally connect to the remote server.
+@@ -384,6 +395,16 @@ BAD_REQUEST_ERROR:
+ goto fail;
+ }
+
++ /*
++ * Check to see if they're requesting the stat host
++ */
++ if (is_stathost (pseudomap_find (hashofheaders, "host"))) {
++got_stathost:
++ log_message (LOG_NOTICE, "Request for the stathost.");
++ connptr->show_stats = TRUE;
++ goto fail;
++ }
++
+ #ifdef REVERSE_SUPPORT
+ if (config->reversepath_list != NULL) {
+ /*
+@@ -497,19 +518,11 @@ BAD_REQUEST_ERROR:
+ }
+ }
+ #endif
+-
+-
+- /*
+- * Check to see if they're requesting the stat host
+- */
+- if (config->stathost && strcmp (config->stathost, request->host) == 0) {
+- log_message (LOG_NOTICE, "Request for the stathost.");
+- connptr->show_stats = TRUE;
+- goto fail;
+- }
++ /* check whether hostname from url is the stathost */
++ if (is_stathost (request->host))
++ goto got_stathost;
+
+ safefree (url);
+-
+ return request;
+
+ fail:
+@@ -1688,7 +1701,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr)
+
+ if (!authstring && config->stathost) {
+ authstring = pseudomap_find (hashofheaders, "host");
+- if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) {
++ if (authstring && is_stathost(authstring)) {
+ authstring = pseudomap_find (hashofheaders, "authorization");
+ stathost_connect = 1;
+ } else authstring = 0;
diff --git a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
index b8a508d8d4..f9d425b45a 100644
--- a/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
+++ b/meta-networking/recipes-support/tinyproxy/tinyproxy_1.11.3.bb
@@ -11,6 +11,7 @@ SRC_URI = "https://github.com/${BPN}/${BPN}/releases/download/${PV}/${BP}.tar.gz
file://CVE-2026-3945-2.patch \
file://CVE-2026-31842.patch \
file://CVE-2026-54387.patch \
+ file://CVE-2026-55202.patch \
"
SRC_URI[sha256sum] = "9bcf46db1a2375ff3e3d27a41982f1efec4706cce8899ff9f33323a8218f7592"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-webserver][wrynose][PATCH 15/33] nginx: mark CVE-2026-1642 patched
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (12 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-networking][wrynose][PATCH 14/33] tinyproxy: patch CVE-2026-55202 ankur.tyagi85
@ 2026-09-07 10:22 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 16/33] open62541: patch CVE-2026-11946 ankur.tyagi85
` (17 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:22 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Debian[1] also identified the commit[2]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-1642
[1]https://security-tracker.debian.org/tracker/CVE-2026-1642
[2]https://github.com/nginx/nginx/commit/784fa05025cb8cd0c770f99bc79d2794b9f85b6e
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-webserver/recipes-httpd/nginx/nginx_1.28.3.bb | 1 +
1 file changed, 1 insertion(+)
diff --git a/meta-webserver/recipes-httpd/nginx/nginx_1.28.3.bb b/meta-webserver/recipes-httpd/nginx/nginx_1.28.3.bb
index 9872a6de3b..f797f4a289 100644
--- a/meta-webserver/recipes-httpd/nginx/nginx_1.28.3.bb
+++ b/meta-webserver/recipes-httpd/nginx/nginx_1.28.3.bb
@@ -5,3 +5,4 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=3dc49537b08b14c8b66ad247bb4c4593"
SRC_URI[sha256sum] = "2c96a946bfb0882a21744ed429770a2123ae1828c7c48665092993ddee91a918"
CVE_STATUS[CVE-2025-53859] = "cpe-stable-backport: Fix is included in 1.28.1"
+CVE_STATUS[CVE-2026-1642] = "fixed-version: fixed since v1.28.2"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 16/33] open62541: patch CVE-2026-11946
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (13 preceding siblings ...)
2026-09-07 10:22 ` [oe][meta-webserver][wrynose][PATCH 15/33] nginx: mark CVE-2026-1642 patched ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched ankur.tyagi85
` (16 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Backport commit identified by Debian[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-11946
[1]https://security-tracker.debian.org/tracker/CVE-2026-11946
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../opcua/open62541/CVE-2026-11946.patch | 48 +++++++++++++++++++
.../opcua/open62541_1.4.16.bb | 1 +
2 files changed, 49 insertions(+)
create mode 100644 meta-networking/recipes-protocols/opcua/open62541/CVE-2026-11946.patch
diff --git a/meta-networking/recipes-protocols/opcua/open62541/CVE-2026-11946.patch b/meta-networking/recipes-protocols/opcua/open62541/CVE-2026-11946.patch
new file mode 100644
index 0000000000..36c9c83af1
--- /dev/null
+++ b/meta-networking/recipes-protocols/opcua/open62541/CVE-2026-11946.patch
@@ -0,0 +1,48 @@
+From 47df558c02eef86bec125a54284b78563d1928b8 Mon Sep 17 00:00:00 2001
+From: Niels Beier <niels.beier@o6-automation.com>
+Date: Thu, 7 May 2026 15:25:30 +0200
+Subject: [PATCH] fix(server): Enforce default message and chunk size limits to
+ prevent DoS
+
+When tcpMaxMsgSize or tcpMaxChunks are configured as 0, the server treats the
+limit as truly unbounded. A remote attacker can exploit this by sending
+arbitrarily large messages or an unbounded number of chunks, exhausting server
+memory and causing a denial of service.
+
+Set safe defaults (512 MB per message, 16384 chunks) whenever the configured
+value is zero, mirroring the existing behaviour for recv/sendBufferSize.
+
+This commit mitigates a vulnerability reported by Lorenzo Cannella.
+
+Internal Vulnerability Advisory: open62541-SA-2026-0002
+
+(cherry picked from commit c9563e8ea4a8db2f64059c8ff7efe0b49a35bea3)
+
+CVE: CVE-2026-11946
+Upstream-Status: Backport [https://github.com/open62541/open62541/commit/c9563e8ea4a8db2f64059c8ff7efe0b49a35bea3]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/server/ua_server_binary.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/src/server/ua_server_binary.c b/src/server/ua_server_binary.c
+index b2de3b271..13859ce56 100644
+--- a/src/server/ua_server_binary.c
++++ b/src/server/ua_server_binary.c
+@@ -1114,6 +1114,15 @@ createServerSecureChannel(UA_BinaryProtocolManager *bpm, UA_ConnectionManager *c
+ if(connConfig.sendBufferSize == 0)
+ connConfig.sendBufferSize = 1 << 16; /* 64kB */
+
++ if(connConfig.localMaxMessageSize == 0)
++ connConfig.localMaxMessageSize = 1 << 29; /* 512 MB */
++ if(connConfig.remoteMaxMessageSize == 0)
++ connConfig.remoteMaxMessageSize = 1 << 29; /* 512 MB */
++ if(connConfig.localMaxChunkCount == 0)
++ connConfig.localMaxChunkCount = 1 << 14; /* 16384 */
++ if(connConfig.remoteMaxChunkCount == 0)
++ connConfig.remoteMaxChunkCount = 1 << 14; /* 16384 */
++
+ /* Set up the new SecureChannel */
+ UA_SecureChannel_init(&entry->channel);
+ entry->channel.config = connConfig;
diff --git a/meta-networking/recipes-protocols/opcua/open62541_1.4.16.bb b/meta-networking/recipes-protocols/opcua/open62541_1.4.16.bb
index 32f7148f4b..b9edc5fe30 100644
--- a/meta-networking/recipes-protocols/opcua/open62541_1.4.16.bb
+++ b/meta-networking/recipes-protocols/opcua/open62541_1.4.16.bb
@@ -18,6 +18,7 @@ SRC_URI = " \
git://github.com/Pro/mdnsd.git;name=mdnsd;protocol=https;branch=master;destsuffix=${BB_GIT_DEFAULT_DESTSUFFIX}/deps/mdnsd \
git://github.com/OPCFoundation/UA-Nodeset;name=ua-nodeset;protocol=https;branch=latest;destsuffix=${BB_GIT_DEFAULT_DESTSUFFIX}/deps/ua-nodeset \
git://github.com/LiamBindle/MQTT-C.git;name=mqtt-c;protocol=https;branch=master;destsuffix=${BB_GIT_DEFAULT_DESTSUFFIX}/deps/mqtt-c \
+ file://CVE-2026-11946.patch \
"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (14 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 16/33] open62541: patch CVE-2026-11946 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-15 1:28 ` Anuj Mittal
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 18/33] tesseract: patch CVE-2026-73066 ankur.tyagi85
` (15 subsequent siblings)
31 siblings, 1 reply; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
CVE-2024-35226[1], CVE-2026-62992[2], CVE-2026-62996[3] are patched in
current version.
Details:
https://nvd.nist.gov/vuln/detail/cve-2024-35226
https://nvd.nist.gov/vuln/detail/cve-2026-62992
https://nvd.nist.gov/vuln/detail/cve-2026-62996
[1]https://github.com/smarty-php/smarty/commit/0be92bc8a6fb83e6e0d883946f7e7c09ba4e857a
[2]https://github.com/smarty-php/smarty/commit/99c048ce7a590c519b79fbd38ad0143a08183a1f
[3]https://github.com/smarty-php/smarty/commit/3c9f77a2e06ce319ae0092496af32cc8f3adc52e
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-oe/recipes-support/smarty/smarty_5.8.4.bb | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
index dcb7c85a80..cf6c42345c 100644
--- a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
+++ b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
@@ -35,3 +35,7 @@ FILES:${PN} += "${datadir}/php/smarty3/"
RDEPENDS:${PN} = "php"
CVE_PRODUCT = "smarty:smarty smarty-php:smarty"
+
+CVE_STATUS[CVE-2024-35226] = "fixed-version: fixed since v5.2.0"
+CVE_STATUS[CVE-2026-62992] = "fixed-version: fixed since v5.8.2"
+CVE_STATUS[CVE-2024-62996] = "fixed-version: fixed in v5.8.4"
^ permalink raw reply related [flat|nested] 37+ messages in thread* Re: [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched ankur.tyagi85
@ 2026-09-15 1:28 ` Anuj Mittal
2026-09-15 1:53 ` Ankur Tyagi
0 siblings, 1 reply; 37+ messages in thread
From: Anuj Mittal @ 2026-09-15 1:28 UTC (permalink / raw)
To: ankur.tyagi85; +Cc: openembedded-devel
On Mon, Sep 7, 2026 at 6:25 PM Ankur Tyagi via lists.openembedded.org
<ankur.tyagi85=gmail.com@lists.openembedded.org> wrote:
>
> From: Ankur Tyagi <ankur.tyagi85@gmail.com>
>
> CVE-2024-35226[1], CVE-2026-62992[2], CVE-2026-62996[3] are patched in
> current version.
>
> Details:
> https://nvd.nist.gov/vuln/detail/cve-2024-35226
> https://nvd.nist.gov/vuln/detail/cve-2026-62992
> https://nvd.nist.gov/vuln/detail/cve-2026-62996
>
> [1]https://github.com/smarty-php/smarty/commit/0be92bc8a6fb83e6e0d883946f7e7c09ba4e857a
> [2]https://github.com/smarty-php/smarty/commit/99c048ce7a590c519b79fbd38ad0143a08183a1f
> [3]https://github.com/smarty-php/smarty/commit/3c9f77a2e06ce319ae0092496af32cc8f3adc52e
>
> Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
> ---
> meta-oe/recipes-support/smarty/smarty_5.8.4.bb | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> index dcb7c85a80..cf6c42345c 100644
> --- a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> +++ b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> @@ -35,3 +35,7 @@ FILES:${PN} += "${datadir}/php/smarty3/"
> RDEPENDS:${PN} = "php"
>
> CVE_PRODUCT = "smarty:smarty smarty-php:smarty"
> +
> +CVE_STATUS[CVE-2024-35226] = "fixed-version: fixed since v5.2.0"
> +CVE_STATUS[CVE-2026-62992] = "fixed-version: fixed since v5.8.2"
> +CVE_STATUS[CVE-2024-62996] = "fixed-version: fixed in v5.8.4"
Should this be CVE-2026-62996?
Thanks,
Anuj
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched
2026-09-15 1:28 ` Anuj Mittal
@ 2026-09-15 1:53 ` Ankur Tyagi
0 siblings, 0 replies; 37+ messages in thread
From: Ankur Tyagi @ 2026-09-15 1:53 UTC (permalink / raw)
To: Anuj Mittal; +Cc: openembedded-devel
On Tue, Sep 15, 2026 at 1:28 PM Anuj Mittal
<anuj.mittal@oss.qualcomm.com> wrote:
>
> On Mon, Sep 7, 2026 at 6:25 PM Ankur Tyagi via lists.openembedded.org
> <ankur.tyagi85=gmail.com@lists.openembedded.org> wrote:
> >
> > From: Ankur Tyagi <ankur.tyagi85@gmail.com>
> >
> > CVE-2024-35226[1], CVE-2026-62992[2], CVE-2026-62996[3] are patched in
> > current version.
> >
> > Details:
> > https://nvd.nist.gov/vuln/detail/cve-2024-35226
> > https://nvd.nist.gov/vuln/detail/cve-2026-62992
> > https://nvd.nist.gov/vuln/detail/cve-2026-62996
> >
> > [1]https://github.com/smarty-php/smarty/commit/0be92bc8a6fb83e6e0d883946f7e7c09ba4e857a
> > [2]https://github.com/smarty-php/smarty/commit/99c048ce7a590c519b79fbd38ad0143a08183a1f
> > [3]https://github.com/smarty-php/smarty/commit/3c9f77a2e06ce319ae0092496af32cc8f3adc52e
> >
> > Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
> > ---
> > meta-oe/recipes-support/smarty/smarty_5.8.4.bb | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> > index dcb7c85a80..cf6c42345c 100644
> > --- a/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> > +++ b/meta-oe/recipes-support/smarty/smarty_5.8.4.bb
> > @@ -35,3 +35,7 @@ FILES:${PN} += "${datadir}/php/smarty3/"
> > RDEPENDS:${PN} = "php"
> >
> > CVE_PRODUCT = "smarty:smarty smarty-php:smarty"
> > +
> > +CVE_STATUS[CVE-2024-35226] = "fixed-version: fixed since v5.2.0"
> > +CVE_STATUS[CVE-2026-62992] = "fixed-version: fixed since v5.8.2"
> > +CVE_STATUS[CVE-2024-62996] = "fixed-version: fixed in v5.8.4"
>
> Should this be CVE-2026-62996?
yep, I'll send v2
>
> Thanks,
>
> Anuj
^ permalink raw reply [flat|nested] 37+ messages in thread
* [oe][meta-oe][wrynose][PATCH 18/33] tesseract: patch CVE-2026-73066
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (15 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 17/33] smarty: mark CVEs patched ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 19/33] tesseract: patch CVE-2026-73067 ankur.tyagi85
` (14 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-73066
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tesseract/tesseract/CVE-2026-73066.patch | 152 ++++++++++++++++++
.../tesseract/tesseract_5.5.2.bb | 4 +-
2 files changed, 155 insertions(+), 1 deletion(-)
create mode 100644 meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch
new file mode 100644
index 0000000000..fc762ba8ca
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch
@@ -0,0 +1,152 @@
+From f66bfcc45d1acd3e3c98f81e1edc8ddf49786555 Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Thu, 23 Jul 2026 18:05:39 +0200
+Subject: [PATCH] Fix integer overflow in LSTM Convolve and Reconfig
+ deserialization (#4588)
+
+Add range and overflow validation in Convolve::DeSerialize and
+Reconfig::DeSerialize to prevent a crafted .traineddata file from
+triggering a heap out-of-bounds write via unchecked signed integer
+multiplication when computing the output-channel count.
+
+Validate ni/no/num_weights in Network::CreateFromFile.
+
+Add defense-in-depth bounds assertions in NetworkIO::Randomize and
+NetworkIO::CopyTimeStepGeneral.
+
+Reported-by: Eunho Kim <eunhok98@gmail.com>
+Signed-off-by: Stefan Weil <sw@weilnetz.de>
+Assisted-by: OpenCode / big-pickle (opencode)
+Tested-by: Eunho Kim <eunhok98@gmail.com>
+(cherry picked from commit 2f4d2f4bf45c363785d7bf1da29b6628f8939a72)
+
+CVE: CVE-2026-73066
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/2f4d2f4bf45c363785d7bf1da29b6628f8939a72]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/lstm/convolve.cpp | 24 +++++++++++++++++++++++-
+ src/lstm/network.cpp | 6 ++++++
+ src/lstm/networkio.cpp | 2 ++
+ src/lstm/reconfig.cpp | 20 +++++++++++++++++++-
+ 4 files changed, 50 insertions(+), 2 deletions(-)
+
+diff --git a/src/lstm/convolve.cpp b/src/lstm/convolve.cpp
+index 6cfaa06e..d20a991c 100644
+--- a/src/lstm/convolve.cpp
++++ b/src/lstm/convolve.cpp
+@@ -23,8 +23,11 @@
+
+ #include "convolve.h"
+
++#include <cstdint>
++
+ #include "networkscratch.h"
+ #include "serialis.h"
++#include "tprintf.h"
+
+ namespace tesseract {
+
+@@ -46,7 +49,26 @@ bool Convolve::DeSerialize(TFile *fp) {
+ if (!fp->DeSerialize(&half_y_)) {
+ return false;
+ }
+- no_ = ni_ * (2 * half_x_ + 1) * (2 * half_y_ + 1);
++ if (half_x_ < 0 || half_y_ < 0 || ni_ <= 0) {
++ tprintf("Error: invalid Convolve parameters: ni=%d half_x=%d half_y=%d\n", ni_, half_x_,
++ half_y_);
++ return false;
++ }
++ int64_t kx = 2LL * half_x_ + 1;
++ int64_t ky = 2LL * half_y_ + 1;
++ // Stepwise overflow check: ni_ * kx * ky must fit in int.
++ if (kx > INT_MAX / ky) {
++ tprintf("Error: Convolve output-channel count overflows: ni=%d half_x=%d half_y=%d\n", ni_,
++ half_x_, half_y_);
++ return false;
++ }
++ int64_t kxky = kx * ky;
++ if (static_cast<int64_t>(ni_) > INT_MAX / kxky) {
++ tprintf("Error: Convolve output-channel count overflows: ni=%d half_x=%d half_y=%d\n", ni_,
++ half_x_, half_y_);
++ return false;
++ }
++ no_ = static_cast<int>(static_cast<int64_t>(ni_) * kxky);
+ return true;
+ }
+
+diff --git a/src/lstm/network.cpp b/src/lstm/network.cpp
+index cfddbfd4..8230992a 100644
+--- a/src/lstm/network.cpp
++++ b/src/lstm/network.cpp
+@@ -247,6 +247,12 @@ Network *Network::CreateFromFile(TFile *fp) {
+ return nullptr;
+ }
+
++ if (ni < 0 || no < 0 || num_weights < 0) {
++ tprintf("Error: invalid network layer parameters: type=%d ni=%d no=%d num_weights=%d\n", type,
++ ni, no, num_weights);
++ return nullptr;
++ }
++
+ switch (type) {
+ case NT_CONVOLVE:
+ network = new Convolve(name, ni, 0, 0);
+diff --git a/src/lstm/networkio.cpp b/src/lstm/networkio.cpp
+index 3cb068c6..8636075b 100644
+--- a/src/lstm/networkio.cpp
++++ b/src/lstm/networkio.cpp
+@@ -405,6 +405,7 @@ void NetworkIO::CopyTimeStepFrom(int dest_t, const NetworkIO &src, int src_t) {
+ void NetworkIO::CopyTimeStepGeneral(int dest_t, int dest_offset, int num_features,
+ const NetworkIO &src, int src_t, int src_offset) {
+ ASSERT_HOST(int_mode_ == src.int_mode_);
++ ASSERT_HOST(dest_offset + num_features <= NumFeatures());
+ if (int_mode_) {
+ memcpy(i_[dest_t] + dest_offset, src.i_[src_t] + src_offset, num_features * sizeof(i_[0][0]));
+ } else {
+@@ -414,6 +415,7 @@ void NetworkIO::CopyTimeStepGeneral(int dest_t, int dest_offset, int num_feature
+
+ // Sets the given range to random values.
+ void NetworkIO::Randomize(int t, int offset, int num_features, TRand *randomizer) {
++ ASSERT_HOST(offset + num_features <= NumFeatures());
+ if (int_mode_) {
+ int8_t *line = i_[t] + offset;
+ for (int i = 0; i < num_features; ++i) {
+diff --git a/src/lstm/reconfig.cpp b/src/lstm/reconfig.cpp
+index 2f49d63e..a4e99497 100644
+--- a/src/lstm/reconfig.cpp
++++ b/src/lstm/reconfig.cpp
+@@ -18,6 +18,10 @@
+
+ #include "reconfig.h"
+
++#include <cstdint>
++
++#include "tprintf.h"
++
+ namespace tesseract {
+
+ Reconfig::Reconfig(const std::string &name, int ni, int x_scale, int y_scale)
+@@ -60,7 +64,21 @@ bool Reconfig::DeSerialize(TFile *fp) {
+ if (!fp->DeSerialize(&y_scale_)) {
+ return false;
+ }
+- no_ = ni_ * x_scale_ * y_scale_;
++ if (x_scale_ <= 0 || y_scale_ <= 0 || ni_ <= 0) {
++ tprintf("Error: invalid Reconfig parameters: ni=%d x_scale=%d y_scale=%d\n", ni_, x_scale_,
++ y_scale_);
++ return false;
++ }
++ int64_t xs = x_scale_;
++ int64_t ys = y_scale_;
++ // Stepwise overflow check: ni_ * x_scale_ * y_scale_ must fit in int.
++ int64_t xsys = xs * ys;
++ if (static_cast<int64_t>(ni_) > INT_MAX / xsys) {
++ tprintf("Error: Reconfig output-channel count overflows: ni=%d x_scale=%d y_scale=%d\n", ni_,
++ x_scale_, y_scale_);
++ return false;
++ }
++ no_ = static_cast<int>(static_cast<int64_t>(ni_) * xsys);
+ return true;
+ }
+
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
index 46b789cbc4..1b5a5fe2df 100644
--- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
+++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
@@ -6,7 +6,9 @@ LICENSE = "Apache-2.0"
LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
SRCREV = "6e1d56a847e697de07b38619356550e5cf4e8633"
-SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag=${PV}"
+SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag=${PV} \
+ file://CVE-2026-73066.patch \
+"
DEPENDS = "leptonica"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-oe][wrynose][PATCH 19/33] tesseract: patch CVE-2026-73067
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (16 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 18/33] tesseract: patch CVE-2026-73066 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 20/33] wolfssl: mark CVEs patched ankur.tyagi85
` (13 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-73067
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tesseract/CVE-2026-73067-1.patch | 423 ++++++++++++++++++
.../tesseract/CVE-2026-73067-2.patch | 96 ++++
.../tesseract/tesseract_5.5.2.bb | 2 +
3 files changed, 521 insertions(+)
create mode 100644 meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-1.patch
create mode 100644 meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-2.patch
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-1.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-1.patch
new file mode 100644
index 0000000000..95fd102ebf
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-1.patch
@@ -0,0 +1,423 @@
+From 9d83107c2511e7e2a7dc649c7bb1633da746669f Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Sun, 12 Jul 2026 13:49:46 +0200
+Subject: [PATCH] Fix memory-safety issues in .traineddata deserialization
+
+Add bounds checking on count/length fields read from untrusted
+.traineddata files before they are used to size allocations or index
+arrays. Use unsigned types where negative values make no sense.
+
+Key changes:
+- serialis.cpp: overflow check in DeSerializeSkip, size limits for
+ DeSerialize(string) and DeSerialize(vector<char>)
+- unicharcompress.h: validate RecodedCharID length before reading
+ into fixed-size code array (buffer overflow fix), change length_
+ to uint32_t, use unsigned types for Set() index and length()
+- dawg.cpp/h: use uint32_t for num_edges_, add bounds checks on all
+ edge traversal loops, replace no-op ASSERT_HOST with proper errors
+- fontinfo.cpp: bounds check font name size to prevent integer
+ overflow in size+1, use uint32_t for spacing vector size
+- tessdatamanager.cpp: validate offsets are within file bounds
+- bitvector.cpp, weightmatrix.cpp, plumbing.cpp: add size limits
+
+Assisted-by: OpenCode / big-pickle (opencode)
+Signed-off-by: Stefan Weil <stweil@tessus.org>
+(cherry picked from commit 82727cc11c34eaf1249af002d69f6bbae70993b9)
+
+CVE: CVE-2026-73067
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/82727cc11c34eaf1249af002d69f6bbae70993b9]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/ccstruct/fontinfo.cpp | 10 ++++++---
+ src/ccstruct/fontinfo.h | 2 +-
+ src/ccutil/bitvector.cpp | 4 ++++
+ src/ccutil/serialis.cpp | 10 +++++++++
+ src/ccutil/tessdatamanager.cpp | 9 ++++++++
+ src/ccutil/unicharcompress.h | 19 ++++++++++------
+ src/dict/dawg.cpp | 40 ++++++++++++++++++++++++++--------
+ src/dict/dawg.h | 16 +++++++++-----
+ src/lstm/plumbing.cpp | 4 ++++
+ src/lstm/weightmatrix.cpp | 4 ++++
+ 10 files changed, 92 insertions(+), 26 deletions(-)
+
+diff --git a/src/ccstruct/fontinfo.cpp b/src/ccstruct/fontinfo.cpp
+index 65d4d032..9984469f 100644
+--- a/src/ccstruct/fontinfo.cpp
++++ b/src/ccstruct/fontinfo.cpp
+@@ -145,6 +145,10 @@ bool read_info(TFile *f, FontInfo *fi) {
+ if (!f->DeSerialize(&size)) {
+ return false;
+ }
++ // Reject unreasonably large font names to prevent overflow in size + 1.
++ if (size > 100000) {
++ return false;
++ }
+ char *font_name = new char[size + 1];
+ fi->name = font_name;
+ if (!f->DeSerialize(font_name, size)) {
+@@ -161,16 +165,16 @@ bool write_info(FILE *f, const FontInfo &fi) {
+ }
+
+ bool read_spacing_info(TFile *f, FontInfo *fi) {
+- int32_t vec_size, kern_size;
++ uint32_t vec_size;
++ int32_t kern_size;
+ if (!f->DeSerialize(&vec_size)) {
+ return false;
+ }
+- ASSERT_HOST(vec_size >= 0);
+ if (vec_size == 0) {
+ return true;
+ }
+ fi->init_spacing(vec_size);
+- for (int i = 0; i < vec_size; ++i) {
++ for (uint32_t i = 0; i < vec_size; ++i) {
+ auto *fs = new FontSpacingInfo();
+ if (!f->DeSerialize(&fs->x_gap_before) || !f->DeSerialize(&fs->x_gap_after) ||
+ !f->DeSerialize(&kern_size)) {
+diff --git a/src/ccstruct/fontinfo.h b/src/ccstruct/fontinfo.h
+index 1a84a567..cfb19e06 100644
+--- a/src/ccstruct/fontinfo.h
++++ b/src/ccstruct/fontinfo.h
+@@ -76,7 +76,7 @@ struct FontInfo {
+ bool DeSerialize(TFile *fp);
+
+ // Reserves unicharset_size spots in spacing_vec.
+- void init_spacing(int unicharset_size) {
++ void init_spacing(uint32_t unicharset_size) {
+ spacing_vec = new std::vector<FontSpacingInfo *>(unicharset_size);
+ }
+ // Adds the given pointer to FontSpacingInfo to spacing_vec member
+diff --git a/src/ccutil/bitvector.cpp b/src/ccutil/bitvector.cpp
+index a02781a8..2b8b7f00 100644
+--- a/src/ccutil/bitvector.cpp
++++ b/src/ccutil/bitvector.cpp
+@@ -102,6 +102,10 @@ bool BitVector::DeSerialize(bool swap, FILE *fp) {
+ if (swap) {
+ ReverseN(&new_bit_size, sizeof(new_bit_size));
+ }
++ // Reject unreasonably large bit vectors.
++ if (new_bit_size > 500000000) {
++ return false;
++ }
+ Alloc(new_bit_size);
+ int wordlen = WordLength();
+ if (!tesseract::DeSerialize(fp, &array_[0], wordlen)) {
+diff --git a/src/ccutil/serialis.cpp b/src/ccutil/serialis.cpp
+index d9c9a8d4..cb663094 100644
+--- a/src/ccutil/serialis.cpp
++++ b/src/ccutil/serialis.cpp
+@@ -88,6 +88,10 @@ bool TFile::DeSerializeSkip(size_t size) {
+ if (!DeSerialize(&len)) {
+ return false;
+ }
++ // Check for overflow: len * size must not overflow size_t.
++ if (size != 0 && len > SIZE_MAX / size) {
++ return false;
++ }
+ return Skip(len * size);
+ }
+
+@@ -95,6 +99,9 @@ bool TFile::DeSerialize(std::string &data) {
+ uint32_t size;
+ if (!DeSerialize(&size)) {
+ return false;
++ } else if (size > 50000000) {
++ // Arbitrarily limit the size to protect against bad data.
++ return false;
+ } else if (size > 0) {
+ // TODO: optimize.
+ data.resize(size);
+@@ -113,6 +120,9 @@ bool TFile::DeSerialize(std::vector<char> &data) {
+ uint32_t size;
+ if (!DeSerialize(&size)) {
+ return false;
++ } else if (size > 50000000) {
++ // Arbitrarily limit the size to protect against bad data.
++ return false;
+ } else if (size > 0) {
+ // TODO: optimize.
+ data.resize(size);
+diff --git a/src/ccutil/tessdatamanager.cpp b/src/ccutil/tessdatamanager.cpp
+index 8ab26506..67e86dc0 100644
+--- a/src/ccutil/tessdatamanager.cpp
++++ b/src/ccutil/tessdatamanager.cpp
+@@ -132,14 +132,23 @@ bool TessdataManager::LoadMemBuffer(const char *name, const char *data, int size
+ }
+ for (unsigned i = 0; i < num_entries && i < TESSDATA_NUM_ENTRIES; ++i) {
+ if (offset_table[i] >= 0) {
++ if (offset_table[i] > size) {
++ return false;
++ }
+ int64_t entry_size = size - offset_table[i];
+ unsigned j = i + 1;
+ while (j < num_entries && offset_table[j] == -1) {
+ ++j;
+ }
+ if (j < num_entries) {
++ if (offset_table[j] < 0 || offset_table[j] > size) {
++ return false;
++ }
+ entry_size = offset_table[j] - offset_table[i];
+ }
++ if (entry_size < 0) {
++ return false;
++ }
+ entries_[i].resize(entry_size);
+ if (!fp.DeSerialize(&entries_[i][0], entry_size)) {
+ return false;
+diff --git a/src/ccutil/unicharcompress.h b/src/ccutil/unicharcompress.h
+index 2e81bbde..67a441e8 100644
+--- a/src/ccutil/unicharcompress.h
++++ b/src/ccutil/unicharcompress.h
+@@ -41,7 +41,7 @@ public:
+ length_ = length;
+ }
+ // Sets the code value at the given index in the code.
+- void Set(int index, int value) {
++ void Set(uint32_t index, int value) {
+ code_[index] = value;
+ if (length_ <= index) {
+ length_ = index + 1;
+@@ -59,7 +59,7 @@ public:
+ return length_ == 0;
+ }
+ // Accessors
+- int length() const {
++ uint32_t length() const {
+ return length_;
+ }
+ int operator()(int index) const {
+@@ -73,14 +73,19 @@ public:
+ }
+ // Reads from the given file. Returns false in case of error.
+ bool DeSerialize(TFile *fp) {
+- return fp->DeSerialize(&self_normalized_) && fp->DeSerialize(&length_) &&
+- fp->DeSerialize(&code_[0], length_);
++ if (!fp->DeSerialize(&self_normalized_) || !fp->DeSerialize(&length_)) {
++ return false;
++ }
++ if (length_ > kMaxCodeLen) {
++ return false;
++ }
++ return fp->DeSerialize(&code_[0], length_);
+ }
+ bool operator==(const RecodedCharID &other) const {
+ if (length_ != other.length_) {
+ return false;
+ }
+- for (int i = 0; i < length_; ++i) {
++ for (uint32_t i = 0; i < length_; ++i) {
+ if (code_[i] != other.code_[i]) {
+ return false;
+ }
+@@ -91,7 +96,7 @@ public:
+ struct RecodedCharIDHash {
+ uint64_t operator()(const RecodedCharID &code) const {
+ uint64_t result = 0;
+- for (int i = 0; i < code.length_; ++i) {
++ for (uint32_t i = 0; i < code.length_; ++i) {
+ result ^= static_cast<uint64_t>(code(i)) << (7 * i);
+ }
+ return result;
+@@ -103,7 +108,7 @@ private:
+ // that map to the same code. Has boolean value, but int8_t for serialization.
+ int8_t self_normalized_;
+ // The number of elements in use in code_;
+- int32_t length_;
++ uint32_t length_;
+ // The re-encoded form of the unichar-id to which this RecodedCharID relates.
+ int32_t code_[kMaxCodeLen];
+ };
+diff --git a/src/dict/dawg.cpp b/src/dict/dawg.cpp
+index af45176f..dab4dfc2 100644
+--- a/src/dict/dawg.cpp
++++ b/src/dict/dawg.cpp
+@@ -221,7 +221,11 @@ EDGE_REF SquishedDawg::edge_char_of(NODE_REF node, UNICHAR_ID unichar_id,
+ (!word_end || end_of_word_from_edge_rec(edges_[edge]))) {
+ return (edge);
+ }
+- } while (!last_edge(edge++));
++ if (last_edge(edge)) {
++ break;
++ }
++ ++edge;
++ } while (edge < num_edges_);
+ }
+ }
+ return (NO_EDGE); // not found
+@@ -234,7 +238,11 @@ int32_t SquishedDawg::num_forward_edges(NODE_REF node) const {
+ if (forward_edge(edge)) {
+ do {
+ num++;
+- } while (!last_edge(edge++));
++ if (last_edge(edge)) {
++ break;
++ }
++ ++edge;
++ } while (edge < num_edges_);
+ }
+
+ return (num);
+@@ -274,7 +282,11 @@ void SquishedDawg::print_node(NODE_REF node, int max_num_edges) const {
+ if (edge - node > max_num_edges) {
+ return;
+ }
+- } while (!last_edge(edge++));
++ if (last_edge(edge)) {
++ break;
++ }
++ ++edge;
++ } while (edge < num_edges_);
+
+ if (edge < num_edges_ && edge_occupied(edge) && backward_edge(edge)) {
+ do {
+@@ -290,7 +302,11 @@ void SquishedDawg::print_node(NODE_REF node, int max_num_edges) const {
+ if (edge - node > MAX_NODE_EDGES_DISPLAY) {
+ return;
+ }
+- } while (!last_edge(edge++));
++ if (last_edge(edge)) {
++ break;
++ }
++ ++edge;
++ } while (edge < num_edges_);
+ }
+ } else {
+ tprintf(REFFORMAT " : no edges in this node\n", node);
+@@ -326,14 +342,17 @@ bool SquishedDawg::read_squished_dawg(TFile *file) {
+ return false;
+ }
+
+- int32_t unicharset_size;
++ uint32_t unicharset_size;
+ if (!file->DeSerialize(&unicharset_size)) {
+ return false;
+ }
+ if (!file->DeSerialize(&num_edges_)) {
+ return false;
+ }
+- ASSERT_HOST(num_edges_ > 0); // DAWG should not be empty
++ if (num_edges_ == 0) {
++ tprintf("Empty dawg: num_edges is 0\n");
++ return false;
++ }
+ Dawg::init(unicharset_size);
+
+ edges_ = new EDGE_RECORD[num_edges_];
+@@ -341,7 +360,7 @@ bool SquishedDawg::read_squished_dawg(TFile *file) {
+ return false;
+ }
+ if (debug_level_ > 2) {
+- tprintf("type: %d lang: %s perm: %d unicharset_size: %d num_edges: %d\n",
++ tprintf("type: %d lang: %s perm: %d unicharset_size: %d num_edges: %" PRIu32 "\n",
+ type_, lang_.c_str(), perm_, unicharset_size_, num_edges_);
+ for (EDGE_REF edge = 0; edge < num_edges_; ++edge) {
+ print_edge(edge);
+@@ -378,8 +397,11 @@ std::unique_ptr<EDGE_REF[]> SquishedDawg::build_node_map(
+ break;
+ }
+ if (backward_edge(edge)) {
+- while (!last_edge(edge++)) {
+- ;
++ while (edge < num_edges_ && !last_edge(edge)) {
++ ++edge;
++ }
++ if (edge < num_edges_) {
++ ++edge; // Skip past the last backward edge.
+ }
+ }
+ edge--;
+diff --git a/src/dict/dawg.h b/src/dict/dawg.h
+index b87b3880..d0f412c3 100644
+--- a/src/dict/dawg.h
++++ b/src/dict/dawg.h
+@@ -418,7 +418,7 @@ public:
+ ASSERT_HOST(read_squished_dawg(&file));
+ num_forward_edges_in_node0 = num_forward_edges(0);
+ }
+- SquishedDawg(EDGE_ARRAY edges, int num_edges, DawgType type,
++ SquishedDawg(EDGE_ARRAY edges, uint32_t num_edges, DawgType type,
+ const std::string &lang, PermuterType perm, int unicharset_size,
+ int debug_level)
+ : Dawg(type, lang, perm, debug_level),
+@@ -441,7 +441,7 @@ public:
+ return true;
+ }
+
+- int NumEdges() {
++ uint32_t NumEdges() const {
+ return num_edges_;
+ }
+
+@@ -462,7 +462,11 @@ public:
+ if (!word_end || end_of_word_from_edge_rec(edges_[edge])) {
+ vec->push_back(NodeChild(unichar_id_from_edge_rec(edges_[edge]), edge));
+ }
+- } while (!last_edge(edge++));
++ if (last_edge(edge)) {
++ break;
++ }
++ ++edge;
++ } while (edge < num_edges_);
+ }
+
+ /// Returns the next node visited by following the edge
+@@ -516,7 +520,7 @@ private:
+ }
+ /// Goes through all the edges and clears each one out.
+ inline void clear_all_edges() {
+- for (int edge = 0; edge < num_edges_; edge++) {
++ for (uint32_t edge = 0; edge < num_edges_; edge++) {
+ set_empty_edge(edge);
+ }
+ }
+@@ -555,7 +559,7 @@ private:
+ /// Prints the contents of the SquishedDawg.
+ void print_all(const char *msg) {
+ tprintf("\n__________________________\n%s\n", msg);
+- for (int i = 0; i < num_edges_; ++i) {
++ for (uint32_t i = 0; i < num_edges_; ++i) {
+ print_edge(i);
+ }
+ tprintf("__________________________\n");
+@@ -565,7 +569,7 @@ private:
+
+ // Member variables.
+ EDGE_ARRAY edges_ = nullptr;
+- int32_t num_edges_ = 0;
++ uint32_t num_edges_ = 0;
+ int num_forward_edges_in_node0 = 0;
+ };
+
+diff --git a/src/lstm/plumbing.cpp b/src/lstm/plumbing.cpp
+index ebb6612e..f0133148 100644
+--- a/src/lstm/plumbing.cpp
++++ b/src/lstm/plumbing.cpp
+@@ -222,6 +222,10 @@ bool Plumbing::DeSerialize(TFile *fp) {
+ if (!fp->DeSerialize(&size)) {
+ return false;
+ }
++ // Reject unreasonably large network stacks.
++ if (size > 10000) {
++ return false;
++ }
+ for (uint32_t i = 0; i < size; ++i) {
+ Network *network = CreateFromFile(fp);
+ if (network == nullptr) {
+diff --git a/src/lstm/weightmatrix.cpp b/src/lstm/weightmatrix.cpp
+index 86255266..d40e9373 100644
+--- a/src/lstm/weightmatrix.cpp
++++ b/src/lstm/weightmatrix.cpp
+@@ -295,6 +295,10 @@ bool WeightMatrix::DeSerialize(bool training, TFile *fp) {
+ if (!fp->DeSerialize(&size)) {
+ return false;
+ }
++ // Reject unreasonably large scale vectors.
++ if (size > 100000000) {
++ return false;
++ }
+ #ifdef FAST_FLOAT
+ scales_.reserve(size);
+ for (auto n = size; n > 0; n--) {
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-2.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-2.patch
new file mode 100644
index 0000000000..04d1db2dc5
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73067-2.patch
@@ -0,0 +1,96 @@
+From 364a1a76ed19fd2f975f267405f8e36f521e7175 Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Sun, 12 Jul 2026 18:04:46 +0200
+Subject: [PATCH] Fix unbounded allocation and validate DAWG edge structure
+
+Bound num_edges_ against remaining component bytes before allocating
+the edges_ array in read_squished_dawg. This prevents a crafted
+num_edges_ = 0x7FFFFFFF from requesting ~17 GB and aborting via
+uncaught std::bad_alloc.
+
+After loading, validate the edge structure:
+- Reject edges whose next_node value exceeds num_edges_ (wild-index
+ read during dictionary lookup).
+- Reject unterminated forward edge runs (the original heap OOB read).
+
+Also add TFile::RemainingBytes() to query how many bytes are left to
+read from the current position.
+
+Assisted-by: OpenCode / big-pickle (opencode)
+Reported-by: GitHub Copilot
+Signed-off-by: Stefan Weil <stweil@tessus.org>
+(cherry picked from commit 55287a94b8044c05ce3fd10f5aca6ebbd238e518)
+
+CVE: CVE-2026-73067
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/55287a94b8044c05ce3fd10f5aca6ebbd238e518]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/ccutil/serialis.h | 4 ++++
+ src/dict/dawg.cpp | 32 ++++++++++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+)
+
+diff --git a/src/ccutil/serialis.h b/src/ccutil/serialis.h
+index d6e4a996..59a4e1f1 100644
+--- a/src/ccutil/serialis.h
++++ b/src/ccutil/serialis.h
+@@ -75,6 +75,10 @@ public:
+ void set_swap(bool value) {
+ swap_ = value;
+ }
++ // Returns the number of bytes remaining to be read.
++ size_t RemainingBytes() const {
++ return data_ != nullptr && offset_ < data_->size() ? data_->size() - offset_ : 0;
++ }
+
+ // Deserialize data.
+ bool DeSerializeSize(int32_t *data);
+diff --git a/src/dict/dawg.cpp b/src/dict/dawg.cpp
+index dab4dfc2..75cb9a1f 100644
+--- a/src/dict/dawg.cpp
++++ b/src/dict/dawg.cpp
+@@ -353,12 +353,44 @@ bool SquishedDawg::read_squished_dawg(TFile *file) {
+ tprintf("Empty dawg: num_edges is 0\n");
+ return false;
+ }
++ // Reject if the declared edge count exceeds the remaining component bytes.
++ if (num_edges_ > file->RemainingBytes() / sizeof(EDGE_RECORD)) {
++ tprintf("Dawg num_edges %u exceeds remaining data\n", num_edges_);
++ return false;
++ }
+ Dawg::init(unicharset_size);
+
+ edges_ = new EDGE_RECORD[num_edges_];
+ if (!file->DeSerialize(&edges_[0], num_edges_)) {
+ return false;
+ }
++ // Validate the loaded edge structure: check that next_node values are in
++ // bounds and that forward edge runs are properly terminated.
++ for (uint32_t i = 0; i < num_edges_; ++i) {
++ if (edges_[i] == next_node_mask_) {
++ continue; // Empty slot.
++ }
++ NODE_REF next = next_node_from_edge_rec(edges_[i]);
++ if (next != 0 && static_cast<uint32_t>(next) >= num_edges_) {
++ tprintf("Dawg edge %u has out-of-bounds next_node\n", i);
++ return false;
++ }
++ if (forward_edge(i)) {
++ uint32_t j = i;
++ bool terminated = false;
++ do {
++ if (last_edge(j)) {
++ terminated = true;
++ break;
++ }
++ ++j;
++ } while (j < num_edges_);
++ if (!terminated) {
++ tprintf("Dawg forward edge run starting at %u is not terminated\n", i);
++ return false;
++ }
++ }
++ }
+ if (debug_level_ > 2) {
+ tprintf("type: %d lang: %s perm: %d unicharset_size: %d num_edges: %" PRIu32 "\n",
+ type_, lang_.c_str(), perm_, unicharset_size_, num_edges_);
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
index 1b5a5fe2df..f26d2f36a1 100644
--- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
+++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
@@ -8,6 +8,8 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57"
SRCREV = "6e1d56a847e697de07b38619356550e5cf4e8633"
SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag=${PV} \
file://CVE-2026-73066.patch \
+ file://CVE-2026-73067-1.patch \
+ file://CVE-2026-73067-2.patch \
"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 20/33] wolfssl: mark CVEs patched
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (17 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-oe][wrynose][PATCH 19/33] tesseract: patch CVE-2026-73067 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 21/33] wolfssl: patch CVE-2026-10098 ankur.tyagi85
` (12 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
CVE-2023-6937[1] and CVE-2024-5814[2] are patched in current version.
Details:
https://nvd.nist.gov/vuln/detail/cve-2023-6937
https://nvd.nist.gov/vuln/detail/cve-2024-5814
[1]https://github.com/wolfSSL/wolfssl/blob/v5.6.6-stable/ChangeLog.md#vulnerabilities
[2]https://github.com/wolfSSL/wolfssl/blob/v5.7.2-stable/ChangeLog.md#vulnerabilities
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 2978ff1cc1..295029246c 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -69,3 +69,5 @@ CVE_STATUS[CVE-2026-5504] = "fixed-version: fixed in 5.9.1"
CVE_STATUS[CVE-2026-5507] = "fixed-version: fixed in 5.9.1"
CVE_STATUS[CVE-2026-5772] = "fixed-version: fixed in 5.9.1"
CVE_STATUS[CVE-2026-5778] = "fixed-version: fixed in 5.9.1"
+CVE_STATUS[CVE-2023-6937] = "fixed-version: fixed since v5.6.6"
+CVE_STATUS[CVE-2024-5814] = "fixed-version: fixed since v5.7.2"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 21/33] wolfssl: patch CVE-2026-10098
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (18 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 20/33] wolfssl: mark CVEs patched ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 22/33] wolfssl: patch CVE-2026-10512 ankur.tyagi85
` (11 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
CVE-2026-10098-1.patch is needed to cherry-pick the commit mentioned in the
PR[1]
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-10098
[1]https://github.com/wolfSSL/wolfssl/pull/10554/commits
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-10098-1.patch | 368 ++++++++++++++++++
.../wolfssl/files/CVE-2026-10098-2.patch | 126 ++++++
.../wolfssl/wolfssl_5.9.1.bb | 2 +
3 files changed, 496 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-2.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-1.patch
new file mode 100644
index 0000000000..86f1c8a9a0
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-1.patch
@@ -0,0 +1,368 @@
+From d2dca1bdcb6dc5f6fb02c44a042130cd11d27cd0 Mon Sep 17 00:00:00 2001
+From: Colton Willey <colton@wolfssl.com>
+Date: Mon, 13 Apr 2026 20:29:50 -0700
+Subject: [PATCH] Fix NULL derefs, buffer overflow, and i2d contract in
+ EVP/OCSP/X509
+
+Harden OpenSSL compatibility layer against NULL pointers, negative lengths,
+and buffer overflows across EVP, OCSP, and X509 APIs. Fix DSA SignFinal
+write-before-check overflow, add missing i2d_OCSP_RESPONSE allocation path,
+and fix unaligned keyUsage access.
+
+(cherry picked from commit 58a27848a800aadca91f606dc4fe5234b9971011)
+
+CVE: CVE-2026-10098
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/58a27848a800aadca91f606dc4fe5234b9971011]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/ocsp.c | 39 +++++++++++++++++++++++++------
+ src/ssl.c | 8 ++++++-
+ src/x509.c | 12 ++++++++--
+ tests/api.c | 2 +-
+ wolfcrypt/src/evp.c | 50 ++++++++++++++++++++++++++++++++--------
+ wolfcrypt/src/pwdbased.c | 3 +++
+ 6 files changed, 94 insertions(+), 20 deletions(-)
+
+diff --git a/src/ocsp.c b/src/ocsp.c
+index 2fff7ced5..9de23cb9e 100644
+--- a/src/ocsp.c
++++ b/src/ocsp.c
+@@ -749,7 +749,9 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs,
+
+ single = bs->single;
+ while (single != NULL) {
+- if ((XMEMCMP(single->status->serial, id->status->serial, (size_t)single->status->serialSz) == 0)
++ if (single->status != NULL && id->status != NULL &&
++ (XMEMCMP(single->status->serial, id->status->serial,
++ (size_t)single->status->serialSz) == 0)
+ && (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0)
+ && (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) {
+ break;
+@@ -757,7 +759,7 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs,
+ single = single->next;
+ }
+
+- if (single == NULL)
++ if (single == NULL || single->status == NULL)
+ return WOLFSSL_FAILURE;
+
+ if (status != NULL)
+@@ -1108,6 +1110,9 @@ int wolfSSL_OCSP_basic_verify(WOLFSSL_OCSP_BASICRESP* bs,
+ int embedded;
+ DecodedCert *cert = NULL;
+
++ if (bs == NULL)
++ return WOLFSSL_FAILURE;
++
+ ret = OcspFindSigner(bs, certs, &cert, &embedded, flags);
+ if (ret != 0) {
+ WOLFSSL_MSG("OCSP no signer found");
+@@ -1300,15 +1305,31 @@ int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response,
+ if (response == NULL)
+ return BAD_FUNC_ARG;
+
++ if (response->source == NULL)
++ return BAD_FUNC_ARG;
++
+ if (data == NULL)
+ return (int)response->maxIdx;
+
+- XMEMCPY(*data, response->source, response->maxIdx);
++ if (*data == NULL) {
++ *data = (unsigned char*)XMALLOC(response->maxIdx, NULL,
++ DYNAMIC_TYPE_OPENSSL);
++ if (*data == NULL)
++ return -1;
++ XMEMCPY(*data, response->source, response->maxIdx);
++ }
++ else {
++ XMEMCPY(*data, response->source, response->maxIdx);
++ *data += response->maxIdx;
++ }
++
+ return (int)response->maxIdx;
+ }
+
+ int wolfSSL_OCSP_response_status(OcspResponse *response)
+ {
++ if (response == NULL)
++ return -1;
+ return response->responseStatus;
+ }
+
+@@ -1335,8 +1356,12 @@ const char *wolfSSL_OCSP_response_status_str(long s)
+ WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response)
+ {
+ WOLFSSL_OCSP_BASICRESP* bs;
+- const unsigned char *ptr = response->source;
++ const unsigned char *ptr;
+
++ if (response == NULL || response->source == NULL)
++ return NULL;
++
++ ptr = response->source;
+ bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx);
+ return bs;
+ }
+@@ -1637,8 +1662,8 @@ int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single,
+ WOLFSSL_ASN1_TIME **thisupd,
+ WOLFSSL_ASN1_TIME **nextupd)
+ {
+- if (single == NULL)
+- return WOLFSSL_FAILURE;
++ if (single == NULL || single->status == NULL)
++ return -1;
+
+ #ifdef WOLFSSL_OCSP_PARSE_STATUS
+ if (thisupd != NULL)
+@@ -1784,7 +1809,7 @@ int wolfSSL_OCSP_REQ_CTX_add1_header(WOLFSSL_OCSP_REQ_CTX *ctx,
+ {
+ WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_add1_header");
+
+- if (name == NULL) {
++ if (ctx == NULL || name == NULL) {
+ WOLFSSL_MSG("Bad parameter");
+ return WOLFSSL_FAILURE;
+ }
+diff --git a/src/ssl.c b/src/ssl.c
+index 58cd6701c..7d6ca462b 100644
+--- a/src/ssl.c
++++ b/src/ssl.c
+@@ -11699,13 +11699,19 @@ char* wolfSSL_CIPHER_description(const WOLFSSL_CIPHER* cipher, char* in,
+ int wolfSSL_OCSP_parse_url(const char* url, char** host, char** port,
+ char** path, int* ssl)
+ {
+- const char* u = url;
++ const char* u;
+ const char* upath; /* path in u */
+ const char* uport; /* port in u */
+ const char* hostEnd;
+
+ WOLFSSL_ENTER("OCSP_parse_url");
+
++ if (url == NULL || host == NULL || port == NULL || path == NULL ||
++ ssl == NULL) {
++ return WOLFSSL_FAILURE;
++ }
++
++ u = url;
+ *host = NULL;
+ *port = NULL;
+ *path = NULL;
+diff --git a/src/x509.c b/src/x509.c
+index 46dfd38ed..b92f7d735 100644
+--- a/src/x509.c
++++ b/src/x509.c
+@@ -1317,7 +1317,9 @@ int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext,
+ if (ext && ext->value.data) {
+ if (ext->value.length == sizeof(word16)) {
+ /* if ext->value is already word16, set directly */
+- x509->keyUsage = *(word16*)ext->value.data;
++ word16 ku;
++ XMEMCPY(&ku, ext->value.data, sizeof(word16));
++ x509->keyUsage = ku;
+ #ifdef BIG_ENDIAN_ORDER
+ x509->keyUsage = rotlFixed16(x509->keyUsage, 8U);
+ #endif
+@@ -10998,6 +11000,11 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509)
+ if (x509->serialNumber != NULL)
+ return x509->serialNumber;
+
++ if (x509->serialSz < 0) {
++ WOLFSSL_MSG("Invalid serial number size");
++ return NULL;
++ }
++
+ a = wolfSSL_ASN1_INTEGER_new();
+ if (a == NULL)
+ return NULL;
+@@ -16120,7 +16127,8 @@ int wolfSSL_X509_set1_notBefore(WOLFSSL_X509* x509, const WOLFSSL_ASN1_TIME *t)
+ int wolfSSL_X509_set_serialNumber(WOLFSSL_X509* x509, WOLFSSL_ASN1_INTEGER* s)
+ {
+ WOLFSSL_ENTER("wolfSSL_X509_set_serialNumber");
+- if (x509 == NULL || s == NULL || s->length >= EXTERNAL_SERIAL_SIZE)
++ if (x509 == NULL || s == NULL || s->data == NULL ||
++ s->length >= EXTERNAL_SERIAL_SIZE)
+ return WOLFSSL_FAILURE;
+
+ /* WOLFSSL_ASN1_INTEGER has type | size | data
+diff --git a/tests/api.c b/tests/api.c
+index 5eca4071a..919b951b3 100644
+--- a/tests/api.c
++++ b/tests/api.c
+@@ -20274,7 +20274,7 @@ static int test_wolfSSL_OCSP_single_get0_status(void)
+ ExpectPtrEq(nextDate, &certStatus.nextDateParsed);
+
+ ExpectIntEQ(wolfSSL_OCSP_single_get0_status(NULL, NULL, NULL, NULL, NULL),
+- CERT_GOOD);
++ -1);
+ ExpectIntEQ(wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, NULL,
+ NULL), CERT_GOOD);
+ #endif
+diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c
+index 05e406883..996f31f86 100644
+--- a/wolfcrypt/src/evp.c
++++ b/wolfcrypt/src/evp.c
+@@ -1314,7 +1314,11 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out,
+ #ifndef WOLFSSL_AESGCM_STREAM
+ if ((ctx->authBuffer && ctx->authBufferLen > 0)
+ || (ctx->authBufferLen == 0)) {
+- if (ctx->enc)
++ if (ctx->authBufferLen > 0 && out == NULL) {
++ ret = WOLFSSL_FAILURE;
++ *outl = 0;
++ }
++ else if (ctx->enc)
+ ret = wc_AesGcmEncrypt(&ctx->cipher.aes, out,
+ ctx->authBuffer, ctx->authBufferLen,
+ ctx->iv, ctx->ivSz, ctx->authTag, ctx->authTagSz,
+@@ -1397,7 +1401,11 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out,
+ case WC_AES_256_CCM_TYPE:
+ if ((ctx->authBuffer && ctx->authBufferLen > 0)
+ || (ctx->authBufferLen == 0)) {
+- if (ctx->enc) {
++ if (ctx->authBufferLen > 0 && out == NULL) {
++ ret = WOLFSSL_FAILURE;
++ *outl = 0;
++ }
++ else if (ctx->enc) {
+ ret = wc_AesCcmEncrypt(&ctx->cipher.aes, out,
+ ctx->authBuffer, (word32)ctx->authBufferLen,
+ ctx->iv, (word32)ctx->ivSz, ctx->authTag,
+@@ -4309,16 +4317,19 @@ int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret,
+ #ifndef NO_DSA
+ case WC_EVP_PKEY_DSA: {
+ int bytes;
+- ret = wolfSSL_DSA_do_sign(md, sigret, pkey->dsa);
++ unsigned char tmpSig[DSA_MAX_SIG_SIZE];
++ ret = wolfSSL_DSA_do_sign(md, tmpSig, pkey->dsa);
+ /* wolfSSL_DSA_do_sign() can return WOLFSSL_FATAL_ERROR */
+ if (ret != WOLFSSL_SUCCESS)
+ return ret;
+ bytes = wolfSSL_BN_num_bytes(pkey->dsa->q);
+ if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE) ||
+- (int)*siglen < bytes * 2)
++ bytes > DSA_MAX_HALF_SIZE ||
++ bytes * 2 > (int)*siglen)
+ {
+ return WOLFSSL_FAILURE;
+ }
++ XMEMCPY(sigret, tmpSig, bytes * 2);
+ *siglen = (unsigned int)(bytes * 2);
+ return WOLFSSL_SUCCESS;
+ }
+@@ -4398,7 +4409,8 @@ int wolfSSL_EVP_VerifyFinal(WOLFSSL_EVP_MD_CTX *ctx,
+ unsigned char md[WC_MAX_DIGEST_SIZE];
+ unsigned int mdsize;
+
+- if (ctx == NULL) return WOLFSSL_FAILURE;
++ if (ctx == NULL || pkey == NULL || sig == NULL)
++ return WOLFSSL_FAILURE;
+ WOLFSSL_ENTER("EVP_VerifyFinal");
+ ret = wolfSSL_EVP_DigestFinal(ctx, md, &mdsize);
+ if (ret <= 0)
+@@ -4459,6 +4471,9 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_mac_key(int type, WOLFSSL_ENGINE* e,
+ if (type != WC_EVP_PKEY_HMAC || (key == NULL && keylen != 0))
+ return NULL;
+
++ if (keylen < 0)
++ return NULL;
++
+ pkey = wolfSSL_EVP_PKEY_new();
+ if (pkey != NULL) {
+ pkey->pkey.ptr = (char*)XMALLOC((size_t)keylen, NULL,
+@@ -4870,6 +4885,9 @@ int wolfSSL_EVP_DigestSignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sig,
+ return WOLFSSL_SUCCESS;
+ }
+ }
++ else if (ctx->pctx == NULL || ctx->pctx->pkey == NULL) {
++ return WOLFSSL_FAILURE;
++ }
+ #ifndef NO_RSA
+ else if (ctx->pctx->pkey->type == WC_EVP_PKEY_RSA) {
+ if (sig == NULL) {
+@@ -5007,6 +5025,8 @@ int wolfSSL_EVP_DigestVerifyFinal(WOLFSSL_EVP_MD_CTX *ctx,
+ return WOLFSSL_FAILURE;
+ }
+ else {
++ if (ctx->pctx == NULL || ctx->pctx->pkey == NULL)
++ return WOLFSSL_FAILURE;
+ /* Verify the signature with the digest. */
+ switch (ctx->pctx->pkey->type) {
+ #if !defined(NO_RSA)
+@@ -10232,6 +10252,9 @@ int wolfSSL_EVP_Digest(const unsigned char* in, int inSz, unsigned char* out,
+ return WOLFSSL_FAILURE;
+ }
+
++ if (inSz < 0)
++ return WOLFSSL_FAILURE;
++
+ err = wolfSSL_EVP_get_hashinfo(evp, &hashType, &hashSz);
+ if (err != WOLFSSL_SUCCESS)
+ return err;
+@@ -11279,6 +11302,7 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
+ enum wc_HashType macType;
+
+ WOLFSSL_ENTER("wolfSSL_EVP_DigestFinal");
++
+ macType = EvpMd2MacType(wolfSSL_EVP_MD_CTX_md(ctx));
+ switch (macType) {
+ case WC_HASH_TYPE_MD4:
+@@ -11304,16 +11328,18 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type)
+
+ case WC_HASH_TYPE_SHAKE128:
+ #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128)
+- *s = 16; /* if mixing up XOF with plain digest 128 bit is
+- * default for SHAKE128 */
++ if (s != NULL)
++ *s = 16; /* if mixing up XOF with plain digest 128 bit is
++ * default for SHAKE128 */
+ #else
+ return WOLFSSL_FAILURE;
+ #endif
+ break;
+ case WC_HASH_TYPE_SHAKE256:
+ #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE256)
+- *s = 32; /* if mixing up XOF with plain digest 256 bit is
+- * default for SHAKE256 */
++ if (s != NULL)
++ *s = 32; /* if mixing up XOF with plain digest 256 bit is
++ * default for SHAKE256 */
+ #else
+ return WOLFSSL_FAILURE;
+ #endif
+@@ -12881,6 +12907,9 @@ int wolfSSL_EVP_EncodeBlock(unsigned char *out, const unsigned char *in,
+ if (out == NULL || in == NULL)
+ return WOLFSSL_FATAL_ERROR;
+
++ if (inLen < 0)
++ return WOLFSSL_FATAL_ERROR;
++
+ if (Base64_Encode_NoNl(in, (word32)inLen, out, &ret) == 0)
+ return (int)ret;
+ else
+@@ -12897,6 +12926,9 @@ int wolfSSL_EVP_DecodeBlock(unsigned char *out, const unsigned char *in,
+ if (out == NULL || in == NULL)
+ return WOLFSSL_FATAL_ERROR;
+
++ if (inLen < 0)
++ return WOLFSSL_FATAL_ERROR;
++
+ if (Base64_Decode(in, (word32)inLen, out, &ret) == 0)
+ return (int)ret;
+ else
+diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c
+index c2ed5c042..4b9502a8c 100644
+--- a/wolfcrypt/src/pwdbased.c
++++ b/wolfcrypt/src/pwdbased.c
+@@ -76,6 +76,9 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen,
+ return BAD_FUNC_ARG;
+ }
+
++ if (keyLen > INT_MAX - ivLen)
++ return BAD_FUNC_ARG;
++
+ if (iterations <= 0)
+ iterations = 1;
+
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-2.patch
new file mode 100644
index 0000000000..0c28dbbba0
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10098-2.patch
@@ -0,0 +1,126 @@
+From 1260ee5fe45c6e4c0aeb62c26a04db5faec6ff08 Mon Sep 17 00:00:00 2001
+From: Reda Chouk <reda@wolfssl.com>
+Date: Fri, 29 May 2026 05:10:21 -0700
+Subject: [PATCH] Require equal serial lengths before comparing serial bytes so
+ a response serial that is only a prefix of the requested serial is not
+ treated as a match
+
+(cherry picked from commit 53e1db478b62f40ad6ce9b1a1d65d593a9d3d9fb)
+
+CVE: CVE-2026-10098
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/53e1db478b62f40ad6ce9b1a1d65d593a9d3d9fb]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/ocsp.c | 3 ++-
+ tests/api.c | 1 +
+ tests/api/test_ocsp.c | 57 +++++++++++++++++++++++++++++++++++++++++++
+ tests/api/test_ocsp.h | 1 +
+ 4 files changed, 61 insertions(+), 1 deletion(-)
+
+diff --git a/src/ocsp.c b/src/ocsp.c
+index 9de23cb9e..b8352ee72 100644
+--- a/src/ocsp.c
++++ b/src/ocsp.c
+@@ -750,7 +750,8 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs,
+ single = bs->single;
+ while (single != NULL) {
+ if (single->status != NULL && id->status != NULL &&
+- (XMEMCMP(single->status->serial, id->status->serial,
++ (single->status->serialSz == id->status->serialSz)
++ && (XMEMCMP(single->status->serial, id->status->serial,
+ (size_t)single->status->serialSz) == 0)
+ && (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0)
+ && (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) {
+diff --git a/tests/api.c b/tests/api.c
+index 919b951b3..ad5f84573 100644
+--- a/tests/api.c
++++ b/tests/api.c
+@@ -35924,6 +35924,7 @@ TEST_CASE testCases[] = {
+ TEST_DECL(test_ocsp_response_parsing),
+ TEST_DECL(test_ocsp_certid_enc_dec),
+ TEST_DECL(test_ocsp_certid_dup),
++ TEST_DECL(test_ocsp_resp_find_status_serial_prefix),
+ TEST_DECL(test_ocsp_tls_cert_cb),
+ TEST_DECL(test_ocsp_cert_unknown_crl_fallback),
+ TEST_DECL(test_ocsp_cert_unknown_crl_fallback_nonleaf),
+diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c
+index 02938adfd..1f1055fc6 100644
+--- a/tests/api/test_ocsp.c
++++ b/tests/api/test_ocsp.c
+@@ -723,6 +723,63 @@ int test_ocsp_certid_dup(void)
+ }
+ #endif
+
++int test_ocsp_resp_find_status_serial_prefix(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA) && !defined(NO_SHA)
++ OcspResponse response;
++ OcspEntry single;
++ CertStatus responseStatus;
++ OcspEntry requestedId;
++ CertStatus requestedStatus;
++ int status;
++
++ XMEMSET(&response, 0, sizeof(response));
++ XMEMSET(&single, 0, sizeof(single));
++ XMEMSET(&responseStatus, 0, sizeof(responseStatus));
++ XMEMSET(&requestedId, 0, sizeof(requestedId));
++ XMEMSET(&requestedStatus, 0, sizeof(requestedStatus));
++
++ single.status = &responseStatus;
++ requestedId.status = &requestedStatus;
++ response.single = &single;
++
++ /* Matching issuer name and key hashes on both sides. */
++ XMEMSET(single.issuerHash, 0x41, OCSP_DIGEST_SIZE);
++ XMEMSET(single.issuerKeyHash, 0x42, OCSP_DIGEST_SIZE);
++ XMEMCPY(requestedId.issuerHash, single.issuerHash, OCSP_DIGEST_SIZE);
++ XMEMCPY(requestedId.issuerKeyHash, single.issuerKeyHash, OCSP_DIGEST_SIZE);
++
++ /* Response carries a CERT_GOOD status for serial 01:02 (2 bytes). */
++ responseStatus.serial[0] = 0x01;
++ responseStatus.serial[1] = 0x02;
++ responseStatus.serialSz = 2;
++ responseStatus.status = CERT_GOOD;
++
++ /* Sanity check: an exact serial match must be found and report CERT_GOOD. */
++ requestedStatus.serial[0] = 0x01;
++ requestedStatus.serial[1] = 0x02;
++ requestedStatus.serialSz = 2;
++ status = -1;
++ ExpectIntEQ(wolfSSL_OCSP_resp_find_status(&response, &requestedId, &status,
++ NULL, NULL, NULL, NULL), WOLFSSL_SUCCESS);
++ ExpectIntEQ(status, CERT_GOOD);
++
++ /* Request serial 01:02:03 (3 bytes) shares the 01:02 prefix of the
++ * response serial.
++ * The lookup must not bind the good response to this longer and
++ * differing serial. */
++ requestedStatus.serial[0] = 0x01;
++ requestedStatus.serial[1] = 0x02;
++ requestedStatus.serial[2] = 0x03;
++ requestedStatus.serialSz = 3;
++ status = -1;
++ ExpectIntEQ(wolfSSL_OCSP_resp_find_status(&response, &requestedId, &status,
++ NULL, NULL, NULL, NULL), WOLFSSL_FAILURE);
++#endif
++ return EXPECT_RESULT();
++}
++
+ #if defined(HAVE_OCSP) && defined(WOLFSSL_CERT_SETUP_CB) && \
+ defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
+ (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
+diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h
+index 6df114b87..2ad56ce55 100644
+--- a/tests/api/test_ocsp.h
++++ b/tests/api/test_ocsp.h
+@@ -24,6 +24,7 @@
+
+ int test_ocsp_certid_enc_dec(void);
+ int test_ocsp_certid_dup(void);
++int test_ocsp_resp_find_status_serial_prefix(void);
+ int test_ocsp_status_callback(void);
+ int test_ocsp_basic_verify(void);
+ int test_ocsp_response_parsing(void);
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 295029246c..094bf8d78b 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -15,6 +15,8 @@ RPROVIDES:${PN} = "cyassl"
SRC_URI = " \
git://github.com/wolfSSL/wolfssl.git;protocol=https;branch=master;tag=v${PV}-stable \
file://run-ptest \
+ file://CVE-2026-10098-1.patch \
+ file://CVE-2026-10098-2.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 22/33] wolfssl: patch CVE-2026-10512
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (19 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 21/33] wolfssl: patch CVE-2026-10098 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 23/33] wolfssl: ignore CVE-2026-12340 ankur.tyagi85
` (10 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-10512
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-10512.patch | 182 ++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 183 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10512.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10512.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10512.patch
new file mode 100644
index 0000000000..74288c28f9
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-10512.patch
@@ -0,0 +1,182 @@
+From 8c3f7b9c7f7efb9e8539c2d98fe52e116f3402eb Mon Sep 17 00:00:00 2001
+From: Sean Parkinson <sean@wolfssl.com>
+Date: Wed, 27 May 2026 12:16:06 +1000
+Subject: [PATCH] X25519 x64 ASM: fix full reduction
+
+The last add was overflowing into the top bit.
+Must mask the last word to clear top bit.
+
+Add test vectors from Wycheproof.
+
+(cherry picked from commit 14b55a0bc42c4f2d2fa0a06af53a603ed619c9b0)
+
+CVE: CVE-2026-10512
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/14b55a0bc42c4f2d2fa0a06af53a603ed619c9b0]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ tests/api/test_curve25519.c | 93 +++++++++++++++++++++++++++++++++++
+ tests/api/test_curve25519.h | 2 +
+ wolfcrypt/src/fe_x25519_asm.S | 4 ++
+ 3 files changed, 99 insertions(+)
+
+diff --git a/tests/api/test_curve25519.c b/tests/api/test_curve25519.c
+index 36cf643f2..36c8e58af 100644
+--- a/tests/api/test_curve25519.c
++++ b/tests/api/test_curve25519.c
+@@ -353,6 +353,99 @@ int test_wc_curve25519_shared_secret_ex(void)
+ return EXPECT_RESULT();
+ } /* END test_wc_curve25519_shared_secret_ex */
+
++/*
++ * Known-answer tests for wc_curve25519_shared_secret_ex.
++ *
++ * Both vectors share one private scalar and produce a shared secret that is a
++ * small canonical value (9 and 16, little-endian). Because the result is close
++ * to a multiple of the field prime, these exercise the final modular reduction
++ * of the X25519 computation: a result that was only reduced mod 2^256 (or left
++ * in [p, 2^255)) instead of fully reduced mod 2^255-19 would not match.
++ * All values are 32-byte little-endian encodings per RFC 7748.
++ */
++int test_wc_curve25519_shared_secret_ex_kat(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)
++ /* Private scalar shared by both vectors. */
++ static const byte kPriv[CURVE25519_KEYSIZE] = {
++ 0x60, 0xa3, 0xa4, 0xf1, 0x30, 0xb9, 0x8a, 0x5b,
++ 0xe4, 0xb1, 0xce, 0xdb, 0x7c, 0xb8, 0x55, 0x84,
++ 0xa3, 0x52, 0x0e, 0x14, 0x2d, 0x47, 0x4d, 0xc9,
++ 0xcc, 0xb9, 0x09, 0xa0, 0x73, 0xa9, 0x76, 0x7f
++ };
++ /* Vector 1 public value, expected shared secret == 9. */
++ static const byte kPub1[CURVE25519_KEYSIZE] = {
++ 0x3b, 0x18, 0xdf, 0x1e, 0x50, 0xb8, 0x99, 0xeb,
++ 0xd5, 0x88, 0xc3, 0x16, 0x1c, 0xbd, 0x3b, 0xf9,
++ 0x8e, 0xbc, 0xc2, 0xc1, 0xf7, 0xdf, 0x53, 0xb8,
++ 0x11, 0xbd, 0x0e, 0x91, 0xb4, 0xd5, 0x15, 0x3d
++ };
++ static const byte kExpected1[CURVE25519_KEYSIZE] = {
++ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ };
++ /* Vector 2 public value, expected shared secret == 16. */
++ static const byte kPub2[CURVE25519_KEYSIZE] = {
++ 0xca, 0xb6, 0xf9, 0xe7, 0xd8, 0xce, 0x00, 0xdf,
++ 0xce, 0xa9, 0xbb, 0xd8, 0xf0, 0x69, 0xef, 0x7f,
++ 0xb2, 0xac, 0x50, 0x4a, 0xbf, 0x83, 0xb8, 0x7d,
++ 0xb6, 0x01, 0xb5, 0xae, 0x0a, 0x7f, 0x76, 0x15
++ };
++ static const byte kExpected2[CURVE25519_KEYSIZE] = {
++ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++ };
++ /* Table-driven so both vectors run through the identical code path. */
++ struct {
++ const byte* pub;
++ const byte* expected;
++ } vec[2];
++ curve25519_key private_key;
++ curve25519_key public_key;
++ WC_RNG rng;
++ byte out[CURVE25519_KEYSIZE];
++ word32 outLen;
++ int i;
++
++ vec[0].pub = kPub1; vec[0].expected = kExpected1;
++ vec[1].pub = kPub2; vec[1].expected = kExpected2;
++
++ XMEMSET(&rng, 0, sizeof(WC_RNG));
++ ExpectIntEQ(wc_InitRng(&rng), 0);
++
++ for (i = 0; i < 2; i++) {
++ XMEMSET(&private_key, 0, sizeof(private_key));
++ XMEMSET(&public_key, 0, sizeof(public_key));
++ ExpectIntEQ(wc_curve25519_init(&private_key), 0);
++ ExpectIntEQ(wc_curve25519_init(&public_key), 0);
++ #ifdef WOLFSSL_CURVE25519_BLINDING
++ ExpectIntEQ(wc_curve25519_set_rng(&private_key, &rng), 0);
++ #endif
++ ExpectIntEQ(wc_curve25519_import_private_ex(kPriv, sizeof(kPriv),
++ &private_key, EC25519_LITTLE_ENDIAN), 0);
++ ExpectIntEQ(wc_curve25519_import_public_ex(vec[i].pub,
++ CURVE25519_KEYSIZE, &public_key, EC25519_LITTLE_ENDIAN), 0);
++
++ outLen = sizeof(out);
++ ExpectIntEQ(wc_curve25519_shared_secret_ex(&private_key, &public_key,
++ out, &outLen, EC25519_LITTLE_ENDIAN), 0);
++ ExpectIntEQ(outLen, CURVE25519_KEYSIZE);
++ ExpectIntEQ(XMEMCMP(out, vec[i].expected, CURVE25519_KEYSIZE), 0);
++
++ wc_curve25519_free(&private_key);
++ wc_curve25519_free(&public_key);
++ }
++
++ DoExpectIntEQ(wc_FreeRng(&rng), 0);
++#endif
++ return EXPECT_RESULT();
++} /* END test_wc_curve25519_shared_secret_ex_kat */
++
+ /*
+ * Testing wc_curve25519_make_pub
+ */
+diff --git a/tests/api/test_curve25519.h b/tests/api/test_curve25519.h
+index 0e0e65287..770b509af 100644
+--- a/tests/api/test_curve25519.h
++++ b/tests/api/test_curve25519.h
+@@ -30,6 +30,7 @@ int test_wc_curve25519_export_key_raw(void);
+ int test_wc_curve25519_export_key_raw_ex(void);
+ int test_wc_curve25519_make_key(void);
+ int test_wc_curve25519_shared_secret_ex(void);
++int test_wc_curve25519_shared_secret_ex_kat(void);
+ int test_wc_curve25519_make_pub(void);
+ int test_wc_curve25519_export_public_ex(void);
+ int test_wc_curve25519_export_private_raw_ex(void);
+@@ -43,6 +44,7 @@ int test_wc_curve25519_import_private(void);
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_key_raw_ex), \
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_make_key), \
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_shared_secret_ex), \
++ TEST_DECL_GROUP("curve25519", test_wc_curve25519_shared_secret_ex_kat), \
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_make_pub), \
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_public_ex), \
+ TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_private_raw_ex), \
+diff --git a/wolfcrypt/src/fe_x25519_asm.S b/wolfcrypt/src/fe_x25519_asm.S
+index f4cdf343c..5abe4cd5e 100644
+--- a/wolfcrypt/src/fe_x25519_asm.S
++++ b/wolfcrypt/src/fe_x25519_asm.S
+@@ -4639,6 +4639,7 @@ L_curve25519_base_x64_3:
+ adcq $0x00, %r8
+ adcq $0x00, %r9
+ adcq $0x00, %r10
++ andq %rax, %r10
+ # Store
+ movq %rcx, (%rdi)
+ movq %r8, 8(%rdi)
+@@ -7054,6 +7055,7 @@ L_curve25519_x64_3:
+ adcq $0x00, %r9
+ adcq $0x00, %r10
+ adcq $0x00, %r11
++ andq %rax, %r11
+ # Store
+ movq %rcx, (%rdi)
+ movq %r9, 8(%rdi)
+@@ -15107,6 +15109,7 @@ L_curve25519_base_avx2_last_3:
+ adcq $0x00, %r9
+ adcq $0x00, %r10
+ adcq $0x00, %r11
++ andq %rcx, %r11
+ # Store
+ movq %r8, (%rdi)
+ movq %r9, 8(%rdi)
+@@ -17116,6 +17119,7 @@ L_curve25519_avx2_last_3:
+ adcq $0x00, %r10
+ adcq $0x00, %r11
+ adcq $0x00, %r12
++ andq %rcx, %r12
+ # Store
+ movq %r9, (%rdi)
+ movq %r10, 8(%rdi)
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 094bf8d78b..4f6ae56567 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -17,6 +17,7 @@ SRC_URI = " \
file://run-ptest \
file://CVE-2026-10098-1.patch \
file://CVE-2026-10098-2.patch \
+ file://CVE-2026-10512.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 23/33] wolfssl: ignore CVE-2026-12340
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (20 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 22/33] wolfssl: patch CVE-2026-10512 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 24/33] wolfssl: patch CVE-2026-55958 ankur.tyagi85
` (9 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-12340
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb | 1 +
1 file changed, 1 insertion(+)
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 4f6ae56567..f69497a80c 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -74,3 +74,4 @@ CVE_STATUS[CVE-2026-5772] = "fixed-version: fixed in 5.9.1"
CVE_STATUS[CVE-2026-5778] = "fixed-version: fixed in 5.9.1"
CVE_STATUS[CVE-2023-6937] = "fixed-version: fixed since v5.6.6"
CVE_STATUS[CVE-2024-5814] = "fixed-version: fixed since v5.7.2"
+CVE_STATUS[CVE-2026-12340] = "not-applicable-config: this only affects builds with SM2 support"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 24/33] wolfssl: patch CVE-2026-55958
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (21 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 23/33] wolfssl: ignore CVE-2026-12340 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 25/33] wolfssl: patch CVE-2026-6091 ankur.tyagi85
` (8 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-55958
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-55958.patch | 36 +++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 37 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-55958.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-55958.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-55958.patch
new file mode 100644
index 0000000000..a1f77eb40f
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-55958.patch
@@ -0,0 +1,36 @@
+From ea7c289d463c506fe3eb856c5d128b7d0e1799b7 Mon Sep 17 00:00:00 2001
+From: Chris Conlon <chris@wolfssl.com>
+Date: Tue, 16 Jun 2026 13:58:52 -0600
+Subject: [PATCH] Renesas TSIP: skip XMEMCPY on MEMORY_E from
+ tsip_StoreMessage()
+
+(cherry picked from commit 6ebc379f313769ac49a84be0221c626162ffcfab)
+
+CVE: CVE-2026-55958
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/6ebc379f313769ac49a84be0221c626162ffcfab]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/port/Renesas/renesas_tsip_sha.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/wolfcrypt/src/port/Renesas/renesas_tsip_sha.c b/wolfcrypt/src/port/Renesas/renesas_tsip_sha.c
+index 0d5957744..d04072397 100644
+--- a/wolfcrypt/src/port/Renesas/renesas_tsip_sha.c
++++ b/wolfcrypt/src/port/Renesas/renesas_tsip_sha.c
+@@ -203,10 +203,11 @@ WOLFSSL_LOCAL int tsip_StoreMessage(struct WOLFSSL* ssl, const byte* data,
+ WOLFSSL_MSG("Capacity over error in tsip_StoreMessage");
+ ret = MEMORY_E;
+ }
+-
+- XMEMCPY(bag->buff + bag->buffIdx, data, sz);
+- bag->msgTypes[bag->msgIdx++] = *data; /* store message type */
+- bag->buffIdx += sz;
++ else {
++ XMEMCPY(bag->buff + bag->buffIdx, data, sz);
++ bag->msgTypes[bag->msgIdx++] = *data; /* store message type */
++ bag->buffIdx += sz;
++ }
+ }
+
+ WOLFSSL_LEAVE("tsip_StoreMessage", ret);
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index f69497a80c..e2bb53a646 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -18,6 +18,7 @@ SRC_URI = " \
file://CVE-2026-10098-1.patch \
file://CVE-2026-10098-2.patch \
file://CVE-2026-10512.patch \
+ file://CVE-2026-55958.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 25/33] wolfssl: patch CVE-2026-6091
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (22 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 24/33] wolfssl: patch CVE-2026-55958 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 26/33] wolfssl: patch CVE-2026-6092 ankur.tyagi85
` (7 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6091
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6091-1.patch | 295 ++++++++++++++++++
.../wolfssl/files/CVE-2026-6091-2.patch | 30 ++
.../wolfssl/files/CVE-2026-6091-3.patch | 36 +++
.../wolfssl/wolfssl_5.9.1.bb | 3 +
4 files changed, 364 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-2.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-3.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-1.patch
new file mode 100644
index 0000000000..644797266e
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-1.patch
@@ -0,0 +1,295 @@
+From 54bce5dd1dce8839ea64e0bddebe4c6f8fd2cc6c Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Wed, 8 Apr 2026 16:27:07 -0500
+Subject: [PATCH] Fix partial chain verification
+
+(cherry picked from commit a6fd25b94e0c03c1be265a4454390d7225e81129)
+
+CVE: CVE-2026-6091
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/a6fd25b94e0c03c1be265a4454390d7225e81129]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/x509_str.c | 90 ++++++++++++++++++++++-
+ tests/api/test_ossl_x509_str.c | 127 +++++++++++++++++++++++++++++++++
+ 2 files changed, 216 insertions(+), 1 deletion(-)
+
+diff --git a/src/x509_str.c b/src/x509_str.c
+index 90113caed..01e975198 100644
+--- a/src/x509_str.c
++++ b/src/x509_str.c
+@@ -552,6 +552,53 @@ static int X509VerifyCertSetupRetry(WOLFSSL_X509_STORE_CTX* ctx,
+ return ret;
+ }
+
++/* Returns 1 if cur and x509 have identical DER encodings, 0 otherwise. */
++static int X509DerEquals(WOLFSSL_X509* cur, WOLFSSL_X509* x509)
++{
++ if (cur == NULL || cur->derCert == NULL ||
++ x509 == NULL || x509->derCert == NULL) {
++ return 0;
++ }
++ if (cur->derCert->length != x509->derCert->length)
++ return 0;
++ return XMEMCMP(cur->derCert->buffer, x509->derCert->buffer,
++ x509->derCert->length) == 0;
++}
++
++/* Returns 1 if x509's DER matches an entry in either origTrustedSk (an
++ * immutable snapshot of the caller's trusted set captured before any
++ * intermediates were injected for this verification call) or in
++ * store->trusted. Returns 0 otherwise. Used by the
++ * X509_V_FLAG_PARTIAL_CHAIN fallback to confirm that a chain actually
++ * terminates at a caller-trusted certificate. */
++static int X509StoreCertIsTrusted(WOLFSSL_X509_STORE* store,
++ WOLFSSL_X509* x509, WOLF_STACK_OF(WOLFSSL_X509)* origTrustedSk)
++{
++ int i;
++ int n;
++
++ if (x509 == NULL || x509->derCert == NULL)
++ return 0;
++
++ if (origTrustedSk != NULL) {
++ n = wolfSSL_sk_X509_num(origTrustedSk);
++ for (i = 0; i < n; i++) {
++ if (X509DerEquals(wolfSSL_sk_X509_value(origTrustedSk, i), x509))
++ return 1;
++ }
++ }
++
++ if (store != NULL && store->trusted != NULL) {
++ n = wolfSSL_sk_X509_num(store->trusted);
++ for (i = 0; i < n; i++) {
++ if (X509DerEquals(wolfSSL_sk_X509_value(store->trusted, i), x509))
++ return 1;
++ }
++ }
++
++ return 0;
++}
++
+ /* Verifies certificate chain using WOLFSSL_X509_STORE_CTX
+ * returns 1 on success or <= 0 on failure.
+ */
+@@ -570,6 +617,7 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
+ WOLF_STACK_OF(WOLFSSL_X509)* certs = NULL;
+ WOLF_STACK_OF(WOLFSSL_X509)* certsToUse = NULL;
+ WOLF_STACK_OF(WOLFSSL_X509)* failedCerts = NULL;
++ WOLF_STACK_OF(WOLFSSL_X509)* origTrustedSk = NULL;
+ WOLFSSL_ENTER("wolfSSL_X509_verify_cert");
+
+ if (ctx == NULL || ctx->store == NULL || ctx->store->cm == NULL
+@@ -586,9 +634,37 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
+ if (certs == NULL &&
+ wolfSSL_sk_X509_num(ctx->ctxIntermediates) > 0) {
+ certsToUse = wolfSSL_sk_X509_new_null();
++ if (certsToUse == NULL) {
++ ret = WOLFSSL_FAILURE;
++ goto exit;
++ }
+ ret = addAllButSelfSigned(certsToUse, ctx->ctxIntermediates, NULL);
++ /* certsToUse holds only injected intermediates, none are trusted, so
++ * leave origTrustedSk NULL (empty snapshot). */
++ certs = certsToUse;
+ }
+ else {
++ /* Snapshot the caller-trusted entries before injecting the
++ * caller-supplied untrusted intermediates. Only the entries already
++ * present count as trusted for the partial-chain check below, and
++ * we need a stable reference because X509VerifyCertSetupRetry may
++ * remove nodes from `certs` during chain building. */
++ if (certs != NULL && wolfSSL_sk_X509_num(certs) > 0) {
++ int j;
++ int n = wolfSSL_sk_X509_num(certs);
++ origTrustedSk = wolfSSL_sk_X509_new_null();
++ if (origTrustedSk == NULL) {
++ ret = WOLFSSL_FAILURE;
++ goto exit;
++ }
++ for (j = 0; j < n; j++) {
++ if (wolfSSL_sk_X509_push(origTrustedSk,
++ wolfSSL_sk_X509_value(certs, j)) <= 0) {
++ ret = WOLFSSL_FAILURE;
++ goto exit;
++ }
++ }
++ }
+ /* Add the intermediates provided on init to the list of untrusted
+ * intermediates to be used */
+ ret = addAllButSelfSigned(certs, ctx->ctxIntermediates, &numInterAdd);
+@@ -677,9 +753,17 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
+ * a trusted CA in the CM */
+ ret = X509StoreVerifyCert(ctx);
+ if (ret != WOLFSSL_SUCCESS) {
++ /* WOLFSSL_PARTIAL_CHAIN may only terminate the chain at a
++ * certificate the caller actually trusts. The previous
++ * "added == 1" guard merely confirmed that some untrusted
++ * intermediate had been temporarily loaded into the
++ * CertManager during chain building, which would accept
++ * chains that never reach a trust anchor. Verify that
++ * ctx->current_cert is itself in the original trust set. */
+ if (((ctx->flags & WOLFSSL_PARTIAL_CHAIN) ||
+ (ctx->store->param->flags & WOLFSSL_PARTIAL_CHAIN)) &&
+- (added == 1)) {
++ X509StoreCertIsTrusted(ctx->store, ctx->current_cert,
++ origTrustedSk)) {
+ wolfSSL_sk_X509_push(ctx->chain, ctx->current_cert);
+ ret = WOLFSSL_SUCCESS;
+ } else {
+@@ -749,6 +833,10 @@ exit:
+ if (certsToUse != NULL) {
+ wolfSSL_sk_X509_free(certsToUse);
+ }
++ if (origTrustedSk != NULL) {
++ /* Shallow free: only the snapshot's stack nodes, not the X509s. */
++ wolfSSL_sk_X509_free(origTrustedSk);
++ }
+
+ /* Enforce hostname / IP verification from X509_VERIFY_PARAM if set.
+ * Always check against the leaf (end-entity) certificate, captured in
+diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c
+index 99b82877c..01ed9edfb 100644
+--- a/tests/api/test_ossl_x509_str.c
++++ b/tests/api/test_ossl_x509_str.c
+@@ -785,6 +785,127 @@ static int test_wolfSSL_X509_STORE_CTX_ex11(X509_STORE_test_data *testData)
+ return EXPECT_RESULT();
+ }
+
++static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_neg(
++ X509_STORE_test_data *testData)
++{
++ EXPECT_DECLS;
++ X509_STORE* store = NULL;
++ X509_STORE_CTX* ctx = NULL;
++ STACK_OF(X509)* untrusted = NULL;
++
++ /* Negative partial-chain test: with X509_V_FLAG_PARTIAL_CHAIN set, the
++ * intermediates are supplied ONLY as untrusted (passed through the
++ * X509_STORE_CTX_init "chain" argument and never added to the store).
++ * No certificate in the chain is in the store, so verification must
++ * fail. Pre-fix, wolfSSL_X509_verify_cert would incorrectly accept
++ * this chain because its partial-chain fallback only checked that some
++ * intermediate had been temporarily loaded into the CertManager, not
++ * that any chain certificate was actually trusted. */
++ ExpectNotNull(store = X509_STORE_new());
++ /* Intentionally do NOT add x509CaInt, x509CaInt2, or x509Ca. */
++ ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN), 1);
++
++ ExpectNotNull(untrusted = sk_X509_new_null());
++ ExpectIntGT(sk_X509_push(untrusted, testData->x509CaInt2), 0);
++ ExpectIntGT(sk_X509_push(untrusted, testData->x509CaInt), 0);
++
++ ExpectNotNull(ctx = X509_STORE_CTX_new());
++ ExpectIntEQ(X509_STORE_CTX_init(ctx, store, testData->x509Leaf, untrusted),
++ 1);
++ /* Must NOT verify: partial-chain does not relax the trust requirement. */
++ ExpectIntNE(X509_verify_cert(ctx), 1);
++ /* Verify the failure is specifically due to missing trust anchor, not
++ * some unrelated error. */
++ ExpectIntEQ(X509_STORE_CTX_get_error(ctx),
++ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
++
++ X509_STORE_CTX_free(ctx);
++ X509_STORE_free(store);
++ sk_X509_free(untrusted);
++ return EXPECT_RESULT();
++}
++
++static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_mixed(
++ X509_STORE_test_data *testData)
++{
++ EXPECT_DECLS;
++ X509_STORE* store = NULL;
++ X509_STORE_CTX* ctx = NULL;
++ STACK_OF(X509)* untrusted = NULL;
++
++ /* Mixed trusted-store + untrusted-chain partial-chain test: the store
++ * trusts an intermediate (x509CaInt2, the leaf's direct issuer), while
++ * an additional intermediate (x509CaInt) is supplied only as untrusted
++ * via the chain argument. With X509_V_FLAG_PARTIAL_CHAIN, verification
++ * must succeed by terminating at the trusted intermediate. This test
++ * exercises the snapshot-based trust check in X509StoreCertIsTrusted:
++ * the untrusted intermediate injected during verification must not be
++ * treated as a trust anchor, but the intermediate already in the store
++ * must be. */
++ ExpectNotNull(store = X509_STORE_new());
++ ExpectIntEQ(X509_STORE_add_cert(store, testData->x509CaInt2), 1);
++ ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN), 1);
++
++ ExpectNotNull(untrusted = sk_X509_new_null());
++ ExpectIntGT(sk_X509_push(untrusted, testData->x509CaInt), 0);
++
++ ExpectNotNull(ctx = X509_STORE_CTX_new());
++ ExpectIntEQ(X509_STORE_CTX_init(ctx, store, testData->x509Leaf, untrusted),
++ 1);
++ /* Must verify: chain terminates at trusted intermediate in the store. */
++ ExpectIntEQ(X509_verify_cert(ctx), 1);
++
++ X509_STORE_CTX_free(ctx);
++ X509_STORE_free(store);
++ sk_X509_free(untrusted);
++ return EXPECT_RESULT();
++}
++
++static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_untrusted_terminal(
++ X509_STORE_test_data *testData)
++{
++ EXPECT_DECLS;
++ X509_STORE* store = NULL;
++ X509_STORE_CTX* ctx = NULL;
++ STACK_OF(X509)* untrusted = NULL;
++
++ /* Partial-chain boundary test: the store trusts a CA (x509Ca) that is
++ * NOT reachable from the leaf given the supplied untrusted intermediates,
++ * and an untrusted intermediate (x509CaInt2) IS the terminal of the
++ * (truncated) chain. With X509_V_FLAG_PARTIAL_CHAIN set, verification
++ * must FAIL because the chain terminates at an untrusted certificate.
++ *
++ * This test specifically targets the snapshot-based trust check in
++ * X509StoreCertIsTrusted. Before addAllButSelfSigned injects
++ * x509CaInt2, origTrustedSk is snapshotted from the caller-trusted set
++ * and contains only x509Ca. When the chain terminates at x509CaInt2,
++ * the trust check consults origTrustedSk (not the mutated working
++ * stack) and correctly finds no match. A regression that consulted
++ * the post-injection working stack instead of the snapshot would
++ * incorrectly mark x509CaInt2 as trusted and cause verification to
++ * succeed. */
++ ExpectNotNull(store = X509_STORE_new());
++ ExpectIntEQ(X509_STORE_add_cert(store, testData->x509Ca), 1);
++ ExpectIntEQ(X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN), 1);
++
++ /* Only x509CaInt2 supplied as untrusted; x509CaInt is intentionally
++ * withheld so the chain cannot actually reach the trusted x509Ca. */
++ ExpectNotNull(untrusted = sk_X509_new_null());
++ ExpectIntGT(sk_X509_push(untrusted, testData->x509CaInt2), 0);
++
++ ExpectNotNull(ctx = X509_STORE_CTX_new());
++ ExpectIntEQ(X509_STORE_CTX_init(ctx, store, testData->x509Leaf, untrusted),
++ 1);
++ /* Must NOT verify: the chain terminal (x509CaInt2) is not in the
++ * original trust set, even though the store is non-empty. */
++ ExpectIntNE(X509_verify_cert(ctx), 1);
++
++ X509_STORE_CTX_free(ctx);
++ X509_STORE_free(store);
++ sk_X509_free(untrusted);
++ return EXPECT_RESULT();
++}
++
+ #ifdef HAVE_ECC
+ static int test_wolfSSL_X509_STORE_CTX_ex12(void)
+ {
+@@ -870,6 +991,12 @@ int test_wolfSSL_X509_STORE_CTX_ex(void)
+ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex9(&testData), 1);
+ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex10(&testData), 1);
+ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex11(&testData), 1);
++ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex_partial_chain_neg(&testData), 1);
++ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex_partial_chain_mixed(&testData),
++ 1);
++ ExpectIntEQ(
++ test_wolfSSL_X509_STORE_CTX_ex_partial_chain_untrusted_terminal(
++ &testData), 1);
+ #ifdef HAVE_ECC
+ ExpectIntEQ(test_wolfSSL_X509_STORE_CTX_ex12(), 1);
+ #endif
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-2.patch
new file mode 100644
index 0000000000..373f942496
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-2.patch
@@ -0,0 +1,30 @@
+From 62e57461f512849af4f466f77594085c90ffad73 Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Tue, 14 Apr 2026 07:58:43 -0500
+Subject: [PATCH] Fix from review
+
+(cherry picked from commit c873f3f77da8273e160612468f08892b2ba0ed99)
+
+CVE: CVE-2026-6091
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/c873f3f77da8273e160612468f08892b2ba0ed99]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/x509_str.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/x509_str.c b/src/x509_str.c
+index 01e975198..67e3fcae6 100644
+--- a/src/x509_str.c
++++ b/src/x509_str.c
+@@ -765,6 +765,10 @@ int wolfSSL_X509_verify_cert(WOLFSSL_X509_STORE_CTX* ctx)
+ X509StoreCertIsTrusted(ctx->store, ctx->current_cert,
+ origTrustedSk)) {
+ wolfSSL_sk_X509_push(ctx->chain, ctx->current_cert);
++ /* Clear error set by the failed X509StoreVerifyCert
++ * attempt; the partial-chain fallback accepted the
++ * chain at a caller-trusted certificate. */
++ ctx->error = 0;
+ ret = WOLFSSL_SUCCESS;
+ } else {
+ X509VerifyCertSetupRetry(ctx, certs, failedCerts,
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-3.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-3.patch
new file mode 100644
index 0000000000..418e45fc13
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6091-3.patch
@@ -0,0 +1,36 @@
+From 13c4a12c9904a2e50d1729d8a6899f111f3bcd2a Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Tue, 14 Apr 2026 07:41:30 -0500
+Subject: [PATCH] Fix from review
+
+(cherry picked from commit 2b503dae543d09c63a08b1e24238e0e9ce1ac6c5)
+
+CVE: CVE-2026-6091
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/2b503dae543d09c63a08b1e24238e0e9ce1ac6c5]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ tests/api/test_ossl_x509_str.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c
+index 01ed9edfb..14f3538a4 100644
+--- a/tests/api/test_ossl_x509_str.c
++++ b/tests/api/test_ossl_x509_str.c
+@@ -854,6 +854,7 @@ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_mixed(
+ 1);
+ /* Must verify: chain terminates at trusted intermediate in the store. */
+ ExpectIntEQ(X509_verify_cert(ctx), 1);
++ ExpectIntEQ(X509_STORE_CTX_get_error(ctx), X509_V_OK);
+
+ X509_STORE_CTX_free(ctx);
+ X509_STORE_free(store);
+@@ -899,6 +900,8 @@ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_untrusted_terminal(
+ /* Must NOT verify: the chain terminal (x509CaInt2) is not in the
+ * original trust set, even though the store is non-empty. */
+ ExpectIntNE(X509_verify_cert(ctx), 1);
++ ExpectIntEQ(X509_STORE_CTX_get_error(ctx),
++ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
+
+ X509_STORE_CTX_free(ctx);
+ X509_STORE_free(store);
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index e2bb53a646..139d73c572 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -19,6 +19,9 @@ SRC_URI = " \
file://CVE-2026-10098-2.patch \
file://CVE-2026-10512.patch \
file://CVE-2026-55958.patch \
+ file://CVE-2026-6091-1.patch \
+ file://CVE-2026-6091-2.patch \
+ file://CVE-2026-6091-3.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 26/33] wolfssl: patch CVE-2026-6092
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (23 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 25/33] wolfssl: patch CVE-2026-6091 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 27/33] wolfssl: patch CVE-2026-6094 ankur.tyagi85
` (6 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6092
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6092.patch | 163 ++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 164 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6092.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6092.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6092.patch
new file mode 100644
index 0000000000..80868d255e
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6092.patch
@@ -0,0 +1,163 @@
+From 5e2fa43aed59a2524c33a443c93445882519677b Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Wed, 8 Apr 2026 15:06:11 -0500
+Subject: [PATCH] Fix ETM on resumption
+
+(cherry picked from commit af5369636a938faf4a79d1f550d3b206c51fafec)
+
+CVE: CVE-2026-6092
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/af5369636a938faf4a79d1f550d3b206c51fafec]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/internal.c | 20 +++++++----
+ tests/api/test_tls.c | 83 ++++++++++++++++++++++++++++++++++++++++++++
+ tests/api/test_tls.h | 2 ++
+ 3 files changed, 99 insertions(+), 6 deletions(-)
+
+diff --git a/src/internal.c b/src/internal.c
+index ad1587e0f..267c75a5d 100644
+--- a/src/internal.c
++++ b/src/internal.c
+@@ -38436,13 +38436,21 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
+
+ #if defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_ENCRYPT_THEN_MAC) && \
+ !defined(WOLFSSL_AEAD_ONLY)
+- if (ssl->options.encThenMac && ssl->specs.cipher_type == block) {
+- ret = TLSX_EncryptThenMac_Respond(ssl);
+- if (ret != 0)
+- goto out;
++ /* Only respond to ETM here when resumption actually succeeded;
++ * HandleTlsResumption populates ssl->specs via SetCipherSpecs only
++ * on the success path. If resumption failed, resuming has been
++ * cleared and ssl->specs.cipher_type is still zero-initialized,
++ * so we must defer the ETM decision until after MatchSuite. */
++ if (ssl->options.resuming) {
++ if (ssl->options.encThenMac &&
++ ssl->specs.cipher_type == block) {
++ ret = TLSX_EncryptThenMac_Respond(ssl);
++ if (ret != 0)
++ goto out;
++ }
++ else
++ ssl->options.encThenMac = 0;
+ }
+- else
+- ssl->options.encThenMac = 0;
+ #endif
+ if (ssl->options.clientState == CLIENT_KEYEXCHANGE_COMPLETE) {
+ WOLFSSL_LEAVE("DoClientHello", ret);
+diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c
+index 565006171..f6cbb6d38 100644
+--- a/tests/api/test_tls.c
++++ b/tests/api/test_tls.c
+@@ -730,6 +730,89 @@ int test_tls12_no_null_compression(void)
+ * uppercase names like "SECP384R1" do not match the lowercase "secp384r1"
+ * entry; they fall through to the wolfCrypt ECC look-up which uses
+ * XSTRCASECMP. */
++/* Regression test for the encrypt-then-MAC silent-disable bug.
++ *
++ * Before the fix, when a client sent a 32-byte session ID in its ClientHello
++ * (so the server set ssl->options.resuming = 1) but the server's session
++ * cache did not contain that session, DoClientHello would run an
++ * encrypt_then_mac decision *before* MatchSuite/SetCipherSpecs had populated
++ * ssl->specs.cipher_type. Because cipher_type was zero-initialized
++ * (== stream, not block), the ETM block cleared encThenMac to 0, and the
++ * post-MatchSuite block could not re-enable it. The connection then
++ * silently negotiated MAC-then-encrypt instead of encrypt-then-MAC.
++ *
++ * This test forces a stale-resumption ClientHello against a server with an
++ * empty session cache, using a CBC-mode cipher suite, and asserts that the
++ * server still negotiates encrypt-then-MAC. */
++int test_tls12_etm_failed_resumption(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
++ !defined(WOLFSSL_NO_TLS12) && defined(HAVE_ENCRYPT_THEN_MAC) && \
++ !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_RSA) && !defined(NO_AES) && \
++ defined(HAVE_AES_CBC) && !defined(NO_SHA256) && \
++ defined(HAVE_SESSION_TICKET) && defined(HAVE_ECC)
++ /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 — a CBC suite, where ETM applies. */
++ const char* cbcSuite = "ECDHE-RSA-AES128-SHA256";
++ WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
++ WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
++ WOLFSSL_SESSION *sess = NULL;
++ struct test_memio_ctx test_ctx;
++
++ /* First handshake: establish a session-ID-based session on the client.
++ * Disable TLS 1.2 session tickets on both sides so resumption uses the
++ * session ID path (not tickets), which is the path the bug lives on. */
++ XMEMSET(&test_ctx, 0, sizeof(test_ctx));
++ ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
++ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
++ ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cbcSuite), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cbcSuite), WOLFSSL_SUCCESS);
++ ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
++ /* Sanity: the first handshake itself must use ETM. */
++ ExpectIntEQ(ssl_s->options.encThenMac, 1);
++ ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
++
++ wolfSSL_free(ssl_c); ssl_c = NULL;
++ wolfSSL_free(ssl_s); ssl_s = NULL;
++ wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
++ wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
++
++ /* Second handshake against a *fresh* server context (empty cache). The
++ * client offers the saved session, so the server's ClientHello parser
++ * sets options.resuming = 1, but HandleTlsResumption then fails to find
++ * the session and clears resuming. Pre-fix, ETM was silently dropped
++ * here. */
++ XMEMSET(&test_ctx, 0, sizeof(test_ctx));
++ ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
++ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
++ /* The internal session cache is process-global, so the saved session is
++ * still findable via the cache. Disable lookups on this server SSL
++ * directly so that HandleTlsResumption hits its "session lookup failed"
++ * path — exactly the scenario the bug fix targets. */
++ ssl_s->options.sessionCacheOff = 1;
++ ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cbcSuite), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cbcSuite), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
++ ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
++ /* The server should NOT have actually resumed (fresh ctx, empty cache). */
++ ExpectIntEQ(ssl_s->options.resuming, 0);
++ /* And — the regression check — encrypt-then-MAC must still be active. */
++ ExpectIntEQ(ssl_s->options.encThenMac, 1);
++ ExpectIntEQ(ssl_c->options.encThenMac, 1);
++
++ wolfSSL_SESSION_free(sess);
++ wolfSSL_free(ssl_c);
++ wolfSSL_free(ssl_s);
++ wolfSSL_CTX_free(ctx_c);
++ wolfSSL_CTX_free(ctx_s);
++#endif
++ return EXPECT_RESULT();
++}
++
+ int test_tls_set_curves_list_ecc_fallback(void)
+ {
+ EXPECT_DECLS;
+diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h
+index 46d540434..9d28a599a 100644
+--- a/tests/api/test_tls.h
++++ b/tests/api/test_tls.h
+@@ -30,6 +30,7 @@ int test_tls13_curve_intersection(void);
+ int test_tls_certreq_order(void);
+ int test_tls12_bad_cv_sig_alg(void);
+ int test_tls12_no_null_compression(void);
++int test_tls12_etm_failed_resumption(void);
+ int test_tls_set_curves_list_ecc_fallback(void);
+
+ #define TEST_TLS_DECLS \
+@@ -41,6 +42,7 @@ int test_tls_set_curves_list_ecc_fallback(void);
+ TEST_DECL_GROUP("tls", test_tls_certreq_order), \
+ TEST_DECL_GROUP("tls", test_tls12_bad_cv_sig_alg), \
+ TEST_DECL_GROUP("tls", test_tls12_no_null_compression), \
++ TEST_DECL_GROUP("tls", test_tls12_etm_failed_resumption), \
+ TEST_DECL_GROUP("tls", test_tls_set_curves_list_ecc_fallback)
+
+ #endif /* TESTS_API_TEST_TLS_H */
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 139d73c572..ce3839e9a2 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -22,6 +22,7 @@ SRC_URI = " \
file://CVE-2026-6091-1.patch \
file://CVE-2026-6091-2.patch \
file://CVE-2026-6091-3.patch \
+ file://CVE-2026-6092.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 27/33] wolfssl: patch CVE-2026-6094
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (24 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 26/33] wolfssl: patch CVE-2026-6092 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 28/33] wolfssl: patch CVE-2026-6291 ankur.tyagi85
` (5 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details
https://nvd.nist.gov/vuln/detail/cve-2026-6094
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6094-1.patch | 32 ++++++++
.../wolfssl/files/CVE-2026-6094-2.patch | 35 +++++++++
.../wolfssl/files/CVE-2026-6094-3.patch | 39 ++++++++++
.../wolfssl/files/CVE-2026-6094-4.patch | 36 +++++++++
.../wolfssl/files/CVE-2026-6094-5.patch | 73 +++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 5 ++
6 files changed, 220 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-2.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-3.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-4.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-5.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-1.patch
new file mode 100644
index 0000000000..c9a7c1867e
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-1.patch
@@ -0,0 +1,32 @@
+From f162b7679bafacd5b9866d72400b1115f8fbc7b7 Mon Sep 17 00:00:00 2001
+From: Kareem <kareem@wolfssl.com>
+Date: Fri, 3 Apr 2026 16:05:44 -0700
+Subject: [PATCH] Ensure esd->signedAttribsCount contains the correct count in
+ case some are skipped by using the current idx rather than the total array
+ size.
+
+Thanks to Zou Dikai for the report.
+
+(cherry picked from commit 7f218574c4d30a8aa8c520c7023c3d017fc13b86)
+
+CVE: CVE-2026-6094
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/7f218574c4d30a8aa8c520c7023c3d017fc13b86]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/pkcs7.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 3f6649d0a..67803f84d 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -2253,7 +2253,7 @@ static int wc_PKCS7_BuildSignedAttributes(wc_PKCS7* pkcs7, ESD* esd,
+ idx++;
+ }
+
+- esd->signedAttribsCount += cannedAttribsCount;
++ esd->signedAttribsCount += idx;
+ esd->signedAttribsSz += (word32)EncodeAttributes(
+ &esd->signedAttribs[atrIdx], (int)idx, cannedAttribs,
+ (int)cannedAttribsCount);
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-2.patch
new file mode 100644
index 0000000000..8df72e6f5b
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-2.patch
@@ -0,0 +1,35 @@
+From 6d7fe2926a18982278c53b4ec413fe7b2349a019 Mon Sep 17 00:00:00 2001
+From: Kareem <kareem@wolfssl.com>
+Date: Fri, 3 Apr 2026 16:06:35 -0700
+Subject: [PATCH] In wc_PKCS7_DecodeEnvelopedData, confirm
+ encryptedContentTotalSz does not exceed the total message size before using
+ it in the non-streaming case.
+
+Thanks to Zou Dikai for the report.
+
+(cherry picked from commit 1397268aa12e2cf3f80c3acfa9b6036b809c08ef)
+
+CVE: CVE-2026-6094
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/1397268aa12e2cf3f80c3acfa9b6036b809c08ef]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/pkcs7.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 67803f84d..43bcf3ee4 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -13231,6 +13231,11 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ }
+ wc_PKCS7_DecryptContentFree(pkcs7, encOID, pkcs7->heap);
+ } else {
++ if ((idx + (word32)encryptedContentTotalSz) > pkiMsgSz) {
++ ret = BUFFER_E;
++ break;
++ }
++
+ pkcs7->cachedEncryptedContentSz =
+ (word32)encryptedContentTotalSz;
+ pkcs7->totalEncryptedContentSz =
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-3.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-3.patch
new file mode 100644
index 0000000000..6e0d902c85
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-3.patch
@@ -0,0 +1,39 @@
+From c7f7a8ef00e6a7a33a623543507f8fa089f6037b Mon Sep 17 00:00:00 2001
+From: Kareem <kareem@wolfssl.com>
+Date: Fri, 3 Apr 2026 16:56:04 -0700
+Subject: [PATCH] Code review feedback
+
+(cherry picked from commit ebdcc03b718cd7175355097b1a3831a0eb4875b2)
+
+CVE: CVE-2026-6094
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/ebdcc03b718cd7175355097b1a3831a0eb4875b2]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/pkcs7.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 43bcf3ee4..50d33bdb8 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -2256,7 +2256,7 @@ static int wc_PKCS7_BuildSignedAttributes(wc_PKCS7* pkcs7, ESD* esd,
+ esd->signedAttribsCount += idx;
+ esd->signedAttribsSz += (word32)EncodeAttributes(
+ &esd->signedAttribs[atrIdx], (int)idx, cannedAttribs,
+- (int)cannedAttribsCount);
++ (int)idx);
+ atrIdx += idx;
+ } else {
+ esd->signedAttribsCount = 0;
+@@ -13231,7 +13231,9 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ }
+ wc_PKCS7_DecryptContentFree(pkcs7, encOID, pkcs7->heap);
+ } else {
+- if ((idx + (word32)encryptedContentTotalSz) > pkiMsgSz) {
++ word32 tmpSum;
++ if (!WC_SAFE_SUM_WORD32(idx, (word32)encryptedContentTotalSz, tmpSum) ||
++ tmpSum > pkiMsgSz) {
+ ret = BUFFER_E;
+ break;
+ }
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-4.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-4.patch
new file mode 100644
index 0000000000..3c49028ec4
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-4.patch
@@ -0,0 +1,36 @@
+From a9015491db03d415f766f47c139841249adce843 Mon Sep 17 00:00:00 2001
+From: Kareem <kareem@wolfssl.com>
+Date: Mon, 6 Apr 2026 11:58:12 -0700
+Subject: [PATCH] Fix unused variable error
+
+(cherry picked from commit 3e04475875a4942652fdf6794a01fdfd65801fdc)
+
+CVE: CVE-2026-6094
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/3e04475875a4942652fdf6794a01fdfd65801fdc]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/pkcs7.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 50d33bdb8..2cde73846 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -2197,7 +2197,6 @@ static int wc_PKCS7_BuildSignedAttributes(wc_PKCS7* pkcs7, ESD* esd,
+ #endif
+ word32 idx = 0;
+ word32 atrIdx = 0;
+- word32 cannedAttribsCount;
+
+ if (pkcs7 == NULL || esd == NULL || contentType == NULL ||
+ contentTypeOid == NULL || messageDigestOid == NULL ||
+@@ -2220,8 +2219,6 @@ static int wc_PKCS7_BuildSignedAttributes(wc_PKCS7* pkcs7, ESD* esd,
+ return timeSz;
+ #endif
+
+- cannedAttribsCount = sizeof(cannedAttribs)/sizeof(PKCS7Attrib);
+-
+ XMEMSET(&cannedAttribs[idx], 0, sizeof(cannedAttribs[idx]));
+
+ if ((pkcs7->defaultSignedAttribs & WOLFSSL_CONTENT_TYPE_ATTRIBUTE) ||
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-5.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-5.patch
new file mode 100644
index 0000000000..a0dcd8ce72
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6094-5.patch
@@ -0,0 +1,73 @@
+From c27fbdecfd2f4a31acdc73229a5bc631184265f1 Mon Sep 17 00:00:00 2001
+From: Kareem <kareem@wolfssl.com>
+Date: Mon, 6 Apr 2026 16:41:32 -0700
+Subject: [PATCH] Add additional checks for encryptedContentSz exceeding
+ pkiMsgSz.
+
+(cherry picked from commit b3c2877a146e0c75715368ca5dfe2387bfc2cadf)
+
+CVE: CVE-2026-6094
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/b3c2877a146e0c75715368ca5dfe2387bfc2cadf]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/pkcs7.c | 38 ++++++++++++++++++++++++++------------
+ 1 file changed, 26 insertions(+), 12 deletions(-)
+
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 2cde73846..8df3a2430 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -14380,9 +14380,17 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ }
+
+ if (ret == 0) {
+- XMEMCPY(encryptedContent, &pkiMsg[idx],
++ word32 tmpSum;
++ if (!WC_SAFE_SUM_WORD32(idx, (word32)encryptedContentSz,
++ tmpSum) ||
++ tmpSum > pkiMsgSz) {
++ ret = BUFFER_E;
++ break;
++ } else {
++ XMEMCPY(encryptedContent, &pkiMsg[idx],
+ (word32)encryptedContentSz);
+- idx += (word32)encryptedContentSz;
++ idx += (word32)encryptedContentSz;
++ }
+ }
+ #ifndef NO_PKCS7_STREAM
+ pkcs7->stream->bufferPt = encryptedContent;
+@@ -15316,16 +15324,22 @@ int wc_PKCS7_DecodeEncryptedData(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ }
+
+ if (ret == 0) {
+- XMEMCPY(encryptedContent, &pkiMsg[idx],
+- (unsigned int)encryptedContentSz);
+- idx += (word32)encryptedContentSz;
+-
+- /* decrypt encryptedContent */
+- ret = wc_PKCS7_DecryptContent(pkcs7, encOID,
+- pkcs7->encryptionKey, pkcs7->encryptionKeySz,
+- tmpIv, expBlockSz, NULL, 0, NULL, 0,
+- encryptedContent, encryptedContentSz,
+- encryptedContent, pkcs7->devId, pkcs7->heap);
++ word32 tmpSum;
++ if (!WC_SAFE_SUM_WORD32(idx, (word32)encryptedContentSz, tmpSum) ||
++ tmpSum > pkiMsgSz) {
++ ret = BUFFER_E;
++ } else {
++ XMEMCPY(encryptedContent, &pkiMsg[idx],
++ (unsigned int)encryptedContentSz);
++ idx += (word32)encryptedContentSz;
++
++ /* decrypt encryptedContent */
++ ret = wc_PKCS7_DecryptContent(pkcs7, encOID,
++ pkcs7->encryptionKey, pkcs7->encryptionKeySz,
++ tmpIv, expBlockSz, NULL, 0, NULL, 0,
++ encryptedContent, encryptedContentSz,
++ encryptedContent, pkcs7->devId, pkcs7->heap);
++ }
+ if (ret != 0) {
+ XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ }
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index ce3839e9a2..8802202114 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -23,6 +23,11 @@ SRC_URI = " \
file://CVE-2026-6091-2.patch \
file://CVE-2026-6091-3.patch \
file://CVE-2026-6092.patch \
+ file://CVE-2026-6094-1.patch \
+ file://CVE-2026-6094-2.patch \
+ file://CVE-2026-6094-3.patch \
+ file://CVE-2026-6094-4.patch \
+ file://CVE-2026-6094-5.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 28/33] wolfssl: patch CVE-2026-6291
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (25 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 27/33] wolfssl: patch CVE-2026-6094 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 29/33] wolfssl: patch CVE-2026-6325 ankur.tyagi85
` (4 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6291
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6291.patch | 2948 +++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 2949 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6291.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6291.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6291.patch
new file mode 100644
index 0000000000..dd41f9f15d
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6291.patch
@@ -0,0 +1,2948 @@
+From 3aab2df7436240515c608971ca9b899c37f93f47 Mon Sep 17 00:00:00 2001
+From: Sean Parkinson <sean@wolfssl.com>
+Date: Fri, 24 Apr 2026 10:13:43 +1000
+Subject: [PATCH] Merge pull request #10203 from Frauschi/pkcs7_fixes
+
+PKCS#7 fixes
+
+(cherry picked from commit 936f8e54230b11dee50e82bcc5a2247ea9d9c91c)
+
+CVE: CVE-2026-6291
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/936f8e54230b11dee50e82bcc5a2247ea9d9c91c]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ .wolfssl_known_macro_extras | 1 +
+ src/ssl_p7p12.c | 28 +-
+ tests/api.c | 707 +++++++++++++++++++++++++++++++++
+ tests/api/test_ossl_p7p12.c | 454 +++++++++++++++++++++
+ tests/api/test_ossl_p7p12.h | 4 +
+ tests/api/test_pkcs7.c | 356 +++++++++++++++--
+ tests/api/test_pkcs7.h | 25 +-
+ wolfcrypt/src/pkcs7.c | 633 +++++++++++++++++++++++++++--
+ wolfssl/wolfcrypt/wc_encrypt.h | 3 +
+ 9 files changed, 2154 insertions(+), 57 deletions(-)
+
+diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras
+index ba5e47ef6..3e411ca86 100644
+--- a/.wolfssl_known_macro_extras
++++ b/.wolfssl_known_macro_extras
+@@ -825,6 +825,7 @@ WOLFSSL_NO_KCAPI_HMAC_SHA256
+ WOLFSSL_NO_KCAPI_HMAC_SHA384
+ WOLFSSL_NO_KCAPI_HMAC_SHA512
+ WOLFSSL_NO_KCAPI_SHA224
++WOLFSSL_NO_KTRI_ORACLE_WARNING
+ WOLFSSL_NO_OCSP_DATE_CHECK
+ WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK
+ WOLFSSL_NO_OCSP_OPTIONAL_CERTS
+diff --git a/src/ssl_p7p12.c b/src/ssl_p7p12.c
+index ee48d700a..a07f018b2 100644
+--- a/src/ssl_p7p12.c
++++ b/src/ssl_p7p12.c
+@@ -269,24 +269,42 @@ WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs,
+ WOLFSSL_X509* x509 = NULL;
+ WOLFSSL_STACK* signers = NULL;
+ WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
++ byte* signerCert;
++ word32 signerCertSz;
+
+ if (p7 == NULL)
+ return NULL;
+
+- /* Only PKCS#7 messages with a single cert that is the verifying certificate
+- * is supported.
+- */
+ if (flags & PKCS7_NOINTERN) {
+ WOLFSSL_MSG("PKCS7_NOINTERN flag not supported");
+ return NULL;
+ }
+
++ /* Prefer the certificate that actually verified the signature. Falling
++ * back to singleCert (cert[0]) would let an attacker that bundles a
++ * trusted cert ahead of their own attacker cert have the trusted cert
++ * reported as the signer even though it did not produce the signature.
++ *
++ * Copy the chosen pointer into a local before passing its address to
++ * wolfSSL_d2i_X509; d2i_X509 advances *in by the DER length, and if
++ * we handed it the address of the struct field directly it would
++ * permanently corrupt the field, producing a heap-OOB read on the
++ * next use (pointer advanced, singleCertSz unchanged). */
++ if (p7->pkcs7.verifyCert != NULL && p7->pkcs7.verifyCertSz > 0) {
++ signerCert = p7->pkcs7.verifyCert;
++ signerCertSz = p7->pkcs7.verifyCertSz;
++ }
++ else {
++ signerCert = p7->pkcs7.singleCert;
++ signerCertSz = p7->pkcs7.singleCertSz;
++ }
++
+ signers = wolfSSL_sk_X509_new_null();
+ if (signers == NULL)
+ return NULL;
+
+- if (wolfSSL_d2i_X509(&x509, (const byte**)&p7->pkcs7.singleCert,
+- p7->pkcs7.singleCertSz) == NULL) {
++ if (wolfSSL_d2i_X509(&x509, (const byte**)&signerCert,
++ signerCertSz) == NULL) {
+ wolfSSL_sk_X509_pop_free(signers, NULL);
+ return NULL;
+ }
+diff --git a/tests/api.c b/tests/api.c
+index ad5f84573..78fcf13ce 100644
+--- a/tests/api.c
++++ b/tests/api.c
+@@ -34990,6 +34990,706 @@ static int test_pkcs7_ori_oversized_oid(void)
+ return EXPECT_RESULT();
+ }
+
++/* ORI callback that flags if oriValueSz looks like an underflow (>= 0x80000000) */
++#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC)
++static int test_ori_underflow_cb(wc_PKCS7* pkcs7, byte* oriType,
++ word32 oriTypeSz, byte* oriValue,
++ word32 oriValueSz, byte* decryptedKey,
++ word32* decryptedKeySz, void* ctx)
++{
++ int* called = (int*)ctx;
++ (void)pkcs7; (void)oriType; (void)oriTypeSz;
++ (void)oriValue; (void)decryptedKey; (void)decryptedKeySz;
++ if (called != NULL)
++ *called = (int)oriValueSz; /* record what we received */
++ return -1;
++}
++#endif
++
++/* Test: PKCS#7 ORI must reject when OID consumption exceeds the [4] implicit
++ * SEQUENCE length (integer underflow in oriValueSz computation).
++ *
++ * With implicit tagging, [4] CONSTRUCTED replaces the SEQUENCE tag, so
++ * wc_PKCS7_DecryptOri reads seqSz directly from the [4] length field.
++ * We set [4] length = 5 while the OID inside consumes 22 bytes
++ * (tag + length + 20 content), triggering oriValueSz = 5 - 22 = underflow.
++ *
++ * The buffer includes a dummy EncryptedContentInfo after the RecipientInfos
++ * so the total message is large enough for the PKCS7 streaming code (which
++ * requests the full remaining message before parsing the ORI). */
++static int test_pkcs7_ori_seqsz_underflow(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC)
++ wc_PKCS7* p7 = NULL;
++ byte out[256];
++ int cbCalled = 0;
++
++ /*
++ * Byte layout (all outer lengths match actual byte counts on wire):
++ *
++ * OID inside [4]: 06 14 <20 bytes> = 22 bytes
++ * [4] (declared len 5, actual content 22):
++ * a4 05 <22 bytes> = 24 bytes on wire
++ * SET: 31 18 <24 bytes> = 26 bytes on wire
++ * version: 02 01 00 = 3 bytes
++ * EncryptedContentInfo (filler, never parsed):
++ * 30 0b { 06 09 <9 bytes OID> } = 13 bytes on wire
++ * EnvelopedData: 30 2a <3+26+13=42 bytes> = 44 bytes on wire
++ * [0] EXPLICIT: a0 2c <44 bytes> = 46 bytes on wire
++ * OID(envelopedData): 06 09 <9 bytes> = 11 bytes on wire
++ * ContentInfo: 30 39 <11+46=57 bytes> = 59 bytes total
++ */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE (length 57) */
++ 0x30, 0x39,
++ /* contentType = envelopedData 1.2.840.113549.1.7.3 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT (length 44) */
++ 0xa0, 0x2c,
++ /* EnvelopedData SEQUENCE (length 42) */
++ 0x30, 0x2a,
++ /* version = 0 */
++ 0x02, 0x01, 0x00,
++ /* RecipientInfos SET (length 24) */
++ 0x31, 0x18,
++ /* [4] CONSTRUCTED = ORI implicit SEQUENCE, declared len 5 */
++ /* Actual OID is 22 bytes -> exceeds declared 5 */
++ 0xa4, 0x05,
++ /* OID: tag=06, len=0x14(20), content=20 bytes = 22 total */
++ 0x06, 0x14,
++ 0x2a, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
++ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
++ 0x12, 0x13, 0x14, 0x15,
++ /* EncryptedContentInfo SEQUENCE (length 11) - filler so
++ * streaming has enough data; never actually parsed because
++ * DecryptOri fails before we get here */
++ 0x30, 0x0b,
++ /* contentType = data 1.2.840.113549.1.7.1 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01
++ };
++
++ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL) {
++ wc_PKCS7_SetOriDecryptCb(p7, test_ori_underflow_cb);
++ wc_PKCS7_SetOriDecryptCtx(p7, &cbCalled);
++
++ /* Must return an error before the callback sees an underflowed size */
++ ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ /* The callback must NOT have been invoked with a wrapped oriValueSz.
++ * cbCalled == 0 means the callback was never reached (ideal).
++ * cbCalled < 0 would indicate the underflow was passed through. */
++ ExpectIntGE(cbCalled, 0);
++
++ wc_PKCS7_Free(p7);
++ }
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 ORI must reject when seqSz extends oriValue past input buffer.
++ *
++ * The first ORI bounds check (OID exceeds SEQUENCE boundary) is covered by
++ * test_pkcs7_ori_seqsz_underflow. This test covers the *second* check:
++ * oriValue region extends past the end of the input buffer. We craft a
++ * message where the [4] SEQUENCE length is valid relative to the OID
++ * (OID fits inside it), but the remaining oriValue portion extends past
++ * the end of the actual input buffer.
++ *
++ * To bypass GetLength's own bounds validation, we set the outer lengths
++ * (EnvelopedData SEQUENCE, RecipientInfos SET) to match the [4] claim,
++ * but truncate the actual buffer we pass to DecodeEnvelopedData. */
++static int test_pkcs7_ori_orivalue_overflow(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC)
++ wc_PKCS7* p7 = NULL;
++ byte out[256];
++
++ /* EnvelopedData with [4] ORI whose seqSz (40) is valid for the OID
++ * (6 bytes consumed) but oriValueSz (34) extends past the truncated
++ * input buffer.
++ *
++ * Layout:
++ * ContentInfo SEQUENCE
++ * OID envelopedData
++ * [0] EXPLICIT
++ * EnvelopedData SEQUENCE
++ * version = 0
++ * RecipientInfos SET
++ * [4] CONSTRUCTED (seqSz = 40)
++ * OID (tag 06, len 04, 4 content bytes = 6 total)
++ * oriValue should be 34 bytes but input is truncated
++ * EncryptedContentInfo (filler for streaming)
++ *
++ * We pass the full array to DecodeEnvelopedData, but the actual
++ * input ends before [4]'s declared 40 bytes are consumed.
++ */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE */
++ 0x30, 0x43,
++ /* contentType = envelopedData 1.2.840.113549.1.7.3 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT */
++ 0xa0, 0x36,
++ /* EnvelopedData SEQUENCE */
++ 0x30, 0x34,
++ /* version = 0 */
++ 0x02, 0x01, 0x00,
++ /* RecipientInfos SET (len = 44 covers [4] tag+len+40) */
++ 0x31, 0x2c,
++ /* [4] CONSTRUCTED = ORI implicit SEQUENCE, seqSz = 40 */
++ 0xa4, 0x28,
++ /* OID: tag=06, len=04, 4 content bytes = 6 total */
++ 0x06, 0x04, 0x2a, 0x03, 0x04, 0x05,
++ /* Only 4 bytes of oriValue here, but seqSz claims 34 more */
++ 0x00, 0x00, 0x00, 0x00,
++ /* EncryptedContentInfo SEQUENCE (filler) */
++ 0x30, 0x0b,
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01
++ };
++
++ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL) {
++ wc_PKCS7_SetOriDecryptCb(p7, test_dummy_ori_cb);
++
++ /* Must return error - oriValue extends past input buffer */
++ ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ wc_PKCS7_Free(p7);
++ }
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 KTRI must not match recipient when SKID length differs
++ * from expected keyIdSize.
++ *
++ * The fix adds a `length == keyIdSize` check before comparing the SKID
++ * bytes. Without this check, XMEMCMP could compare against data beyond
++ * the SKID content. This test crafts a KTRI RecipientInfo where the
++ * [0] SubjectKeyIdentifier has length 5 instead of KEYID_SIZE (20).
++ * The decode must return an error (no matching recipient). */
++static int test_pkcs7_ktri_skid_length_mismatch(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(WOLFSSL_NO_MALLOC)
++ wc_PKCS7* p7 = NULL;
++ byte out[256];
++
++ /* Minimal EnvelopedData with KTRI using version=2 (SKID path).
++ * The SKID [0] has length 5 instead of 20.
++ *
++ * ContentInfo SEQUENCE
++ * OID envelopedData
++ * [0] EXPLICIT
++ * EnvelopedData SEQUENCE
++ * version = 2
++ * RecipientInfos SET
++ * KTRI SEQUENCE
++ * version = 2
++ * [0] SKID (5 bytes, should be 20)
++ * AlgorithmIdentifier (RSA OID)
++ * OCTET STRING (fake encrypted key)
++ * EncryptedContentInfo (filler)
++ */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE */
++ 0x30, 0x46,
++ /* contentType = envelopedData 1.2.840.113549.1.7.3 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT */
++ 0xa0, 0x39,
++ /* EnvelopedData SEQUENCE */
++ 0x30, 0x37,
++ /* version = 2 (triggers SKID-based recipient identification) */
++ 0x02, 0x01, 0x02,
++ /* RecipientInfos SET */
++ 0x31, 0x21,
++ /* KTRI SEQUENCE */
++ 0x30, 0x1f,
++ /* version = 2 (SKID) */
++ 0x02, 0x01, 0x02,
++ /* [0] IMPLICIT SubjectKeyIdentifier, length = 5 (wrong!) */
++ 0x80, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05,
++ /* AlgorithmIdentifier: RSA 1.2.840.113549.1.1.1 */
++ 0x30, 0x0d,
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
++ 0x01, 0x01, 0x01,
++ 0x05, 0x00, /* NULL params */
++ /* encryptedKey OCTET STRING (2 bytes fake) */
++ 0x04, 0x02, 0xAA, 0xBB,
++ /* EncryptedContentInfo SEQUENCE (filler) */
++ 0x30, 0x0b,
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01
++ };
++
++ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL) {
++ /* Decode without a cert - SKID will never match, and the
++ * mismatched SKID length must not cause out-of-bounds reads */
++ ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ wc_PKCS7_Free(p7);
++ }
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 KARI must reject BIT STRING with length < 2 in
++ * OriginatorPublicKey.
++ *
++ * The fix adds `if (length < 2) return ASN_PARSE_E` after parsing
++ * the BIT STRING tag and length. A BIT STRING must have at least
++ * the unused-bits byte plus one byte of content. This test crafts
++ * a KARI [1] RecipientInfo where the OriginatorPublicKey's BIT STRING
++ * has length 1 (degenerate). The PKCS7 object is initialized with a
++ * real ECC cert so that KariParseRecipCert succeeds and parsing reaches
++ * the BIT STRING validation. */
++static int test_pkcs7_kari_degenerate_bitstring(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && defined(HAVE_ECC) && defined(HAVE_X963_KDF) && \
++ !defined(WOLFSSL_NO_MALLOC)
++ wc_PKCS7* p7 = NULL;
++ byte out[256];
++ byte* eccCert = NULL;
++ byte* eccPrivKey = NULL;
++ word32 eccCertSz = 0;
++ word32 eccPrivKeySz = 0;
++#if !defined(USE_CERT_BUFFERS_256) && !defined(NO_FILESYSTEM)
++ XFILE f = XBADFILE;
++#endif
++
++ /* Minimal EnvelopedData with KARI [1] containing a degenerate
++ * BIT STRING (length 1) in OriginatorPublicKey.
++ *
++ * ContentInfo SEQUENCE
++ * OID envelopedData
++ * [0] EXPLICIT
++ * EnvelopedData SEQUENCE
++ * version = 2
++ * RecipientInfos SET
++ * [1] CONSTRUCTED (KARI)
++ * version = 3
++ * [0] CONSTRUCTED (OriginatorIdentifierOrKey)
++ * [1] CONSTRUCTED (OriginatorPublicKey)
++ * AlgorithmIdentifier (ECDSAk)
++ * BIT STRING length=1 (degenerate!)
++ * EncryptedContentInfo (filler)
++ */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE */
++ 0x30, 0x44,
++ /* contentType = envelopedData 1.2.840.113549.1.7.3 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT */
++ 0xa0, 0x37,
++ /* EnvelopedData SEQUENCE */
++ 0x30, 0x35,
++ /* version = 2 */
++ 0x02, 0x01, 0x02,
++ /* RecipientInfos SET */
++ 0x31, 0x1f,
++ /* [1] CONSTRUCTED (KARI implicit) */
++ 0xa1, 0x1d,
++ /* version = 3 */
++ 0x02, 0x01, 0x03,
++ /* [0] CONSTRUCTED (OriginatorIdentifierOrKey) */
++ 0xa0, 0x18,
++ /* [1] CONSTRUCTED (OriginatorPublicKey) */
++ 0xa1, 0x16,
++ /* AlgorithmIdentifier SEQUENCE */
++ 0x30, 0x13,
++ /* OID: id-ecPublicKey 1.2.840.10045.2.1 */
++ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
++ /* OID: prime256v1 1.2.840.10045.3.1.7 */
++ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
++ 0x01, 0x07,
++ /* BIT STRING with length 1 - degenerate! */
++ 0x03, 0x01, 0x00,
++ /* EncryptedContentInfo SEQUENCE (filler) */
++ 0x30, 0x0b,
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01
++ };
++
++ /* Load ECC cert and key so KariParseRecipCert succeeds and
++ * parsing reaches the BIT STRING check */
++#ifdef USE_CERT_BUFFERS_256
++ eccCertSz = (word32)sizeof_cliecc_cert_der_256;
++ ExpectNotNull(eccCert = (byte*)XMALLOC(eccCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (eccCert != NULL)
++ XMEMCPY(eccCert, cliecc_cert_der_256, eccCertSz);
++ eccPrivKeySz = (word32)sizeof_ecc_clikey_der_256;
++ ExpectNotNull(eccPrivKey = (byte*)XMALLOC(eccPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (eccPrivKey != NULL)
++ XMEMCPY(eccPrivKey, ecc_clikey_der_256, eccPrivKeySz);
++#elif !defined(NO_FILESYSTEM)
++ eccCertSz = FOURK_BUF;
++ ExpectNotNull(eccCert = (byte*)XMALLOC(eccCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/client-ecc-cert.der", "rb")) != XBADFILE);
++ ExpectTrue((eccCertSz = (word32)XFREAD(eccCert, 1, eccCertSz, f)) > 0);
++ if (f != XBADFILE) {
++ XFCLOSE(f);
++ f = XBADFILE;
++ }
++ eccPrivKeySz = FOURK_BUF;
++ ExpectNotNull(eccPrivKey = (byte*)XMALLOC(eccPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/ecc-client-key.der", "rb")) != XBADFILE);
++ ExpectTrue((eccPrivKeySz = (word32)XFREAD(eccPrivKey, 1, eccPrivKeySz,
++ f)) > 0);
++ if (f != XBADFILE)
++ XFCLOSE(f);
++#else
++ eccCert = NULL;
++ eccCertSz = 0;
++ eccPrivKey = NULL;
++ eccPrivKeySz = 0;
++#endif
++
++ p7 = wc_PKCS7_New(HEAP_HINT, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL && eccCert != NULL) {
++ ExpectIntEQ(wc_PKCS7_InitWithCert(p7, eccCert, eccCertSz), 0);
++ if (p7 != NULL) {
++ p7->privateKey = eccPrivKey;
++ p7->privateKeySz = eccPrivKeySz;
++ }
++
++ /* Must return error - BIT STRING length < 2 is invalid */
++ ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ if (p7 != NULL) {
++ p7->privateKey = NULL;
++ p7->privateKeySz = 0;
++ }
++ wc_PKCS7_Free(p7);
++ }
++ else if (p7 != NULL) {
++ wc_PKCS7_Free(p7);
++ }
++
++ XFREE(eccCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(eccPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 EncryptedData must reject when encryptedContentSz exceeds
++ * the remaining input buffer.
++ *
++ * The fix adds `encryptedContentSz > (int)(pkiMsgSz - idx)` to the
++ * existing `encryptedContentSz <= 0` check in stage 6. This test crafts
++ * a minimal EncryptedData where the [0] IMPLICIT content length claims
++ * 0x200 (512) bytes but only 32 bytes of ciphertext are present. */
++static int test_pkcs7_encrypted_content_size_overflow(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \
++ defined(WOLFSSL_AES_256) && !defined(NO_PKCS7_ENCRYPTED_DATA) && \
++ !defined(WOLFSSL_NO_MALLOC)
++ wc_PKCS7* p7 = NULL;
++ byte key[32];
++ byte out[256];
++
++ /* EncryptedData with [0] content claiming 512 bytes but only 32 present.
++ *
++ * ContentInfo SEQUENCE
++ * OID encryptedData (1.2.840.113549.1.7.6)
++ * [0] EXPLICIT
++ * EncryptedData SEQUENCE
++ * version = 0
++ * EncryptedContentInfo SEQUENCE
++ * OID data (1.2.840.113549.1.7.1)
++ * AlgorithmIdentifier
++ * OID AES-256-CBC (2.16.840.1.101.3.4.1.42)
++ * OCTET STRING IV (16 zero bytes)
++ * [0] IMPLICIT content (claimed len=0x200, actual=32 bytes)
++ */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE (len covers entire message) */
++ 0x30, 0x50,
++ /* OID encryptedData 1.2.840.113549.1.7.6 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06,
++ /* [0] EXPLICIT */
++ 0xa0, 0x43,
++ /* EncryptedData SEQUENCE */
++ 0x30, 0x41,
++ /* version = 0 */
++ 0x02, 0x01, 0x00,
++ /* EncryptedContentInfo SEQUENCE */
++ 0x30, 0x3c,
++ /* OID data 1.2.840.113549.1.7.1 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01,
++ /* AlgorithmIdentifier SEQUENCE */
++ 0x30, 0x1d,
++ /* OID AES-256-CBC 2.16.840.1.101.3.4.1.42 */
++ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01,
++ 0x2a,
++ /* IV: OCTET STRING (16 zero bytes) */
++ 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ /* [0] IMPLICIT encryptedContent - claims 512 bytes!
++ * Only 16 bytes of fake ciphertext follow. */
++ 0x80, 0x82, 0x02, 0x00,
++ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
++ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
++ };
++
++ XMEMSET(key, 0, sizeof(key));
++
++ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL) {
++ p7->encryptionKey = key;
++ p7->encryptionKeySz = sizeof(key);
++
++ /* Must return error - content extends past input buffer */
++ ExpectIntLT(wc_PKCS7_DecodeEncryptedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ wc_PKCS7_Free(p7);
++ }
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 SignedData must reject when the signature field is not
++ * an OCTET STRING.
++ *
++ * The fix adds `else if (ret == 0) { ret = ASN_PARSE_E; }` so that
++ * when the tag at the signature position is not ASN_OCTET_STRING,
++ * parsing returns an error instead of silently continuing with no
++ * signature. This test encodes a valid SignedData, then corrupts
++ * the signature OCTET STRING tag and verifies that decode fails. */
++static int test_pkcs7_signed_bad_sig_tag(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
++ !defined(WOLFSSL_NO_MALLOC)
++ PKCS7* pkcs7 = NULL;
++ WC_RNG rng;
++ byte encoded[FOURK_BUF];
++ int encodedSz = 0;
++ int i;
++ byte* rsaCert = NULL;
++ byte* rsaPrivKey = NULL;
++ word32 rsaCertSz = 0;
++ word32 rsaPrivKeySz = 0;
++#if !defined(USE_CERT_BUFFERS_2048) && !defined(USE_CERT_BUFFERS_1024) && \
++ !defined(NO_FILESYSTEM)
++ XFILE f = XBADFILE;
++#endif
++
++ const byte data[] = "Test signed data";
++
++ XMEMSET(&rng, 0, sizeof(WC_RNG));
++
++ /* Load RSA cert and key */
++#if defined(USE_CERT_BUFFERS_2048)
++ rsaCertSz = (word32)sizeof_client_cert_der_2048;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaCert != NULL)
++ XMEMCPY(rsaCert, client_cert_der_2048, rsaCertSz);
++ rsaPrivKeySz = (word32)sizeof_client_key_der_2048;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaPrivKey != NULL)
++ XMEMCPY(rsaPrivKey, client_key_der_2048, rsaPrivKeySz);
++#elif defined(USE_CERT_BUFFERS_1024)
++ rsaCertSz = (word32)sizeof_client_cert_der_1024;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaCert != NULL)
++ XMEMCPY(rsaCert, client_cert_der_1024, rsaCertSz);
++ rsaPrivKeySz = (word32)sizeof_client_key_der_1024;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaPrivKey != NULL)
++ XMEMCPY(rsaPrivKey, client_key_der_1024, rsaPrivKeySz);
++#elif !defined(NO_FILESYSTEM)
++ rsaCertSz = FOURK_BUF;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/client-cert.der", "rb")) != XBADFILE);
++ ExpectTrue((rsaCertSz = (word32)XFREAD(rsaCert, 1, rsaCertSz, f)) > 0);
++ if (f != XBADFILE) { XFCLOSE(f); f = XBADFILE; }
++ rsaPrivKeySz = FOURK_BUF;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/client-key.der", "rb")) != XBADFILE);
++ ExpectTrue((rsaPrivKeySz = (word32)XFREAD(rsaPrivKey, 1,
++ rsaPrivKeySz, f)) > 0);
++ if (f != XBADFILE) XFCLOSE(f);
++#else
++ rsaCert = NULL; rsaCertSz = 0;
++ rsaPrivKey = NULL; rsaPrivKeySz = 0;
++#endif
++
++ if (rsaCert == NULL || rsaPrivKey == NULL) {
++ XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ return TEST_SKIPPED;
++ }
++
++ ExpectIntEQ(wc_InitRng(&rng), 0);
++
++ /* Encode a valid SignedData */
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, rsaCert, rsaCertSz), 0);
++ if (pkcs7 != NULL) {
++ pkcs7->content = (byte*)data;
++ pkcs7->contentSz = (word32)sizeof(data);
++ pkcs7->contentOID = DATA;
++ pkcs7->hashOID = SHA256h;
++ pkcs7->encryptOID = RSAk;
++ pkcs7->privateKey = rsaPrivKey;
++ pkcs7->privateKeySz = rsaPrivKeySz;
++ pkcs7->rng = &rng;
++ }
++
++ ExpectIntGT(encodedSz = wc_PKCS7_EncodeSignedData(pkcs7, encoded,
++ sizeof(encoded)), 0);
++ wc_PKCS7_Free(pkcs7);
++ pkcs7 = NULL;
++
++ /* Find the signature OCTET STRING tag (0x04) near the end of the
++ * encoded message and corrupt it. The signature is the last large
++ * OCTET STRING in the SignerInfo. Search backwards for 0x04 followed
++ * by a length that looks like an RSA signature (>= 64 bytes).
++ * This heuristic depends on the signature being the last large
++ * OCTET STRING; the found==1 assertion below guards against
++ * silent false passes if encoding changes. */
++ if (EXPECT_SUCCESS()) {
++ int found = 0;
++ for (i = encodedSz - 10; i > 10; i--) {
++ if (encoded[i] == 0x04) {
++ int len = 0, lbytes = 0;
++ if (encoded[i+1] < 0x80) {
++ len = encoded[i+1]; lbytes = 1;
++ }
++ else if (encoded[i+1] == 0x81) {
++ len = encoded[i+2]; lbytes = 2;
++ }
++ else if (encoded[i+1] == 0x82) {
++ len = (encoded[i+2] << 8) | encoded[i+3]; lbytes = 3;
++ }
++ /* RSA signature is typically >= 128 bytes */
++ if (len >= 64 && i + 1 + lbytes + len <= encodedSz) {
++ /* Corrupt the OCTET STRING tag to INTEGER */
++ encoded[i] = 0x02; /* ASN_INTEGER instead of OCTET STRING */
++ found = 1;
++ break;
++ }
++ }
++ }
++ ExpectIntEQ(found, 1);
++ }
++
++ /* Verify the corrupted SignedData - must fail */
++ if (EXPECT_SUCCESS()) {
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
++ ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, encoded,
++ (word32)encodedSz), 0);
++ wc_PKCS7_Free(pkcs7);
++ pkcs7 = NULL;
++ }
++
++ DoExpectIntEQ(wc_FreeRng(&rng), 0);
++ XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Test: PKCS#7 EnvelopedData must reject when encryptedContentTotalSz
++ * exceeds the remaining input buffer.
++ *
++ * The fix adds a bounds check under NO_PKCS7_STREAM, but the same
++ * crafted message should also be properly handled in streaming mode.
++ * This test crafts an EnvelopedData where the [0] content length
++ * claims 512 bytes but only minimal data follows. */
++static int test_pkcs7_enveloped_content_size_overflow(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC) && !defined(NO_RSA)
++ wc_PKCS7* p7 = NULL;
++ byte out[256];
++
++ /* EnvelopedData with KTRI where the EncryptedContentInfo [0] claims
++ * 512 bytes but only 16 are present.
++ *
++ * The outer structure is valid enough to reach the content parsing:
++ * ContentInfo -> EnvelopedData -> version=0 ->
++ * RecipientInfos (empty SET) -> EncryptedContentInfo ->
++ * contentType + AlgorithmIdentifier + [0] oversized content */
++ static const byte poc[] = {
++ /* ContentInfo SEQUENCE */
++ 0x30, 0x50,
++ /* OID envelopedData 1.2.840.113549.1.7.3 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT */
++ 0xa0, 0x43,
++ /* EnvelopedData SEQUENCE */
++ 0x30, 0x41,
++ /* version = 0 */
++ 0x02, 0x01, 0x00,
++ /* RecipientInfos SET (empty - no recipients) */
++ 0x31, 0x00,
++ /* EncryptedContentInfo SEQUENCE */
++ 0x30, 0x3c,
++ /* OID data 1.2.840.113549.1.7.1 */
++ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
++ 0x01,
++ /* AlgorithmIdentifier SEQUENCE */
++ 0x30, 0x1d,
++ /* OID AES-256-CBC 2.16.840.1.101.3.4.1.42 */
++ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01,
++ 0x2a,
++ /* IV: OCTET STRING (16 zero bytes) */
++ 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ /* [0] IMPLICIT encryptedContent - claims 512 bytes! */
++ 0x80, 0x82, 0x02, 0x00,
++ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
++ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
++ };
++
++ p7 = wc_PKCS7_New(NULL, INVALID_DEVID);
++ ExpectNotNull(p7);
++ if (p7 != NULL) {
++ /* With an empty RecipientInfos SET, the function may fail at
++ * the recipient matching stage before reaching the content-size
++ * bounds check. The ExpectIntLT assertion ensures the
++ * oversized content does not cause a buffer over-read. */
++ ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc),
++ out, sizeof(out)), 0);
++
++ wc_PKCS7_Free(p7);
++ }
++#endif
++ return EXPECT_RESULT();
++}
++
+ /* Dilithium verify_ctx_msg must reject absurdly large msgLen */
+ static int test_dilithium_hash(void)
+ {
+@@ -35936,6 +36636,13 @@ TEST_CASE testCases[] = {
+ TEST_DECL(test_ed448_rejects_identity_key),
+ TEST_DECL(test_pkcs7_decode_encrypted_outputsz),
+ TEST_DECL(test_pkcs7_ori_oversized_oid),
++ TEST_DECL(test_pkcs7_ori_seqsz_underflow),
++ TEST_DECL(test_pkcs7_ori_orivalue_overflow),
++ TEST_DECL(test_pkcs7_ktri_skid_length_mismatch),
++ TEST_DECL(test_pkcs7_kari_degenerate_bitstring),
++ TEST_DECL(test_pkcs7_encrypted_content_size_overflow),
++ TEST_DECL(test_pkcs7_signed_bad_sig_tag),
++ TEST_DECL(test_pkcs7_enveloped_content_size_overflow),
+ TEST_DECL(test_pkcs7_padding),
+
+ #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT)
+diff --git a/tests/api/test_ossl_p7p12.c b/tests/api/test_ossl_p7p12.c
+index c92b80ee5..03590a3ad 100644
+--- a/tests/api/test_ossl_p7p12.c
++++ b/tests/api/test_ossl_p7p12.c
+@@ -446,6 +446,460 @@ int test_wolfSSL_PKCS7_sign(void)
+ return EXPECT_RESULT();
+ }
+
++/* Regression test for CMS SignedData signer-identity forgery.
++ *
++ * The embedded DER is a CMS SignedData message crafted so that the
++ * certificates SET contains two certificates:
++ * cert[0] = certs/ca-cert.pem (trusted wolfSSL CA; attacker does NOT hold
++ * its private key)
++ * cert[1] = a self-signed "attacker" P-256 certificate (attacker holds
++ * the private key)
++ * The signerInfo sid names the attacker certificate, and the signature
++ * was produced with the attacker's key over "Hello World".
++ *
++ * The bug that was present: wolfSSL_PKCS7_verify() iterated all bundled
++ * certificates trying each public key against the signature. When the
++ * attacker's key verified, it still reported cert[0] (the trusted CA cert,
++ * via singleCert) as the signer, and chain validation therefore succeeded
++ * on an unrelated trusted cert - a full signer-identity forgery.
++ *
++ * The expected, correct behavior: the CMS message is rejected because the
++ * signer certificate named by the sid (the attacker cert) does not chain
++ * to any certificate in the trust store. */
++int test_wolfSSL_PKCS7_verify_signer_forgery(void)
++{
++ EXPECT_DECLS;
++#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_BIO) && \
++ !defined(NO_FILESYSTEM) && !defined(NO_RSA) && defined(HAVE_ECC)
++ static const byte forgedSignedData[] = {
++ 0x30, 0x82, 0x07, 0x9c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
++ 0x01, 0x07, 0x02, 0xa0, 0x82, 0x07, 0x8d, 0x30, 0x82, 0x07, 0x89, 0x02,
++ 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
++ 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48,
++ 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x0e, 0x04, 0x0c, 0x48, 0x65,
++ 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0a, 0xa0, 0x82,
++ 0x06, 0xab, 0x30, 0x82, 0x04, 0xff, 0x30, 0x82, 0x03, 0xe7, 0xa0, 0x03,
++ 0x02, 0x01, 0x02, 0x02, 0x14, 0x3f, 0x29, 0x11, 0x20, 0x57, 0x71, 0xe7,
++ 0x8e, 0xf9, 0x18, 0x0d, 0xca, 0x70, 0x4d, 0x5b, 0x15, 0x2a, 0x43, 0xd6,
++ 0x24, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++ 0x01, 0x0b, 0x05, 0x00, 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09, 0x06,
++ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e,
++ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x4d, 0x6f, 0x6e, 0x74, 0x61,
++ 0x6e, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
++ 0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61, 0x6e, 0x31, 0x11, 0x30, 0x0f,
++ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x53, 0x61, 0x77, 0x74, 0x6f,
++ 0x6f, 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b,
++ 0x0c, 0x0a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67,
++ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, 0x77,
++ 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63,
++ 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
++ 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66, 0x6f, 0x40,
++ 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
++ 0x1e, 0x17, 0x0d, 0x32, 0x35, 0x31, 0x31, 0x31, 0x33, 0x32, 0x30, 0x34,
++ 0x31, 0x31, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x38, 0x30, 0x39,
++ 0x32, 0x30, 0x34, 0x31, 0x31, 0x31, 0x5a, 0x30, 0x81, 0x94, 0x31, 0x0b,
++ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
++ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x4d, 0x6f,
++ 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
++ 0x04, 0x07, 0x0c, 0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61, 0x6e, 0x31,
++ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x53, 0x61,
++ 0x77, 0x74, 0x6f, 0x6f, 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
++ 0x55, 0x04, 0x0b, 0x0c, 0x0a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74,
++ 0x69, 0x6e, 0x67, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03,
++ 0x0c, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73,
++ 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a,
++ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e,
++ 0x66, 0x6f, 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63,
++ 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
++ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
++ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbf,
++ 0x0c, 0xca, 0x2d, 0x14, 0xb2, 0x1e, 0x84, 0x42, 0x5b, 0xcd, 0x38, 0x1f,
++ 0x4a, 0xf2, 0x4d, 0x75, 0x10, 0xf1, 0xb6, 0x35, 0x9f, 0xdf, 0xca, 0x7d,
++ 0x03, 0x98, 0xd3, 0xac, 0xde, 0x03, 0x66, 0xee, 0x2a, 0xf1, 0xd8, 0xb0,
++ 0x7d, 0x6e, 0x07, 0x54, 0x0b, 0x10, 0x98, 0x21, 0x4d, 0x80, 0xcb, 0x12,
++ 0x20, 0xe7, 0xcc, 0x4f, 0xde, 0x45, 0x7d, 0xc9, 0x72, 0x77, 0x32, 0xea,
++ 0xca, 0x90, 0xbb, 0x69, 0x52, 0x10, 0x03, 0x2f, 0xa8, 0xf3, 0x95, 0xc5,
++ 0xf1, 0x8b, 0x62, 0x56, 0x1b, 0xef, 0x67, 0x6f, 0xa4, 0x10, 0x41, 0x95,
++ 0xad, 0x0a, 0x9b, 0xe3, 0xa5, 0xc0, 0xb0, 0xd2, 0x70, 0x76, 0x50, 0x30,
++ 0x5b, 0xa8, 0xe8, 0x08, 0x2c, 0x7c, 0xed, 0xa7, 0xa2, 0x7a, 0x8d, 0x38,
++ 0x29, 0x1c, 0xac, 0xc7, 0xed, 0xf2, 0x7c, 0x95, 0xb0, 0x95, 0x82, 0x7d,
++ 0x49, 0x5c, 0x38, 0xcd, 0x77, 0x25, 0xef, 0xbd, 0x80, 0x75, 0x53, 0x94,
++ 0x3c, 0x3d, 0xca, 0x63, 0x5b, 0x9f, 0x15, 0xb5, 0xd3, 0x1d, 0x13, 0x2f,
++ 0x19, 0xd1, 0x3c, 0xdb, 0x76, 0x3a, 0xcc, 0xb8, 0x7d, 0xc9, 0xe5, 0xc2,
++ 0xd7, 0xda, 0x40, 0x6f, 0xd8, 0x21, 0xdc, 0x73, 0x1b, 0x42, 0x2d, 0x53,
++ 0x9c, 0xfe, 0x1a, 0xfc, 0x7d, 0xab, 0x7a, 0x36, 0x3f, 0x98, 0xde, 0x84,
++ 0x7c, 0x05, 0x67, 0xce, 0x6a, 0x14, 0x38, 0x87, 0xa9, 0xf1, 0x8c, 0xb5,
++ 0x68, 0xcb, 0x68, 0x7f, 0x71, 0x20, 0x2b, 0xf5, 0xa0, 0x63, 0xf5, 0x56,
++ 0x2f, 0xa3, 0x26, 0xd2, 0xb7, 0x6f, 0xb1, 0x5a, 0x17, 0xd7, 0x38, 0x99,
++ 0x08, 0xfe, 0x93, 0x58, 0x6f, 0xfe, 0xc3, 0x13, 0x49, 0x08, 0x16, 0x0b,
++ 0xa7, 0x4d, 0x67, 0x00, 0x52, 0x31, 0x67, 0x23, 0x4e, 0x98, 0xed, 0x51,
++ 0x45, 0x1d, 0xb9, 0x04, 0xd9, 0x0b, 0xec, 0xd8, 0x28, 0xb3, 0x4b, 0xbd,
++ 0xed, 0x36, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x45,
++ 0x30, 0x82, 0x01, 0x41, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
++ 0x16, 0x04, 0x14, 0x27, 0x8e, 0x67, 0x11, 0x74, 0xc3, 0x26, 0x1d, 0x3f,
++ 0xed, 0x33, 0x63, 0xb3, 0xa4, 0xd8, 0x1d, 0x30, 0xe5, 0xe8, 0xd5, 0x30,
++ 0x81, 0xd4, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xcc, 0x30, 0x81,
++ 0xc9, 0x80, 0x14, 0x27, 0x8e, 0x67, 0x11, 0x74, 0xc3, 0x26, 0x1d, 0x3f,
++ 0xed, 0x33, 0x63, 0xb3, 0xa4, 0xd8, 0x1d, 0x30, 0xe5, 0xe8, 0xd5, 0xa1,
++ 0x81, 0x9a, 0xa4, 0x81, 0x97, 0x30, 0x81, 0x94, 0x31, 0x0b, 0x30, 0x09,
++ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30,
++ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x07, 0x4d, 0x6f, 0x6e, 0x74,
++ 0x61, 0x6e, 0x61, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
++ 0x0c, 0x07, 0x42, 0x6f, 0x7a, 0x65, 0x6d, 0x61, 0x6e, 0x31, 0x11, 0x30,
++ 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x08, 0x53, 0x61, 0x77, 0x74,
++ 0x6f, 0x6f, 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
++ 0x0b, 0x0c, 0x0a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
++ 0x67, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f,
++ 0x77, 0x77, 0x77, 0x2e, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e,
++ 0x63, 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x09, 0x2a, 0x86, 0x48,
++ 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6e, 0x66, 0x6f,
++ 0x40, 0x77, 0x6f, 0x6c, 0x66, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
++ 0x82, 0x14, 0x3f, 0x29, 0x11, 0x20, 0x57, 0x71, 0xe7, 0x8e, 0xf9, 0x18,
++ 0x0d, 0xca, 0x70, 0x4d, 0x5b, 0x15, 0x2a, 0x43, 0xd6, 0x24, 0x30, 0x0c,
++ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
++ 0x30, 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x15, 0x30, 0x13, 0x82,
++ 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
++ 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
++ 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
++ 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
++ 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x0f, 0xae, 0x89,
++ 0xd5, 0x68, 0xe4, 0x41, 0xf8, 0x9b, 0xe0, 0xc5, 0x61, 0x06, 0x57, 0xff,
++ 0xa0, 0x92, 0x0f, 0xb2, 0xed, 0xd3, 0x99, 0x5b, 0x99, 0x5e, 0x32, 0x7e,
++ 0x97, 0xc7, 0xaf, 0x6c, 0xfe, 0x8c, 0xa6, 0xae, 0x32, 0xa1, 0x0d, 0xca,
++ 0xcd, 0xfc, 0x18, 0xe5, 0xd1, 0xf8, 0x20, 0x5b, 0x5a, 0x38, 0x81, 0x46,
++ 0x5b, 0x48, 0x87, 0xa5, 0x3f, 0x3b, 0x7b, 0xc7, 0xea, 0xf5, 0x35, 0x29,
++ 0x31, 0x15, 0x39, 0x38, 0x5d, 0x48, 0xe6, 0x01, 0x81, 0x5c, 0x5e, 0x7c,
++ 0x10, 0xf5, 0x16, 0xe3, 0x59, 0xaf, 0x44, 0xc8, 0xb5, 0x8d, 0xc1, 0x32,
++ 0x23, 0xb3, 0xb8, 0x12, 0x6e, 0x5c, 0x8d, 0xe6, 0xc2, 0xd2, 0x41, 0x03,
++ 0xeb, 0x17, 0x42, 0xe2, 0x7f, 0xbc, 0x00, 0x5d, 0xa5, 0x31, 0xef, 0xc6,
++ 0x48, 0xee, 0xdb, 0xcc, 0xe0, 0xf1, 0x56, 0xf5, 0xd4, 0xca, 0x45, 0xa1,
++ 0x59, 0xb5, 0xe4, 0xd7, 0x60, 0x9c, 0x57, 0xe0, 0xa7, 0x5a, 0xf2, 0x35,
++ 0x1e, 0xa0, 0x22, 0xdb, 0x5e, 0x1c, 0x0c, 0x61, 0xbd, 0xa1, 0xc5, 0x7b,
++ 0x9f, 0x69, 0xf2, 0xd5, 0x95, 0xe2, 0xbc, 0x52, 0xb9, 0x1d, 0x9c, 0x2c,
++ 0xda, 0xb6, 0x73, 0x75, 0x4a, 0x84, 0xe5, 0x94, 0xb8, 0x19, 0x4d, 0xdd,
++ 0x70, 0xbd, 0x7f, 0x4c, 0xb9, 0x17, 0x6a, 0x58, 0x16, 0x89, 0x22, 0x44,
++ 0x37, 0x57, 0x55, 0x26, 0x42, 0xe3, 0xb7, 0xe5, 0xc7, 0x2b, 0x40, 0x0c,
++ 0xe9, 0xe4, 0x7f, 0x52, 0x75, 0xdf, 0x06, 0xc9, 0xfb, 0x01, 0x44, 0x34,
++ 0xac, 0x20, 0x3c, 0xb4, 0xbe, 0x2b, 0x3e, 0xef, 0x85, 0x38, 0x96, 0x5b,
++ 0x9b, 0x1e, 0x25, 0x86, 0x18, 0x4c, 0xa4, 0x06, 0x70, 0x06, 0x6a, 0xc8,
++ 0x4b, 0x6f, 0x5f, 0xc4, 0x05, 0x1f, 0x03, 0x62, 0x30, 0x11, 0x61, 0xbc,
++ 0xc1, 0x40, 0x31, 0x66, 0xdc, 0x64, 0xf0, 0x4f, 0x6b, 0xb9, 0xec, 0xc8,
++ 0x29, 0x30, 0x82, 0x01, 0xa4, 0x30, 0x82, 0x01, 0x49, 0xa0, 0x03, 0x02,
++ 0x01, 0x02, 0x02, 0x14, 0x62, 0x4d, 0x11, 0x9c, 0xcf, 0x5d, 0xe5, 0x71,
++ 0xa2, 0x82, 0xd9, 0x8f, 0xe0, 0x04, 0xb8, 0x5f, 0x0e, 0x4d, 0x07, 0xad,
++ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
++ 0x30, 0x27, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
++ 0x08, 0x61, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x31, 0x12, 0x30,
++ 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x75, 0x6e, 0x74, 0x72,
++ 0x75, 0x73, 0x74, 0x65, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x36, 0x30,
++ 0x34, 0x32, 0x31, 0x31, 0x31, 0x31, 0x36, 0x32, 0x38, 0x5a, 0x17, 0x0d,
++ 0x33, 0x36, 0x30, 0x34, 0x31, 0x38, 0x31, 0x31, 0x31, 0x36, 0x32, 0x38,
++ 0x5a, 0x30, 0x27, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03,
++ 0x0c, 0x08, 0x61, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x65, 0x72, 0x31, 0x12,
++ 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x75, 0x6e, 0x74,
++ 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
++ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
++ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xae, 0xdb, 0xf7,
++ 0x3b, 0x7e, 0x82, 0x88, 0xfc, 0x1a, 0xfb, 0x86, 0x56, 0x83, 0x03, 0xdd,
++ 0x05, 0x14, 0x79, 0x51, 0x0f, 0x3c, 0x86, 0x85, 0x2d, 0xeb, 0x18, 0x17,
++ 0x20, 0x3b, 0x37, 0x6f, 0x7f, 0x78, 0x19, 0x3b, 0xf6, 0x71, 0xad, 0xc9,
++ 0x65, 0x81, 0x7e, 0xe0, 0xa9, 0x29, 0xdd, 0xfd, 0xf0, 0xff, 0x04, 0x7d,
++ 0x5a, 0x59, 0xd6, 0x6c, 0xe2, 0xde, 0xc5, 0xd5, 0xb6, 0x1f, 0x69, 0xd9,
++ 0x33, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
++ 0x04, 0x16, 0x04, 0x14, 0xf7, 0xab, 0x3f, 0x49, 0xcf, 0x7d, 0x48, 0x9c,
++ 0x04, 0x49, 0x1a, 0xac, 0x8f, 0x26, 0x16, 0x09, 0xa8, 0x2a, 0x74, 0xf5,
++ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
++ 0x14, 0xf7, 0xab, 0x3f, 0x49, 0xcf, 0x7d, 0x48, 0x9c, 0x04, 0x49, 0x1a,
++ 0xac, 0x8f, 0x26, 0x16, 0x09, 0xa8, 0x2a, 0x74, 0xf5, 0x30, 0x0f, 0x06,
++ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
++ 0x01, 0xff, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04,
++ 0x03, 0x02, 0x03, 0x49, 0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0x8d, 0xbf,
++ 0x36, 0xe5, 0x51, 0x9a, 0xde, 0xf4, 0x7f, 0xbf, 0xbd, 0x7f, 0x71, 0x66,
++ 0xc1, 0x67, 0xfa, 0x71, 0x0d, 0x79, 0xc6, 0x60, 0x3a, 0x6c, 0xeb, 0x43,
++ 0xc3, 0xf2, 0x5e, 0xe8, 0x74, 0xb6, 0x02, 0x21, 0x00, 0xfa, 0xdb, 0x40,
++ 0x47, 0x72, 0xf0, 0x15, 0x52, 0xc1, 0x78, 0x11, 0x6b, 0x76, 0xc5, 0x1f,
++ 0xcf, 0xb6, 0x09, 0x6d, 0x8f, 0xcb, 0x92, 0x2f, 0x1b, 0x3c, 0xc3, 0x28,
++ 0x48, 0x61, 0x0f, 0x60, 0x71, 0x31, 0x81, 0xa8, 0x30, 0x81, 0xa5, 0x02,
++ 0x01, 0x01, 0x30, 0x3f, 0x30, 0x27, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03,
++ 0x55, 0x04, 0x03, 0x0c, 0x08, 0x61, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x65,
++ 0x72, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09,
++ 0x75, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x02, 0x14, 0x62,
++ 0x4d, 0x11, 0x9c, 0xcf, 0x5d, 0xe5, 0x71, 0xa2, 0x82, 0xd9, 0x8f, 0xe0,
++ 0x04, 0xb8, 0x5f, 0x0e, 0x4d, 0x07, 0xad, 0x30, 0x0b, 0x06, 0x09, 0x60,
++ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0a, 0x06, 0x08,
++ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x04, 0x46, 0x30, 0x44,
++ 0x02, 0x20, 0x22, 0x4a, 0x99, 0xb1, 0xbc, 0xa9, 0xee, 0x24, 0x60, 0x81,
++ 0xb9, 0x64, 0xba, 0x86, 0x00, 0xae, 0xb5, 0xd7, 0xb8, 0x72, 0xb9, 0x8c,
++ 0xb3, 0xe7, 0x78, 0x29, 0xdb, 0xa8, 0x27, 0xf7, 0x30, 0xf0, 0x02, 0x20,
++ 0x19, 0x2d, 0xd3, 0x17, 0x9a, 0xc1, 0xf9, 0xd2, 0x63, 0x92, 0x8e, 0x78,
++ 0xcc, 0xa4, 0x0b, 0x91, 0x12, 0xa5, 0xb2, 0xbc, 0x35, 0x87, 0x8e, 0x33,
++ 0xa7, 0xe0, 0x5e, 0xab, 0x95, 0xb2, 0x2a, 0xf4
++ };
++ PKCS7* p7 = NULL;
++ X509_STORE* store = NULL;
++ X509* caCert = NULL;
++ WOLFSSL_BIO* caBio = NULL;
++ const byte* p = forgedSignedData;
++ const char* ca = "./certs/ca-cert.pem";
++
++ /* Load the same CA into the trust store that the attacker bundled at
++ * cert[0] in the forged message. */
++ ExpectNotNull(caBio = BIO_new_file(ca, "r"));
++ ExpectNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL));
++ ExpectNotNull(store = X509_STORE_new());
++ ExpectIntEQ(X509_STORE_add_cert(store, caCert), 1);
++
++ /* Parse the forged message. d2i_PKCS7 internally runs
++ * wc_PKCS7_VerifySignedData, which must NOT accept the attacker's
++ * signature under any bundled cert other than the one named by the
++ * signerInfo sid. Since the sid names the attacker cert (which does
++ * not chain to the trusted CA), the parse may succeed but verification
++ * against the trust store must fail. */
++ ExpectNotNull(p7 = d2i_PKCS7(NULL, &p, (int)sizeof(forgedSignedData)));
++
++ /* PKCS7_verify() MUST fail: the only certificate in the trust store
++ * is the wolfSSL CA - it is bundled at cert[0] but did NOT sign this
++ * message. The actual signer (the attacker's self-signed cert at
++ * cert[1]) cannot chain to any trust anchor. */
++ ExpectIntEQ(PKCS7_verify(p7, NULL, store, NULL, NULL, 0),
++ WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
++
++ PKCS7_free(p7);
++ X509_STORE_free(store);
++ X509_free(caCert);
++ BIO_free(caBio);
++#endif
++ return EXPECT_RESULT();
++}
++
++/* Exercise the SignerInfo-sid binding enforcement end-to-end.
++ *
++ * For both supported sid encodings (v1 = IssuerAndSerialNumber, v3 =
++ * SubjectKeyIdentifier), this builds a valid CMS SignedData message with
++ * two certificates in the bundle:
++ * cert[0] = ca-cert (extra, non-signing)
++ * cert[1] = server-cert (actual signer)
++ * and checks that:
++ * - parsing + signature verification succeeds,
++ * - chain validation against a trust store containing ca-cert succeeds,
++ * - PKCS7_get0_signers() returns the *signer* (server-cert), not the
++ * extra cert at cert[0] - which would be the pre-fix behavior and the
++ * core of the signer-identity forgery bug. */
++int test_wolfSSL_PKCS7_verify_sid_binding(void)
++{
++ EXPECT_DECLS;
++#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_BIO) && \
++ !defined(NO_FILESYSTEM) && !defined(NO_RSA)
++ const char* signerCertFile = "./certs/server-cert.pem";
++ const char* signerKeyFile = "./certs/server-key.pem";
++ const char* caFile = "./certs/ca-cert.pem";
++ const byte content[] = "sid-binding test content";
++ /* Build both variants: default v1 (IssuerAndSerialNumber) and v3
++ * (SubjectKeyIdentifier). */
++ const int sidTypes[2] = { CMS_ISSUER_AND_SERIAL_NUMBER, CMS_SKID };
++ int variant;
++
++ BIO* signerCertBio = NULL;
++ BIO* caBio = NULL;
++ X509* signerCertX509 = NULL;
++ X509* caX509 = NULL;
++ byte* signerCertDer = NULL;
++ byte* caDer = NULL;
++ byte* signerKey = NULL;
++ int signerCertDerSz = 0;
++ int caDerSz = 0;
++ size_t signerKeySz = 0;
++ XFILE keyFile = XBADFILE;
++ WC_RNG rng;
++ int rngInited = 0;
++
++ /* ---- Load signer cert + key and the CA cert. ---- */
++ ExpectNotNull(signerCertBio = BIO_new_file(signerCertFile, "r"));
++ ExpectNotNull(signerCertX509 = PEM_read_bio_X509(signerCertBio, NULL, 0,
++ NULL));
++ ExpectIntGT(signerCertDerSz = i2d_X509(signerCertX509, &signerCertDer), 0);
++
++ ExpectNotNull(caBio = BIO_new_file(caFile, "r"));
++ ExpectNotNull(caX509 = PEM_read_bio_X509(caBio, NULL, 0, NULL));
++ ExpectIntGT(caDerSz = i2d_X509(caX509, &caDer), 0);
++
++ /* Slurp the DER private key straight from a PEM->DER round-trip via
++ * wc_KeyPemToDer. The test only needs the bytes in a form
++ * wc_PKCS7_EncodeSignedData can consume. */
++ {
++ long filePemLen = 0;
++ byte* keyPem = NULL;
++ int derLen = 0;
++
++ ExpectTrue((keyFile = XFOPEN(signerKeyFile, "rb")) != XBADFILE);
++ if (keyFile != XBADFILE) {
++ (void)XFSEEK(keyFile, 0, XSEEK_END);
++ filePemLen = XFTELL(keyFile);
++ (void)XFSEEK(keyFile, 0, XSEEK_SET);
++ ExpectIntGT(filePemLen, 0);
++ keyPem = (byte*)XMALLOC((size_t)filePemLen, NULL,
++ DYNAMIC_TYPE_TMP_BUFFER);
++ ExpectNotNull(keyPem);
++ if (keyPem != NULL) {
++ ExpectIntEQ(XFREAD(keyPem, 1, (size_t)filePemLen, keyFile),
++ (size_t)filePemLen);
++ /* First call sizes the output buffer. */
++ derLen = wc_KeyPemToDer(keyPem, (word32)filePemLen, NULL, 0,
++ NULL);
++ ExpectIntGT(derLen, 0);
++ if (derLen > 0) {
++ signerKey = (byte*)XMALLOC((size_t)derLen, NULL,
++ DYNAMIC_TYPE_TMP_BUFFER);
++ ExpectNotNull(signerKey);
++ if (signerKey != NULL) {
++ derLen = wc_KeyPemToDer(keyPem, (word32)filePemLen,
++ signerKey, (word32)derLen,
++ NULL);
++ ExpectIntGT(derLen, 0);
++ signerKeySz = (size_t)derLen;
++ }
++ }
++ }
++ XFREE(keyPem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ XFCLOSE(keyFile);
++ keyFile = XBADFILE;
++ }
++ }
++
++ ExpectIntEQ(wc_InitRng(&rng), 0);
++ if (EXPECT_SUCCESS())
++ rngInited = 1;
++
++ for (variant = 0; variant < 2; variant++) {
++ wc_PKCS7* p7Enc = NULL;
++ byte encoded[4096];
++ int encodedSz = 0;
++ PKCS7* p7Ver = NULL;
++ X509_STORE* store = NULL;
++ const byte* encodedPtr = NULL;
++ STACK_OF(X509)* signers = NULL;
++ X509* reportedSigner = NULL;
++ byte* reportedSignerDer = NULL;
++ int reportedSignerDerSz = 0;
++ X509* caForStore = NULL;
++ BIO* caForStoreBio = NULL;
++
++ /* ---- Encode: signer=server-cert, extra bundle cert=ca. ---- */
++ ExpectNotNull(p7Enc = wc_PKCS7_New(HEAP_HINT, INVALID_DEVID));
++ ExpectIntEQ(wc_PKCS7_Init(p7Enc, HEAP_HINT, INVALID_DEVID), 0);
++ ExpectIntEQ(wc_PKCS7_InitWithCert(p7Enc, signerCertDer,
++ (word32)signerCertDerSz), 0);
++ /* wc_PKCS7_AddCertificate prepends to the cert list - the encoded
++ * SET therefore ends up [ca, signer], putting the actual signer
++ * at index 1 and exercising the sid-selection path (cert[0] is
++ * NOT the signer and must be skipped). */
++ ExpectIntEQ(wc_PKCS7_AddCertificate(p7Enc, caDer, (word32)caDerSz), 0);
++
++ if (p7Enc != NULL) {
++ p7Enc->content = (byte*)content;
++ p7Enc->contentSz = (word32)sizeof(content);
++ p7Enc->encryptOID = RSAk;
++ p7Enc->hashOID = SHA256h;
++ p7Enc->privateKey = signerKey;
++ p7Enc->privateKeySz = (word32)signerKeySz;
++ p7Enc->rng = &rng;
++ }
++
++ ExpectIntEQ(wc_PKCS7_SetSignerIdentifierType(p7Enc, sidTypes[variant]),
++ 0);
++
++ ExpectIntGT((encodedSz = wc_PKCS7_EncodeSignedData(p7Enc, encoded,
++ sizeof(encoded))),
++ 0);
++ wc_PKCS7_Free(p7Enc);
++ p7Enc = NULL;
++
++ /* ---- Parse + verify through the OpenSSL compat layer. ---- */
++ encodedPtr = encoded;
++ ExpectNotNull(p7Ver = d2i_PKCS7(NULL, &encodedPtr, encodedSz));
++
++ /* Trust store holds only ca-cert. Reload it rather than reusing
++ * caX509, since X509_STORE_free takes ownership-like semantics. */
++ ExpectNotNull(caForStoreBio = BIO_new_file(caFile, "r"));
++ ExpectNotNull(caForStore = PEM_read_bio_X509(caForStoreBio, NULL, 0,
++ NULL));
++ ExpectNotNull(store = X509_STORE_new());
++ ExpectIntEQ(X509_STORE_add_cert(store, caForStore), 1);
++
++ ExpectIntEQ(PKCS7_verify(p7Ver, NULL, store, NULL, NULL, 0), 1);
++
++ /* Snapshot the singleCert / verifyCert pointers and sizes after
++ * PKCS7_verify has finished re-parsing the message. A buggy
++ * implementation that passes &p7->pkcs7.singleCert (or verifyCert)
++ * directly to wolfSSL_d2i_X509 permanently advances the struct
++ * field, which corrupts it for any subsequent use - producing a
++ * heap-OOB read on the next call since the size isn't advanced.
++ * These pointers must stay exactly where they are across repeated
++ * get0_signers calls. */
++ if (p7Ver != NULL) {
++ wc_PKCS7* wcP7 = &((WOLFSSL_PKCS7*)p7Ver)->pkcs7;
++ byte* singleBefore = wcP7->singleCert;
++ word32 singleSzBefore = wcP7->singleCertSz;
++ byte* verifyBefore = wcP7->verifyCert;
++ word32 verifySzBefore = wcP7->verifyCertSz;
++ int i;
++
++ /* Call get0_signers repeatedly. Each invocation must return
++ * the correct cert and must not mutate singleCert/verifyCert.
++ * Three iterations so the "second call reads past the end"
++ * pattern (the exact OOB the reporter hit) is exercised. */
++ for (i = 0; i < 3; i++) {
++ ExpectNotNull(signers = PKCS7_get0_signers(p7Ver, NULL, 0));
++ ExpectIntEQ(sk_X509_num(signers), 1);
++ ExpectNotNull(reportedSigner = sk_X509_value(signers, 0));
++ ExpectIntGT(reportedSignerDerSz = i2d_X509(reportedSigner,
++ &reportedSignerDer), 0);
++ /* DER-compare: reportedSigner must equal server-cert and
++ * must NOT equal ca-cert (the pre-fix signer-confusion
++ * outcome). */
++ ExpectIntEQ(reportedSignerDerSz, signerCertDerSz);
++ if (reportedSignerDer != NULL && signerCertDer != NULL) {
++ ExpectIntEQ(XMEMCMP(reportedSignerDer, signerCertDer,
++ (size_t)signerCertDerSz), 0);
++ if (reportedSignerDerSz == caDerSz) {
++ ExpectIntNE(XMEMCMP(reportedSignerDer, caDer,
++ (size_t)caDerSz), 0);
++ }
++ }
++ XFREE(reportedSignerDer, NULL, DYNAMIC_TYPE_OPENSSL);
++ reportedSignerDer = NULL;
++ sk_X509_pop_free(signers, NULL);
++ signers = NULL;
++
++ /* Struct fields must survive every call unchanged. */
++ ExpectPtrEq(wcP7->singleCert, singleBefore);
++ ExpectIntEQ(wcP7->singleCertSz, singleSzBefore);
++ ExpectPtrEq(wcP7->verifyCert, verifyBefore);
++ ExpectIntEQ(wcP7->verifyCertSz, verifySzBefore);
++ }
++ }
++
++ PKCS7_free(p7Ver);
++ X509_STORE_free(store);
++ X509_free(caForStore);
++ BIO_free(caForStoreBio);
++ }
++
++ if (rngInited)
++ wc_FreeRng(&rng);
++
++ XFREE(signerKey, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(signerCertDer, NULL, DYNAMIC_TYPE_OPENSSL);
++ XFREE(caDer, NULL, DYNAMIC_TYPE_OPENSSL);
++ X509_free(signerCertX509);
++ X509_free(caX509);
++ BIO_free(signerCertBio);
++ BIO_free(caBio);
++#endif
++ return EXPECT_RESULT();
++}
++
+ int test_wolfSSL_PKCS7_SIGNED_new(void)
+ {
+ EXPECT_DECLS;
+diff --git a/tests/api/test_ossl_p7p12.h b/tests/api/test_ossl_p7p12.h
+index 58aa9dd12..6704ab51e 100644
+--- a/tests/api/test_ossl_p7p12.h
++++ b/tests/api/test_ossl_p7p12.h
+@@ -27,6 +27,8 @@
+ int test_wolfssl_PKCS7(void);
+ int test_wolfSSL_PKCS7_certs(void);
+ int test_wolfSSL_PKCS7_sign(void);
++int test_wolfSSL_PKCS7_verify_signer_forgery(void);
++int test_wolfSSL_PKCS7_verify_sid_binding(void);
+ int test_wolfSSL_PKCS7_SIGNED_new(void);
+ int test_wolfSSL_PEM_write_bio_PKCS7(void);
+ int test_wolfSSL_PEM_write_bio_encryptedKey(void);
+@@ -38,6 +40,8 @@ int test_wolfSSL_PKCS12(void);
+ TEST_DECL_GROUP("ossl_p7", test_wolfssl_PKCS7), \
+ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_certs), \
+ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_sign), \
++ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_signer_forgery), \
++ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_sid_binding), \
+ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_SIGNED_new), \
+ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_PKCS7), \
+ TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_encryptedKey), \
+diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c
+index 3c8b59fb8..69b463160 100644
+--- a/tests/api/test_pkcs7.c
++++ b/tests/api/test_pkcs7.c
+@@ -1118,6 +1118,229 @@ int test_wc_PKCS7_EnvelopedData_KTRI_RSA_PSS(void)
+ #endif
+
+
++/*
++ * Bleichenbacher padding-oracle regression: wc_PKCS7_DecryptKtri must not
++ * return a distinguishable error when RSA PKCS#1 v1.5 unwrap of the
++ * encrypted CEK fails vs. when it succeeds with a wrong key. The
++ * mitigation substitutes a deterministic pseudo-random CEK on RSA failure
++ * so content decryption fails indistinguishably. This test corrupts the
++ * encryptedKey in a valid EnvelopedData and asserts the error matches
++ * content corruption rather than surfacing an RSA/recipient-level code.
++ * Runs for AES-128 and AES-256 because the fake CEK is a fixed 32 bytes:
++ * AES-128 (key size 16) exercises the path where the fake size differs
++ * from the real CEK size.
++ */
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
++ !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128) && \
++ defined(WOLFSSL_AES_256) && !defined(NO_HMAC) && \
++ !defined(WOLFSSL_NO_MALLOC) && \
++ (defined(USE_CERT_BUFFERS_2048) || defined(USE_CERT_BUFFERS_1024) || \
++ !defined(NO_FILESYSTEM))
++static int pkcs7_ktri_bad_pad_case(int encryptOID, byte* rsaCert,
++ word32 rsaCertSz, byte* rsaPrivKey,
++ word32 rsaPrivKeySz, byte* encrypted,
++ word32 encryptedCap, byte* decoded,
++ word32 decodedCap)
++{
++ EXPECT_DECLS;
++ PKCS7* pkcs7 = NULL;
++ byte data[] = "PKCS7 KTRI bad-RSA-padding regression payload.";
++ int encryptedSz = 0;
++ int badKeyRet = 0;
++ int badContentRet = 0;
++ byte savedKeyByte = 0;
++ byte savedContentByte = 0;
++ word32 i;
++ word32 encryptedKeyOff = 0;
++ static const byte rsaEncOid[] = {
++ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
++ 0x01, 0x01, 0x01
++ };
++
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, rsaCert, rsaCertSz), 0);
++ if (pkcs7 != NULL) {
++ pkcs7->content = data;
++ pkcs7->contentSz = (word32)sizeof(data);
++ pkcs7->contentOID = DATA;
++ pkcs7->encryptOID = encryptOID;
++ }
++ ExpectIntGT(encryptedSz = wc_PKCS7_EncodeEnvelopedData(pkcs7,
++ encrypted, encryptedCap), 0);
++ wc_PKCS7_Free(pkcs7);
++ pkcs7 = NULL;
++
++ /* Locate the KTRI encryptedKey OCTET STRING. After the rsaEncryption
++ * OID there are NULL algorithm parameters (05 00), then a 256-byte
++ * OCTET STRING (tag 04, long-form length 82 01 00 for RSA-2048). */
++ for (i = 0; (int)(i + sizeof(rsaEncOid)) < encryptedSz; i++) {
++ if (XMEMCMP(&encrypted[i], rsaEncOid, sizeof(rsaEncOid)) == 0) {
++ word32 p = i + (word32)sizeof(rsaEncOid);
++ if (p + 2 < (word32)encryptedSz &&
++ encrypted[p] == 0x05 && encrypted[p + 1] == 0x00) {
++ p += 2;
++ }
++ if (p + 4 < (word32)encryptedSz && encrypted[p] == 0x04) {
++ if (encrypted[p + 1] == 0x82) {
++ encryptedKeyOff = p + 4;
++ }
++ else if (encrypted[p + 1] == 0x81) {
++ encryptedKeyOff = p + 3;
++ }
++ else {
++ encryptedKeyOff = p + 2;
++ }
++ }
++ break;
++ }
++ }
++ ExpectIntGT(encryptedKeyOff, 0);
++ ExpectIntLT(encryptedKeyOff + 32, (word32)encryptedSz);
++
++ /* Case 1: corrupt a byte inside the RSA ciphertext, decode, restore. */
++ savedKeyByte = encrypted[encryptedKeyOff + 16];
++ encrypted[encryptedKeyOff + 16] ^= 0xA5;
++
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, rsaCert, rsaCertSz), 0);
++ if (pkcs7 != NULL) {
++ pkcs7->privateKey = rsaPrivKey;
++ pkcs7->privateKeySz = rsaPrivKeySz;
++ }
++ badKeyRet = wc_PKCS7_DecodeEnvelopedData(pkcs7, encrypted,
++ (word32)encryptedSz, decoded, decodedCap);
++ wc_PKCS7_Free(pkcs7);
++ pkcs7 = NULL;
++ encrypted[encryptedKeyOff + 16] = savedKeyByte;
++
++ /* Case 2: corrupt a byte in the second-to-last AES ciphertext block.
++ * In CBC mode this deterministically XOR-flips the corresponding byte
++ * in the last plaintext block, invalidating the PKCS#7 padding
++ * (original pad byte 0x01 becomes 0x01^0xA5 = 0xA4 > blockSz).
++ * Corrupting the last ciphertext block directly would randomize the
++ * entire last plaintext block, giving ~1/256 chance of accidentally
++ * valid padding and intermittent test failures. */
++ savedContentByte = encrypted[encryptedSz - 17];
++ encrypted[encryptedSz - 17] ^= 0xA5;
++
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, rsaCert, rsaCertSz), 0);
++ if (pkcs7 != NULL) {
++ pkcs7->privateKey = rsaPrivKey;
++ pkcs7->privateKeySz = rsaPrivKeySz;
++ }
++ badContentRet = wc_PKCS7_DecodeEnvelopedData(pkcs7, encrypted,
++ (word32)encryptedSz, decoded, decodedCap);
++ wc_PKCS7_Free(pkcs7);
++ pkcs7 = NULL;
++ encrypted[encryptedSz - 17] = savedContentByte;
++
++ /* Case 2 must always fail: the CBC-chain corruption deterministically
++ * invalidates the PKCS#7 padding. */
++ ExpectIntLT(badContentRet, 0);
++ /* Bad-key must NOT leak as an RSA- or recipient-level error. */
++ ExpectIntNE(badKeyRet, WC_NO_ERR_TRACE(PKCS7_RECIP_E));
++ ExpectIntNE(badKeyRet, WC_NO_ERR_TRACE(RSA_PAD_E));
++ ExpectIntNE(badKeyRet, WC_NO_ERR_TRACE(RSA_BUFFER_E));
++ ExpectIntNE(badKeyRet, WC_NO_ERR_TRACE(BAD_PADDING_E));
++ /* Case 1 (bad RSA key) decrypts content with a random fake CEK,
++ * producing fully random plaintext. With ~1/256 probability the
++ * PKCS#7 padding accidentally looks valid, causing a positive
++ * garbage-length return instead of an error. This does not leak
++ * RSA key information, so it is acceptable. When both cases do
++ * fail, verify they fail at the same content-decryption layer. */
++ if (badKeyRet < 0) {
++ ExpectIntEQ(badKeyRet, badContentRet);
++ }
++
++ return EXPECT_RESULT();
++}
++
++int test_wc_PKCS7_EnvelopedData_KTRI_BadRsaPad(void)
++{
++ EXPECT_DECLS;
++ byte encrypted[FOURK_BUF];
++ byte decoded[FOURK_BUF];
++ byte* rsaCert = NULL;
++ byte* rsaPrivKey = NULL;
++ word32 rsaCertSz = 0;
++ word32 rsaPrivKeySz = 0;
++#if !defined(USE_CERT_BUFFERS_1024) && !defined(USE_CERT_BUFFERS_2048) && \
++ !defined(NO_FILESYSTEM)
++ XFILE f = XBADFILE;
++#endif
++
++ /* Load RSA cert and key */
++#if defined(USE_CERT_BUFFERS_1024)
++ rsaCertSz = (word32)sizeof_client_cert_der_1024;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaCert != NULL)
++ XMEMCPY(rsaCert, client_cert_der_1024, rsaCertSz);
++ rsaPrivKeySz = (word32)sizeof_client_key_der_1024;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaPrivKey != NULL)
++ XMEMCPY(rsaPrivKey, client_key_der_1024, rsaPrivKeySz);
++#elif defined(USE_CERT_BUFFERS_2048)
++ rsaCertSz = (word32)sizeof_client_cert_der_2048;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaCert != NULL)
++ XMEMCPY(rsaCert, client_cert_der_2048, rsaCertSz);
++ rsaPrivKeySz = (word32)sizeof_client_key_der_2048;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ if (rsaPrivKey != NULL)
++ XMEMCPY(rsaPrivKey, client_key_der_2048, rsaPrivKeySz);
++#elif !defined(NO_FILESYSTEM)
++ rsaCertSz = FOURK_BUF;
++ ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/client-cert.der", "rb")) != XBADFILE);
++ ExpectTrue((rsaCertSz = (word32)XFREAD(rsaCert, 1, rsaCertSz, f)) > 0);
++ if (f != XBADFILE) {
++ XFCLOSE(f);
++ f = XBADFILE;
++ }
++ rsaPrivKeySz = FOURK_BUF;
++ ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++ ExpectTrue((f = XFOPEN("./certs/client-key.der", "rb")) != XBADFILE);
++ ExpectTrue((rsaPrivKeySz = (word32)XFREAD(rsaPrivKey, 1,
++ rsaPrivKeySz, f)) > 0);
++ if (f != XBADFILE)
++ XFCLOSE(f);
++#endif
++
++ if (rsaCert == NULL || rsaPrivKey == NULL) {
++ XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ return TEST_SKIPPED;
++ }
++
++ /* AES-128: 32-byte fake CEK larger than real CEK size (16 bytes). */
++ ExpectIntEQ(pkcs7_ktri_bad_pad_case(AES128CBCb, rsaCert, rsaCertSz,
++ rsaPrivKey, rsaPrivKeySz, encrypted, sizeof(encrypted),
++ decoded, sizeof(decoded)), TEST_SUCCESS);
++#ifdef WOLFSSL_AES_192
++ /* AES-192: fake CEK (32) vs real CEK (24) - another size mismatch. */
++ ExpectIntEQ(pkcs7_ktri_bad_pad_case(AES192CBCb, rsaCert, rsaCertSz,
++ rsaPrivKey, rsaPrivKeySz, encrypted, sizeof(encrypted),
++ decoded, sizeof(decoded)), TEST_SUCCESS);
++#endif
++ /* AES-256: fake CEK size matches real CEK size (32 bytes). */
++ ExpectIntEQ(pkcs7_ktri_bad_pad_case(AES256CBCb, rsaCert, rsaCertSz,
++ rsaPrivKey, rsaPrivKeySz, encrypted, sizeof(encrypted),
++ decoded, sizeof(decoded)), TEST_SUCCESS);
++
++ XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ return EXPECT_RESULT();
++} /* END test_wc_PKCS7_EnvelopedData_KTRI_BadRsaPad */
++#endif
++
++
+ /*
+ * Testing wc_PKCS7_EncodeSignedData_ex() and wc_PKCS7_VerifySignedData_ex()
+ */
+@@ -2397,7 +2620,8 @@ static int myCEKwrapFunc(PKCS7* pkcs7, byte* cek, word32 cekSz, byte* keyId,
+ HAVE_AES_KEYWRAP */
+
+
+-#if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER) && !defined(NO_RSA)
++#if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER) && !defined(NO_RSA) && \
++ !defined(NO_PKCS7_STREAM)
+ #define MAX_TEST_DECODE_SIZE 6000
+ static int test_wc_PKCS7_DecodeEnvelopedData_stream_decrypt_cb(wc_PKCS7* pkcs7,
+ const byte* output, word32 outputSz, void* ctx) {
+@@ -2430,7 +2654,8 @@ static int test_wc_PKCS7_DecodeEnvelopedData_stream_decrypt_cb(wc_PKCS7* pkcs7,
+ int test_wc_PKCS7_DecodeEnvelopedData_stream(void)
+ {
+ EXPECT_DECLS;
+-#if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER) && !defined(NO_RSA)
++#if defined(HAVE_PKCS7) && defined(ASN_BER_TO_DER) && !defined(NO_RSA) && \
++ !defined(NO_PKCS7_STREAM)
+ PKCS7* pkcs7 = NULL;
+ int ret = 0;
+ XFILE f = XBADFILE;
+@@ -2486,6 +2711,72 @@ int test_wc_PKCS7_DecodeEnvelopedData_stream(void)
+ } /* END test_wc_PKCS7_DecodeEnvelopedData_stream() */
+
+
++/*
++ * Regression test: a PKCS#7 EnvelopedData with a forged RecipientInfo SET
++ * length (parsed via GetSet_ex with NO_USER_CHECK) must not drive an
++ * uncapped heap allocation through wc_PKCS7_GrowStream(). The decoder
++ * must reject the oversized allocation rather than attempting it.
++ */
++int test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen(void)
++{
++ EXPECT_DECLS;
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_PKCS7_STREAM)
++ /* Crafted ContentInfo/EnvelopedData header. All lengths use the
++ * 4-byte long form for clarity. The RecipientInfo SET length is
++ * forged to 0x01000001 (16 MB + 1), which exceeds the default
++ * WOLFSSL_PKCS7_MAX_STREAM_ALLOC cap and should be rejected before
++ * any allocation succeeds. The body after the SET header is never
++ * consumed because the decoder fails at the GrowStream() cap. */
++ static const byte forged[] = {
++ /* ContentInfo SEQUENCE, body length 0x01000021 */
++ 0x30, 0x84, 0x01, 0x00, 0x00, 0x21,
++ /* OID 1.2.840.113549.1.7.3 (id-envelopedData) */
++ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
++ 0x01, 0x07, 0x03,
++ /* [0] EXPLICIT content, body length 0x01000016 */
++ 0xA0, 0x84, 0x01, 0x00, 0x00, 0x16,
++ /* EnvelopedData SEQUENCE, body length 0x01000010 */
++ 0x30, 0x84, 0x01, 0x00, 0x00, 0x10,
++ /* version INTEGER 0 */
++ 0x02, 0x01, 0x00,
++ /* Forged RecipientInfo SET header: length = 0x01000001 */
++ 0x31, 0x84, 0x01, 0x00, 0x00, 0x01,
++ /* Padding so that header-parsing states can buffer their
++ * required lookahead without returning WC_PKCS7_WANT_READ_E.
++ * These bytes are never interpreted. */
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++ };
++ PKCS7* pkcs7 = NULL;
++ byte out[32];
++ int ret;
++
++ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
++ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)client_cert_der_2048,
++ sizeof_client_cert_der_2048), 0);
++ ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)client_key_der_2048,
++ sizeof_client_key_der_2048), 0);
++
++ ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, (byte*)forged,
++ (word32)sizeof(forged), out, (word32)sizeof(out));
++ /* Must NOT return WC_PKCS7_WANT_READ_E (which would imply the
++ * oversized allocation succeeded and the decoder is waiting for
++ * the around 16 MB of SET body). Must NOT return 0 / positive length.
++ * Expected: BUFFER_E from the GrowStream cap. */
++ ExpectIntEQ(ret, WC_NO_ERR_TRACE(BUFFER_E));
++
++ wc_PKCS7_Free(pkcs7);
++#endif
++ return EXPECT_RESULT();
++} /* END test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen() */
++
++
+ /*
+ * Testing wc_PKCS7_DecodeEnvelopedData with streaming
+ */
+@@ -2501,6 +2792,8 @@ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void)
+ bytes */
+ size_t testDerBufferSz = 0;
+ byte decodedData[8192];
++ byte serverDecodedData[8192];
++ int serverRet = 0;
+
+ ExpectTrue((f = XFOPEN(testFile, "rb")) != XBADFILE);
+ if (f != XBADFILE) {
+@@ -2520,12 +2813,13 @@ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void)
+ ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)server_key_der_2048,
+ sizeof_server_key_der_2048), 0);
+
+- ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer,
+- (word32)testDerBufferSz, decodedData, sizeof(decodedData));
++ serverRet = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer,
++ (word32)testDerBufferSz, serverDecodedData,
++ sizeof(serverDecodedData));
+ #if defined(NO_AES) || defined(NO_AES_256)
+- ExpectIntEQ(ret, ALGO_ID_E);
++ ExpectIntEQ(serverRet, ALGO_ID_E);
+ #else
+- ExpectIntGT(ret, 0);
++ ExpectIntGT(serverRet, 0);
+ #endif
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+@@ -2551,7 +2845,14 @@ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void)
+ pkcs7 = NULL;
+ }
+
+- /* test with ca cert recipient (which should fail) */
++ /* Test with ca cert recipient. The ca cert is not a listed recipient,
++ * so RSA unwrap fails. The Bleichenbacher mitigation substitutes a
++ * pseudo-random fake CEK on unwrap failure, so the call normally
++ * returns a negative error when content decryption rejects the
++ * resulting garbage padding - but around 1/256 of the time the random
++ * CEK yields plaintext with accidentally-valid PKCS#7 padding and the
++ * call returns a non-negative "decrypted" size. That case must not
++ * produce the real plaintext. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
+ if (pkcs7) {
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)ca_cert_der_2048,
+@@ -2560,9 +2861,15 @@ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void)
+ ExpectIntEQ(wc_PKCS7_SetKey(pkcs7, (byte*)ca_key_der_2048,
+ sizeof_ca_key_der_2048), 0);
+
++ XMEMSET(decodedData, 0, sizeof(decodedData));
+ ret = wc_PKCS7_DecodeEnvelopedData(pkcs7, testDerBuffer,
+ (word32)testDerBufferSz, decodedData, sizeof(decodedData));
+- ExpectIntLT(ret, 0);
++ #if defined(NO_AES) || defined(NO_AES_256)
++ ExpectIntEQ(ret, ALGO_ID_E);
++ #else
++ ExpectTrue(ret < 0 || ret != serverRet ||
++ XMEMCMP(decodedData, serverDecodedData, (size_t)ret) != 0);
++ #endif
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+ }
+@@ -2579,7 +2886,7 @@ int test_wc_PKCS7_EncodeDecodeEnvelopedData(void)
+ EXPECT_DECLS;
+ #if defined(HAVE_PKCS7)
+ PKCS7* pkcs7 = NULL;
+-#ifdef ASN_BER_TO_DER
++#if defined(ASN_BER_TO_DER) && !defined(NO_PKCS7_STREAM)
+ int encodedSz = 0;
+ #endif
+ #ifdef ECC_TIMING_RESISTANT
+@@ -2761,6 +3068,11 @@ int test_wc_PKCS7_EncodeDecodeEnvelopedData(void)
+ AES128CBCb, AES128_WRAP, dhSinglePass_stdDH_sha1kdf_scheme,
+ eccCert, eccCertSz, eccPrivKey, eccPrivKeySz},
+ #endif
++ #if defined(WOLFSSL_SHA224) && defined(WOLFSSL_AES_128)
++ {(byte*)input, (word32)(sizeof(input)/sizeof(char)), DATA,
++ AES128CBCb, AES128_WRAP, dhSinglePass_stdDH_sha224kdf_scheme,
++ eccCert, eccCertSz, eccPrivKey, eccPrivKeySz},
++ #endif
+ #if !defined(NO_SHA256) && defined(WOLFSSL_AES_256)
+ {(byte*)input, (word32)(sizeof(input)/sizeof(char)), DATA,
+ AES256CBCb, AES256_WRAP, dhSinglePass_stdDH_sha256kdf_scheme,
+@@ -2784,7 +3096,7 @@ int test_wc_PKCS7_EncodeDecodeEnvelopedData(void)
+
+ testSz = (int)sizeof(testVectors)/(int)sizeof(pkcs7EnvelopedVector);
+ for (i = 0; i < testSz; i++) {
+- #ifdef ASN_BER_TO_DER
++ #if defined(ASN_BER_TO_DER) && !defined(NO_PKCS7_STREAM)
+ encodeSignedDataStream strm;
+
+ /* test setting stream mode, the first one using IO callbacks */
+@@ -2950,17 +3262,11 @@ int test_wc_PKCS7_EncodeDecodeEnvelopedData(void)
+ pkcs7->singleCert = NULL;
+ }
+ #ifndef NO_RSA
+- #if defined(NO_PKCS7_STREAM)
+- /* when none streaming mode is used and PKCS7 is in bad state buffer error
+- * is returned from kari parse which gets set to bad func arg */
+- ExpectIntEQ(wc_PKCS7_DecodeEnvelopedData(pkcs7, output,
+- (word32)sizeof(output), decoded, (word32)sizeof(decoded)),
+- WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+- #else
++ /* With corrupted singleCert, decode should fail with a parse error.
++ * State is properly reset on error so re-decode starts from scratch. */
+ ExpectIntEQ(wc_PKCS7_DecodeEnvelopedData(pkcs7, output,
+ (word32)sizeof(output), decoded, (word32)sizeof(decoded)),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+- #endif
+ #endif /* !NO_RSA */
+ if (pkcs7 != NULL) {
+ pkcs7->singleCert = tmpBytePtr;
+@@ -3991,7 +4297,8 @@ int test_wc_PKCS7_Degenerate(void)
+ } /* END test_wc_PKCS7_Degenerate() */
+
+ #if defined(HAVE_PKCS7) && !defined(NO_FILESYSTEM) && \
+- defined(ASN_BER_TO_DER) && !defined(NO_DES3) && !defined(NO_SHA)
++ defined(ASN_BER_TO_DER) && !defined(NO_DES3) && !defined(NO_SHA) && \
++ !defined(NO_PKCS7_STREAM)
+ static byte berContent[] = {
+ 0x30, 0x80, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x07, 0x03, 0xA0, 0x80, 0x30,
+@@ -4182,7 +4489,7 @@ static byte berContent[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ #endif /* HAVE_PKCS7 && !NO_FILESYSTEM && ASN_BER_TO_DER &&
+- * !NO_DES3 && !NO_SHA
++ * !NO_DES3 && !NO_SHA && !NO_PKCS7_STREAM
+ */
+
+ /*
+@@ -4197,7 +4504,7 @@ int test_wc_PKCS7_BER(void)
+ char fName[] = "./certs/test-ber-exp02-05-2022.p7b";
+ XFILE f = XBADFILE;
+ byte der[4096];
+-#ifndef NO_DES3
++#if !defined(NO_DES3) && !defined(NO_PKCS7_STREAM)
+ byte decoded[2048];
+ #endif
+ word32 derSz = 0;
+@@ -4242,8 +4549,9 @@ int test_wc_PKCS7_BER(void)
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+
+-#ifndef NO_DES3
+- /* decode BER content */
++#if !defined(NO_DES3) && !defined(NO_PKCS7_STREAM)
++ /* decode BER content - requires PKCS7 streaming to handle indefinite
++ * length encoding in the EnvelopedData structure */
+ ExpectTrue((f = XFOPEN("./certs/1024/client-cert.der", "rb")) != XBADFILE);
+ ExpectTrue((derSz = (word32)XFREAD(der, 1, sizeof(der), f)) > 0);
+ if (f != XBADFILE) {
+@@ -4280,7 +4588,7 @@ int test_wc_PKCS7_BER(void)
+ sizeof(berContent), decoded, sizeof(decoded)), WC_NO_ERR_TRACE(NOT_COMPILED_IN));
+ #endif
+ wc_PKCS7_Free(pkcs7);
+-#endif /* !NO_DES3 */
++#endif /* !NO_DES3 && !NO_PKCS7_STREAM */
+ #endif
+ return EXPECT_RESULT();
+ } /* END test_wc_PKCS7_BER() */
+diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h
+index 084744d43..f4aed161d 100644
+--- a/tests/api/test_pkcs7.h
++++ b/tests/api/test_pkcs7.h
+@@ -38,6 +38,14 @@ int test_wc_PKCS7_EncodeSignedData_RSA_PSS(void);
+ !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_256)
+ int test_wc_PKCS7_EnvelopedData_KTRI_RSA_PSS(void);
+ #endif
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
++ !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128) && \
++ defined(WOLFSSL_AES_256) && !defined(NO_HMAC) && \
++ !defined(WOLFSSL_NO_MALLOC) && \
++ (defined(USE_CERT_BUFFERS_2048) || defined(USE_CERT_BUFFERS_1024) || \
++ !defined(NO_FILESYSTEM))
++int test_wc_PKCS7_EnvelopedData_KTRI_BadRsaPad(void);
++#endif
+ int test_wc_PKCS7_EncodeSignedData_ex(void);
+ int test_wc_PKCS7_VerifySignedData_RSA(void);
+ int test_wc_PKCS7_VerifySignedData_ECC(void);
+@@ -57,6 +65,7 @@ int test_wc_PKCS7_SetOriEncryptCtx(void);
+ int test_wc_PKCS7_SetOriDecryptCtx(void);
+ int test_wc_PKCS7_DecodeCompressedData(void);
+ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void);
++int test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen(void);
+ int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void);
+ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
+
+@@ -82,6 +91,18 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
+ #define TEST_PKCS7_RSA_PSS_ED_DECL
+ #endif
+
++#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
++ !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128) && \
++ defined(WOLFSSL_AES_256) && !defined(NO_HMAC) && \
++ !defined(WOLFSSL_NO_MALLOC) && \
++ (defined(USE_CERT_BUFFERS_2048) || defined(USE_CERT_BUFFERS_1024) || \
++ !defined(NO_FILESYSTEM))
++#define TEST_PKCS7_KTRI_BADRSAPAD_DECL \
++ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_EnvelopedData_KTRI_BadRsaPad),
++#else
++#define TEST_PKCS7_KTRI_BADRSAPAD_DECL
++#endif
++
+ #define TEST_PKCS7_SIGNED_DATA_DECLS \
+ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_InitWithCert), \
+ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeData), \
+@@ -100,6 +121,7 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_stream), \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_EncodeDecodeEnvelopedData), \
+ TEST_PKCS7_RSA_PSS_ED_DECL \
++ TEST_PKCS7_KTRI_BADRSAPAD_DECL \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetAESKeyWrapUnwrapCb), \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_GetEnvelopedDataKariRid), \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_EncodeEncryptedData), \
+@@ -108,7 +130,8 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeOneSymmetricKey), \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetOriEncryptCtx), \
+ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_SetOriDecryptCtx), \
+- TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients)
++ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients), \
++ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_forgedRecipientSetLen)
+
+ #define TEST_PKCS7_SIGNED_ENCRYPTED_DATA_DECLS \
+ TEST_DECL_GROUP("pkcs7_sed", test_wc_PKCS7_signed_enveloped)
+diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
+index 8df3a2430..f2c98efe5 100644
+--- a/wolfcrypt/src/pkcs7.c
++++ b/wolfcrypt/src/pkcs7.c
+@@ -44,6 +44,9 @@
+
+ #include <wolfssl/wolfcrypt/pkcs7.h>
+ #include <wolfssl/wolfcrypt/hash.h>
++#ifndef NO_HMAC
++ #include <wolfssl/wolfcrypt/hmac.h>
++#endif
+ #ifndef NO_RSA
+ #include <wolfssl/wolfcrypt/rsa.h>
+ #endif
+@@ -94,6 +97,7 @@ typedef enum {
+ /* holds information about the signers */
+ struct PKCS7SignerInfo {
+ int version;
++ int sidType; /* CMS_ISSUER_AND_SERIAL_NUMBER or CMS_SKID */
+ byte *sid;
+ word32 sidSz;
+ };
+@@ -109,6 +113,17 @@ struct PKCS7SignerInfo {
+
+ #ifndef NO_PKCS7_STREAM
+
++/* Hard upper bound on a single PKCS7 streaming buffer allocation. Guards
++ * wc_PKCS7_GrowStream against attacker-controlled ASN.1 lengths that were
++ * parsed with NO_USER_CHECK and would otherwise drive allocations up to
++ * around 2GB (e.g. via a forged RecipientInfo SET length). 16 MB is well above
++ * any legitimate RecipientInfo / encoded-attribute size but small enough
++ * that a forged length fails allocation on constrained targets and is
++ * rejected on larger ones. */
++#ifndef WOLFSSL_PKCS7_MAX_STREAM_ALLOC
++ #define WOLFSSL_PKCS7_MAX_STREAM_ALLOC (16 * 1024 * 1024)
++#endif
++
+ #define MAX_PKCS7_STREAM_BUFFER 256
+ struct PKCS7State {
+ byte* tmpCert;
+@@ -207,10 +222,15 @@ static void wc_PKCS7_ResetStream(wc_PKCS7* pkcs7)
+ #endif
+
+ /* free any buffers that may be allocated */
++ if (pkcs7->stream->aad != NULL && pkcs7->stream->aadSz > 0)
++ ForceZero(pkcs7->stream->aad, pkcs7->stream->aadSz);
+ XFREE(pkcs7->stream->aad, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(pkcs7->stream->tag, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(pkcs7->stream->nonce, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(pkcs7->stream->buffer, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ /* stream->key is always allocated with MAX_ENCRYPTED_KEY_SZ */
++ if (pkcs7->stream->key != NULL)
++ ForceZero(pkcs7->stream->key, MAX_ENCRYPTED_KEY_SZ);
+ XFREE(pkcs7->stream->key, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ pkcs7->stream->aad = NULL;
+ pkcs7->stream->tag = NULL;
+@@ -265,6 +285,16 @@ static void wc_PKCS7_FreeStream(wc_PKCS7* pkcs7)
+ static int wc_PKCS7_GrowStream(wc_PKCS7* pkcs7, word32 newSz)
+ {
+ byte* pt;
++
++ /* Guard against attacker-controlled ASN.1 lengths reaching this
++ * allocation. Several callers parse lengths with NO_USER_CHECK and
++ * pass them here unvalidated (e.g. wc_PKCS7_ParseToRecipientInfoSet
++ * on a forged RecipientInfo SET header). */
++ if (newSz > WOLFSSL_PKCS7_MAX_STREAM_ALLOC) {
++ WOLFSSL_MSG("PKCS7 streaming allocation exceeds maximum");
++ return BUFFER_E;
++ }
++
+ pt = (byte*)XMALLOC(newSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ if (pt == NULL) {
+ return MEMORY_E;
+@@ -4275,6 +4305,94 @@ int wc_PKCS7_SetEccSignRawDigestCb(wc_PKCS7* pkcs7, CallbackEccSignRawDigest cb)
+ #endif /* HAVE_ECC */
+
+
++#if !defined(NO_RSA) || defined(HAVE_ECC)
++/* Check whether the given decoded certificate matches the SignerIdentifier
++ * (sid) field of the currently parsed SignerInfo. Per RFC 5652 Section 5.3,
++ * the sid selects which certificate's public key must be used to verify the
++ * signature. Returns 1 on match, 0 on no match or when the sid is not
++ * available for comparison. */
++static int wc_PKCS7_CertMatchesSignerInfo(wc_PKCS7* pkcs7, DecodedCert* dCert)
++{
++ PKCS7SignerInfo* signerInfo;
++
++ if (pkcs7 == NULL || dCert == NULL)
++ return 0;
++
++ signerInfo = pkcs7->signerInfo;
++ if (signerInfo == NULL || signerInfo->sid == NULL ||
++ signerInfo->sidSz == 0) {
++ /* No SID parsed, cannot perform an identity binding check. */
++ return 0;
++ }
++
++ if (signerInfo->sidType == CMS_ISSUER_AND_SERIAL_NUMBER) {
++ /* IssuerAndSerialNumber: SID blob stores the content of the outer
++ * SEQUENCE (issuer Name followed by INTEGER serialNumber). */
++ word32 idx = 0;
++ byte sidIssuerHash[KEYID_SIZE];
++ WC_DECLARE_VAR(sidSerial, mp_int, 1, pkcs7->heap);
++ WC_DECLARE_VAR(certSerial, mp_int, 1, pkcs7->heap);
++ int cmp;
++ int match = 0;
++
++ if (GetNameHash_ex(signerInfo->sid, &idx, sidIssuerHash,
++ (int)signerInfo->sidSz, dCert->signatureOID) < 0) {
++ return 0;
++ }
++ if (XMEMCMP(sidIssuerHash, dCert->issuerHash, KEYID_SIZE) != 0)
++ return 0;
++
++ WC_ALLOC_VAR_EX(sidSerial, mp_int, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER,
++ { return 0; });
++ WC_ALLOC_VAR_EX(certSerial, mp_int, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER,
++ { WC_FREE_VAR_EX(sidSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ return 0; });
++
++ if (mp_init(sidSerial) != MP_OKAY) {
++ WC_FREE_VAR_EX(sidSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ WC_FREE_VAR_EX(certSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ return 0;
++ }
++ if (mp_init(certSerial) != MP_OKAY) {
++ mp_clear(sidSerial);
++ WC_FREE_VAR_EX(sidSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ WC_FREE_VAR_EX(certSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ return 0;
++ }
++
++ if (GetInt(sidSerial, signerInfo->sid, &idx, signerInfo->sidSz) == 0 &&
++ mp_read_unsigned_bin(certSerial, dCert->serial,
++ (word32)dCert->serialSz) == MP_OKAY) {
++ cmp = mp_cmp(sidSerial, certSerial);
++ if (cmp == MP_EQ)
++ match = 1;
++ }
++
++ mp_clear(sidSerial);
++ mp_clear(certSerial);
++ WC_FREE_VAR_EX(sidSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ WC_FREE_VAR_EX(certSerial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ return match;
++ }
++ else if (signerInfo->sidType == CMS_SKID) {
++ /* SubjectKeyIdentifier: SID blob is the raw SKID octet string
++ * content. Normalize the same way the certificate side does so
++ * that comparisons between SHA-1 SKIDs and other lengths match. */
++ byte sidKid[KEYID_SIZE];
++
++ if (GetHashId(signerInfo->sid, (int)signerInfo->sidSz, sidKid,
++ HashIdAlg(dCert->signatureOID)) != 0) {
++ return 0;
++ }
++ if (XMEMCMP(sidKid, dCert->extSubjKeyId, KEYID_SIZE) == 0)
++ return 1;
++ return 0;
++ }
++
++ return 0;
++}
++#endif /* !NO_RSA || HAVE_ECC */
++
+ #ifndef NO_RSA
+
+ /* returns size of signature put into out, negative on error */
+@@ -4354,6 +4472,31 @@ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
+ continue;
+ }
+
++ /* If the SignerInfo sid was parsed, only try the certificate whose
++ * identity matches it. This binds the verifying public key to the
++ * signer identity advertised in the CMS message and prevents signer
++ * confusion when multiple certificates are embedded. */
++ if (pkcs7->signerInfo != NULL && pkcs7->signerInfo->sid != NULL &&
++ !wc_PKCS7_CertMatchesSignerInfo(pkcs7, dCert)) {
++ FreeDecodedCert(dCert);
++ wc_FreeRsaKey(key);
++ continue;
++ }
++
++ /* Defense in depth: the sid-matched cert must actually carry an
++ * RSA-family key before we feed its SPKI to the RSA key decoder.
++ * Rejecting here avoids depending on wc_RsaPublicKeyDecode to reject
++ * wrong-type SPKIs. */
++ if (dCert->keyOID != RSAk
++ #ifdef WC_RSA_PSS
++ && dCert->keyOID != RSAPSSk
++ #endif
++ ) {
++ FreeDecodedCert(dCert);
++ wc_FreeRsaKey(key);
++ continue;
++ }
++
+ if (wc_RsaPublicKeyDecode(dCert->publicKey, &scratch, key,
+ dCert->pubKeySize) < 0) {
+ WOLFSSL_MSG("ASN RSA key decode error");
+@@ -4464,6 +4607,24 @@ static int wc_PKCS7_RsaPssVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
+ continue;
+ }
+
++ /* Only try the certificate identified by the SignerInfo sid (see
++ * matching comment in wc_PKCS7_RsaVerify). */
++ if (pkcs7->signerInfo != NULL && pkcs7->signerInfo->sid != NULL &&
++ !wc_PKCS7_CertMatchesSignerInfo(pkcs7, dCert)) {
++ FreeDecodedCert(dCert);
++ wc_FreeRsaKey(key);
++ continue;
++ }
++
++ /* Defense in depth: reject non-RSA SPKIs before key decode. RSA
++ * rsaEncryption certs (keyOID=RSAk) are accepted for PSS signatures
++ * per RFC 8017 - a RSASSA-PSS cert is not required. */
++ if (dCert->keyOID != RSAk && dCert->keyOID != RSAPSSk) {
++ FreeDecodedCert(dCert);
++ wc_FreeRsaKey(key);
++ continue;
++ }
++
+ pkSz = dCert->pubKeySize;
+ if (pkSz > (MAX_RSA_INT_SZ + MAX_RSA_E_SZ))
+ pkSz = (MAX_RSA_INT_SZ + MAX_RSA_E_SZ);
+@@ -4629,6 +4790,22 @@ static int wc_PKCS7_EcdsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
+ continue;
+ }
+
++ /* Only try the certificate identified by the SignerInfo sid (see
++ * matching comment in wc_PKCS7_RsaVerify). */
++ if (pkcs7->signerInfo != NULL && pkcs7->signerInfo->sid != NULL &&
++ !wc_PKCS7_CertMatchesSignerInfo(pkcs7, dCert)) {
++ FreeDecodedCert(dCert);
++ wc_ecc_free(key);
++ continue;
++ }
++
++ /* Defense in depth: reject non-ECDSA SPKIs before key decode. */
++ if (dCert->keyOID != ECDSAk) {
++ FreeDecodedCert(dCert);
++ wc_ecc_free(key);
++ continue;
++ }
++
+ if (wc_EccPublicKeyDecode(dCert->publicKey, &idx, key,
+ dCert->pubKeySize) < 0) {
+ WOLFSSL_MSG("ASN ECC key decode error");
+@@ -5337,11 +5514,16 @@ static int wc_PKCS7_ParseSignerInfo(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ ret = ASN_PARSE_E;
+
+ if (ret == 0) {
++ pkcs7->signerInfo->sidType = CMS_ISSUER_AND_SERIAL_NUMBER;
+ ret = wc_PKCS7_SignerInfoSetSID(pkcs7, in + idx, length);
+ idx += (word32)length;
+ }
+
+ } else if (ret == 0 && version == 3) {
++ /* Default: SignerInfo version 3 carries SubjectKeyIdentifier.
++ * May be overridden below if the parser instead finds a
++ * SEQUENCE (IssuerAndSerialNumber fallback). */
++ pkcs7->signerInfo->sidType = CMS_SKID;
+ /* Get the sequence of SubjectKeyIdentifier */
+ if (idx + 1 > inSz)
+ ret = BUFFER_E;
+@@ -5384,6 +5566,12 @@ static int wc_PKCS7_ParseSignerInfo(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+
+ if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0)
+ ret = ASN_PARSE_E;
++
++ if (ret == 0) {
++ /* v3 carrying IssuerAndSerialNumber fallback */
++ pkcs7->signerInfo->sidType =
++ CMS_ISSUER_AND_SERIAL_NUMBER;
++ }
+ }
+ }
+
+@@ -7029,6 +7217,9 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf,
+
+ idx += (word32)length;
+ }
++ else if (ret == 0) {
++ ret = ASN_PARSE_E;
++ }
+
+ pkcs7->content = content;
+ pkcs7->contentSz = (word32)contentSz;
+@@ -7721,6 +7912,9 @@ static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, WC_RNG* rng,
+ secret = (byte*)XMALLOC(secretSz, kari->heap, DYNAMIC_TYPE_PKCS7);
+ if (secret == NULL)
+ return MEMORY_E;
++#ifdef WOLFSSL_CHECK_MEM_ZERO
++ wc_MemZero_Add("wc_PKCS7_KariGenerateKEK secret", secret, secretSz);
++#endif
+
+ #if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
+ (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
+@@ -7756,6 +7950,7 @@ static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, WC_RNG* rng,
+ }
+
+ if (ret != 0) {
++ ForceZero(secret, secretSz);
+ XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+ }
+@@ -7768,7 +7963,7 @@ static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, WC_RNG* rng,
+ kdfType = WC_HASH_TYPE_SHA;
+ break;
+ #endif
+- #ifndef WOLFSSL_SHA224
++ #ifdef WOLFSSL_SHA224
+ case dhSinglePass_stdDH_sha224kdf_scheme:
+ kdfType = WC_HASH_TYPE_SHA224;
+ break;
+@@ -7790,6 +7985,7 @@ static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, WC_RNG* rng,
+ #endif
+ default:
+ WOLFSSL_MSG("Unsupported key agreement algorithm");
++ ForceZero(secret, secretSz);
+ XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7);
+ return BAD_FUNC_ARG;
+ };
+@@ -7802,6 +7998,7 @@ static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, WC_RNG* rng,
+ ret = NOT_COMPILED_IN;
+ #endif
+
++ ForceZero(secret, secretSz);
+ XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+ }
+@@ -9606,7 +9803,7 @@ static int wc_PKCS7_PwriKek_KeyUnWrap(wc_PKCS7* pkcs7, const byte* kek,
+ cekLen = outTmp[0];
+
+ /* verify length */
+- fail |= ctMaskGT(cekLen, (int)inSz);
++ fail |= ctMaskGT(cekLen, (int)inSz - 4);
+ /* verify check bytes */
+ fail |= ctMaskNotEq((int)(outTmp[1] ^ outTmp[4]), 0xFF);
+ fail |= ctMaskNotEq((int)(outTmp[2] ^ outTmp[5]), 0xFF);
+@@ -9747,6 +9944,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ (word32)kekKeySz);
+ if (ret < 0) {
+ XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+@@ -9758,6 +9956,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ tmpIv, (word32)kekBlockSz, encryptOID);
+ if (ret < 0) {
+ XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+@@ -9782,6 +9981,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ ret = wc_SetContentType(PWRI_KEK_WRAP, keyEncAlgoId, sizeof(keyEncAlgoId));
+ if (ret <= 0) {
+ XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+@@ -9813,6 +10013,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ ret = wc_SetContentType(kdfOID, kdfAlgoId, sizeof(kdfAlgoId));
+ if (ret <= 0) {
+ XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+@@ -9838,6 +10039,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ if (totalSz > MAX_RECIP_SZ) {
+ WOLFSSL_MSG("CMS Recipient output buffer too small");
+ XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return BUFFER_E;
+@@ -9875,7 +10077,7 @@ int wc_PKCS7_AddRecipient_PWRI(wc_PKCS7* pkcs7, byte* passwd, word32 pLen,
+ XMEMCPY(recip->recip + idx, encryptedKey, encryptedKeySz);
+ idx += encryptedKeySz;
+
+- ForceZero(kek, (word32)kekBlockSz);
++ ForceZero(kek, (word32)kekKeySz);
+ ForceZero(encryptedKey, encryptedKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+@@ -10580,6 +10782,81 @@ int wc_PKCS7_EncodeEnvelopedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz)
+ }
+
+ #ifndef NO_RSA
++#if !defined(NO_HMAC) && !defined(NO_SHA256)
++/* Bleichenbacher padding-oracle mitigation for PKCS#7/CMS KTRI: produce a
++ * WC_SHA256_DIGEST_SIZE-byte pseudo-random CEK derived from a fresh
++ * random seed and the encrypted-key ciphertext. The output is random per
++ * call (driven by the RNG seed); deriving via HMAC of the ciphertext
++ * simply gives the same value within one call regardless of where it is
++ * referenced. Called unconditionally so the work is in the timing path
++ * regardless of RSA padding validity. */
++static int wc_PKCS7_KtriFakeCEK(wc_PKCS7* pkcs7, const byte* encryptedKey,
++ word32 encryptedKeySz, byte* out)
++{
++ int ret;
++ byte seed[WC_SHA256_DIGEST_SIZE];
++ WC_RNG* rng = NULL;
++ int ownRng = 0;
++ WC_DECLARE_VAR(localRng, WC_RNG, 1, pkcs7->heap);
++ WC_DECLARE_VAR(hmac, Hmac, 1, pkcs7->heap);
++
++ if (pkcs7 == NULL || encryptedKey == NULL || out == NULL) {
++ return BAD_FUNC_ARG;
++ }
++
++ WC_ALLOC_VAR_EX(hmac, Hmac, 1, pkcs7->heap, DYNAMIC_TYPE_HMAC,
++ return MEMORY_E);
++
++ /* Prefer a caller-provided RNG to avoid paying a DRBG init/reseed cost
++ * on every decrypt (and to keep the timing envelope flatter on FIPS /
++ * HW-RNG builds). Fall back to a one-shot RNG when pkcs7->rng is not
++ * set. */
++ if (pkcs7->rng != NULL) {
++ rng = pkcs7->rng;
++ }
++ else {
++ WC_ALLOC_VAR_EX(localRng, WC_RNG, 1, pkcs7->heap, DYNAMIC_TYPE_RNG,
++ WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC);
++ return MEMORY_E);
++ ret = wc_InitRng_ex(localRng, pkcs7->heap, pkcs7->devId);
++ if (ret != 0) {
++ WC_FREE_VAR_EX(localRng, pkcs7->heap, DYNAMIC_TYPE_RNG);
++ WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC);
++ return ret;
++ }
++ rng = localRng;
++ ownRng = 1;
++ }
++
++ ret = wc_RNG_GenerateBlock(rng, seed, (word32)sizeof(seed));
++
++ if (ownRng) {
++ wc_FreeRng(localRng);
++ WC_FREE_VAR_EX(localRng, pkcs7->heap, DYNAMIC_TYPE_RNG);
++ }
++
++ if (ret != 0) {
++ WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC);
++ return ret;
++ }
++
++ ret = wc_HmacInit(hmac, pkcs7->heap, pkcs7->devId);
++ if (ret == 0) {
++ ret = wc_HmacSetKey(hmac, WC_SHA256, seed, (word32)sizeof(seed));
++ if (ret == 0) {
++ ret = wc_HmacUpdate(hmac, encryptedKey, encryptedKeySz);
++ }
++ if (ret == 0) {
++ ret = wc_HmacFinal(hmac, out);
++ }
++ wc_HmacFree(hmac);
++ }
++ ForceZero(seed, sizeof(seed));
++ WC_FREE_VAR_EX(hmac, pkcs7->heap, DYNAMIC_TYPE_HMAC);
++ return ret;
++}
++#endif /* !NO_HMAC && !NO_SHA256 */
++
+ /* decode KeyTransRecipientInfo (ktri), return 0 on success, <0 on error */
+ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ word32* idx, byte* decryptedKey,
+@@ -10596,7 +10873,9 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ word32 pkiMsgSz = inSz;
+ byte tag;
+
+-
++#ifndef WC_NO_RSA_OAEP
++ word32 outKeySz = 0;
++#endif
+ #ifndef NO_PKCS7_STREAM
+ word32 tmpIdx = *idx;
+ #endif
+@@ -10755,15 +11034,17 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0)
+ return ASN_PARSE_E;
+
+- if ((word32)keyIdSize > pkiMsgSz - (*idx))
++ /* Validate SKID container is within buffer */
++ if ((word32)length > pkiMsgSz - (*idx))
+ return BUFFER_E;
+
+ /* if we found correct recipient, SKID will match */
+- if (XMEMCMP(pkiMsg + (*idx), pkcs7->issuerSubjKeyId,
++ if (length == keyIdSize &&
++ XMEMCMP(pkiMsg + (*idx), pkcs7->issuerSubjKeyId,
+ (word32)keyIdSize) == 0) {
+ *recipFound = 1;
+ }
+- (*idx) += (word32)keyIdSize;
++ (*idx) += (word32)length;
+ }
+
+ if (GetAlgoId(pkiMsg, idx, &encOID, oidKeyType, pkiMsgSz) < 0)
+@@ -10903,8 +11184,8 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ #ifndef WC_NO_RSA_OAEP
+ }
+ else {
+- word32 outLen = (word32)wc_RsaEncryptSize(privKey);
+- outKey = (byte*)XMALLOC(outLen, pkcs7->heap,
++ outKeySz = (word32)wc_RsaEncryptSize(privKey);
++ outKey = (byte*)XMALLOC(outKeySz, pkcs7->heap,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if (!outKey) {
+ WOLFSSL_MSG("Failed to allocate out key buffer");
+@@ -10918,9 +11199,9 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ }
+
+ keySz = wc_RsaPrivateDecrypt_ex(encryptedKey,
+- (word32)encryptedKeySz, outKey, outLen, privKey,
+- WC_RSA_OAEP_PAD,
+- WC_HASH_TYPE_SHA, WC_MGF1SHA1, NULL, 0);
++ (word32)encryptedKeySz, outKey, outKeySz,
++ privKey, WC_RSA_OAEP_PAD, WC_HASH_TYPE_SHA,
++ WC_MGF1SHA1, NULL, 0);
+ }
+ #endif
+ }
+@@ -10935,30 +11216,153 @@ static int wc_PKCS7_DecryptKtri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ }
+ wc_FreeRsaKey(privKey);
+
++ #if !defined(NO_HMAC) && !defined(NO_SHA256)
++ {
++ /* Bleichenbacher padding-oracle mitigation: always compute
++ * a pseudo-random fallback CEK so timing and error
++ * behaviour do not depend on RSA padding validity. On
++ * unwrap failure we substitute the fallback and let
++ * content decryption fail indistinguishably from "unwrap
++ * succeeded but CEK is wrong". */
++ byte fakeKey[WC_SHA256_DIGEST_SIZE];
++ int fakeRet = wc_PKCS7_KtriFakeCEK(pkcs7, encryptedKey,
++ (word32)encryptedKeySz,
++ fakeKey);
++
++ if (fakeRet != 0) {
++ /* Fallback generation failed (e.g. RNG/HMAC error).
++ * Return the fallback-generation status, which does
++ * not depend on RSA padding validity, rather than the
++ * RSA status which would re-open the oracle. */
++ ForceZero(fakeKey, sizeof(fakeKey));
++ /* In the non-OAEP path RSA is decrypted in-place via
++ * wc_RsaPrivateDecryptInline, so encryptedKey holds
++ * the (possibly valid) plaintext CEK. Zero it before
++ * free. */
++ ForceZero(encryptedKey, (word32)encryptedKeySz);
++ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_WOLF_BIGINT);
++ WC_FREE_VAR_EX(privKey, pkcs7->heap,
++ DYNAMIC_TYPE_TMP_BUFFER);
++ #ifndef WC_NO_RSA_OAEP
++ if (encOID == RSAESOAEPk) {
++ if (outKey != NULL) {
++ ForceZero(outKey, outKeySz);
++ XFREE(outKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
++ }
++ }
++ #endif
++ return fakeRet;
++ }
++
++ /* Constant-time select between fake and real CEK. On RSA
++ * failure outKey may be NULL or keySz may be <= 0; in
++ * both cases the mask selects fakeKey for every byte.
++ *
++ * To avoid data-dependent branches that leak realLen,
++ * copy the real key into a fixed-size zero-padded buffer
++ * first, then select byte-by-byte in constant time. */
++ {
++ word32 i;
++ byte useFake;
++ int realLen = keySz;
++ byte realPad[WC_SHA256_DIGEST_SIZE];
++
++ XMEMSET(realPad, 0, sizeof(realPad));
++ /* Constant-time copy: avoid data-dependent branches
++ * that could leak whether RSA padding was valid.
++ * When outKey is NULL (inline RSA failure), use
++ * encryptedKey as a safe readable source; the mask
++ * will zero out all bytes anyway. Both encryptedKey
++ * and outKey (when non-NULL) are at least
++ * sizeof(realPad) bytes for any RSA key size.
++ *
++ * Use constant-time pointer selection to avoid
++ * branching on outKey nullity, which would leak
++ * whether RSA PKCS#1 v1.5 padding was valid. */
++ {
++ byte haveSrc = ctMaskGTE(realLen, 1);
++ const byte* srcTbl[2];
++ const byte* src;
++ word32 j = 0;
++ word32 safeJ = 0;
++
++ /* Select source without integer pointer synthesis.
++ * Some safety-oriented compilers (e.g. Fil-C) treat
++ * int-to-pointer reconstruction as a null-object
++ * pointer on dereference. */
++ srcTbl[0] = encryptedKey;
++ srcTbl[1] = outKey;
++ src = srcTbl[haveSrc & 1];
++
++ /* safeJ is clamped to max(0, realLen-1): it
++ * only advances while the next index would
++ * still be inside realLen, so src[safeJ] is
++ * always in bounds. Bytes at j >= realLen are
++ * masked to zero by inBounds anyway. */
++ for (j = 0; j < (word32)sizeof(realPad); j++) {
++ byte inBounds = ctMaskLT((int)j, realLen);
++ byte advance = ctMaskLT((int)(safeJ + 1),
++ realLen);
++ realPad[j] = src[safeJ] & haveSrc & inBounds;
++ safeJ += (word32)(advance & 1);
++ }
++ }
++ useFake = ctMaskLT(realLen, 1); /* 0xFF if realLen<=0 */
++
++ for (i = 0; i < (word32)sizeof(fakeKey); i++) {
++ decryptedKey[i] = ctMaskSel(useFake, fakeKey[i],
++ realPad[i]);
++ }
++ /* Report the real key size on success; on RSA
++ * failure (realLen <= 0) report sizeof(fakeKey).
++ * Constant-time select avoids branching on RSA
++ * padding validity. */
++ *decryptedKeySz = (word32)ctMaskSelInt(useFake,
++ (int)sizeof(fakeKey), realLen);
++ ForceZero(realPad, sizeof(realPad));
++ }
++ ForceZero(fakeKey, sizeof(fakeKey));
++ /* In the non-OAEP path RSA is decrypted in-place via
++ * wc_RsaPrivateDecryptInline, so encryptedKey holds the
++ * plaintext CEK after the unwrap. Zero it before free. */
++ ForceZero(encryptedKey, (word32)encryptedKeySz);
++ }
++ #else /* NO_HMAC || NO_SHA256: mitigation unavailable */
++ #if !defined(WOLFSSL_NO_KTRI_ORACLE_WARNING)
++ #warning "PKCS7 KTRI Bleichenbacher mitigation requires HMAC " \
++ "and SHA256; build without them leaves the RSA unwrap " \
++ "error path observable to callers. " \
++ "Define WOLFSSL_NO_KTRI_ORACLE_WARNING to silence."
++ #endif
++
+ if (keySz <= 0 || outKey == NULL) {
+ ForceZero(encryptedKey, (word32)encryptedKeySz);
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_WOLF_BIGINT);
+ WC_FREE_VAR_EX(privKey, pkcs7->heap,
+ DYNAMIC_TYPE_TMP_BUFFER);
+- #ifndef WC_NO_RSA_OAEP
++ #ifndef WC_NO_RSA_OAEP
+ if (encOID == RSAESOAEPk) {
+ if (outKey) {
++ ForceZero(outKey, outKeySz);
+ XFREE(outKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ }
+- #endif
++ #endif
+ return keySz;
+- } else {
++ }
++ else {
+ *decryptedKeySz = (word32)keySz;
+ XMEMCPY(decryptedKey, outKey, (word32)keySz);
+ ForceZero(encryptedKey, (word32)encryptedKeySz);
+ }
++ #endif /* !NO_HMAC && !NO_SHA256 */
+
+ XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_WOLF_BIGINT);
+ WC_FREE_VAR_EX(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
+ #ifndef WC_NO_RSA_OAEP
+ if (encOID == RSAESOAEPk) {
+ if (outKey) {
++ ForceZero(outKey, outKeySz);
+ XFREE(outKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+ }
+@@ -11040,6 +11444,14 @@ static int wc_PKCS7_KariGetOriginatorIdentifierOrKey(WC_PKCS7_KARI* kari,
+ if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0)
+ return ASN_PARSE_E;
+
++ /* BIT STRING must have at least unused-bits byte + 1 byte of content */
++ if (length < 2)
++ return ASN_PARSE_E;
++
++ /* Validate BIT STRING content is within input buffer */
++ if (*idx > pkiMsgSz || (word32)length > pkiMsgSz - *idx)
++ return ASN_PARSE_E;
++
+ if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0)
+ return ASN_EXPECT_0_E;
+
+@@ -11519,9 +11931,22 @@ static int wc_PKCS7_DecryptOri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ XMEMCPY(oriOID, pkiMsg + *idx, (word32)oriOIDSz);
+ *idx += (word32)oriOIDSz;
+
++ /* Validate OID did not consume more than the SEQUENCE declared */
++ if ((*idx - tmpIdx) > (word32)seqSz) {
++ WOLFSSL_MSG("ORI oriType OID exceeds SEQUENCE boundary");
++ return ASN_PARSE_E;
++ }
++
+ /* get oriValue, increment idx */
+ oriValue = pkiMsg + *idx;
+ oriValueSz = (word32)seqSz - (*idx - tmpIdx);
++
++ /* Validate oriValue region is within input buffer */
++ if (*idx > pkiMsgSz || oriValueSz > pkiMsgSz - *idx) {
++ WOLFSSL_MSG("ORI oriValue exceeds input buffer");
++ return ASN_PARSE_E;
++ }
++
+ *idx += oriValueSz;
+
+ /* pass oriOID and oriValue to user callback, expect back
+@@ -11699,6 +12124,12 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ return ASN_PARSE_E;
+ }
+
++ /* Validate IV is within input buffer */
++ if (*idx > pkiMsgSz || (word32)length > pkiMsgSz - *idx) {
++ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ return ASN_PARSE_E;
++ }
++
+ XMEMCPY(tmpIv, pkiMsg + (*idx), (word32)length);
+ *idx += (word32)length;
+
+@@ -11718,6 +12149,12 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ return ASN_PARSE_E;
+ }
+
++ /* Validate EncryptedKey is within input buffer */
++ if (*idx > pkiMsgSz || (word32)length > pkiMsgSz - *idx) {
++ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ return ASN_PARSE_E;
++ }
++
+ /* allocate temporary space for decrypted key */
+ cekSz = (word32)length;
+ cek = (byte*)XMALLOC(cekSz, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
+@@ -11740,6 +12177,7 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ iterations, kek, (word32)kekKeySz);
+ if (ret < 0) {
+ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ASN_PARSE_E;
+@@ -11752,7 +12190,9 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ pwriEncAlgoId);
+ if (ret < 0) {
+ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(cek, cekSz);
+ XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return ret;
+ }
+@@ -11761,7 +12201,9 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ if (*decryptedKeySz < cekSz) {
+ WOLFSSL_MSG("Decrypted key buffer too small for CEK");
+ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(cek, cekSz);
+ XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+ return BUFFER_E;
+ }
+@@ -11770,7 +12212,9 @@ static int wc_PKCS7_DecryptPwri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ *decryptedKeySz = cekSz;
+
+ XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(kek, (word32)kekKeySz);
+ XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ ForceZero(cek, cekSz);
+ XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+
+ /* mark recipFound, since we only support one RecipientInfo for now */
+@@ -11804,7 +12248,7 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ byte* keyId = NULL;
+ const byte* datePtr = NULL;
+ byte dateFormat, tag;
+- word32 keyIdSz, kekIdSz, keyWrapOID, localIdx;
++ word32 keyIdSz, kekIdSz, kekIdEnd, keyWrapOID, localIdx;
+
+ int ret = 0;
+ byte* pkiMsg = in;
+@@ -11830,6 +12274,11 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ return ASN_PARSE_E;
+
+ kekIdSz = (word32)length;
++ kekIdEnd = *idx + kekIdSz;
++
++ /* Validate KEKIdentifier boundary is within input buffer */
++ if (kekIdEnd < *idx || kekIdEnd > pkiMsgSz)
++ return ASN_PARSE_E;
+
+ if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0)
+ return ASN_PARSE_E;
+@@ -11840,6 +12289,10 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0)
+ return ASN_PARSE_E;
+
++ /* Validate keyIdentifier is within input buffer */
++ if (*idx > pkiMsgSz || (word32)length > pkiMsgSz - *idx)
++ return ASN_PARSE_E;
++
+ /* save keyIdentifier and length */
+ keyId = pkiMsg + *idx;
+ keyIdSz = (word32)length;
+@@ -11847,13 +12300,15 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+
+ /* may have OPTIONAL GeneralizedTime */
+ localIdx = *idx;
+- if ((*idx < kekIdSz) && GetASNTag(pkiMsg, &localIdx, &tag,
++ if ((*idx < kekIdEnd) && GetASNTag(pkiMsg, &localIdx, &tag,
+ pkiMsgSz) == 0 && tag == ASN_GENERALIZED_TIME) {
+- if (wc_GetDateInfo(pkiMsg + *idx, (int)pkiMsgSz, &datePtr,
+- &dateFormat, &dateLen) != 0) {
++ if (wc_GetDateInfo(pkiMsg + *idx, (int)(pkiMsgSz - *idx),
++ &datePtr, &dateFormat, &dateLen) != 0) {
+ return ASN_PARSE_E;
+ }
+- *idx += (word32)(dateLen + 1);
++ /* datePtr points to the start of the date value
++ * within pkiMsg; advance past the full TLV. */
++ *idx = (word32)(datePtr - pkiMsg) + (word32)dateLen;
+ }
+
+ if (*idx > pkiMsgSz) {
+@@ -11862,7 +12317,7 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+
+ /* may have OPTIONAL OtherKeyAttribute */
+ localIdx = *idx;
+- if ((*idx < kekIdSz) && GetASNTag(pkiMsg, &localIdx, &tag,
++ if ((*idx < kekIdEnd) && GetASNTag(pkiMsg, &localIdx, &tag,
+ pkiMsgSz) == 0 && tag == (ASN_SEQUENCE |
+ ASN_CONSTRUCTED)) {
+ if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0)
+@@ -11891,6 +12346,10 @@ static int wc_PKCS7_DecryptKekri(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0)
+ return ASN_PARSE_E;
+
++ /* Validate EncryptedKey is within input buffer */
++ if (*idx > pkiMsgSz || (word32)length > pkiMsgSz - *idx)
++ return ASN_PARSE_E;
++
+ #ifndef NO_AES
+ direction = AES_DECRYPTION;
+ #else
+@@ -12644,6 +13103,22 @@ static int wc_PKCS7_ParseToRecipientInfoSet(wc_PKCS7* pkcs7, byte* in,
+ NO_USER_CHECK) < 0)
+ ret = ASN_PARSE_E;
+
++ /* GetSet_ex is called with NO_USER_CHECK, which skips the
++ * (idx + length > maxIdx) bounds check in GetLength_ex. In
++ * non-streaming mode, validate the SET length against the
++ * remaining input buffer; in streaming mode the length flows
++ * into pkcs7->stream->expected and then wc_PKCS7_GrowStream,
++ * where it is capped by WOLFSSL_PKCS7_MAX_STREAM_ALLOC. */
++ if (ret == 0 && length < 0)
++ ret = ASN_PARSE_E;
++ #ifdef NO_PKCS7_STREAM
++ if (ret == 0 &&
++ (*idx > pkiMsgSz ||
++ (word32)length > pkiMsgSz - *idx)) {
++ ret = ASN_PARSE_E;
++ }
++ #endif
++
+ if (ret < 0)
+ break;
+
+@@ -12799,6 +13274,11 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ DYNAMIC_TYPE_PKCS7);
+ if (decryptedKey == NULL)
+ return MEMORY_E;
++ XMEMSET(decryptedKey, 0, MAX_ENCRYPTED_KEY_SZ);
++ #ifdef WOLFSSL_CHECK_MEM_ZERO
++ wc_MemZero_Add("wc_PKCS7 decryptedKey", decryptedKey,
++ MAX_ENCRYPTED_KEY_SZ);
++ #endif
+ wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_2);
+ tmpIdx = idx;
+ recipientSetSz = (word32)ret;
+@@ -13018,6 +13498,14 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ ret = ASN_PARSE_E;
+ }
+
++ #ifdef NO_PKCS7_STREAM
++ if (ret == 0 && encryptedContentTotalSz > (int)(pkiMsgSz - idx)) {
++ /* In non-streaming mode, ensure the content fits in the buffer.
++ * Streaming mode handles this via AddDataToStream. */
++ ret = BUFFER_E;
++ }
++ #endif
++
+ if (ret != 0)
+ break;
+
+@@ -13033,10 +13521,18 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ wc_PKCS7_StreamStoreVar(pkcs7, encOID, expBlockSz, explicitOctet);
+
+ if (explicitOctet) {
+- /* initialize decryption state in preparation */
++ /* initialize decryption state in preparation. Use
++ * contentSz (blockKeySz from the content algorithm) as
++ * the AES key size rather than aadSz (the unwrapped CEK
++ * length): the two are equal for well-formed messages,
++ * but using blockKeySz avoids BAD_FUNC_ARG on crafted
++ * messages where the CEK length does not match the
++ * content cipher, which would otherwise be a
++ * distinguishable error. */
+ if (pkcs7->decryptionCb == NULL) {
+ ret = wc_PKCS7_DecryptContentInit(pkcs7, encOID,
+- pkcs7->stream->aad, pkcs7->stream->aadSz,
++ pkcs7->stream->aad,
++ (word32)pkcs7->stream->contentSz,
+ pkcs7->stream->tmpIv, expBlockSz,
+ pkcs7->devId, pkcs7->heap);
+ if (ret != 0)
+@@ -13319,8 +13815,13 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+
+ ret = (int)pkcs7->totalEncryptedContentSz - padLen;
+ #ifndef NO_PKCS7_STREAM
+- pkcs7->stream->aad = NULL;
+- pkcs7->stream->aadSz = 0;
++ /* decryptedKey (just freed) is the same buffer stream->aad
++ * aliases. Null the stream handle so ResetStream doesn't
++ * double-free it. */
++ if (pkcs7->stream != NULL) {
++ pkcs7->stream->aad = NULL;
++ pkcs7->stream->aadSz = 0;
++ }
+ wc_PKCS7_ResetStream(pkcs7);
+ #endif
+ wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START);
+@@ -13333,6 +13834,16 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+
+ #ifndef NO_PKCS7_STREAM
+ if (ret < 0 && ret != WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E)) {
++ /* stream->aad aliases the MAX_ENCRYPTED_KEY_SZ decryptedKey
++ * buffer in this flow. ResetStream only zeros aadSz bytes, so
++ * explicitly zero and release the full buffer here to satisfy
++ * WOLFSSL_CHECK_MEM_ZERO and avoid leaking key material. */
++ if (pkcs7->stream != NULL && pkcs7->stream->aad != NULL) {
++ ForceZero(pkcs7->stream->aad, MAX_ENCRYPTED_KEY_SZ);
++ XFREE(pkcs7->stream->aad, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
++ pkcs7->stream->aad = NULL;
++ pkcs7->stream->aadSz = 0;
++ }
+ wc_PKCS7_ResetStream(pkcs7);
+ wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START);
+ if (pkcs7->cachedEncryptedContent != NULL) {
+@@ -13343,6 +13854,9 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ }
+ }
+ #else
++ if (ret < 0) {
++ wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START);
++ }
+ if (decryptedKey != NULL && ret < 0) {
+ ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ);
+ XFREE(decryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7);
+@@ -14103,6 +14617,10 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ }
+ else {
+ XMEMSET(decryptedKey, 0, MAX_ENCRYPTED_KEY_SZ);
++ #ifdef WOLFSSL_CHECK_MEM_ZERO
++ wc_MemZero_Add("wc_PKCS7 decryptedKey", decryptedKey,
++ MAX_ENCRYPTED_KEY_SZ);
++ #endif
+ }
+ #ifndef NO_PKCS7_STREAM
+ pkcs7->stream->key = decryptedKey;
+@@ -14238,6 +14756,11 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ break;
+ }
+ pkiMsgSz = (pkcs7->stream->length > 0)? pkcs7->stream->length: inSz;
++
++ /* Restore encOID across WANT_READ re-entries so the nonce
++ * length validation below always sees the content-cipher
++ * algorithm parsed in AUTHENV_3. */
++ wc_PKCS7_StreamGetVar(pkcs7, &encOID, &blockKeySz, NULL);
+ #endif
+ /* get length of optional parameter sequence */
+ if (ret == 0 && GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) {
+@@ -14255,6 +14778,46 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ ret = ASN_PARSE_E;
+ }
+
++ /* Enforce algorithm-specific nonce length bounds at the parser
++ * layer so malformed lengths (notably zero-length, which would
++ * catastrophically break AEAD uniqueness on HW backends that
++ * skip their own checks) cannot reach the cipher engine.
++ * - AES-GCM in CMS: RFC 5084 Sec. 3.2 mandates a 12-octet IV.
++ * - AES-CCM: RFC 3610 Sec. 2.3 requires 7..13 octets.
++ * Any other encOID here is a parser-state invariant violation. */
++ if (ret == 0) {
++ int nonceMin = 0, nonceMax = 0;
++ switch (encOID) {
++ #ifdef HAVE_AESGCM
++ case AES128GCMb:
++ case AES192GCMb:
++ case AES256GCMb:
++ nonceMin = GCM_NONCE_MID_SZ;
++ nonceMax = GCM_NONCE_MID_SZ;
++ break;
++ #endif
++ #ifdef HAVE_AESCCM
++ case AES128CCMb:
++ case AES192CCMb:
++ case AES256CCMb:
++ nonceMin = CCM_NONCE_MIN_SZ;
++ nonceMax = CCM_NONCE_MAX_SZ;
++ break;
++ #endif
++ default:
++ WOLFSSL_MSG(
++ "AuthEnvelopedData unexpected content cipher");
++ ret = ALGO_ID_E;
++ break;
++ }
++ if (ret == 0 &&
++ (nonceSz < nonceMin || nonceSz > nonceMax)) {
++ WOLFSSL_MSG(
++ "AuthEnvelopedData nonce length invalid for cipher");
++ ret = ASN_PARSE_E;
++ }
++ }
++
+ if (ret == 0) {
+ XMEMCPY(nonce, &pkiMsg[idx], (word32)nonceSz);
+ idx += (word32)nonceSz;
+@@ -14406,7 +14969,16 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in,
+ if (GetLength_ex(pkiMsg, &idx, &length, pkiMsgSz, 0) <= 0) {
+ ret = ASN_PARSE_E;
+ }
+- #ifndef NO_PKCS7_STREAM
++
++ #ifdef NO_PKCS7_STREAM
++ /* In non-streaming mode, validate authenticatedAttributes
++ * length is within the input buffer. The streaming path
++ * handles this via wc_PKCS7_AddDataToStream instead. */
++ if (ret == 0 &&
++ (idx > pkiMsgSz || (word32)length > pkiMsgSz - idx)) {
++ ret = ASN_PARSE_E;
++ }
++ #else
+ pkcs7->stream->expected = (word32)length;
+ #endif
+ encodedAttribSz = (word32)length + (idx - encodedAttribIdx);
+@@ -15274,6 +15846,12 @@ int wc_PKCS7_DecodeEncryptedData(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ pkiMsgSz, NO_USER_CHECK) <= 0)
+ ret = ASN_PARSE_E;
+
++#ifdef NO_PKCS7_STREAM
++ if (ret == 0 && encryptedContentSz > (int)(pkiMsgSz - idx)) {
++ ret = BUFFER_E;
++ }
++#endif
++
+ if (ret < 0)
+ break;
+ #ifndef NO_PKCS7_STREAM
+@@ -15311,7 +15889,8 @@ int wc_PKCS7_DecodeEncryptedData(wc_PKCS7* pkcs7, byte* in, word32 inSz,
+ version = (int)pkcs7->stream->vers;
+ tmpIv = pkcs7->stream->tmpIv;
+ #endif
+- if (encryptedContentSz <= 0) {
++ if (encryptedContentSz <= 0 ||
++ encryptedContentSz > (int)(pkiMsgSz - idx)) {
+ ret = BUFFER_E;
+ break;
+ }
+diff --git a/wolfssl/wolfcrypt/wc_encrypt.h b/wolfssl/wolfcrypt/wc_encrypt.h
+index da11b5392..7f1c48b63 100644
+--- a/wolfssl/wolfcrypt/wc_encrypt.h
++++ b/wolfssl/wolfcrypt/wc_encrypt.h
+@@ -73,6 +73,9 @@
+ #ifndef CCM_NONCE_MIN_SZ
+ #define CCM_NONCE_MIN_SZ 7
+ #endif
++ #ifndef CCM_NONCE_MAX_SZ
++ #define CCM_NONCE_MAX_SZ 13
++ #endif
+ #endif
+
+
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 8802202114..5569df4d56 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -28,6 +28,7 @@ SRC_URI = " \
file://CVE-2026-6094-3.patch \
file://CVE-2026-6094-4.patch \
file://CVE-2026-6094-5.patch \
+ file://CVE-2026-6291.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 29/33] wolfssl: patch CVE-2026-6325
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (26 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 28/33] wolfssl: patch CVE-2026-6291 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 30/33] wolfssl: patch CVE-2026-6412 ankur.tyagi85
` (3 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6325
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6325.patch | 123 ++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 124 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6325.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6325.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6325.patch
new file mode 100644
index 0000000000..5a67a30dc0
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6325.patch
@@ -0,0 +1,123 @@
+From b47170ed386de50deb3f1ce7c9f39cdb17630fc0 Mon Sep 17 00:00:00 2001
+From: Sean Parkinson <sean@wolfssl.com>
+Date: Wed, 15 Apr 2026 11:39:26 +1000
+Subject: [PATCH] Merge pull request #10204 from mattia-moffa/20260413-fixes
+
+SetSuitesHashSigAlgo fix
+
+(cherry picked from commit 6ac0f82b8589736e15683b7d94a822681b8949fc)
+
+CVE: CVE-2026-6325
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/6ac0f82b8589736e15683b7d94a822681b8949fc]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/internal.c | 11 ++++++++++
+ tests/api.c | 58 +++++++++++++++++++++++++++-----------------------
+ 2 files changed, 42 insertions(+), 27 deletions(-)
+
+diff --git a/src/internal.c b/src/internal.c
+index 267c75a5d..769ba6764 100644
+--- a/src/internal.c
++++ b/src/internal.c
+@@ -29546,6 +29546,17 @@ int SetSuitesHashSigAlgo(Suites* suites, const char* list)
+ break;
+ }
+ }
++ {
++ word32 needed = 2;
++#if defined(WC_RSA_PSS) && defined(WOLFSSL_TLS13)
++ if (sig_alg == rsa_pss_sa_algo)
++ needed = 4;
++#endif
++ if ((word32)idx + needed > WOLFSSL_MAX_SIGALGO) {
++ ret = 0;
++ break;
++ }
++ }
+ AddSuiteHashSigAlgo(suites->hashSigAlgo, mac_alg, sig_alg, 0, &idx);
+ sig_alg = 0;
+ mac_alg = no_mac;
+diff --git a/tests/api.c b/tests/api.c
+index 78fcf13ce..739cf0c68 100644
+--- a/tests/api.c
++++ b/tests/api.c
+@@ -15878,6 +15878,33 @@ static int test_wolfSSL_set1_sigalgs_list(void)
+ WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
+ ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, "RSA+SHA256+RSA"),
+ WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
++
++ {
++ const char entry[] = "RSA+SHA256";
++ const int entryLen = (int)sizeof(entry) - 1;
++ const int entries = WOLFSSL_MAX_SIGALGO + 1;
++ int listSz = entries * (entryLen + 1);
++ char* longList = (char*)XMALLOC(listSz, NULL,
++ DYNAMIC_TYPE_TMP_BUFFER);
++ int i;
++ int pos = 0;
++
++ ExpectNotNull(longList);
++ if (longList != NULL) {
++ for (i = 0; i < entries; i++) {
++ if (i != 0)
++ longList[pos++] = ':';
++ XMEMCPY(longList + pos, entry, entryLen);
++ pos += entryLen;
++ }
++ longList[pos] = '\0';
++ ExpectIntEQ(wolfSSL_CTX_set1_sigalgs_list(ctx, longList),
++ WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
++ ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl, longList),
++ WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
++ XFREE(longList, NULL, DYNAMIC_TYPE_TMP_BUFFER);
++ }
++ }
+ #endif
+ #endif
+ #ifdef HAVE_ECC
+@@ -35744,7 +35771,6 @@ static int test_pkcs7_padding(void)
+ int outSz;
+ int ctOff = -1;
+ int ctLen = 0;
+- int i;
+
+ XMEMSET(key, 0xAA, sizeof(key));
+ XMEMSET(plaintext, 'X', sizeof(plaintext));
+@@ -35767,32 +35793,10 @@ static int test_pkcs7_padding(void)
+ (word32)encodedSz, output, sizeof(output)), (int)sizeof(plaintext));
+ wc_PKCS7_Free(&pkcs7);
+
+- /* Find ciphertext block in encoded DER */
+- if (EXPECT_SUCCESS()) {
+- for (i = encodedSz - 10; i > 10; i--) {
+- if (encoded[i] == 0x04 || encoded[i] == 0x80) {
+- int len, lbytes;
+-
+- if (encoded[i+1] < 0x80) {
+- len = encoded[i+1]; lbytes = 1;
+- }
+- else if (encoded[i+1] == 0x81) {
+- len = encoded[i+2]; lbytes = 2;
+- }
+- else {
+- continue;
+- }
+- if (len > 0 && len % 16 == 0 &&
+- i + 1 + lbytes + len <= encodedSz) {
+- ctOff = i + 1 + lbytes;
+- ctLen = len;
+- break;
+- }
+- }
+- }
+- }
+- ExpectIntGT(ctOff, 0);
+- ExpectIntGE(ctLen, 32);
++ /* encryptedContent is the last element in the DER, so it ends at encodedSz;
++ * 27-byte plaintext -> 32-byte AES-256-CBC ciphertext. */
++ ctLen = 32;
++ ctOff = encodedSz - ctLen;
+
+ /* Corrupt an interior padding byte via CBC bit-flip */
+ if (EXPECT_SUCCESS()) {
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 5569df4d56..843d5071b4 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -29,6 +29,7 @@ SRC_URI = " \
file://CVE-2026-6094-4.patch \
file://CVE-2026-6094-5.patch \
file://CVE-2026-6291.patch \
+ file://CVE-2026-6325.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 30/33] wolfssl: patch CVE-2026-6412
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (27 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 29/33] wolfssl: patch CVE-2026-6325 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 31/33] wolfssl: patch CVE-2026-6450 ankur.tyagi85
` (2 subsequent siblings)
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6412
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6412-1.patch | 159 ++++++++++++++++++
.../wolfssl/files/CVE-2026-6412-2.patch | 56 ++++++
.../wolfssl/wolfssl_5.9.1.bb | 2 +
3 files changed, 217 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-2.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-1.patch
new file mode 100644
index 0000000000..4d1d3b71cb
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-1.patch
@@ -0,0 +1,159 @@
+From eda0a3001ebe036da39f8911a347174a759ffce0 Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Tue, 14 Apr 2026 12:26:45 -0500
+Subject: [PATCH] Report cert verify failure with MD5
+
+(cherry picked from commit 4a13896b2ea6d6080d41cd32539193713e69935f)
+
+CVE: CVE-2026-6412
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/4a13896b2ea6d6080d41cd32539193713e69935f]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ .wolfssl_known_macro_extras | 1 +
+ tests/api/test_certman.c | 81 +++++++++++++++++++++++++++++++++++++
+ tests/api/test_certman.h | 4 +-
+ wolfcrypt/src/asn.c | 7 ++++
+ 4 files changed, 92 insertions(+), 1 deletion(-)
+
+diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras
+index 3e411ca86..3233644ba 100644
+--- a/.wolfssl_known_macro_extras
++++ b/.wolfssl_known_macro_extras
+@@ -680,6 +680,7 @@ WOLFSSL_ALLOW_CRIT_AIA
+ WOLFSSL_ALLOW_CRIT_AKID
+ WOLFSSL_ALLOW_CRIT_SKID
+ WOLFSSL_ALLOW_MAX_FRAGMENT_ADJUST
++WOLFSSL_ALLOW_MD5_CERT_SIGS
+ WOLFSSL_ALLOW_NO_CN_IN_SAN
+ WOLFSSL_ALLOW_NO_SUITES
+ WOLFSSL_ALLOW_SERVER_SC_EXT
+diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c
+index dfce12334..9f87f4535 100644
+--- a/tests/api/test_certman.c
++++ b/tests/api/test_certman.c
+@@ -2540,3 +2540,84 @@ int test_various_pathlen_chains(void)
+ #endif
+ return EXPECT_RESULT();
+ }
++
++/* Verify that certificates signed with MD5 (md5WithRSAEncryption) are
++ * rejected during chain verification. MD5 must not be acceptable as a
++ * certificate signature hash, even when MD5 is compiled in (e.g. for TLS
++ * 1.0 PRF or HMAC uses). Trust anchors are exempt from this check because
++ * ParseCertRelative skips ConfirmSignature for CA_TYPE. */
++int test_wolfSSL_CertManagerRejectMD5Cert(void)
++{
++ EXPECT_DECLS;
++#if !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_MD5) && \
++ !defined(WOLFSSL_ALLOW_MD5_CERT_SIGS) && defined(WOLFSSL_CERT_GEN) && \
++ !defined(NO_WOLFSSL_CM_VERIFY) && !defined(NO_ASN_CRYPT) && \
++ !defined(USE_CERT_BUFFERS_1024)
++ WOLFSSL_CERT_MANAGER* cm = NULL;
++ RsaKey caKey;
++ WC_RNG rng;
++ Cert leaf;
++ byte* der = NULL;
++ int derSz = 0;
++ word32 idx = 0;
++ int caKeyInit = 0;
++ int rngInit = 0;
++
++ XMEMSET(&caKey, 0, sizeof(caKey));
++ XMEMSET(&rng, 0, sizeof(rng));
++
++ ExpectIntEQ(wc_InitRng(&rng), 0);
++ if (EXPECT_SUCCESS()) rngInit = 1;
++
++ ExpectIntEQ(wc_InitRsaKey_ex(&caKey, HEAP_HINT, testDevId), 0);
++ if (EXPECT_SUCCESS()) caKeyInit = 1;
++ ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey,
++ sizeof_ca_key_der_2048), 0);
++
++ ExpectNotNull(der = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT,
++ DYNAMIC_TYPE_TMP_BUFFER));
++
++ /* Build a leaf certificate whose issuer is the built-in 2048-bit
++ * wolfSSL test CA and sign it with MD5+RSA using the matching CA
++ * private key. */
++ ExpectIntEQ(wc_InitCert(&leaf), 0);
++ leaf.sigType = CTC_MD5wRSA;
++ leaf.isCA = 0;
++ XSTRNCPY(leaf.subject.country, "US", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.state, "MT", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.locality, "Bozeman", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.org, "wolfSSL", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.unit, "Test", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.commonName, "md5-leaf", CTC_NAME_SIZE);
++ XSTRNCPY(leaf.subject.email, "info@wolfssl.com", CTC_NAME_SIZE);
++
++ ExpectIntEQ(wc_SetIssuerBuffer(&leaf, ca_cert_der_2048,
++ sizeof_ca_cert_der_2048), 0);
++
++ /* wc_MakeCert needs an RSA public key for the subject; reuse caKey
++ * for simplicity (we only care about signature-side verification). */
++ ExpectIntGT((derSz = wc_MakeCert(&leaf, der, FOURK_BUF, &caKey, NULL,
++ &rng)), 0);
++ ExpectIntGT((derSz = wc_SignCert(leaf.bodySz, leaf.sigType, der,
++ FOURK_BUF, &caKey, NULL, &rng)), 0);
++
++ /* Load the SHA-256 signed CA cert as a trust anchor and attempt
++ * to verify the MD5-signed leaf: it must be rejected because
++ * HashForSignature() now returns HASH_TYPE_E for MD5 in verify mode,
++ * which surfaces as ASN_SIG_CONFIRM_E from ConfirmSignature(). */
++ ExpectNotNull(cm = wolfSSL_CertManagerNew());
++ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_der_2048,
++ sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
++ WOLFSSL_SUCCESS);
++
++ ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1),
++ WC_NO_ERR_TRACE(HASH_TYPE_E));
++
++ wolfSSL_CertManagerFree(cm);
++ XFREE(der, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
++ if (caKeyInit) wc_FreeRsaKey(&caKey);
++ if (rngInit) wc_FreeRng(&rng);
++#endif
++ return EXPECT_RESULT();
++}
+diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h
+index 1588c81ec..a0b5d9548 100644
+--- a/tests/api/test_certman.h
++++ b/tests/api/test_certman.h
+@@ -41,6 +41,7 @@ int test_wolfSSL_CRL_static_revoked_list(void);
+ int test_wolfSSL_CRL_duplicate_extensions(void);
+ int test_wolfSSL_CertManagerCheckOCSPResponse(void);
+ int test_various_pathlen_chains(void);
++int test_wolfSSL_CertManagerRejectMD5Cert(void);
+
+ #define TEST_CERTMAN_DECLS \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerAPI), \
+@@ -59,7 +60,8 @@ int test_various_pathlen_chains(void);
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
+- TEST_DECL_GROUP("certman", test_various_pathlen_chains)
++ TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
++ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert)
+
+ #endif /* WOLFCRYPT_TEST_CERTMAN_H */
+
+diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c
+index fea449863..8d4f40a70 100644
+--- a/wolfcrypt/src/asn.c
++++ b/wolfcrypt/src/asn.c
+@@ -15880,6 +15880,13 @@ static int HashForSignature(const byte* buf, word32 bufSz, word32 sigOID,
+ #endif
+ #ifndef NO_MD5
+ case CTC_MD5wRSA:
++ #ifndef WOLFSSL_ALLOW_MD5_CERT_SIGS
++ if (verify) {
++ ret = HASH_TYPE_E;
++ WOLFSSL_MSG("MD5 not supported for certificate verification");
++ break;
++ }
++ #endif
+ if ((ret = wc_Md5Hash_ex(buf, bufSz, digest, heap, devId)) == 0) {
+ *typeH = MD5h;
+ *digestSz = WC_MD5_DIGEST_SIZE;
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-2.patch
new file mode 100644
index 0000000000..0747e9dd99
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6412-2.patch
@@ -0,0 +1,56 @@
+From ab4cfd06ce09ca6aa33e06ca12bcfc1186c9f357 Mon Sep 17 00:00:00 2001
+From: Eric Blankenhorn <eric@wolfssl.com>
+Date: Tue, 14 Apr 2026 13:39:17 -0500
+Subject: [PATCH] Fix from review
+
+(cherry picked from commit a8ea8a898c45a4199ac196044ac14314dc82d981)
+
+CVE: CVE-2026-6412
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/a8ea8a898c45a4199ac196044ac14314dc82d981]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ tests/api/test_certman.c | 20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c
+index 9f87f4535..6c5875dc5 100644
+--- a/tests/api/test_certman.c
++++ b/tests/api/test_certman.c
+@@ -2576,6 +2576,9 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void)
+
+ ExpectNotNull(der = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT,
+ DYNAMIC_TYPE_TMP_BUFFER));
++ if (der == NULL) {
++ goto cleanup;
++ }
+
+ /* Build a leaf certificate whose issuer is the built-in 2048-bit
+ * wolfSSL test CA and sign it with MD5+RSA using the matching CA
+@@ -2604,16 +2607,19 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void)
+ /* Load the SHA-256 signed CA cert as a trust anchor and attempt
+ * to verify the MD5-signed leaf: it must be rejected because
+ * HashForSignature() now returns HASH_TYPE_E for MD5 in verify mode,
+- * which surfaces as ASN_SIG_CONFIRM_E from ConfirmSignature(). */
++ * and wolfSSL_CertManagerVerifyBuffer() returns that error. */
+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
+- ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_der_2048,
+- sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
+- WOLFSSL_SUCCESS);
++ if (cm != NULL) {
++ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_der_2048,
++ sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
++ WOLFSSL_SUCCESS);
+
+- ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
+- WOLFSSL_FILETYPE_ASN1),
+- WC_NO_ERR_TRACE(HASH_TYPE_E));
++ ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1),
++ WC_NO_ERR_TRACE(HASH_TYPE_E));
++ }
+
++cleanup:
+ wolfSSL_CertManagerFree(cm);
+ XFREE(der, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ if (caKeyInit) wc_FreeRsaKey(&caKey);
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 843d5071b4..4ca330b029 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -30,6 +30,8 @@ SRC_URI = " \
file://CVE-2026-6094-5.patch \
file://CVE-2026-6291.patch \
file://CVE-2026-6325.patch \
+ file://CVE-2026-6412-1.patch \
+ file://CVE-2026-6412-2.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 31/33] wolfssl: patch CVE-2026-6450
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (28 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 30/33] wolfssl: patch CVE-2026-6412 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 32/33] wolfssl: patch CVE-2026-6731 ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 33/33] wolfssl: patch CVE-2026-7531 ankur.tyagi85
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Also backport follow-up PR[1] to the initial PR mentioned in the NVD.
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6450
[1]https://github.com/wolfSSL/wolfssl/pull/10274
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6450-1.patch | 456 ++++++++++++++++++
.../wolfssl/files/CVE-2026-6450-2.patch | 404 ++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 2 +
3 files changed, 862 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-2.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-1.patch
new file mode 100644
index 0000000000..a754fc3e33
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-1.patch
@@ -0,0 +1,456 @@
+From b60e4b6fc0d757f5983a803ad02a1a57c0d0a463 Mon Sep 17 00:00:00 2001
+From: Reda Chouk <reda@wolfssl.com>
+Date: Thu, 16 Apr 2026 19:45:09 +0200
+Subject: [PATCH] reject crls with unrecognized critical extensions per rfc
+ 5280 section 5.2
+
+(cherry picked from commit 857141da35b67a14a580fb26ec57af1efa68a1d9)
+
+CVE: CVE-2026-6450
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/857141da35b67a14a580fb26ec57af1efa68a1d9]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ tests/api/test_certman.c | 342 +++++++++++++++++++++++++++++++++++++++
+ tests/api/test_certman.h | 4 +
+ wolfcrypt/src/asn.c | 7 +-
+ wolfcrypt/src/asn_orig.c | 9 +-
+ 4 files changed, 359 insertions(+), 3 deletions(-)
+
+diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c
+index 6c5875dc5..7405f4bff 100644
+--- a/tests/api/test_certman.c
++++ b/tests/api/test_certman.c
+@@ -1964,6 +1964,348 @@ int test_wolfSSL_CRL_duplicate_extensions(void)
+ return EXPECT_RESULT();
+ }
+
++int test_wolfSSL_CRL_critical_idp(void)
++{
++ EXPECT_DECLS;
++#if !defined(NO_CERTS) && defined(HAVE_CRL) && !defined(NO_RSA)
++
++ /* CA cert (CN=claim-root), self-signed, 799 bytes DER */
++ static const unsigned char ca_cert_idp[] = {
++ 0x30, 0x82, 0x03, 0x1b, 0x30, 0x82, 0x02, 0x03, 0xa0, 0x03, 0x02,
++ 0x01, 0x02, 0x02, 0x14, 0x1e, 0x25, 0xc1, 0x5d, 0x6f, 0x02, 0x21,
++ 0xa0, 0xf0, 0x14, 0x15, 0x9c, 0x3b, 0x4d, 0x1d, 0x73, 0x16, 0x00,
++ 0xe4, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
++ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30,
++ 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61,
++ 0x69, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d,
++ 0x32, 0x36, 0x30, 0x34, 0x31, 0x36, 0x31, 0x31, 0x33, 0x38, 0x35,
++ 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x30, 0x34, 0x31, 0x33, 0x31,
++ 0x31, 0x33, 0x38, 0x35, 0x35, 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30,
++ 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61,
++ 0x69, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x82, 0x01, 0x22,
++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
++ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xba, 0x49, 0x8c, 0xb5,
++ 0x17, 0xc1, 0x01, 0x24, 0x6f, 0x56, 0x1a, 0xa9, 0x3b, 0x03, 0xe2,
++ 0x9f, 0x24, 0xb1, 0x25, 0x98, 0xfb, 0x38, 0x82, 0x78, 0x54, 0xa7,
++ 0x1f, 0x69, 0x87, 0xe4, 0x96, 0x1b, 0x81, 0x18, 0x10, 0xb0, 0xc0,
++ 0x5b, 0x4b, 0xbf, 0xb8, 0x1d, 0xf4, 0xee, 0x75, 0x0f, 0xb5, 0x45,
++ 0x72, 0x70, 0xce, 0x65, 0x84, 0x44, 0x3e, 0x30, 0x78, 0xc4, 0xf3,
++ 0xec, 0xba, 0x96, 0x78, 0xa4, 0x65, 0xfc, 0x62, 0x8d, 0xf5, 0x29,
++ 0xf9, 0x7c, 0x3d, 0x78, 0x6c, 0x1d, 0x4a, 0x4c, 0xc9, 0x15, 0x2d,
++ 0x22, 0x10, 0xea, 0x93, 0x26, 0xb8, 0xa6, 0x17, 0xd3, 0x0e, 0xbc,
++ 0x0c, 0xab, 0x83, 0x63, 0xf6, 0x1c, 0xcc, 0x83, 0x73, 0x29, 0x7e,
++ 0x7f, 0x83, 0x7f, 0xbd, 0x63, 0xaa, 0x8d, 0xfa, 0x78, 0x85, 0xd2,
++ 0x3e, 0x60, 0x95, 0x5a, 0x8d, 0xfa, 0x8f, 0xcd, 0x94, 0x3f, 0x13,
++ 0x28, 0xd9, 0xd0, 0x87, 0x28, 0x17, 0x78, 0xe2, 0x61, 0x8d, 0x79,
++ 0x97, 0x01, 0xa9, 0x7c, 0x84, 0xc0, 0x1c, 0xbe, 0x5f, 0x5d, 0xca,
++ 0x28, 0x6b, 0x5e, 0xdd, 0x83, 0xa5, 0x55, 0x34, 0x11, 0xba, 0xfa,
++ 0x8b, 0x92, 0xa3, 0xde, 0xb6, 0xf3, 0xba, 0xab, 0x7f, 0x1a, 0x67,
++ 0xfd, 0x6f, 0x20, 0x85, 0x4c, 0x77, 0xa7, 0x8e, 0xbe, 0xb8, 0xf8,
++ 0x8f, 0x70, 0xe3, 0x5a, 0xd3, 0x77, 0xc9, 0x9e, 0x10, 0x60, 0xb4,
++ 0xdb, 0x0c, 0xc5, 0x05, 0xe1, 0x1f, 0xbd, 0xe6, 0x79, 0xee, 0x82,
++ 0x3f, 0x51, 0x76, 0xe2, 0x7f, 0x5c, 0x11, 0x6d, 0xd3, 0x21, 0x69,
++ 0xec, 0x05, 0x11, 0x8b, 0xc8, 0x39, 0xb3, 0x2c, 0xa6, 0x83, 0xb4,
++ 0x6f, 0xac, 0x19, 0xd6, 0x6a, 0x65, 0x0d, 0x08, 0x94, 0x58, 0xde,
++ 0x3d, 0xc9, 0x0c, 0x54, 0x03, 0x73, 0x0c, 0x8d, 0x24, 0x09, 0xf3,
++ 0xb1, 0x5d, 0xd2, 0xe3, 0xeb, 0x56, 0xd6, 0x28, 0x66, 0x5b, 0x02,
++ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0f, 0x06,
++ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
++ 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
++ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06,
++ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x97, 0x58,
++ 0x47, 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e,
++ 0xea, 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55,
++ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x52, 0x97, 0x58,
++ 0x47, 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e,
++ 0xea, 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x0d, 0x06, 0x09, 0x2a,
++ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
++ 0x82, 0x01, 0x01, 0x00, 0x7d, 0x30, 0xd4, 0x6a, 0x01, 0x89, 0x3b,
++ 0x62, 0xed, 0x16, 0x46, 0x59, 0x0f, 0xf2, 0x3b, 0xb5, 0xde, 0x89,
++ 0x08, 0x17, 0x68, 0xcb, 0x46, 0xdc, 0x39, 0xa6, 0xcb, 0x56, 0xb0,
++ 0x91, 0xeb, 0x03, 0xb2, 0x15, 0xc4, 0x3b, 0x4d, 0x63, 0x55, 0x22,
++ 0x0a, 0x26, 0xe6, 0x64, 0x46, 0xe8, 0x0f, 0xa8, 0xf3, 0xde, 0xe1,
++ 0x43, 0x54, 0xe6, 0xd7, 0x8a, 0xf4, 0x4f, 0xab, 0x56, 0x93, 0x12,
++ 0x71, 0x4b, 0x25, 0x71, 0x0a, 0x31, 0x18, 0x79, 0xee, 0x45, 0xa4,
++ 0xf5, 0x72, 0x67, 0xfa, 0x41, 0xd9, 0x87, 0x97, 0x09, 0xef, 0x55,
++ 0xad, 0x6f, 0x47, 0x1d, 0x5a, 0xb2, 0xe9, 0xf7, 0x22, 0x05, 0x2d,
++ 0x5a, 0x81, 0xa8, 0xe8, 0x53, 0xb0, 0x94, 0xf6, 0x63, 0xff, 0x3f,
++ 0x51, 0x7a, 0x08, 0xac, 0x27, 0x9a, 0x57, 0x11, 0x22, 0xa4, 0x00,
++ 0x84, 0x70, 0x86, 0x76, 0x39, 0x0f, 0x4f, 0x57, 0xcf, 0x8e, 0x94,
++ 0xd2, 0x8e, 0x43, 0xc0, 0xd5, 0x34, 0x7d, 0xf5, 0xa1, 0x45, 0x1e,
++ 0xb7, 0xc8, 0x7e, 0x7c, 0xfe, 0x5d, 0x4d, 0x53, 0x43, 0x25, 0x15,
++ 0x9e, 0x08, 0x01, 0x56, 0xa4, 0xff, 0x79, 0x59, 0x25, 0xc9, 0x23,
++ 0x98, 0xaf, 0x05, 0xaf, 0xc1, 0x0b, 0x29, 0xf1, 0xe2, 0xc4, 0x36,
++ 0x31, 0x91, 0xfa, 0xf2, 0xbb, 0x12, 0xe8, 0x67, 0xf9, 0xc7, 0xa1,
++ 0x5e, 0x8c, 0xed, 0x92, 0x12, 0xa3, 0x2b, 0xe1, 0xc2, 0xe1, 0xa0,
++ 0xb0, 0x0e, 0x12, 0xa7, 0xd0, 0xa2, 0xae, 0xd6, 0xfa, 0x30, 0x21,
++ 0x0f, 0x73, 0xfe, 0x24, 0x21, 0x5f, 0x03, 0x86, 0x69, 0xcd, 0xec,
++ 0x76, 0x18, 0xe1, 0xfd, 0xb6, 0x64, 0x90, 0xa6, 0x06, 0x2e, 0x19,
++ 0x40, 0x93, 0x50, 0x37, 0xe4, 0x90, 0xe3, 0x1f, 0x07, 0xae, 0xfb,
++ 0x89, 0xc3, 0xf6, 0xc4, 0x90, 0xab, 0x40, 0x67, 0x4c, 0x43, 0x2c,
++ 0xa2, 0xb0, 0x3e, 0x61, 0x16, 0x69, 0x8f
++ };
++
++ /* CRL with critical IDP onlyuser=TRUE, revokes serial 0x1000, 480 bytes */
++ static const unsigned char crl_user_idp[] = {
++ 0x30, 0x82, 0x01, 0xdc, 0x30, 0x81, 0xc5, 0x02, 0x01, 0x01, 0x30,
++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++ 0x0b, 0x05, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
++ 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2d,
++ 0x72, 0x6f, 0x6f, 0x74, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x34, 0x31,
++ 0x36, 0x31, 0x31, 0x33, 0x38, 0x35, 0x35, 0x5a, 0x17, 0x0d, 0x33,
++ 0x36, 0x30, 0x34, 0x31, 0x33, 0x31, 0x31, 0x33, 0x38, 0x35, 0x35,
++ 0x5a, 0x30, 0x15, 0x30, 0x13, 0x02, 0x02, 0x10, 0x00, 0x17, 0x0d,
++ 0x32, 0x36, 0x30, 0x34, 0x31, 0x36, 0x31, 0x31, 0x33, 0x38, 0x35,
++ 0x35, 0x5a, 0xa0, 0x65, 0x30, 0x63, 0x30, 0x1f, 0x06, 0x03, 0x55,
++ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x52, 0x97, 0x58,
++ 0x47, 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e,
++ 0xea, 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x33, 0x06, 0x03, 0x55,
++ 0x1d, 0x1c, 0x01, 0x01, 0xff, 0x04, 0x29, 0x30, 0x27, 0xa0, 0x22,
++ 0xa0, 0x20, 0x86, 0x1e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
++ 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2f,
++ 0x63, 0x72, 0x6c, 0x2d, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x65,
++ 0x6d, 0x81, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x14,
++ 0x04, 0x04, 0x02, 0x02, 0x20, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
++ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
++ 0x82, 0x01, 0x01, 0x00, 0x9b, 0x1a, 0x70, 0xba, 0xf8, 0x38, 0xff,
++ 0xc6, 0x36, 0x59, 0x6e, 0xab, 0x87, 0x74, 0x04, 0xe3, 0x17, 0xb3,
++ 0xdd, 0x62, 0x03, 0x25, 0x9e, 0xff, 0x53, 0xf7, 0xde, 0x48, 0xb0,
++ 0x56, 0x0c, 0x19, 0xea, 0x86, 0x30, 0x21, 0x01, 0x63, 0xd6, 0xd2,
++ 0xef, 0xd1, 0x0e, 0x1d, 0xde, 0xc1, 0x18, 0x33, 0xd2, 0x1b, 0x79,
++ 0x2e, 0xa1, 0xd5, 0x51, 0xcc, 0x31, 0x35, 0x28, 0xa6, 0x6f, 0xc0,
++ 0xcf, 0x78, 0xbf, 0x5d, 0xdd, 0x66, 0x81, 0x71, 0xa3, 0x52, 0xb5,
++ 0x48, 0x81, 0x1a, 0x34, 0xf1, 0x03, 0x37, 0x3a, 0x97, 0x02, 0xd6,
++ 0x56, 0x4a, 0x24, 0xeb, 0x93, 0x47, 0xb6, 0xc3, 0x69, 0xc6, 0x2b,
++ 0xd8, 0xfc, 0xf9, 0x9f, 0x85, 0xab, 0xe2, 0x81, 0x66, 0x8f, 0xcf,
++ 0x7a, 0x81, 0xd7, 0x46, 0xb4, 0x8d, 0x44, 0x05, 0x40, 0xd2, 0x3b,
++ 0x1c, 0xb8, 0x4a, 0x88, 0xb8, 0x65, 0x69, 0x5e, 0x7f, 0x6c, 0x43,
++ 0x1c, 0x4f, 0xbf, 0x48, 0x55, 0x6b, 0xb0, 0xb3, 0x70, 0x49, 0x1a,
++ 0xfa, 0xd1, 0x55, 0xe7, 0xb9, 0x5d, 0x4f, 0x2d, 0x7e, 0xc1, 0xa5,
++ 0x5f, 0x5e, 0x38, 0xef, 0x74, 0xe8, 0x72, 0x89, 0x9c, 0x86, 0x24,
++ 0x65, 0x2d, 0x38, 0x88, 0x53, 0x81, 0x48, 0x8a, 0x7d, 0xc3, 0x0d,
++ 0x87, 0xaf, 0xd3, 0xf7, 0x39, 0xeb, 0xac, 0x36, 0xc2, 0xc9, 0x1f,
++ 0x78, 0xa9, 0x53, 0x1c, 0x4a, 0xa6, 0xba, 0x63, 0xd1, 0xc2, 0x62,
++ 0x81, 0x00, 0x39, 0xb1, 0x1c, 0x1c, 0xad, 0x96, 0x83, 0xf7, 0x99,
++ 0x34, 0xc6, 0x9c, 0x93, 0xbb, 0x6a, 0x7c, 0xf5, 0x18, 0xed, 0xbd,
++ 0x29, 0xe4, 0x29, 0x50, 0x3c, 0xcb, 0x94, 0x72, 0x8f, 0xad, 0x15,
++ 0x91, 0x38, 0x4a, 0xb4, 0xde, 0x98, 0x3e, 0xd6, 0xb2, 0xd1, 0x2a,
++ 0x8c, 0xa2, 0xc9, 0x0f, 0x2f, 0x7c, 0x4a, 0xd6, 0x56, 0x02, 0x9f,
++ 0x6c, 0xda, 0xa9, 0x4c, 0x04, 0x64, 0x7c
++ };
++
++ /* CRL with critical IDP onlyCA=TRUE, empty revocation list, 459 bytes */
++ static const unsigned char crl_caonly_idp[] = {
++ 0x30, 0x82, 0x01, 0xc7, 0x30, 0x81, 0xb0, 0x02, 0x01, 0x01, 0x30,
++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++ 0x0b, 0x05, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
++ 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2d,
++ 0x72, 0x6f, 0x6f, 0x74, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x34, 0x31,
++ 0x36, 0x31, 0x31, 0x33, 0x38, 0x35, 0x35, 0x5a, 0x17, 0x0d, 0x33,
++ 0x36, 0x30, 0x34, 0x31, 0x33, 0x31, 0x31, 0x33, 0x38, 0x35, 0x35,
++ 0x5a, 0xa0, 0x67, 0x30, 0x65, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
++ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x52, 0x97, 0x58, 0x47,
++ 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e, 0xea,
++ 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d,
++ 0x1c, 0x01, 0x01, 0xff, 0x04, 0x2b, 0x30, 0x29, 0xa0, 0x24, 0xa0,
++ 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
++ 0x6c, 0x61, 0x69, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x63,
++ 0x72, 0x6c, 0x2d, 0x63, 0x61, 0x6f, 0x6e, 0x6c, 0x79, 0x2e, 0x70,
++ 0x65, 0x6d, 0x82, 0x01, 0xff, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
++ 0x14, 0x04, 0x04, 0x02, 0x02, 0x20, 0x01, 0x30, 0x0d, 0x06, 0x09,
++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
++ 0x03, 0x82, 0x01, 0x01, 0x00, 0x9d, 0x6e, 0x0d, 0x93, 0x89, 0xab,
++ 0x6e, 0x74, 0x52, 0x2c, 0xe6, 0x89, 0xcb, 0x72, 0x49, 0x90, 0x0d,
++ 0x91, 0x80, 0xb5, 0xca, 0x7b, 0x95, 0x0d, 0xa8, 0x05, 0x31, 0x04,
++ 0x50, 0xb8, 0xf3, 0xce, 0x9c, 0xbb, 0x05, 0x38, 0x0a, 0x64, 0x1a,
++ 0x61, 0x68, 0xa7, 0xa8, 0xa0, 0x69, 0x2a, 0x79, 0x01, 0x42, 0x67,
++ 0xf5, 0x72, 0xdf, 0x37, 0x5b, 0x42, 0x6d, 0x3c, 0x59, 0x95, 0x09,
++ 0x34, 0xb3, 0xb6, 0x8b, 0x2b, 0xd8, 0xab, 0xb6, 0x8b, 0xff, 0x8e,
++ 0xae, 0xd0, 0xc6, 0x9a, 0xbe, 0x7e, 0x29, 0xbc, 0x4d, 0xfb, 0xe1,
++ 0xac, 0xd8, 0x23, 0x1a, 0xec, 0x0d, 0xa1, 0xa0, 0xf6, 0x52, 0x8e,
++ 0x64, 0xc4, 0x11, 0x0f, 0x7c, 0x5b, 0x9f, 0x65, 0x4f, 0x5a, 0xd6,
++ 0x64, 0xe0, 0x64, 0xf6, 0xac, 0x9d, 0xdc, 0x21, 0x3f, 0xa8, 0x5c,
++ 0xd2, 0xf5, 0x87, 0xec, 0x49, 0x19, 0xff, 0x01, 0x9e, 0x8d, 0x83,
++ 0x08, 0xd2, 0xdc, 0x83, 0xf6, 0x03, 0xc4, 0x6f, 0xf6, 0xa2, 0x13,
++ 0x41, 0xfe, 0x66, 0xcd, 0xeb, 0xe8, 0x0f, 0x28, 0x7d, 0xd2, 0xcd,
++ 0xfa, 0x7a, 0xd7, 0xae, 0x08, 0xa1, 0x31, 0x17, 0x60, 0x59, 0x39,
++ 0x98, 0x85, 0xe1, 0xa4, 0xd2, 0x35, 0x70, 0xb7, 0xff, 0xf3, 0x2f,
++ 0xee, 0x45, 0x9c, 0xbe, 0xcc, 0x18, 0x49, 0x94, 0xe9, 0xf6, 0xd0,
++ 0x45, 0x54, 0x6f, 0xe4, 0xe8, 0x3a, 0x0d, 0x5b, 0x05, 0xe8, 0x02,
++ 0x51, 0x5b, 0x63, 0xb5, 0xf2, 0x47, 0x86, 0x9b, 0xf3, 0x07, 0xc2,
++ 0x49, 0x26, 0xa0, 0x77, 0x94, 0xe7, 0x4f, 0xbc, 0x5f, 0x9f, 0xf9,
++ 0x06, 0x0e, 0xcb, 0x45, 0x9c, 0x02, 0x11, 0xfc, 0xcb, 0x12, 0x7f,
++ 0xba, 0x7d, 0x93, 0x5b, 0x57, 0x6a, 0x15, 0x5e, 0xd2, 0xc1, 0x97,
++ 0xb2, 0xbb, 0x00, 0x2c, 0xdd, 0x41, 0x97, 0x2a, 0xe4, 0x53, 0x40,
++ 0xf8, 0xb5, 0x56, 0xf2, 0x9a, 0x04, 0xe6, 0x89
++ };
++
++ WOLFSSL_CERT_MANAGER* cm = NULL;
++
++ ExpectNotNull(cm = wolfSSL_CertManagerNew());
++
++ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_idp,
++ sizeof(ca_cert_idp), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++
++ ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL),
++ WOLFSSL_SUCCESS);
++
++ /* User-scope CRL has a critical IDP extension, must be rejected */
++ ExpectIntNE(wolfSSL_CertManagerLoadCRLBuffer(cm, crl_user_idp,
++ sizeof(crl_user_idp), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++
++ /* CA-only CRL also has a critical IDP extension, must be rejected */
++ ExpectIntNE(wolfSSL_CertManagerLoadCRLBuffer(cm, crl_caonly_idp,
++ sizeof(crl_caonly_idp), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++
++ wolfSSL_CertManagerFree(cm);
++#endif
++ return EXPECT_RESULT();
++}
++
++int test_wolfSSL_CRL_unknown_critical_ext(void)
++{
++ EXPECT_DECLS;
++#if !defined(NO_CERTS) && defined(HAVE_CRL) && !defined(NO_RSA)
++
++ static const unsigned char ca_cert[] = {
++ 0x30, 0x82, 0x03, 0x1b, 0x30, 0x82, 0x02, 0x03, 0xa0, 0x03, 0x02,
++ 0x01, 0x02, 0x02, 0x14, 0x1e, 0x25, 0xc1, 0x5d, 0x6f, 0x02, 0x21,
++ 0xa0, 0xf0, 0x14, 0x15, 0x9c, 0x3b, 0x4d, 0x1d, 0x73, 0x16, 0x00,
++ 0xe4, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
++ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30,
++ 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61,
++ 0x69, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d,
++ 0x32, 0x36, 0x30, 0x34, 0x31, 0x36, 0x31, 0x31, 0x33, 0x38, 0x35,
++ 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x30, 0x34, 0x31, 0x33, 0x31,
++ 0x31, 0x33, 0x38, 0x35, 0x35, 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30,
++ 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61,
++ 0x69, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x30, 0x82, 0x01, 0x22,
++ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
++ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
++ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xba, 0x49, 0x8c, 0xb5,
++ 0x17, 0xc1, 0x01, 0x24, 0x6f, 0x56, 0x1a, 0xa9, 0x3b, 0x03, 0xe2,
++ 0x9f, 0x24, 0xb1, 0x25, 0x98, 0xfb, 0x38, 0x82, 0x78, 0x54, 0xa7,
++ 0x1f, 0x69, 0x87, 0xe4, 0x96, 0x1b, 0x81, 0x18, 0x10, 0xb0, 0xc0,
++ 0x5b, 0x4b, 0xbf, 0xb8, 0x1d, 0xf4, 0xee, 0x75, 0x0f, 0xb5, 0x45,
++ 0x72, 0x70, 0xce, 0x65, 0x84, 0x44, 0x3e, 0x30, 0x78, 0xc4, 0xf3,
++ 0xec, 0xba, 0x96, 0x78, 0xa4, 0x65, 0xfc, 0x62, 0x8d, 0xf5, 0x29,
++ 0xf9, 0x7c, 0x3d, 0x78, 0x6c, 0x1d, 0x4a, 0x4c, 0xc9, 0x15, 0x2d,
++ 0x22, 0x10, 0xea, 0x93, 0x26, 0xb8, 0xa6, 0x17, 0xd3, 0x0e, 0xbc,
++ 0x0c, 0xab, 0x83, 0x63, 0xf6, 0x1c, 0xcc, 0x83, 0x73, 0x29, 0x7e,
++ 0x7f, 0x83, 0x7f, 0xbd, 0x63, 0xaa, 0x8d, 0xfa, 0x78, 0x85, 0xd2,
++ 0x3e, 0x60, 0x95, 0x5a, 0x8d, 0xfa, 0x8f, 0xcd, 0x94, 0x3f, 0x13,
++ 0x28, 0xd9, 0xd0, 0x87, 0x28, 0x17, 0x78, 0xe2, 0x61, 0x8d, 0x79,
++ 0x97, 0x01, 0xa9, 0x7c, 0x84, 0xc0, 0x1c, 0xbe, 0x5f, 0x5d, 0xca,
++ 0x28, 0x6b, 0x5e, 0xdd, 0x83, 0xa5, 0x55, 0x34, 0x11, 0xba, 0xfa,
++ 0x8b, 0x92, 0xa3, 0xde, 0xb6, 0xf3, 0xba, 0xab, 0x7f, 0x1a, 0x67,
++ 0xfd, 0x6f, 0x20, 0x85, 0x4c, 0x77, 0xa7, 0x8e, 0xbe, 0xb8, 0xf8,
++ 0x8f, 0x70, 0xe3, 0x5a, 0xd3, 0x77, 0xc9, 0x9e, 0x10, 0x60, 0xb4,
++ 0xdb, 0x0c, 0xc5, 0x05, 0xe1, 0x1f, 0xbd, 0xe6, 0x79, 0xee, 0x82,
++ 0x3f, 0x51, 0x76, 0xe2, 0x7f, 0x5c, 0x11, 0x6d, 0xd3, 0x21, 0x69,
++ 0xec, 0x05, 0x11, 0x8b, 0xc8, 0x39, 0xb3, 0x2c, 0xa6, 0x83, 0xb4,
++ 0x6f, 0xac, 0x19, 0xd6, 0x6a, 0x65, 0x0d, 0x08, 0x94, 0x58, 0xde,
++ 0x3d, 0xc9, 0x0c, 0x54, 0x03, 0x73, 0x0c, 0x8d, 0x24, 0x09, 0xf3,
++ 0xb1, 0x5d, 0xd2, 0xe3, 0xeb, 0x56, 0xd6, 0x28, 0x66, 0x5b, 0x02,
++ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0f, 0x06,
++ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
++ 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
++ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06,
++ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x97, 0x58,
++ 0x47, 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e,
++ 0xea, 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55,
++ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x52, 0x97, 0x58,
++ 0x47, 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e,
++ 0xea, 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x0d, 0x06, 0x09, 0x2a,
++ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
++ 0x82, 0x01, 0x01, 0x00, 0x7d, 0x30, 0xd4, 0x6a, 0x01, 0x89, 0x3b,
++ 0x62, 0xed, 0x16, 0x46, 0x59, 0x0f, 0xf2, 0x3b, 0xb5, 0xde, 0x89,
++ 0x08, 0x17, 0x68, 0xcb, 0x46, 0xdc, 0x39, 0xa6, 0xcb, 0x56, 0xb0,
++ 0x91, 0xeb, 0x03, 0xb2, 0x15, 0xc4, 0x3b, 0x4d, 0x63, 0x55, 0x22,
++ 0x0a, 0x26, 0xe6, 0x64, 0x46, 0xe8, 0x0f, 0xa8, 0xf3, 0xde, 0xe1,
++ 0x43, 0x54, 0xe6, 0xd7, 0x8a, 0xf4, 0x4f, 0xab, 0x56, 0x93, 0x12,
++ 0x71, 0x4b, 0x25, 0x71, 0x0a, 0x31, 0x18, 0x79, 0xee, 0x45, 0xa4,
++ 0xf5, 0x72, 0x67, 0xfa, 0x41, 0xd9, 0x87, 0x97, 0x09, 0xef, 0x55,
++ 0xad, 0x6f, 0x47, 0x1d, 0x5a, 0xb2, 0xe9, 0xf7, 0x22, 0x05, 0x2d,
++ 0x5a, 0x81, 0xa8, 0xe8, 0x53, 0xb0, 0x94, 0xf6, 0x63, 0xff, 0x3f,
++ 0x51, 0x7a, 0x08, 0xac, 0x27, 0x9a, 0x57, 0x11, 0x22, 0xa4, 0x00,
++ 0x84, 0x70, 0x86, 0x76, 0x39, 0x0f, 0x4f, 0x57, 0xcf, 0x8e, 0x94,
++ 0xd2, 0x8e, 0x43, 0xc0, 0xd5, 0x34, 0x7d, 0xf5, 0xa1, 0x45, 0x1e,
++ 0xb7, 0xc8, 0x7e, 0x7c, 0xfe, 0x5d, 0x4d, 0x53, 0x43, 0x25, 0x15,
++ 0x9e, 0x08, 0x01, 0x56, 0xa4, 0xff, 0x79, 0x59, 0x25, 0xc9, 0x23,
++ 0x98, 0xaf, 0x05, 0xaf, 0xc1, 0x0b, 0x29, 0xf1, 0xe2, 0xc4, 0x36,
++ 0x31, 0x91, 0xfa, 0xf2, 0xbb, 0x12, 0xe8, 0x67, 0xf9, 0xc7, 0xa1,
++ 0x5e, 0x8c, 0xed, 0x92, 0x12, 0xa3, 0x2b, 0xe1, 0xc2, 0xe1, 0xa0,
++ 0xb0, 0x0e, 0x12, 0xa7, 0xd0, 0xa2, 0xae, 0xd6, 0xfa, 0x30, 0x21,
++ 0x0f, 0x73, 0xfe, 0x24, 0x21, 0x5f, 0x03, 0x86, 0x69, 0xcd, 0xec,
++ 0x76, 0x18, 0xe1, 0xfd, 0xb6, 0x64, 0x90, 0xa6, 0x06, 0x2e, 0x19,
++ 0x40, 0x93, 0x50, 0x37, 0xe4, 0x90, 0xe3, 0x1f, 0x07, 0xae, 0xfb,
++ 0x89, 0xc3, 0xf6, 0xc4, 0x90, 0xab, 0x40, 0x67, 0x4c, 0x43, 0x2c,
++ 0xa2, 0xb0, 0x3e, 0x61, 0x16, 0x69, 0x8f
++ };
++
++ /* CRL with critical obsolete extension OID 2.5.29.1, 422 bytes DER.
++ * OID 2.5.29.1 is the old X.509v2 Authority Key Identifier, permanently
++ * superseded by 2.5.29.35. No implementation will ever support it. */
++ static const unsigned char crl_obsolete_critical[] = {
++ 0x30, 0x82, 0x01, 0xa6, 0x30, 0x81, 0x8f, 0x02, 0x01, 0x01, 0x30,
++ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
++ 0x0b, 0x05, 0x00, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
++ 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2d,
++ 0x72, 0x6f, 0x6f, 0x74, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x34, 0x31,
++ 0x36, 0x31, 0x35, 0x32, 0x31, 0x30, 0x37, 0x5a, 0x17, 0x0d, 0x33,
++ 0x36, 0x30, 0x34, 0x31, 0x33, 0x31, 0x35, 0x32, 0x31, 0x30, 0x37,
++ 0x5a, 0xa0, 0x46, 0x30, 0x44, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
++ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x52, 0x97, 0x58, 0x47,
++ 0x98, 0xca, 0xf8, 0x99, 0xa0, 0x7e, 0x8e, 0x1c, 0x38, 0x2e, 0xea,
++ 0xbb, 0xea, 0x9b, 0x74, 0x30, 0x30, 0x14, 0x06, 0x03, 0x55, 0x1d,
++ 0x01, 0x01, 0x01, 0xff, 0x04, 0x0a, 0x0c, 0x08, 0x6f, 0x62, 0x73,
++ 0x6f, 0x6c, 0x65, 0x74, 0x65, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
++ 0x14, 0x04, 0x04, 0x02, 0x02, 0x20, 0x02, 0x30, 0x0d, 0x06, 0x09,
++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
++ 0x03, 0x82, 0x01, 0x01, 0x00, 0x05, 0xf3, 0x8f, 0xdb, 0x7f, 0x75,
++ 0x2c, 0x34, 0x4b, 0x7e, 0x70, 0x17, 0x5e, 0x34, 0xc6, 0xdb, 0xcb,
++ 0x54, 0x33, 0x06, 0x58, 0x6d, 0xae, 0x9c, 0xc8, 0xe3, 0xaf, 0x82,
++ 0xe5, 0xf6, 0x86, 0x42, 0xb3, 0x01, 0x72, 0x1a, 0xca, 0xf9, 0x10,
++ 0x5d, 0x14, 0xe6, 0x84, 0x34, 0x56, 0x55, 0x74, 0xb5, 0x06, 0x64,
++ 0x49, 0x1d, 0xb3, 0xb0, 0x13, 0xff, 0x1c, 0x05, 0x4f, 0x43, 0x29,
++ 0xbc, 0xfe, 0xb5, 0x92, 0x54, 0xf6, 0x9b, 0x81, 0x07, 0x5e, 0x2e,
++ 0x75, 0xd8, 0xfd, 0x9b, 0x5b, 0xc9, 0xd3, 0xc2, 0x15, 0xa7, 0x6e,
++ 0x2f, 0x4b, 0x3a, 0x27, 0x57, 0xef, 0x40, 0x61, 0x8c, 0x11, 0x9d,
++ 0x0a, 0xb1, 0x2b, 0x0e, 0xed, 0x5d, 0xf2, 0xf5, 0x1a, 0xce, 0xdc,
++ 0xd7, 0x75, 0xc6, 0x25, 0x22, 0xe4, 0x70, 0xad, 0x93, 0xff, 0x36,
++ 0xa1, 0xa2, 0xa0, 0xd9, 0x82, 0x23, 0x6e, 0xc8, 0x3a, 0x80, 0x82,
++ 0xbf, 0x12, 0xac, 0xa1, 0xf9, 0x03, 0x9c, 0xb9, 0x20, 0x91, 0x33,
++ 0x80, 0x7b, 0xb7, 0x6e, 0xa5, 0x32, 0x98, 0xd6, 0x2c, 0x5d, 0x9d,
++ 0x3b, 0x64, 0x3b, 0xb4, 0xea, 0x03, 0x2d, 0x65, 0xcf, 0x7f, 0x0f,
++ 0x97, 0xef, 0x5b, 0x17, 0x8c, 0xcf, 0x98, 0x69, 0xba, 0x2d, 0x62,
++ 0xe9, 0x40, 0xe2, 0x3d, 0xbd, 0xd2, 0x0f, 0x4a, 0xf8, 0xb0, 0xa7,
++ 0xdb, 0x80, 0xa3, 0x47, 0x56, 0xe5, 0xe6, 0x6f, 0x93, 0x5c, 0x6f,
++ 0xdd, 0x62, 0x43, 0x28, 0x5c, 0xe5, 0x8f, 0x0e, 0x11, 0xa6, 0x1f,
++ 0x61, 0xaf, 0x39, 0x15, 0x40, 0xf4, 0x6e, 0x79, 0x40, 0xf6, 0x28,
++ 0xf3, 0xd4, 0x30, 0x3b, 0x25, 0xb6, 0xf0, 0x4a, 0x51, 0xc3, 0x18,
++ 0xff, 0xad, 0x4d, 0x6e, 0x10, 0x73, 0x68, 0xfa, 0x54, 0x9e, 0xdc,
++ 0x34, 0x70, 0xe4, 0x5d, 0x9e, 0x7c, 0xfa, 0x59, 0x97, 0xde, 0x35,
++ 0x17, 0xbb, 0xaf, 0xa0, 0x28, 0x78, 0x13, 0xbf
++ };
++
++ WOLFSSL_CERT_MANAGER* cm = NULL;
++
++ ExpectNotNull(cm = wolfSSL_CertManagerNew());
++ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert,
++ sizeof(ca_cert), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL),
++ WOLFSSL_SUCCESS);
++
++ ExpectIntNE(wolfSSL_CertManagerLoadCRLBuffer(cm, crl_obsolete_critical,
++ sizeof(crl_obsolete_critical), WOLFSSL_FILETYPE_ASN1),
++ WOLFSSL_SUCCESS);
++
++ wolfSSL_CertManagerFree(cm);
++#endif
++ return EXPECT_RESULT();
++}
++
+ int test_wolfSSL_CertManagerCheckOCSPResponse(void)
+ {
+ EXPECT_DECLS;
+diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h
+index a0b5d9548..3b6afd0fc 100644
+--- a/tests/api/test_certman.h
++++ b/tests/api/test_certman.h
+@@ -39,6 +39,8 @@ int test_wolfSSL_CertManagerCRL(void);
+ int test_wolfSSL_CRL_reason_extensions_cleanup(void);
+ int test_wolfSSL_CRL_static_revoked_list(void);
+ int test_wolfSSL_CRL_duplicate_extensions(void);
++int test_wolfSSL_CRL_critical_idp(void);
++int test_wolfSSL_CRL_unknown_critical_ext(void);
+ int test_wolfSSL_CertManagerCheckOCSPResponse(void);
+ int test_various_pathlen_chains(void);
+ int test_wolfSSL_CertManagerRejectMD5Cert(void);
+@@ -59,6 +61,8 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void);
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_reason_extensions_cleanup), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \
++ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_critical_idp), \
++ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_ext), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
+ TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert)
+diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c
+index 8d4f40a70..f8db5c457 100644
+--- a/wolfcrypt/src/asn.c
++++ b/wolfcrypt/src/asn.c
+@@ -34497,14 +34497,17 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf, word32 idx,
+ mp_free(m);
+ FREE_MP_INT_SIZE(m, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
++ else if (critical) {
++ WOLFSSL_MSG("Unknown critical CRL extension");
++ ret = ASN_CRIT_EXT_E;
++ }
+ }
+- /* TODO: check criticality */
+ /* Move index on to next extension. */
+ idx += (word32)length;
+ }
+ }
+
+- if (ret < 0) {
++ if (ret < 0 && ret != WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
+ ret = ASN_PARSE_E;
+ }
+
+diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c
+index 9ecb821d3..d6568aa5d 100644
+--- a/wolfcrypt/src/asn_orig.c
++++ b/wolfcrypt/src/asn_orig.c
+@@ -9327,6 +9327,7 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf,
+ while (idx < (word32)ext_bound) {
+ word32 localIdx;
+ int ret;
++ int critical = 0;
+
+ if (GetSequence(buf, &idx, &length, sz) < 0) {
+ WOLFSSL_MSG("\tfail: should be a SEQUENCE");
+@@ -9346,11 +9347,13 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf,
+ }
+
+ localIdx = idx;
+- if (GetASNTag(buf, &localIdx, &tag, sz) == 0 && tag == ASN_BOOLEAN) {
++ if (GetASNTag(buf, &localIdx, &tag, sz) == 0 &&
++ tag == ASN_BOOLEAN) {
+ WOLFSSL_MSG("\tfound optional critical flag, moving past");
+ ret = GetBoolean(buf, &idx, sz);
+ if (ret < 0)
+ return ret;
++ critical = ret;
+ }
+
+ ret = GetOctetString(buf, &idx, &length, sz);
+@@ -9428,6 +9431,10 @@ static int ParseCRL_Extensions(DecodedCRL* dcrl, const byte* buf,
+ }
+ }
+ }
++ else if (critical) {
++ WOLFSSL_MSG("Unknown critical CRL extension");
++ return ASN_CRIT_EXT_E;
++ }
+
+ idx += length;
+ }
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-2.patch
new file mode 100644
index 0000000000..0118a8d1ab
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6450-2.patch
@@ -0,0 +1,404 @@
+From 501762b4913f744f42605f8cdb3e3ac6dd37916e Mon Sep 17 00:00:00 2001
+From: Reda Chouk <reda@wolfssl.com>
+Date: Tue, 21 Apr 2026 16:53:59 +0200
+Subject: [PATCH] reject crls with unrecognized critical entry extensions per
+ rfc 5280 section 5.3
+
+(cherry picked from commit 6111c60ea69fa02e1a74cf432c20bd6b789eae67)
+
+CVE: CVE-2026-6450
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/6111c60ea69fa02e1a74cf432c20bd6b789eae67]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ certs/crl/extra-crls/claim-root.pem | 19 +++
+ certs/crl/extra-crls/crl_critical_entry.pem | 11 ++
+ certs/crl/include.am | 4 +-
+ tests/api/test_certman.c | 25 ++++
+ tests/api/test_certman.h | 2 +
+ wolfcrypt/src/asn.c | 133 +++++++++++++-------
+ wolfcrypt/src/asn_orig.c | 28 ++++-
+ 7 files changed, 171 insertions(+), 51 deletions(-)
+ create mode 100644 certs/crl/extra-crls/claim-root.pem
+ create mode 100644 certs/crl/extra-crls/crl_critical_entry.pem
+
+diff --git a/certs/crl/extra-crls/claim-root.pem b/certs/crl/extra-crls/claim-root.pem
+new file mode 100644
+index 000000000..a212501aa
+--- /dev/null
++++ b/certs/crl/extra-crls/claim-root.pem
+@@ -0,0 +1,19 @@
++-----BEGIN CERTIFICATE-----
++MIIDGzCCAgOgAwIBAgIUHiXBXW8CIaDwFBWcO00dcxYA5FEwDQYJKoZIhvcNAQEL
++BQAwFTETMBEGA1UEAwwKY2xhaW0tcm9vdDAeFw0yNjA0MTYxMTM4NTVaFw0zNjA0
++MTMxMTM4NTVaMBUxEzARBgNVBAMMCmNsYWltLXJvb3QwggEiMA0GCSqGSIb3DQEB
++AQUAA4IBDwAwggEKAoIBAQC6SYy1F8EBJG9WGqk7A+KfJLElmPs4gnhUpx9ph+SW
++G4EYELDAW0u/uB307nUPtUVycM5lhEQ+MHjE8+y6lnikZfxijfUp+Xw9eGwdSkzJ
++FS0iEOqTJrimF9MOvAyrg2P2HMyDcyl+f4N/vWOqjfp4hdI+YJVajfqPzZQ/EyjZ
++0IcoF3jiYY15lwGpfITAHL5fXcooa17dg6VVNBG6+ouSo96287qrfxpn/W8ghUx3
++p46+uPiPcONa03fJnhBgtNsMxQXhH73mee6CP1F24n9cEW3TIWnsBRGLyDmzLKaD
++tG+sGdZqZQ0IlFjePckMVANzDI0kCfOxXdLj61bWKGZbAgMBAAGjYzBhMA8GA1Ud
++EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRSl1hHmMr4maB+
++jhw4Luq76pt0MDAfBgNVHSMEGDAWgBRSl1hHmMr4maB+jhw4Luq76pt0MDANBgkq
++hkiG9w0BAQsFAAOCAQEAfTDUagGJO2LtFkZZD/I7td6JCBdoy0bcOabLVrCR6wOy
++FcQ7TWNVIgom5mRG6A+o897hQ1Tm14r0T6tWkxJxSyVxCjEYee5FpPVyZ/pB2YeX
++Ce9VrW9HHVqy6fciBS1agajoU7CU9mP/P1F6CKwnmlcRIqQAhHCGdjkPT1fPjpTS
++jkPA1TR99aFFHrfIfnz+XU1TQyUVnggBVqT/eVklySOYrwWvwQsp8eLENjGR+vK7
++Euhn+cehXoztkhKjK+HC4aCwDhKn0KKu1vowIQ9z/iQhXwOGac3sdhjh/bZkkKYG
++LhlAk1A35JDjHweu+4nD9sSQq0BnTEMsorA+YRZpjw==
++-----END CERTIFICATE-----
+diff --git a/certs/crl/extra-crls/crl_critical_entry.pem b/certs/crl/extra-crls/crl_critical_entry.pem
+new file mode 100644
+index 000000000..11d9d31ab
+--- /dev/null
++++ b/certs/crl/extra-crls/crl_critical_entry.pem
+@@ -0,0 +1,11 @@
++-----BEGIN X509 CRL-----
++MIIBjDB2AgEBMA0GCSqGSIb3DQEBCwUAMBUxEzARBgNVBAMMCmNsYWltLXJvb3QX
++DTI2MDQyMTEyMzM0OFoXDTM2MDQxODEyMzM0OFowLTArAgIQABcNMjYwNDIxMTIz
++MzQ4WjAWMBQGA1UdAQEB/wQKDAhvYnNvbGV0ZTANBgkqhkiG9w0BAQsFAAOCAQEA
++PPG5bodrM2jK+6KcqRh9vEkWhLyxCkJij1om7R8BMO5bpvxZFOSqdN9GvIqYANYT
++ZNQzZZOer9DSXc1I9Cha179dyGnBsX33geSdhF99JxA/kZUVynfOn56El7iywkPQ
++yEpuY7uQQRfcp7D7tGz8S7mNhOMAeaN+jR2Psmn8q1Yc3W01vwYZos3qjHG1xDZ6
++97QVFX6tbnbeu/hBsF87oiAyCbXnhRQOvrVsKqjrZXGkfRKfJ1deHc1vWgio7fhr
++A1/Hb4sdfTWZUwtduTRI9Vyaw1IL41d+0ExdzTshIHbddB/dyByrRqlIzJdeiigt
++d1Gcf1EYSoWiV8Xaj9SFng==
++-----END X509 CRL-----
+diff --git a/certs/crl/include.am b/certs/crl/include.am
+index 46d0ed3e7..a0c526d91 100644
+--- a/certs/crl/include.am
++++ b/certs/crl/include.am
+@@ -27,7 +27,9 @@ EXTRA_DIST += \
+ certs/crl/extra-crls/large_crlnum.pem \
+ certs/crl/extra-crls/large_crlnum2.pem \
+ certs/crl/extra-crls/crlnum_57oct.pem \
+- certs/crl/extra-crls/crlnum_64oct.pem
++ certs/crl/extra-crls/crlnum_64oct.pem \
++ certs/crl/extra-crls/claim-root.pem \
++ certs/crl/extra-crls/crl_critical_entry.pem
+
+ # Intermediate cert CRL's
+ EXTRA_DIST += \
+diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c
+index 7405f4bff..d9000c7cf 100644
+--- a/tests/api/test_certman.c
++++ b/tests/api/test_certman.c
+@@ -2306,6 +2306,31 @@ int test_wolfSSL_CRL_unknown_critical_ext(void)
+ return EXPECT_RESULT();
+ }
+
++int test_wolfSSL_CRL_unknown_critical_entry_ext(void)
++{
++ EXPECT_DECLS;
++#if !defined(NO_CERTS) && defined(HAVE_CRL) && !defined(NO_RSA) && \
++ !defined(NO_FILESYSTEM)
++ WOLFSSL_CERT_MANAGER* cm = NULL;
++
++ ExpectNotNull(cm = wolfSSL_CertManagerNew());
++ ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm,
++ "./certs/crl/extra-crls/claim-root.pem", NULL), WOLFSSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECKALL),
++ WOLFSSL_SUCCESS);
++
++ /* CRL with a revoked entry that carries a critical unknown extension
++ * (OID 2.5.29.1, old X.509v2 AKI, permanently superseded).
++ * Per RFC 5280 Section 5.3, the CRL must not be used. */
++ ExpectIntNE(wolfSSL_CertManagerLoadCRLFile(cm,
++ "./certs/crl/extra-crls/crl_critical_entry.pem", WOLFSSL_FILETYPE_PEM),
++ WOLFSSL_SUCCESS);
++
++ wolfSSL_CertManagerFree(cm);
++#endif
++ return EXPECT_RESULT();
++}
++
+ int test_wolfSSL_CertManagerCheckOCSPResponse(void)
+ {
+ EXPECT_DECLS;
+diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h
+index 3b6afd0fc..0962b0228 100644
+--- a/tests/api/test_certman.h
++++ b/tests/api/test_certman.h
+@@ -41,6 +41,7 @@ int test_wolfSSL_CRL_static_revoked_list(void);
+ int test_wolfSSL_CRL_duplicate_extensions(void);
+ int test_wolfSSL_CRL_critical_idp(void);
+ int test_wolfSSL_CRL_unknown_critical_ext(void);
++int test_wolfSSL_CRL_unknown_critical_entry_ext(void);
+ int test_wolfSSL_CertManagerCheckOCSPResponse(void);
+ int test_various_pathlen_chains(void);
+ int test_wolfSSL_CertManagerRejectMD5Cert(void);
+@@ -63,6 +64,7 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void);
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_duplicate_extensions), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_critical_idp), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_ext), \
++ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_unknown_critical_entry_ext), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
+ TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert)
+diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c
+index f8db5c457..1dc27cfd0 100644
+--- a/wolfcrypt/src/asn.c
++++ b/wolfcrypt/src/asn.c
+@@ -34039,16 +34039,22 @@ enum {
+ /* CRL Reason Code OID: 2.5.29.21 */
+ static const byte crlReasonOid[] = { 0x55, 0x1d, 0x15 };
+
+-/* Parse CRL entry extensions to extract the reason code.
+- * Sets *reasonCode if found, otherwise leaves it unchanged. */
+-static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
+- int* reasonCode)
++/* Parse CRL entry extensions.
++ * Extracts the reason code into *reasonCode if the CRL Reason extension
++ * is present. Per RFC 5280 Section 5.3, returns ASN_CRIT_EXT_E if any
++ * unknown extension is marked critical. Returns 0 on success. */
++static int ParseCRL_EntryExtensions(const byte* buff, word32 idx, word32 maxIdx,
++ int* reasonCode)
+ {
+ while (idx < maxIdx) {
+ int len;
++ int oidLen;
+ word32 end;
+ word32 localIdx;
++ word32 oidContent;
+ byte tag;
++ int critical = 0;
++ int isReasonOid = 0;
+
+ /* Each extension is a SEQUENCE */
+ if (GetSequence(buff, &idx, &len, maxIdx) < 0) {
+@@ -34056,23 +34062,39 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
+ }
+ end = idx + (word32)len;
+
+- /* Check for CRL Reason OID: 2.5.29.21 */
+- if (end - idx >= (word32)(2 + sizeof(crlReasonOid)) &&
+- buff[idx] == ASN_OBJECT_ID &&
+- buff[idx + 1] == sizeof(crlReasonOid) &&
+- XMEMCMP(buff + idx + 2, crlReasonOid,
++ /* Parse OID: tag, length (short or long form), content */
++ if (GetASNTag(buff, &idx, &tag, end) < 0 ||
++ tag != ASN_OBJECT_ID) {
++ break;
++ }
++ if (GetLength(buff, &idx, &oidLen, end) < 0) {
++ break;
++ }
++ oidContent = idx;
++ if (idx + (word32)oidLen > end) {
++ break;
++ }
++
++ /* Check if it's the CRL Reason OID: 2.5.29.21 */
++ if ((word32)oidLen == sizeof(crlReasonOid) &&
++ XMEMCMP(buff + oidContent, crlReasonOid,
+ sizeof(crlReasonOid)) == 0) {
+- /* Skip past the OID */
+- idx += 2 + (word32)sizeof(crlReasonOid);
+- /* Skip optional critical BOOLEAN */
+- localIdx = idx;
+- if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
+- tag == ASN_BOOLEAN) {
+- /* Consume full BOOLEAN TLV (tag + length + value). */
+- if (GetBoolean(buff, &idx, end) < 0) {
+- break;
+- }
++ isReasonOid = 1;
++ }
++ idx = oidContent + (word32)oidLen;
++
++ /* Parse optional critical BOOLEAN */
++ localIdx = idx;
++ if (GetASNTag(buff, &localIdx, &tag, end) == 0 &&
++ tag == ASN_BOOLEAN) {
++ int ret = GetBoolean(buff, &idx, end);
++ if (ret < 0) {
++ break;
+ }
++ critical = ret;
++ }
++
++ if (isReasonOid) {
+ /* Get OCTET STRING wrapping the ENUMERATED */
+ if (GetOctetString(buff, &idx, &len, end) >= 0) {
+ /* Parse ENUMERATED reason value */
+@@ -34088,8 +34110,15 @@ static void ParseCRL_ReasonCode(const byte* buff, word32 idx, word32 maxIdx,
+ }
+ }
+ }
++ else if (critical) {
++ /* RFC 5280 Section 5.3: reject CRL with unknown critical
++ * entry extension. */
++ WOLFSSL_MSG("Unknown critical CRL entry extension");
++ return ASN_CRIT_EXT_E;
++ }
+ idx = end;
+ }
++ return 0;
+ }
+
+ #ifdef HAVE_CRL
+@@ -34102,8 +34131,7 @@ WOLFSSL_TEST_VIS int wc_ParseCRLReasonFromExtensions(const byte* ext,
+ return BAD_FUNC_ARG;
+ }
+
+- ParseCRL_ReasonCode(ext, 0, extSz, reasonCode);
+- return 0;
++ return ParseCRL_EntryExtensions(ext, 0, extSz, reasonCode);
+ }
+ #endif
+
+@@ -34166,49 +34194,58 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
+ /* Parse CRL entry extensions (v2 only) */
+ if (dataASN[REVOKEDASN_IDX_TIME_EXT].length > 0) {
+ word32 extOff = dataASN[REVOKEDASN_IDX_TIME_EXT].offset;
+- word32 extLen = dataASN[REVOKEDASN_IDX_TIME_EXT].length;
+- word32 extEnd = extOff + extLen;
+- word32 extIdx2 = extOff;
++ word32 extTagEnd = extOff +
++ dataASN[REVOKEDASN_IDX_TIME_EXT].length + 6;
++ int extLen;
++
++ /* .offset points at the outer SEQUENCE tag. Re-parse the
++ * SEQUENCE header to locate the content start (list of
++ * Extension SEQUENCEs), which handles long-form length.
++ * extTagEnd adds 6 to cover the worst-case tag+long-form-length
++ * header for the outer SEQUENCE. */
++ if (GetSequence(buff, &extOff, &extLen, extTagEnd) < 0) {
++ ret = ASN_PARSE_E;
++ }
++ else {
++ word32 extEnd = extOff + (word32)extLen;
+
+ #if defined(OPENSSL_EXTRA)
+- /* Store raw DER of extensions for OpenSSL compat API.
+- * Include the outer SEQUENCE tag+length. */
+- {
+- /* Back up to include the SEQUENCE header. We know the
+- * content starts at extOff, so the header is just before.
+- * Use the raw buffer start from before GetASN_Items. */
+- word32 seqHdrSz = 0;
+- /* The outer SEQUENCE header is at most 4 bytes before
+- * content. Rather than guess, store just the content. */
+- rc->extensions = (byte*)XMALLOC(extLen, dcrl->heap,
++ /* Store raw DER of extension contents for OpenSSL compat. */
++ rc->extensions = (byte*)XMALLOC((size_t)extLen, dcrl->heap,
+ DYNAMIC_TYPE_REVOKED);
+ if (rc->extensions != NULL) {
+- XMEMCPY(rc->extensions, buff + extOff, extLen);
+- rc->extensionsSz = extLen;
++ XMEMCPY(rc->extensions, buff + extOff, (size_t)extLen);
++ rc->extensionsSz = (word32)extLen;
+ }
+- (void)seqHdrSz;
+- }
+ #endif
+
+- ParseCRL_ReasonCode(buff, extIdx2, extEnd, &rc->reasonCode);
++ ret = ParseCRL_EntryExtensions(buff, extOff, extEnd,
++ &rc->reasonCode);
++ }
+ }
+
+- /* Add revoked certificate to chain. */
++ if (ret == 0) {
++ /* Add revoked certificate to chain. */
+ #ifndef CRL_STATIC_REVOKED_LIST
+- rc->next = dcrl->certs;
+- dcrl->certs = rc;
++ rc->next = dcrl->certs;
++ dcrl->certs = rc;
+ #endif
+- dcrl->totalCerts++;
++ dcrl->totalCerts++;
++ }
+ }
+
+ FREE_ASNGETDATA(dataASN, dcrl->heap);
+-#ifndef CRL_STATIC_REVOKED_LIST
+ if ((ret != 0) && (rc != NULL)) {
+ #if defined(OPENSSL_EXTRA)
+ XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
++ rc->extensions = NULL;
++ rc->extensionsSz = 0;
+ #endif
++#ifndef CRL_STATIC_REVOKED_LIST
+ XFREE(rc, dcrl->heap, DYNAMIC_TYPE_CRL);
++#endif
+ }
++#ifndef CRL_STATIC_REVOKED_LIST
+ (void)rcert;
+ #endif
+ return ret;
+@@ -34232,7 +34269,13 @@ static int ParseCRL_RevokedCerts(RevokedCert* rcert, DecodedCRL* dcrl,
+ /* Parse each revoked certificate. */
+ while ((ret == 0) && (idx < maxIdx)) {
+ /* Parse a revoked certificate. */
+- if (GetRevoked(rcert, buff, &idx, dcrl, maxIdx) < 0) {
++ int r = GetRevoked(rcert, buff, &idx, dcrl, maxIdx);
++ if (r == WC_NO_ERR_TRACE(ASN_CRIT_EXT_E)) {
++ /* Preserve the specific error so callers can distinguish a
++ * rejected critical extension from a generic parse failure. */
++ ret = r;
++ }
++ else if (r < 0) {
+ ret = ASN_PARSE_E;
+ }
+ }
+diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c
+index d6568aa5d..bb58eb6d4 100644
+--- a/wolfcrypt/src/asn_orig.c
++++ b/wolfcrypt/src/asn_orig.c
+@@ -9061,18 +9061,17 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
+ XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
+ return ret;
+ }
+- /* add to list */
+- rc->next = dcrl->certs;
+- dcrl->certs = rc;
+
+ (void)rcert;
+ #endif /* CRL_STATIC_REVOKED_LIST */
+- dcrl->totalCerts++;
+ /* get date */
+ #ifndef NO_ASN_TIME
+ ret = GetBasicDate(buff, idx, rc->revDate, &rc->revDateFormat, maxIdx);
+ if (ret < 0) {
+ WOLFSSL_MSG("Expecting Date");
++#ifndef CRL_STATIC_REVOKED_LIST
++ XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
++#endif
+ return ret;
+ }
+ #endif
+@@ -9106,11 +9105,30 @@ static int GetRevoked(RevokedCert* rcert, const byte* buff, word32* idx,
+ }
+ #endif
+
+- ParseCRL_ReasonCode(buff, seqIdx, extEnd, &rc->reasonCode);
++ ret = ParseCRL_EntryExtensions(buff, seqIdx, extEnd,
++ &rc->reasonCode);
++ if (ret != 0) {
++#if defined(OPENSSL_EXTRA)
++ XFREE(rc->extensions, dcrl->heap, DYNAMIC_TYPE_REVOKED);
++ rc->extensions = NULL;
++ rc->extensionsSz = 0;
++#endif
++#ifndef CRL_STATIC_REVOKED_LIST
++ XFREE(rc, dcrl->heap, DYNAMIC_TYPE_REVOKED);
++#endif
++ return ret;
++ }
+ }
+ }
+ }
+
++#ifndef CRL_STATIC_REVOKED_LIST
++ /* add to list only after all parsing succeeded */
++ rc->next = dcrl->certs;
++ dcrl->certs = rc;
++#endif
++ dcrl->totalCerts++;
++
+ *idx = end;
+
+ return 0;
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 4ca330b029..26b86c1b2b 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -32,6 +32,8 @@ SRC_URI = " \
file://CVE-2026-6325.patch \
file://CVE-2026-6412-1.patch \
file://CVE-2026-6412-2.patch \
+ file://CVE-2026-6450-1.patch \
+ file://CVE-2026-6450-2.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 32/33] wolfssl: patch CVE-2026-6731
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (29 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 31/33] wolfssl: patch CVE-2026-6450 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 33/33] wolfssl: patch CVE-2026-7531 ankur.tyagi85
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-6731
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-6731-1.patch | 39 ++++
.../wolfssl/files/CVE-2026-6731-2.patch | 171 ++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 2 +
3 files changed, 212 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-1.patch
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-2.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-1.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-1.patch
new file mode 100644
index 0000000000..c6a1762b27
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-1.patch
@@ -0,0 +1,39 @@
+From df2df57a027066708b498f41ce0b591704b579cc Mon Sep 17 00:00:00 2001
+From: Ruby Martin <ruby@wolfssl.com>
+Date: Tue, 14 Apr 2026 12:39:34 -0600
+Subject: [PATCH] Apply DNS constraints to subject CN when SAN is not
+ available.
+
+(cherry picked from commit e7b7fddacb4cc794e5dfc7693586d87a539f5aad)
+
+CVE: CVE-2026-6731
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/e7b7fddacb4cc794e5dfc7693586d87a539f5aad]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ wolfcrypt/src/asn.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c
+index f8db5c457..d12a78850 100644
+--- a/wolfcrypt/src/asn.c
++++ b/wolfcrypt/src/asn.c
+@@ -17665,9 +17665,16 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
+ XMEMSET(&subjectDnsName, 0, sizeof(DNS_entry));
+ switch (nameType) {
+ case ASN_DNS_TYPE:
+- /* Should it also consider CN in subject? It could use
+- * subjectDnsName too */
+ name = cert->altNames;
++
++ /* When no SAN is present, apply DNS name constraints to the
++ * Subject CN. */
++ if (cert->subjectCN != NULL && cert->altNames == NULL) {
++ subjectDnsName.next = NULL;
++ subjectDnsName.type = ASN_DNS_TYPE;
++ subjectDnsName.len = cert->subjectCNLen;
++ subjectDnsName.name = cert->subjectCN;
++ }
+ break;
+ case ASN_IP_TYPE:
+ /* IP addresses are stored in altNames with type ASN_IP_TYPE */
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-2.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-2.patch
new file mode 100644
index 0000000000..be5132048e
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-6731-2.patch
@@ -0,0 +1,171 @@
+From 9d1ee979f67c8e31a04b73fadac61f4697bcb7c6 Mon Sep 17 00:00:00 2001
+From: Ruby Martin <ruby@wolfssl.com>
+Date: Tue, 14 Apr 2026 12:44:21 -0600
+Subject: [PATCH] test DNS name constraints on CA are applied against Subject
+ CN name when SAN name is unavailable
+
+test correct CN with no SAN available is accepted
+
+(cherry picked from commit 797ba3f03b1a8dc05c7b91a86c2e6698d76bfc8a)
+
+CVE: CVE-2026-6731
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/797ba3f03b1a8dc05c7b91a86c2e6698d76bfc8a]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ tests/api/test_certman.c | 121 +++++++++++++++++++++++++++++++++++++++
+ tests/api/test_certman.h | 2 +
+ 2 files changed, 123 insertions(+)
+
+diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c
+index 7405f4bff..c76902af2 100644
+--- a/tests/api/test_certman.c
++++ b/tests/api/test_certman.c
+@@ -1584,6 +1584,127 @@ int test_wolfSSL_CertManagerNameConstraint5(void)
+ return EXPECT_RESULT();
+ }
+
++int test_wolfSSL_CertManagerNameConstraint_DNS_CN(void)
++{
++ EXPECT_DECLS;
++#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
++ !defined(NO_WOLFSSL_CM_VERIFY) && !defined(NO_RSA) && \
++ defined(OPENSSL_EXTRA) && defined(WOLFSSL_CERT_GEN) && \
++ defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_ALT_NAMES) && \
++ !defined(NO_SHA256)
++ /* Test that DNS name constraints are enforced against the Subject CN
++ * when no SAN extension is present. The CA cert (cert-ext-ncdns.der)
++ * permits only DNS:wolfssl.com and DNS:example.com. A leaf cert with
++ * CN=evil.attacker.com and no SAN should be REJECTED. */
++ WOLFSSL_CERT_MANAGER* cm = NULL;
++ WOLFSSL_EVP_PKEY *priv = NULL;
++ WOLFSSL_X509_NAME* name = NULL;
++ const char* ca_cert = "./certs/test/cert-ext-ncdns.der";
++ const char* server_cert = "./certs/test/server-goodcn.pem";
++
++ byte *der = NULL;
++ int derSz;
++ byte *pt;
++ WOLFSSL_X509 *x509 = NULL;
++ WOLFSSL_X509 *ca = NULL;
++
++ pt = (byte*)server_key_der_2048;
++ ExpectNotNull(priv = wolfSSL_d2i_PrivateKey(EVP_PKEY_RSA, NULL,
++ (const unsigned char**)&pt, sizeof_server_key_der_2048));
++
++ ExpectNotNull(cm = wolfSSL_CertManagerNew());
++ ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(ca_cert,
++ WOLFSSL_FILETYPE_ASN1));
++ ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(ca, &derSz)));
++ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++
++ /* Sanity check: cert with SAN=evil.attacker.com is correctly rejected */
++ ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
++ WOLFSSL_FILETYPE_PEM));
++ ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
++ ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
++ name = NULL;
++
++ ExpectNotNull(name = X509_NAME_new());
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
++ (byte*)"US", 2, -1, 0), SSL_SUCCESS);
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
++ (byte*)"evil.attacker.com", 17, -1, 0),
++ SSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
++ X509_NAME_free(name);
++ name = NULL;
++
++ ExpectIntEQ(wolfSSL_X509_add_altname(x509, "evil.attacker.com",
++ ASN_DNS_TYPE), WOLFSSL_SUCCESS);
++ ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
++ ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
++ ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
++ wolfSSL_X509_free(x509);
++ x509 = NULL;
++
++ /* NOW the actual vulnerability test: cert with CN=evil.attacker.com
++ * but NO SAN. The DNS name constraint should still reject this, since
++ * wolfSSL's hostname verification falls back to CN when no SAN exists. */
++ ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
++ WOLFSSL_FILETYPE_PEM));
++ ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
++ ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
++ name = NULL;
++
++ ExpectNotNull(name = X509_NAME_new());
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
++ (byte*)"US", 2, -1, 0), SSL_SUCCESS);
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
++ (byte*)"evil.attacker.com", 17, -1, 0),
++ SSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
++ X509_NAME_free(name);
++ name = NULL;
++
++ /* Do NOT add any SAN this is the bypass vector */
++ ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
++ ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
++ /* Should be ASN_NAME_INVALID_E because CN violates the constraint */
++ ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
++ wolfSSL_X509_free(x509);
++ x509 = NULL;
++
++ /* Positive test: CN matches a permitted name (wolfssl.com) and no SAN is
++ * present. The CN fallback should accept this cert. */
++ ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
++ WOLFSSL_FILETYPE_PEM));
++ ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
++ ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
++ name = NULL;
++
++ ExpectNotNull(name = X509_NAME_new());
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
++ (byte*)"US", 2, -1, 0), SSL_SUCCESS);
++ ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
++ (byte*)"wolfssl.com", 11, -1, 0),
++ SSL_SUCCESS);
++ ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
++ X509_NAME_free(name);
++ name = NULL;
++
++ /* No SAN added; CN=wolfssl.com matches the permitted DNS constraint. */
++ ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
++ ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
++ ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
++ WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
++
++ wolfSSL_CertManagerFree(cm);
++ wolfSSL_X509_free(x509);
++ wolfSSL_X509_free(ca);
++ wolfSSL_EVP_PKEY_free(priv);
++#endif
++ return EXPECT_RESULT();
++}
++
+ int test_wolfSSL_CertManagerCRL(void)
+ {
+ EXPECT_DECLS;
+diff --git a/tests/api/test_certman.h b/tests/api/test_certman.h
+index 3b6afd0fc..60047cfa3 100644
+--- a/tests/api/test_certman.h
++++ b/tests/api/test_certman.h
+@@ -35,6 +35,7 @@ int test_wolfSSL_CertManagerNameConstraint2(void);
+ int test_wolfSSL_CertManagerNameConstraint3(void);
+ int test_wolfSSL_CertManagerNameConstraint4(void);
+ int test_wolfSSL_CertManagerNameConstraint5(void);
++int test_wolfSSL_CertManagerNameConstraint_DNS_CN(void);
+ int test_wolfSSL_CertManagerCRL(void);
+ int test_wolfSSL_CRL_reason_extensions_cleanup(void);
+ int test_wolfSSL_CRL_static_revoked_list(void);
+@@ -57,6 +58,7 @@ int test_wolfSSL_CertManagerRejectMD5Cert(void);
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint3), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint4), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint5), \
++ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint_DNS_CN), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCRL), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_reason_extensions_cleanup), \
+ TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index 26b86c1b2b..ef03d0c9ff 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -34,6 +34,8 @@ SRC_URI = " \
file://CVE-2026-6412-2.patch \
file://CVE-2026-6450-1.patch \
file://CVE-2026-6450-2.patch \
+ file://CVE-2026-6731-1.patch \
+ file://CVE-2026-6731-2.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread* [oe][meta-networking][wrynose][PATCH 33/33] wolfssl: patch CVE-2026-7531
2026-09-07 10:22 [oe][meta-oe][wrynose][PATCH 1/33] libnfs: patch CVE-2026-53689 ankur.tyagi85
` (30 preceding siblings ...)
2026-09-07 10:23 ` [oe][meta-networking][wrynose][PATCH 32/33] wolfssl: patch CVE-2026-6731 ankur.tyagi85
@ 2026-09-07 10:23 ` ankur.tyagi85
31 siblings, 0 replies; 37+ messages in thread
From: ankur.tyagi85 @ 2026-09-07 10:23 UTC (permalink / raw)
To: openembedded-devel; +Cc: Ankur Tyagi
From: Ankur Tyagi <ankur.tyagi85@gmail.com>
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-7531
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../wolfssl/files/CVE-2026-7531.patch | 180 ++++++++++++++++++
.../wolfssl/wolfssl_5.9.1.bb | 1 +
2 files changed, 181 insertions(+)
create mode 100644 meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-7531.patch
diff --git a/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-7531.patch b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-7531.patch
new file mode 100644
index 0000000000..1d4e7605b3
--- /dev/null
+++ b/meta-networking/recipes-connectivity/wolfssl/files/CVE-2026-7531.patch
@@ -0,0 +1,180 @@
+From 5ab994ddce690cc904554ff03af263ef0624d29d Mon Sep 17 00:00:00 2001
+From: David Garske <david@wolfssl.com>
+Date: Tue, 5 May 2026 11:41:43 -0700
+Subject: [PATCH] Merge pull request #10327 from embhorn/zd21704
+
+Hardening in TLSX_KeyShare_ProcessPqcHybridClient
+
+CVE: CVE-2026-7531
+Upstream-Status: Backport [https://github.com/wolfSSL/wolfssl/commit/3b7ac9fd256b26c33b66f9315c319465ba3bcfeb]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ src/tls.c | 19 +++++---
+ tests/api/test_tls13.c | 98 ++++++++++++++++++++++++++++++++++++++++++
+ tests/api/test_tls13.h | 2 +
+ 3 files changed, 114 insertions(+), 5 deletions(-)
+
+diff --git a/src/tls.c b/src/tls.c
+index 18aad71d3..e5b989482 100644
+--- a/src/tls.c
++++ b/src/tls.c
+@@ -10187,15 +10187,24 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl,
+ ecc_kse->key = NULL;
+ pqc_kse->privKey = NULL;
+ }
++ else
+ #endif
++ {
++ /* Re-sync keyShareEntry->key with ecc_kse->key. ecc_kse->key was
++ * aliased to keyShareEntry->key above. The inner Process*_ex
++ * either ran its end-of-function cleanup and set ecc_kse->key
++ * to NULL (so the outer pointer must also become NULL to avoid
++ * UAF/double-free in TLSX_KeyShare_FreeAll), or returned early
++ * before cleanup with ecc_kse->key still pointing at the live
++ * key (so the outer pointer must keep that pointer for later
++ * freeing). Mirroring whatever the inner left in ecc_kse->key
++ * handles both cases correctly. */
++ keyShareEntry->key = ecc_kse->key;
++ }
+ }
+
+ if (ret == 0) {
+- keyShareEntry->key = ecc_kse->key;
+- ecc_kse->key = NULL;
+-
+- if ((ret == 0) &&
+- ((ssl->arrays->preMasterSz + ssSzPqc) > ENCRYPT_LEN)) {
++ if ((ssl->arrays->preMasterSz + ssSzPqc) > ENCRYPT_LEN) {
+ WOLFSSL_MSG("shared secret is too long.");
+ ret = LENGTH_ERROR;
+ }
+diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c
+index 63ddffb7a..af050a234 100644
+--- a/tests/api/test_tls13.c
++++ b/tests/api/test_tls13.c
+@@ -3581,6 +3581,104 @@ int test_tls13_pqc_hybrid_truncated_keyshare(void)
+ return EXPECT_RESULT();
+ }
+
++/* Test that a malformed ECDH portion in a correctly-sized PQC hybrid
++ * KeyShare does not leave a dangling pointer in keyShareEntry->key.
++ *
++ * The earlier truncated-keyshare test is rejected by the keLen <= ctSz
++ * check before TLSX_KeyShare_ProcessPqcHybridClient sets up the
++ * ecc_kse->key = keyShareEntry->key alias, so it does not exercise the
++ * dangling-pointer path. This test sends a SECP256R1MLKEM768 key_share
++ * whose total length is correct (65-byte ECDH point + 1088-byte ML-KEM
++ * ciphertext = 1153 bytes) but whose ECDH leading byte (0x05) is not a
++ * valid X9.63 marker. ProcessEcc_ex then fails at wc_ecc_import_x963
++ * AFTER its unconditional cleanup at the end of the function frees the
++ * aliased key. Without the fix, the outer keyShareEntry->key still
++ * holds the freed pointer; wolfSSL_free -> TLSX_KeyShare_FreeAll calls
++ * wc_ecc_free + XFREE on it, producing a use-after-free and a double
++ * free that ASAN flags. */
++int test_tls13_pqc_hybrid_malformed_ecdh(void)
++{
++ EXPECT_DECLS;
++#if defined(WOLFSSL_TLS13) && !defined(NO_WOLFSSL_CLIENT) && \
++ defined(WOLFSSL_HAVE_MLKEM) && defined(WOLFSSL_PQC_HYBRIDS) && \
++ !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \
++ !defined(WOLFSSL_MLKEM_NO_DECAPSULATE) && \
++ !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
++ (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \
++ !defined(NO_ECC_SECP)
++ WOLFSSL_CTX *ctx = NULL;
++ WOLFSSL *ssl = NULL;
++ /* 5 (record) + 4 (HS) + 1207 (ServerHello body) = 1216 bytes. */
++ static byte serverHello[1216];
++ word32 i = 0;
++ WOLFSSL_BUFFER_INFO msg;
++
++ XMEMSET(serverHello, 0, sizeof(serverHello));
++
++ /* Record: handshake, TLS 1.2 compat, length 1211 (0x04bb). */
++ serverHello[i++] = 0x16; serverHello[i++] = 0x03; serverHello[i++] = 0x03;
++ serverHello[i++] = 0x04; serverHello[i++] = 0xbb;
++ /* Handshake: ServerHello (0x02), length 1207 (0x0004b7). */
++ serverHello[i++] = 0x02;
++ serverHello[i++] = 0x00; serverHello[i++] = 0x04; serverHello[i++] = 0xb7;
++ /* legacy_version */
++ serverHello[i++] = 0x03; serverHello[i++] = 0x03;
++ /* random (32 bytes) */
++ XMEMSET(&serverHello[i], 0x42, 32); i += 32;
++ /* legacy_session_id_echo length: 0 */
++ serverHello[i++] = 0x00;
++ /* cipher_suite: TLS_AES_128_GCM_SHA256 */
++ serverHello[i++] = 0x13; serverHello[i++] = 0x01;
++ /* legacy_compression_method: null */
++ serverHello[i++] = 0x00;
++ /* extensions length: 1167 (0x048f) */
++ serverHello[i++] = 0x04; serverHello[i++] = 0x8f;
++ /* extension: supported_versions -> TLS 1.3 */
++ serverHello[i++] = 0x00; serverHello[i++] = 0x2b;
++ serverHello[i++] = 0x00; serverHello[i++] = 0x02;
++ serverHello[i++] = 0x03; serverHello[i++] = 0x04;
++ /* extension: key_share, extension_data length 1157 (0x0485) */
++ serverHello[i++] = 0x00; serverHello[i++] = 0x33;
++ serverHello[i++] = 0x04; serverHello[i++] = 0x85;
++ /* server_share.group: SECP256R1MLKEM768 (0x11eb) */
++ serverHello[i++] = 0x11; serverHello[i++] = 0xeb;
++ /* key_exchange length: 1153 (0x0481) */
++ serverHello[i++] = 0x04; serverHello[i++] = 0x81;
++ /* ECDH portion (65 bytes): leading 0x05 is not a valid X9.63 marker
++ * (valid markers: 0x04, 0x06, 0x07). The remaining 64 bytes stay zero
++ * from the initial XMEMSET. */
++ serverHello[i++] = 0x05;
++ i += 64;
++ /* PQC portion (1088 bytes): all zero from the initial XMEMSET. */
++ i += 1088;
++ AssertIntEQ((int)i, (int)sizeof(serverHello));
++
++ ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
++ wolfSSL_SetIORecv(ctx, PqcHybridUafRecv);
++ wolfSSL_SetIOSend(ctx, PqcHybridUafSend);
++
++ ExpectNotNull(ssl = wolfSSL_new(ctx));
++
++ /* Match the server's offered group so this key_share is processed. */
++ ExpectIntEQ(wolfSSL_UseKeyShare(ssl, WOLFSSL_SECP256R1MLKEM768),
++ WOLFSSL_SUCCESS);
++
++ msg.buffer = serverHello;
++ msg.length = (unsigned int)sizeof(serverHello);
++ wolfSSL_SetIOReadCtx(ssl, &msg);
++
++ /* Connect should fail gracefully on the malformed ECDH point. */
++ ExpectIntEQ(wolfSSL_connect_TLSv13(ssl),
++ WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
++
++ /* Without the fix, this triggers UAF + double-free in
++ * TLSX_KeyShare_FreeAll. */
++ wolfSSL_free(ssl);
++ wolfSSL_CTX_free(ctx);
++#endif
++ return EXPECT_RESULT();
++}
++
+ /* Test that a TLS 1.3 NewSessionTicket with a ticket shorter than ID_LEN
+ * (32 bytes) does not cause an unsigned integer underflow / OOB read in
+ * SetTicket. Uses a full memio handshake, then injects a crafted
+diff --git a/tests/api/test_tls13.h b/tests/api/test_tls13.h
+index c8eaa3b7f..94232f18e 100644
+--- a/tests/api/test_tls13.h
++++ b/tests/api/test_tls13.h
+@@ -43,6 +43,7 @@ int test_tls13_warning_alert_is_fatal(void);
+ int test_tls13_cert_req_sigalgs(void);
+ int test_tls13_derive_keys_no_key(void);
+ int test_tls13_pqc_hybrid_truncated_keyshare(void);
++int test_tls13_pqc_hybrid_malformed_ecdh(void);
+ int test_tls13_short_session_ticket(void);
+
+ #define TEST_TLS13_DECLS \
+@@ -65,6 +66,7 @@ int test_tls13_short_session_ticket(void);
+ TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs), \
+ TEST_DECL_GROUP("tls13", test_tls13_derive_keys_no_key), \
+ TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_truncated_keyshare), \
++ TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_malformed_ecdh), \
+ TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket)
+
+ #endif /* WOLFCRYPT_TEST_TLS13_H */
diff --git a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
index ef03d0c9ff..644c5379f5 100644
--- a/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
+++ b/meta-networking/recipes-connectivity/wolfssl/wolfssl_5.9.1.bb
@@ -36,6 +36,7 @@ SRC_URI = " \
file://CVE-2026-6450-2.patch \
file://CVE-2026-6731-1.patch \
file://CVE-2026-6731-2.patch \
+ file://CVE-2026-7531.patch \
"
SRCREV = "1d363f3adceba9d1478230ede476a37b0dcdef24"
^ permalink raw reply related [flat|nested] 37+ messages in thread