* [OE-core][wrynose][PATCH 1/9] curl: fix CVE-2026-92391
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 2/9] curl: fix CVE-2026-87863 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Avoid recursive cleanup of deeply nested MIME structures. Retain
upstream test3595 and register its helper in the older grouped test
lists.
[1] https://github.com/curl/curl/commit/d7546809d5bd548b69ec49fca26565c8f6f0d344
[2] https://curl.se/docs/CVE-2026-92391.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-92391.patch | 203 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 204 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92391.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92391.patch b/meta/recipes-support/curl/curl/CVE-2026-92391.patch
new file mode 100644
index 0000000000..4b952050f1
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92391.patch
@@ -0,0 +1,203 @@
+From d7546809d5bd548b69ec49fca26565c8f6f0d344 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Sun, 13 Sep 2026 00:06:44 +0200
+Subject: [PATCH] mime: do curl_mime_free() non-recursively
+
+Verify with test 3595: deeply nested curl_mime_free()
+
+Closes #22927
+
+CVE: CVE-2026-92391
+Upstream-Status: Backport [https://github.com/curl/curl/commit/d7546809d5bd548b69ec49fca26565c8f6f0d344]
+
+Backport Changes:
+- Register test3595 and lib3595.c in the older grouped TESTCASES and
+ TESTS_C lists instead of upstream's one-entry-per-line layout.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/mime.c | 28 +++++++++++----
+ tests/data/Makefile.am | 2 +-
+ tests/data/test3595 | 21 ++++++++++++
+ tests/libtest/Makefile.inc | 3 +-
+ tests/libtest/lib3595.c | 70 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 116 insertions(+), 8 deletions(-)
+ create mode 100644 tests/data/test3595
+ create mode 100644 tests/libtest/lib3595.c
+
+diff --git a/lib/mime.c b/lib/mime.c
+index 37ea514..3da989b 100644
+--- a/lib/mime.c
++++ b/lib/mime.c
+@@ -1078,20 +1078,36 @@ void Curl_mime_cleanpart(curl_mimepart *part)
+ }
+ }
+
+-/* Recursively delete a mime handle and its parts. */
++/* Non-recursively delete a mime handle and its parts. */
+ void curl_mime_free(curl_mime *mime)
+ {
+ curl_mimepart *part;
+
+- if(mime) {
+- mime_subparts_unbind(mime); /* Be sure it is not referenced anymore. */
+- while(mime->firstpart) {
+- part = mime->firstpart;
++ if(!mime)
++ return;
++
++ mime_subparts_unbind(mime); /* Be sure it is not referenced anymore. */
++
++ while(mime) {
++ part = mime->firstpart;
++ if(part) {
+ mime->firstpart = part->nextpart;
++ if(part->kind == MIMEKIND_MULTIPART && part->arg &&
++ part->freefunc == mime_subparts_free) {
++ curl_mime *subparts = (curl_mime *)part->arg;
++ part->freefunc = NULL;
++ cleanup_part_content(part);
++ subparts->parent = mime->parent;
++ mime->parent = (curl_mimepart *)subparts;
++ }
+ Curl_mime_cleanpart(part);
+ curlx_free(part);
+ }
+- curlx_free(mime);
++ else {
++ curl_mime *parent = (curl_mime *)mime->parent;
++ curlx_free(mime);
++ mime = parent;
++ }
+ }
+ }
+
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 7a9b8d0..933c3c7 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -287,7 +287,7 @@ test3209 test3210 test3211 test3212 test3213 test3214 test3215 test3216 \
+ \
+ test3303 test3304 \
+ \
+-test3500 \
++test3500 test3595 \
+ \
+ test4000 test4001
+
+diff --git a/tests/data/test3595 b/tests/data/test3595
+new file mode 100644
+index 0000000..b2b1dd2
+--- /dev/null
++++ b/tests/data/test3595
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++mime
++</keywords>
++</info>
++
++# Client-side
++<client>
++<tool>
++lib%TESTNUMBER
++</tool>
++<name>
++curl_mime_free
++</name>
++</client>
++
++<verify>
++</verify>
++</testcase>
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index d7af26f..e494189 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -118,4 +118,5 @@ TESTS_C = \
+ lib2700.c \
+ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \
+ lib3100.c lib3101.c lib3102.c lib3103.c lib3104.c lib3105.c \
+- lib3207.c lib3208.c
++ lib3207.c lib3208.c \
++ lib3595.c
+diff --git a/tests/libtest/lib3595.c b/tests/libtest/lib3595.c
+new file mode 100644
+index 0000000..1f343f5
+--- /dev/null
++++ b/tests/libtest/lib3595.c
+@@ -0,0 +1,70 @@
++/***************************************************************************
++ * _ _ ____ _
++ * Project ___| | | | _ \| |
++ * / __| | | | |_) | |
++ * | (__| |_| | _ <| |___
++ * \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++#include "unitcheck.h"
++
++static CURLcode test_lib3595(const char *arg)
++{
++ UNITTEST_BEGIN_SIMPLE
++
++#if !defined(CURL_DISABLE_MIME) && \
++ (!defined(CURL_DISABLE_HTTP) || \
++ !defined(CURL_DISABLE_SMTP) || \
++ !defined(CURL_DISABLE_IMAP))
++
++ curl_mime *root = NULL;
++ curl_mime *current = NULL;
++ curl_mime *sub = NULL;
++ curl_mimepart *part = NULL;
++ int i;
++
++ if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
++ curl_mfprintf(stderr, "curl_global_init() failed\n");
++ return TEST_ERR_MAJOR_BAD;
++ }
++
++ /* Create a deeply nested mime structure to verify that non-recursive
++ curl_mime_free works properly. */
++ (void)arg;
++ root = curl_mime_init(NULL);
++ fail_unless(root != NULL, "curl_mime_init root failed");
++ current = root;
++
++ for(i = 0; i < 50; i++) {
++ part = curl_mime_addpart(current);
++ fail_unless(part != NULL, "curl_mime_addpart failed");
++ sub = curl_mime_init(NULL);
++ fail_unless(sub != NULL, "curl_mime_init sub failed");
++ curl_mime_subparts(part, sub);
++ current = sub;
++ }
++
++ /* Free deep structure */
++ curl_mime_free(root);
++ curl_global_cleanup();
++#else
++ (void)arg;
++#endif
++
++ UNITTEST_END_SIMPLE
++}
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index f2de4dfa04..20dbeb9dfd 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -60,6 +60,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-92389.patch \
file://CVE-2026-87865_p1.patch \
file://CVE-2026-87865_p2.patch \
+ file://CVE-2026-92391.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 2/9] curl: fix CVE-2026-87863
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 1/9] curl: fix CVE-2026-92391 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 3/9] curl: fix CVE-2026-86410 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Cap HSTS max-age at two years and keep saving valid entries if one
date cannot be serialized. Adapt the older parser so oversized quoted
ages are capped while preserving unquoted suffix handling. Retain
upstream test1862.
[1] https://github.com/curl/curl/commit/08d50a791b3197a3d0a4fc2f778e243f5501c67e
[2] https://curl.se/docs/CVE-2026-87863.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-87863.patch | 203 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 204 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-87863.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-87863.patch b/meta/recipes-support/curl/curl/CVE-2026-87863.patch
new file mode 100644
index 0000000000..d1f489e47e
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-87863.patch
@@ -0,0 +1,203 @@
+From 08d50a791b3197a3d0a4fc2f778e243f5501c67e Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Tue, 8 Sep 2026 16:04:42 +0200
+Subject: [PATCH] hsts: cap max-age to two years
+
+And ignore problems saving individual HSTS entries - a single entry
+problem should not affect storing other entries.
+
+Added test 1862 to verify.
+
+Closes #22878
+
+CVE: CVE-2026-87863
+Upstream-Status: Backport [https://github.com/curl/curl/commit/08d50a791b3197a3d0a4fc2f778e243f5501c67e]
+
+Backport Changes:
+- Keep curl 8.19.0's max-age parser, which reads directly from the
+ header using TIME_T_MAX as its numeric limit. Upstream first
+ extracts the value and parses it with a two-year numeric limit.
+ Cap parsed ages and numeric overflows at two years.
+- After overflow in a quoted age, skip its digits before checking
+ the closing quote. The older number reader leaves its position
+ unchanged on overflow. For unquoted overflow, keep that position
+ so trailing text is handled as before.
+- Register test1862 in the older grouped tests/data/Makefile.am list.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/hsts.c | 32 ++++++++++--------
+ tests/data/Makefile.am | 2 +-
+ tests/data/test1862 | 73 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 93 insertions(+), 14 deletions(-)
+ create mode 100644 tests/data/test1862
+
+diff --git a/lib/hsts.c b/lib/hsts.c
+index 5ad5bd4..9e5b456 100644
+--- a/lib/hsts.c
++++ b/lib/hsts.c
+@@ -43,6 +43,8 @@
+ #define MAX_HSTS_DATELEN 256
+ #define UNLIMITED "unlimited"
+
++#define CAP_HSTS_MAX_AGE (2*365*24*3600) /* two years cap */
++
+ #if defined(DEBUGBUILD) || defined(UNITTESTS)
+ /* to play well with debug builds, we can *set* a fixed time this will
+ return */
+@@ -152,11 +154,18 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
+ quoted = TRUE;
+
+ rc = curlx_str_number(&p, &expires, TIME_T_MAX);
+- if(rc == STRE_OVERFLOW)
+- expires = CURL_OFF_T_MAX;
++ if(rc == STRE_OVERFLOW) {
++ expires = CAP_HSTS_MAX_AGE;
++ if(quoted) {
++ while(ISDIGIT(*p))
++ p++;
++ }
++ }
+ else if(rc)
+ /* invalid max-age */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
++ else if(expires > CAP_HSTS_MAX_AGE)
++ expires = CAP_HSTS_MAX_AGE;
+
+ if(quoted) {
+ if(*p != '\"')
+@@ -304,22 +313,21 @@ static CURLcode hsts_push(struct Curl_easy *data,
+ /*
+ * Write this single hsts entry to a single output line
+ */
+-static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
++static void hsts_out(struct stsentry *sts, FILE *fp)
+ {
+ struct tm stamp;
+ if(sts->expires != TIME_T_MAX) {
+ CURLcode result = curlx_gmtime((time_t)sts->expires, &stamp);
+- if(result)
+- return result;
+- curl_mfprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
+- sts->includeSubDomains ? "." : "", sts->host,
+- stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+- stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
++ if(!result)
++ /* skip the entry if the date function fails */
++ curl_mfprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
++ sts->includeSubDomains ? "." : "", sts->host,
++ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
++ stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+ }
+ else
+ curl_mfprintf(fp, "%s%s \"%s\"\n",
+ sts->includeSubDomains ? "." : "", sts->host, UNLIMITED);
+- return CURLE_OK;
+ }
+
+ /*
+@@ -354,9 +362,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
+ for(e = Curl_llist_head(&h->list); e; e = n) {
+ struct stsentry *sts = Curl_node_elem(e);
+ n = Curl_node_next(e);
+- result = hsts_out(sts, out);
+- if(result)
+- break;
++ hsts_out(sts, out);
+ }
+ curlx_fclose(out);
+ if(!result && tempstore && curlx_rename(tempstore, file))
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 7a9b8d0..e678290 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -231,7 +231,7 @@ test1680 test1681 test1682 test1683 test1685 \
+ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
+ test1708 test1709 test1710 test1711 test1712 \
+ \
+-test1800 test1801 test1802 test1847 test1848 test1849 test1850 \
++test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1862 \
+ \
+ test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \
+ test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \
+diff --git a/tests/data/test1862 b/tests/data/test1862
+new file mode 100644
+index 0000000..b9a29f6
+--- /dev/null
++++ b/tests/data/test1862
+@@ -0,0 +1,73 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++HSTS
++</keywords>
++</info>
++
++<reply>
++
++<data crlf="headers" nocheck="yes">
++HTTP/1.1 200 OK
++Date: Tue, 09 Nov 2010 14:49:00 GMT
++Content-Length: 6
++Connection: close
++Strict-Transport-Security: max-age=1152921504606846976
++
++-foo-
++</data>
++</reply>
++
++<client>
++<server>
++http
++</server>
++<features>
++HSTS
++Debug
++</features>
++
++# November 11, 2030
++<setenv>
++CURL_HSTS_HTTP=yes
++CURL_TIME=1920582000
++</setenv>
++
++<name>
++HSTS with extreme max-age
++</name>
++<command>
++http://this.hsts.example:%HTTPPORT/%TESTNUMBER --hsts %LOGDIR/input%TESTNUMBER --resolve this.hsts.example:%HTTPPORT:%HOSTIP
++</command>
++<file name="%LOGDIR/input%TESTNUMBER">
++# Your HSTS cache. https://curl.se/docs/hsts.html
++# This file was generated by libcurl! Edit at your own risk.
++somehere.example "20330525 03:33:20"
++elsewhere.example.com "20330727 03:33:20"
++</file>
++<disable>
++test-duphandle
++</disable>
++</client>
++
++<verify>
++# we let it CONNECT to the server to confirm HSTS but deny from there
++<protocol crlf="headers">
++GET /1862 HTTP/1.1
++Host: this.hsts.example:%HTTPPORT
++User-Agent: curl/%VERSION
++Accept: */*
++
++</protocol>
++<file name="%LOGDIR/input%TESTNUMBER" mode="text">
++# Your HSTS cache. https://curl.se/docs/hsts.html
++# This file was generated by libcurl! Edit at your own risk.
++somehere.example "20330525 03:33:20"
++elsewhere.example.com "20330727 03:33:20"
++this.hsts.example "20321109 23:00:00"
++</file>
++
++</verify>
++</testcase>
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 20dbeb9dfd..9fd78a0fd8 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -61,6 +61,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-87865_p1.patch \
file://CVE-2026-87865_p2.patch \
file://CVE-2026-92391.patch \
+ file://CVE-2026-87863.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 3/9] curl: fix CVE-2026-86410
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 1/9] curl: fix CVE-2026-92391 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 2/9] curl: fix CVE-2026-87863 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 4/9] curl: fix CVE-2026-92390 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Install SSH host verification options before following redirects from
other protocols. The missing-file fallback keeps ordinary HTTP usable
when no known_hosts file exists. Adapt the older findfile() ownership
path.
The omitted test1679 hunk only filters a generated
CURLOPT_SSH_KNOWNHOSTS line. The SSL reset test and its data1679.c
fixture were introduced after curl 8.19.0 by [3]; they do not exercise
SSH redirects. The older --libcurl comparisons already filter this
option.
[1] https://github.com/curl/curl/commit/6ae9befb2fb6bdfdb5fc11fdf76bbe519fe1f52c
[2] https://curl.se/docs/CVE-2026-86410.html
[3] https://github.com/curl/curl/commit/19d0f4acfa9df373e8e1f987c11b7165e1edac34
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-86410.patch | 102 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 103 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-86410.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-86410.patch b/meta/recipes-support/curl/curl/CVE-2026-86410.patch
new file mode 100644
index 0000000000..1f15da547b
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-86410.patch
@@ -0,0 +1,102 @@
+From 6ae9befb2fb6bdfdb5fc11fdf76bbe519fe1f52c Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Sat, 5 Sep 2026 15:55:58 +0200
+Subject: [PATCH] config2setopts: always set the security options for SCP/SFTP
+
+If the protocols are built-in
+
+Closes #22839
+
+CVE: CVE-2026-86410
+Upstream-Status: Backport [https://github.com/curl/curl/commit/6ae9befb2fb6bdfdb5fc11fdf76bbe519fe1f52c]
+
+Backport Changes:
+- Use curl 8.19.0's directly owned findfile() result. Add the
+ missing-file fallback when that result is NULL; upstream places
+ it after a newer temporary-path copy branch.
+- Omit test1679's stripfile update. Test1679 and data1679.c were
+ introduced by [1] after curl 8.19.0 and are absent here. They check
+ SSL option resets across --next in generated --libcurl output.
+ The omitted line only strips CURLOPT_SSH_KNOWNHOSTS from that
+ comparison; it does not exercise SSH redirects.
+ Existing --libcurl tests already filter that option.
+
+[1] https://github.com/curl/curl/commit/19d0f4acfa9df373e8e1f987c11b7165e1edac34
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/config2setopts.c | 34 +++++++++++++++++++++++-----------
+ 1 file changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/src/config2setopts.c b/src/config2setopts.c
+index 20d87a0..27ca721 100644
+--- a/src/config2setopts.c
++++ b/src/config2setopts.c
+@@ -178,12 +178,11 @@ static CURLcode url_proto_and_rewrite(char **url,
+ return result;
+ }
+
+-static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl,
+- const char *use_proto)
++static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl)
+ {
+ CURLcode result;
+
+- if(use_proto != proto_scp && use_proto != proto_sftp)
++ if(!proto_scp && !proto_sftp)
+ return CURLE_OK;
+
+ /* SSH and SSL private key uses same command-line option */
+@@ -204,8 +203,27 @@ static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl,
+
+ if(!config->insecure_ok) {
+ char *known = config->knownhosts;
+- if(!known)
++ if(!known) {
+ known = findfile(".ssh/known_hosts", FALSE);
++ if(!known && !config->hostpubmd5 && !config->hostpubsha256) {
++ /* couldn't find an existing one, instead set one that MIGHT work as
++ we don't verify the host using other means */
++ struct dynbuf k;
++ char *home = getenv("HOME");
++ curlx_dyn_init(&k, 256);
++
++ /* If the HOME environment variable is missing this renders a path
++ that is likely to not work but the HOME one also does not exist!
++ */
++ result = curlx_dyn_addf(&k,
++ "%s" DIR_CHAR ".ssh" DIR_CHAR "known_hosts",
++ home ? home : "");
++ if(result)
++ return result;
++ known = curlx_dyn_ptr(&k);
++ }
++ }
++
+ if(known) {
+ result = my_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, known);
+ if(result) {
+@@ -216,12 +234,6 @@ static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl,
+ /* store it in global to avoid repeated checks */
+ config->knownhosts = known;
+ }
+- else if(!config->hostpubmd5 && !config->hostpubsha256) {
+- errorf("Could not find a known_hosts file");
+- return CURLE_FAILED_INIT;
+- }
+- else
+- warnf("Could not find a known_hosts file");
+ }
+ return CURLE_OK; /* ignore if SHA256 did not work */
+ }
+@@ -928,7 +940,7 @@ CURLcode config2setopts(struct OperationConfig *config,
+ MY_SETOPT_STR(curl, CURLOPT_KEYPASSWD, config->key_passwd);
+ MY_SETOPT_STR(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd);
+
+- result = ssh_setopts(config, curl, use_proto);
++ result = ssh_setopts(config, curl);
+ if(setopt_bad(result))
+ return result;
+
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 9fd78a0fd8..e5b9203678 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -62,6 +62,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-87865_p2.patch \
file://CVE-2026-92391.patch \
file://CVE-2026-87863.patch \
+ file://CVE-2026-86410.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 4/9] curl: fix CVE-2026-92390
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (2 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 3/9] curl: fix CVE-2026-86410 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 5/9] curl: fix CVE-2026-92388 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Delay output creation until the remote filename is selected, preserving
-J anti-clobber handling for 103 and ETag headers.
Include the later upstream filename-less Content-Disposition fix [3].
It closes another early-open path and writes buffered headers when a
transfer completes without a body. Keep both fixes in separate patches
with upstream tests 3037, 3038 and 1718.
[1] https://github.com/curl/curl/commit/c24a69d8cd368de4e25e3540bf0b7b000a31d5a7
[2] https://curl.se/docs/CVE-2026-92390.html
[3] https://github.com/curl/curl/commit/631ed306872956f86617df9b5664614e8a2dee54
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-92390_p1.patch | 270 ++++++++++++++++++
.../curl/curl/CVE-2026-92390_p2.patch | 150 ++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 2 +
3 files changed, 422 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92390_p1.patch
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92390_p2.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92390_p1.patch b/meta/recipes-support/curl/curl/CVE-2026-92390_p1.patch
new file mode 100644
index 0000000000..fa25d06b36
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92390_p1.patch
@@ -0,0 +1,270 @@
+From c24a69d8cd368de4e25e3540bf0b7b000a31d5a7 Mon Sep 17 00:00:00 2001
+From: Joshua Rogers <MegaManSec@users.noreply.github.com>
+Date: Sat, 12 Sep 2026 02:57:04 +0200
+Subject: [PATCH] tool: buffer headers before remote filename selection
+
+Verified by test 3037 and 3038.
+
+Closes #22977
+
+CVE: CVE-2026-92390
+Upstream-Status: Backport [https://github.com/curl/curl/commit/c24a69d8cd368de4e25e3540bf0b7b000a31d5a7]
+
+Backport Changes:
+- In content_disposition(), remove curl 8.19.0's curl_maprintf()
+ header-copy block; the upstream block uses curlx_memdup0().
+- Register test3037 and test3038 together in the older grouped
+ tests/data/Makefile.am list instead of one line per test.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/tool_cb_hdr.c | 53 +++++++++++++++++++++++++++--------------
+ tests/data/Makefile.am | 1 +
+ tests/data/test3037 | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
+ tests/data/test3038 | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 162 insertions(+), 18 deletions(-)
+
+diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c
+index fc03cf1..15abb5b 100644
+--- a/src/tool_cb_hdr.c
++++ b/src/tool_cb_hdr.c
+@@ -248,6 +248,28 @@ fail:
+ return rc;
+ }
+
++/* Store a header until the output filename has been decided. */
++static size_t buffer_header(struct HdrCbData *hdrcbdata,
++ const char *str, size_t cb)
++{
++ char *clone = curlx_memdup0(str, cb);
++ if(clone) {
++ struct curl_slist *old = hdrcbdata->headlist;
++ hdrcbdata->headlist = curl_slist_append(old, clone);
++ curlx_free(clone);
++ if(!hdrcbdata->headlist) {
++ curl_slist_free_all(old);
++ return CURL_WRITEFUNC_ERROR;
++ }
++ }
++ else {
++ curl_slist_free_all(hdrcbdata->headlist);
++ hdrcbdata->headlist = NULL;
++ return CURL_WRITEFUNC_ERROR;
++ }
++ return cb;
++}
++
+ /*
+ * Write etag to file when --etag-save option is given.
+ */
+@@ -395,24 +417,8 @@ static size_t content_disposition(const char *str, const char *end,
+
+ if(hdrcbdata->honor_cd_filename &&
+ hdrcbdata->config->show_headers) {
+- /* still awaiting the Content-Disposition header, store the header in
+- memory. Since it is not null-terminated, we need an extra dance. */
+- char *clone = curl_maprintf("%.*s", (int)cb, str);
+- if(clone) {
+- struct curl_slist *old = hdrcbdata->headlist;
+- hdrcbdata->headlist = curl_slist_append(old, clone);
+- curlx_free(clone);
+- if(!hdrcbdata->headlist) {
+- curl_slist_free_all(old);
+- return CURL_WRITEFUNC_ERROR;
+- }
+- }
+- else {
+- curl_slist_free_all(hdrcbdata->headlist);
+- hdrcbdata->headlist = NULL;
+- return CURL_WRITEFUNC_ERROR;
+- }
+- return cb; /* done for now */
++ /* still awaiting the Content-Disposition header */
++ return buffer_header(hdrcbdata, str, cb);
+ }
+
+ return 0; /* ok */
+@@ -434,6 +440,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
+ const size_t cb = size * nmemb;
+ const char *end = ptr + cb;
+ const char *scheme = NULL;
++ bool cd_checked = FALSE;
+
+ if(!per->config)
+ return CURL_WRITEFUNC_ERROR;
+@@ -493,6 +500,9 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
+ size_t rc = content_disposition(str, end, cb, per, response);
+ if(rc)
+ return rc;
++ /* remember that this was checked to avoid duplicating it further
++ down */
++ cd_checked = TRUE;
+ }
+ }
+ if(hdrcbdata->config->writeout) {
+@@ -512,6 +522,13 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
+ /* bold headers only for selected protocols */
+ const char *value = NULL;
+
++ /* Informational responses, and ETag headers handled by --etag-save,
++ bypass the content-disposition handling above. Do not open the output
++ file until the filename decision is complete. Only for HTTP(S). */
++ if(hdrcbdata->honor_cd_filename && !cd_checked &&
++ (scheme == proto_http || scheme == proto_https))
++ return buffer_header(hdrcbdata, str, cb);
++
+ if(!outs->stream && !tool_create_output_file(outs, per->config))
+ return CURL_WRITEFUNC_ERROR;
+
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 7a9b8d0..6d12c7c 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -279,6 +279,7 @@ test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \
+ test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \
+ test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 \
+ test3032 test3033 test3034 test3035 \
++test3037 test3038 \
+ \
+ test3100 test3101 test3102 test3103 test3104 test3105 test3109 \
+ \
+diff --git a/tests/data/test3037 b/tests/data/test3037
+new file mode 100644
+index 0000000..be29b99
+--- /dev/null
++++ b/tests/data/test3037
+@@ -0,0 +1,62 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++HTTP GET
++103
++Early Hints
++-J
++--show-headers
++</keywords>
++</info>
++
++# Server-side
++<reply>
++<data nocheck="yes">
++HTTP/1.1 103 Early Hints
++
++HTTP/1.1 200 OK
++Content-Length: 6
++Content-Disposition: attachment; filename="victim%TESTNUMBER"
++Connection: close
++
++12345
++</data>
++</reply>
++
++# Client-side
++<client>
++<server>
++http
++</server>
++<features>
++http
++</features>
++<name>
++Do not open output for 103 Early Hints with -J and --show-headers
++</name>
++<file name="%LOGDIR/victim%TESTNUMBER">
++original
++</file>
++<command option="no-output">
++http://%HOSTIP:%HTTPPORT/victim%TESTNUMBER -O -J -i --output-dir %LOGDIR
++</command>
++</client>
++
++<verify>
++<protocol crlf="headers">
++GET /victim%TESTNUMBER HTTP/1.1
++Host: %HOSTIP:%HTTPPORT
++User-Agent: curl/%VERSION
++Accept: */*
++
++</protocol>
++<errorcode>
++23
++</errorcode>
++<file name="%LOGDIR/victim%TESTNUMBER">
++original
++</file>
++</verify>
++</testcase>
+diff --git a/tests/data/test3038 b/tests/data/test3038
+new file mode 100644
+index 0000000..ff51458
+--- /dev/null
++++ b/tests/data/test3038
+@@ -0,0 +1,64 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++HTTP GET
++ETag
++--etag-save
++-J
++--show-headers
++</keywords>
++</info>
++
++# Server-side
++<reply>
++<data nocheck="yes">
++HTTP/1.1 200 OK
++ETag: "tag%TESTNUMBER"
++Content-Length: 6
++Content-Disposition: attachment; filename="victim%TESTNUMBER"
++Connection: close
++
++12345
++</data>
++</reply>
++
++# Client-side
++<client>
++<server>
++http
++</server>
++<features>
++http
++</features>
++<name>
++Do not open output for ETag with -J, --show-headers, --etag-save
++</name>
++<file name="%LOGDIR/victim%TESTNUMBER">
++original
++</file>
++<command option="no-output">
++http://%HOSTIP:%HTTPPORT/victim%TESTNUMBER -O -J -i --etag-save %LOGDIR/etag%TESTNUMBER --output-dir %LOGDIR
++</command>
++</client>
++
++<verify>
++<protocol crlf="headers">
++GET /victim%TESTNUMBER HTTP/1.1
++Host: %HOSTIP:%HTTPPORT
++User-Agent: curl/%VERSION
++Accept: */*
++
++</protocol>
++<errorcode>
++23
++</errorcode>
++<file1 name="%LOGDIR/victim%TESTNUMBER">
++original
++</file1>
++<file2 name="%LOGDIR/etag%TESTNUMBER">
++"tag%TESTNUMBER"
++</file2>
++</verify>
++</testcase>
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92390_p2.patch b/meta/recipes-support/curl/curl/CVE-2026-92390_p2.patch
new file mode 100644
index 0000000000..24696ca27b
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92390_p2.patch
@@ -0,0 +1,150 @@
+From 631ed306872956f86617df9b5664614e8a2dee54 Mon Sep 17 00:00:00 2001
+From: matty69v <matty69v@users.noreply.github.com>
+Date: Tue, 20 Oct 2026 10:28:31 +0200
+Subject: [PATCH] tool_cb_hdr: do not truncate existing file on filename-less
+ C-D header
+
+With -O -J, a Content-Disposition header without a usable filename
+(for example "inline") made curl create the URL-derived output file
+right away. When a later Content-Disposition header then named that
+same file, the no-clobber check refused it, but the file had already
+been truncated to zero bytes. The same happened when such a header came
+in a 3xx response before the Location: header with -L.
+
+Defer creating the output file until the body arrives (or the transfer
+ends), like for any other header. When the transfer ends without a body,
+also write the headers buffered for -i, which were lost before.
+
+Test 1718 verifies both.
+Closes #23501
+
+CVE: CVE-2026-92390
+Upstream-Status: Backport [https://github.com/curl/curl/commit/631ed306872956f86617df9b5664614e8a2dee54]
+
+Backport Changes:
+- Register test1718 without indentation to match curl 8.19.0's
+ grouped TESTCASES list; upstream indents the entry by two spaces.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ src/tool_cb_hdr.c | 10 +++++----
+ src/tool_operate.c | 4 ++++
+ tests/data/Makefile.am | 1 +
+ tests/data/test1718 | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 70 insertions(+), 4 deletions(-)
+
+diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c
+index 15abb5b..69c0227 100644
+--- a/src/tool_cb_hdr.c
++++ b/src/tool_cb_hdr.c
+@@ -409,10 +409,12 @@ static size_t content_disposition(const char *str, const char *end,
+ }
+ break;
+ }
+- if(!outs->stream && !tool_create_output_file(outs, per->config))
+- return CURL_WRITEFUNC_ERROR;
+- if(tool_write_headers(&per->hdrcbdata, outs->stream))
+- return CURL_WRITEFUNC_ERROR;
++ /* No usable filename in this header. Do not create the output file
++ yet: a later Content-Disposition header may still provide a name, and
++ opening the URL-derived name here would truncate an existing file
++ that -J is meant to leave alone. The file is created once the body
++ arrives (or at the end of the transfer) and any buffered headers are
++ written then. */
+ } /* content-disposition handling */
+
+ if(hdrcbdata->honor_cd_filename &&
+diff --git a/src/tool_operate.c b/src/tool_operate.c
+index 9a29b8e..1458322 100644
+--- a/src/tool_operate.c
++++ b/src/tool_operate.c
+@@ -649,6 +649,10 @@ static CURLcode post_output_handling(struct per_transfer *per,
+ curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet);
+ if(!cond_unmet && !tool_create_output_file(outs, config))
+ return CURLE_WRITE_ERROR;
++ /* write headers buffered while waiting for a -J filename */
++ if(outs->stream && per->hdrcbdata.headlist &&
++ tool_write_headers(&per->hdrcbdata, outs->stream))
++ return CURLE_WRITE_ERROR;
+ }
+
+ if(!outs->regular_file && outs->stream) {
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 6d12c7c..2528286 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -230,6 +230,7 @@ test1680 test1681 test1682 test1683 test1685 \
+ \
+ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
+ test1708 test1709 test1710 test1711 test1712 \
++test1718 \
+ \
+ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1862 \
+ \
+diff --git a/tests/data/test1718 b/tests/data/test1718
+new file mode 100644
+index 0000000..c4826f3
+--- /dev/null
++++ b/tests/data/test1718
+@@ -0,0 +1,59 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++HTTP
++HTTP GET
++-J
++</keywords>
++</info>
++
++<reply>
++<data nocheck="yes">
++HTTP/1.1 200 OK
++Content-Disposition: inline
++Content-Disposition: attachment; filename="name%TESTNUMBER"
++Content-Length: 8
++
++PAYLOAD
++</data>
++<data2 nocheck="yes">
++HTTP/1.1 200 OK
++Content-Disposition: inline
++Content-Length: 0
++
++</data2>
++</reply>
++
++# Client-side
++<client>
++<server>
++http
++</server>
++<name>
++HTTP GET -OJi, filename-less Content-Disposition first
++</name>
++<command option="no-output,no-include">
++http://%HOSTIP:%HTTPPORT/%TESTNUMBER0002 http://%HOSTIP:%HTTPPORT/name%TESTNUMBER -Ji -O -O --output-dir %LOGDIR
++</command>
++<file name="%LOGDIR/name%TESTNUMBER">
++initial content
++</file>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++<errorcode>
++23
++</errorcode>
++<file name="%LOGDIR/name%TESTNUMBER">
++initial content
++</file>
++<file2 name="%LOGDIR/%TESTNUMBER0002">
++HTTP/1.1 200 OK
++Content-Disposition: inline
++Content-Length: 0
++
++</file2>
++</verify>
++</testcase>
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index e5b9203678..a458a69e20 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -63,6 +63,8 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-92391.patch \
file://CVE-2026-87863.patch \
file://CVE-2026-86410.patch \
+ file://CVE-2026-92390_p1.patch \
+ file://CVE-2026-92390_p2.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 5/9] curl: fix CVE-2026-92388
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (3 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 4/9] curl: fix CVE-2026-92390 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 6/9] curl: fix CVE-2026-92392 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (4 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fixes referenced in [3],
using the commits shown in [1] and [2].
Route wildcard directory listings through the internal write path
without replacing the application's WRITEFUNCTION or WRITEDATA.
Previously, ftp_wc.backup saved those options for restoration after
LIST. Failed listings could skip that restoration and leave internal
parser state in the application settings.
The writer now selects the listing parser and easy handle directly
without changing application options. This removes the need to save
and restore callbacks, so remove the backup fields and that code, as
upstream does. Apply the field removal in curl 8.19.0's lib/ftp.h.
Keep the matching-state correction in a separate patch.
[1] https://github.com/curl/curl/commit/ba8f8a7f27a5c5e19e4ef212b90b35a93559163e
[2] https://github.com/curl/curl/commit/b85acacec9b38442741bb019bc992e77b5f665d4
[3] https://curl.se/docs/CVE-2026-92388.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-92388_p1.patch | 139 ++++++++++++++++++
.../curl/curl/CVE-2026-92388_p2.patch | 33 +++++
meta/recipes-support/curl/curl_8.19.0.bb | 2 +
3 files changed, 174 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92388_p1.patch
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92388_p2.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92388_p1.patch b/meta/recipes-support/curl/curl/CVE-2026-92388_p1.patch
new file mode 100644
index 0000000000..92752eb21e
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92388_p1.patch
@@ -0,0 +1,139 @@
+From ba8f8a7f27a5c5e19e4ef212b90b35a93559163e Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Wed, 16 Sep 2026 15:17:37 +0200
+Subject: [PATCH] ftplistparser: no more overwriting set.write_func
+
+The entries in the 'set' struct should remain as set by the application.
+
+Closes #22969
+
+CVE: CVE-2026-92388
+Upstream-Status: Backport [https://github.com/curl/curl/commit/ba8f8a7f27a5c5e19e4ef212b90b35a93559163e]
+
+Backport Changes:
+- Remove the ftp_wc backup fields from lib/ftp.h instead of
+ lib/ftp-int.h, which is absent from curl 8.19.0.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/cw-out.c | 10 ++++++++--
+ lib/ftp.c | 13 -------------
+ lib/ftp.h | 5 -----
+ lib/ftplistparser.c | 10 ++++++++++
+ lib/ftplistparser.h | 5 +++++
+ 5 files changed, 23 insertions(+), 20 deletions(-)
+
+diff --git a/lib/cw-out.c b/lib/cw-out.c
+index 554c32e..2500ea8 100644
+--- a/lib/cw-out.c
++++ b/lib/cw-out.c
+@@ -148,8 +148,14 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
+ switch(otype) {
+ case CW_OUT_BODY:
+ case CW_OUT_BODY_0LEN:
+- *pwcb = data->set.fwrite_func;
+- *pwcb_data = data->set.out;
++ if(Curl_ftp_listmode(data)) {
++ *pwcb = Curl_ftp_parselist;
++ *pwcb_data = data;
++ }
++ else {
++ *pwcb = data->set.fwrite_func;
++ *pwcb_data = data->set.out;
++ }
+ *pmax_write = CURL_MAX_WRITE_SIZE;
+ /* if we ever want buffering of BODY output, we can set `min_write`
+ * the preferred size. The default should always be to pass data
+diff --git a/lib/ftp.c b/lib/ftp.c
+index 6047759..6b13fb6 100644
+--- a/lib/ftp.c
++++ b/lib/ftp.c
+@@ -3988,15 +3988,6 @@ static CURLcode init_wc_data(struct Curl_easy *data,
+ goto fail;
+ }
+
+- /* backup old write_function */
+- ftpwc->backup.write_function = data->set.fwrite_func;
+- /* parsing write function */
+- data->set.fwrite_func = Curl_ftp_parselist;
+- /* backup old file descriptor */
+- ftpwc->backup.file_descriptor = data->set.out;
+- /* let the writefunc callback know the transfer */
+- data->set.out = data;
+-
+ infof(data, "Wildcard - Parsing started");
+ return CURLE_OK;
+
+@@ -4032,10 +4023,6 @@ static CURLcode wc_statemach(struct Curl_easy *data,
+ /* In this state is LIST response successfully parsed, so lets restore
+ previous WRITEFUNCTION callback and WRITEDATA pointer */
+ struct ftp_wc *ftpwc = wildcard->ftpwc;
+- data->set.fwrite_func = ftpwc->backup.write_function;
+- data->set.out = ftpwc->backup.file_descriptor;
+- ftpwc->backup.write_function = ZERO_NULL;
+- ftpwc->backup.file_descriptor = NULL;
+ wildcard->state = CURLWC_DOWNLOADING;
+
+ if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
+diff --git a/lib/ftp.h b/lib/ftp.h
+index 20b3600..b1d3706 100644
+--- a/lib/ftp.h
++++ b/lib/ftp.h
+@@ -85,11 +85,6 @@ struct ftp_parselist_data; /* defined later in ftplistparser.c */
+
+ struct ftp_wc {
+ struct ftp_parselist_data *parser;
+-
+- struct {
+- curl_write_callback write_function;
+- FILE *file_descriptor;
+- } backup;
+ };
+
+ typedef enum {
+diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c
+index f82895d..9c7ad6e 100644
+--- a/lib/ftplistparser.c
++++ b/lib/ftplistparser.c
+@@ -1010,6 +1010,16 @@ static CURLcode parse_winnt(struct Curl_easy *data,
+ return CURLE_OK;
+ }
+
++/* returns TRUE if in wildcard listing mode */
++bool Curl_ftp_listmode(struct Curl_easy *data)
++{
++ if(data->state.wildcardmatch) {
++ struct WildcardData * const wildcard = data->wildcard;
++ return wildcard && (wildcard->state <= CURLWC_MATCHING);
++ }
++ return FALSE;
++}
++
+ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+ void *connptr)
+ {
+diff --git a/lib/ftplistparser.h b/lib/ftplistparser.h
+index 5d7aa49..942e102 100644
+--- a/lib/ftplistparser.h
++++ b/lib/ftplistparser.h
+@@ -27,6 +27,9 @@
+
+ #ifndef CURL_DISABLE_FTP
+
++/* returns TRUE if in wildcard listing mode */
++bool Curl_ftp_listmode(struct Curl_easy *data);
++
+ /* WRITEFUNCTION callback for parsing LIST responses */
+ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+ void *connptr);
+@@ -71,6 +74,8 @@ void Curl_wildcard_dtor(struct WildcardData **wcp);
+ struct Curl_easy;
+
+ #else /* CURL_DISABLE_FTP */
++#define Curl_ftp_listmode(x) FALSE
++#define Curl_ftp_parselist ZERO_NULL
+ #define Curl_wildcard_dtor(x)
+ #endif /* !CURL_DISABLE_FTP */
+
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92388_p2.patch b/meta/recipes-support/curl/curl/CVE-2026-92388_p2.patch
new file mode 100644
index 0000000000..0f819509f5
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92388_p2.patch
@@ -0,0 +1,33 @@
+From b85acacec9b38442741bb019bc992e77b5f665d4 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Thu, 17 Sep 2026 14:32:52 +0200
+Subject: [PATCH] ftplistparser: only use the list parser in CURLWC_MATCHING
+ state
+
+Follow-up to ba8f8a7f27a5c5e1
+
+Closes #22985
+
+CVE: CVE-2026-92388
+Upstream-Status: Backport [https://github.com/curl/curl/commit/b85acacec9b38442741bb019bc992e77b5f665d4]
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/ftplistparser.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c
+index 9c7ad6e..91b2425 100644
+--- a/lib/ftplistparser.c
++++ b/lib/ftplistparser.c
+@@ -1015,7 +1015,7 @@ bool Curl_ftp_listmode(struct Curl_easy *data)
+ {
+ if(data->state.wildcardmatch) {
+ struct WildcardData * const wildcard = data->wildcard;
+- return wildcard && (wildcard->state <= CURLWC_MATCHING);
++ return wildcard && (wildcard->state == CURLWC_MATCHING);
+ }
+ return FALSE;
+ }
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index a458a69e20..5abde681e7 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -65,6 +65,8 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-86410.patch \
file://CVE-2026-92390_p1.patch \
file://CVE-2026-92390_p2.patch \
+ file://CVE-2026-92388_p1.patch \
+ file://CVE-2026-92388_p2.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 6/9] curl: fix CVE-2026-92392
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (4 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 5/9] curl: fix CVE-2026-92388 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 7/9] curl: fix CVE-2026-94266 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Finish request flushing before connection filters release their
references to the easy handle. Detach or return the connection before
resuming pending transfers, including HTTP/2 upload retry paths.
Adapt the order to curl 8.19.0's Curl_cpool_do_locked() callback and
process_pending_handles(). Keep detachment under the existing pool
share lock rather than importing the newer pool APIs.
[1] https://github.com/curl/curl/commit/8b0744af5b1b631da5b938ca31c3f9895ad5a321
[2] https://curl.se/docs/CVE-2026-92392.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-92392.patch | 59 +++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 60 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92392.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-92392.patch b/meta/recipes-support/curl/curl/CVE-2026-92392.patch
new file mode 100644
index 0000000000..4129d7f013
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92392.patch
@@ -0,0 +1,59 @@
+From 8b0744af5b1b631da5b938ca31c3f9895ad5a321 Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Mon, 12 Oct 2026 07:40:45 +0200
+Subject: [PATCH] multi: adjust the cleanup order in multi_done
+
+Closes #23370
+
+CVE: CVE-2026-92392
+Upstream-Status: Backport [https://github.com/curl/curl/commit/8b0744af5b1b631da5b938ca31c3f9895ad5a321]
+
+Backport Changes:
+- Use curl 8.19.0's process_pending_handles() and retain its
+ connection/multiplex comment; multi_schedule_pending() is absent.
+- Keep Curl_cpool_do_locked(), whose callback detaches the connection,
+ instead of the newer Curl_cpool_return() and outer detach calls.
+ Notify filters after request cleanup and before that callback,
+ then process pending transfers after the pool lock is released.
+- Apply this to the older function's connection-required layout,
+ which has no outer if(conn) block. Keep a blank line between the
+ pending-transfer block and netrc cleanup.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/multi.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/lib/multi.c b/lib/multi.c
+index ec2d531..4e81001 100644
+--- a/lib/multi.c
++++ b/lib/multi.c
+@@ -720,19 +720,21 @@ static CURLcode multi_done(struct Curl_easy *data,
+ /* Make sure that transfer client writes are really done now. */
+ result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature));
+
+- /* Inform connection filters that this transfer is done */
+- Curl_conn_ev_data_done(data, premature);
+-
+- process_pending_handles(data->multi); /* connection / multiplex */
+-
+ if(!result)
+ result = Curl_req_done(&data->req, data, premature);
+
++ /* Inform connection filters that this transfer is done and they
++ * need to give up any references to `data`. */
++ Curl_conn_ev_data_done(data, premature);
++
+ /* Under the potential connection pool's share lock, decide what to
+ * do with the transfer's connection. */
+ mdctx.premature = premature;
+ Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
+
++ if(data->multi)
++ process_pending_handles(data->multi); /* connection / multiplex */
++
+ /* flush the netrc cache */
+ Curl_netrc_cleanup(&data->state.netrc);
+ return result;
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 5abde681e7..71b7c06e71 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -67,6 +67,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-92390_p2.patch \
file://CVE-2026-92388_p1.patch \
file://CVE-2026-92388_p2.patch \
+ file://CVE-2026-92392.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 7/9] curl: fix CVE-2026-94266
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (5 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 6/9] curl: fix CVE-2026-92392 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 8/9] curl: fix CVE-2026-107929 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix referenced in [4], using [1],
and includes the invalid-URL correction in [2].
Remove credentials from CURLINFO_EFFECTIVE_URL, including credentials
loaded from netrc. Own the sanitized result in the easy handle and
release it on transfer reset and cleanup. Return the actual URL error
and a NULL result if parsing fails, rather than exposing an unfiltered
URL or reporting every failure as out of memory.
Retain tests 1876 and 1990. Select lib1990 explicitly for the older
runner and register both tests in curl 8.19.0's grouped lists.
Also apply Git's compatibility change [3] to the Wrynose Git 2.53.0
recipe. Git otherwise interprets stripped credentials as a redirect
and can discard authentication state. Keep this companion change with
the curl API change so the known Git regression is not introduced.
[1] https://github.com/curl/curl/commit/7a6bd027d0de2b6857472d35da090ef60cc29f19
[2] https://github.com/curl/curl/commit/589c35f0cb47266394367659648bf3efc03e3790
[3] https://github.com/git/git/commit/76524c2604bceee48481909e7824494498c39a34
[4] https://curl.se/docs/CVE-2026-94266.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
...l-stripping-creds-from-effective-url.patch | 140 +++++++++++
meta/recipes-devtools/git/git_2.53.0.bb | 1 +
.../curl/curl/CVE-2026-94266_p1.patch | 225 +++++++++++++++++
.../curl/curl/CVE-2026-94266_p2.patch | 235 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 2 +
5 files changed, 603 insertions(+)
create mode 100644 meta/recipes-devtools/git/git/0001-http-handle-curl-stripping-creds-from-effective-url.patch
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-94266_p1.patch
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-94266_p2.patch
diff --git a/meta/recipes-devtools/git/git/0001-http-handle-curl-stripping-creds-from-effective-url.patch b/meta/recipes-devtools/git/git/0001-http-handle-curl-stripping-creds-from-effective-url.patch
new file mode 100644
index 0000000000..597ef1c684
--- /dev/null
+++ b/meta/recipes-devtools/git/git/0001-http-handle-curl-stripping-creds-from-effective-url.patch
@@ -0,0 +1,140 @@
+From 76524c2604bceee48481909e7824494498c39a34 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Mon, 28 Sep 2026 00:01:49 -0400
+Subject: [PATCH] http: handle curl stripping creds from effective url
+
+When we detect that curl performed a redirect of a URL we requested, we
+update our base URL to match the new location and flush the http_auth
+credentials. This goes back to c93c92f309 (http: update base URLs when
+we see redirects, 2013-09-28).
+
+We detect the redirect by comparing the requested URL to the response
+from CURLINFO_EFFECTIVE_URL, using a simple string comparison. This has
+worked fine for years, but a change in the upcoming curl 8.23.0 adds a
+complication. If our URL directly contains credentials (like
+"https://user:pass@example.com/foo.git"), then as of 7a6bd027d0
+(getinfo: make sure CURLINFO_EFFECTIVE_URL does not contain creds,
+2026-09-21), curl will strip the credentials from what it returns (so
+just "https://example.com/foo.git" in this case).
+
+This breaks our direct string comparison, and we believe that we've been
+redirected. We flush our http_auth credentials, and now subsequent
+requests will use the reduced URL, causing us to re-request credentials
+from the user. Notably this causes t5550.15 (among others) to complain;
+it tries a clone with credentials in the URL, and fails if the user is
+prompted at all.
+
+We can handle this new behavior by doing a more careful comparison: if
+the direct string comparison fails, we'll strip out the credentials
+ourselves and compare. This is a little extra work, but in practice it
+should only happen once per process.
+
+I've used curl's curl_url() interface to do the stripping here, mostly
+because its behavior should match the stripping it does internally. And
+also, though we have code to parse a URL, we don't have any to
+reconstruct it, making a single string comparison hard.
+
+One alternative would be to parse with url_parse() or similar, and
+compare the individual fields (skipping username/password). I think that
+would probably also work in practice, but it seemed to me that the
+simplest change would be sticking with string comparisons.
+
+The curl_url() interface appeared in 7.62.0. We document that 7.61.0 is
+still supported, so I've made it conditional here. Only new versions
+strip the result from CURLINFO_EFFECTIVE_URL, so it's OK for very old
+versions to skip the extra comparison. Likewise if we encounter any
+errors, we just quietly skip the comparison. That's fine if you don't
+have creds in your URLs, and if you do, you'll get end up in the
+existing error path (a redirect warning, and eventually an auth
+failure).
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+
+Upstream-Status: Backport [https://github.com/git/git/commit/76524c2604bceee48481909e7824494498c39a34]
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ git-curl-compat.h | 7 +++++++
+ http.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 47 insertions(+), 1 deletion(-)
+
+diff --git a/git-curl-compat.h b/git-curl-compat.h
+index 659e5a3..e390e23 100644
+--- a/git-curl-compat.h
++++ b/git-curl-compat.h
+@@ -28,6 +28,13 @@
+ * introduced, oldest first, in the official version of cURL library.
+ */
+
++/**
++ * curl_url() interface added in 7.62.0 (October 2018)
++ */
++#if LIBCURL_VERSION_NUM >= 0x073e00
++#define GIT_CURL_HAVE_CURL_URL
++#endif
++
+ /**
+ * Versions before curl 7.66.0 (September 2019) required manually setting the
+ * transfer-encoding for a streaming POST; after that this is handled
+diff --git a/http.c b/http.c
+index 7815f14..6dd842a 100644
+--- a/http.c
++++ b/http.c
+@@ -2201,6 +2201,45 @@ static int http_request(const char *url,
+ return ret;
+ }
+
++#ifndef GIT_CURL_HAVE_CURL_URL
++#define strip_url_credential(in) NULL
++#else
++static char *strip_url_credential(const char *in)
++{
++ char *ret = NULL;
++ CURLU *url;
++
++ url = curl_url();
++ if (!url)
++ goto out;
++
++ if (curl_url_set(url, CURLUPART_URL, in, 0))
++ goto out;
++
++ curl_url_set(url, CURLUPART_USER, NULL, 0);
++ curl_url_set(url, CURLUPART_PASSWORD, NULL, 0);
++ curl_url_get(url, CURLUPART_URL, &ret, 0);
++
++out:
++ curl_url_cleanup(url);
++ return ret;
++}
++#endif
++
++static int match_effective_url(const char *asked, const char *got)
++{
++ char *stripped;
++ int ret;
++
++ if (!strcmp(asked, got))
++ return 1;
++
++ stripped = strip_url_credential(asked);
++ ret = stripped && !strcmp(stripped, got);
++ curl_free(stripped);
++ return ret;
++}
++
+ /*
+ * Update the "base" url to a more appropriate value, as deduced by
+ * redirects seen when requesting a URL starting with "url".
+@@ -2233,7 +2272,7 @@ static int update_url_from_redirect(struct strbuf *base,
+ const char *tail;
+ size_t new_len;
+
+- if (!strcmp(asked, got->buf))
++ if (match_effective_url(asked, got->buf))
+ return 0;
+
+ if (!skip_prefix(asked, base->buf, &tail))
+--
+2.35.6
diff --git a/meta/recipes-devtools/git/git_2.53.0.bb b/meta/recipes-devtools/git/git_2.53.0.bb
index 5244891113..202799e05c 100644
--- a/meta/recipes-devtools/git/git_2.53.0.bb
+++ b/meta/recipes-devtools/git/git_2.53.0.bb
@@ -10,6 +10,7 @@ PROVIDES:append:class-native = " git-replacement-native"
SRC_URI = "${KERNELORG_MIRROR}/software/scm/git/git-${PV}.tar.gz;name=tarball \
file://fixsort.patch \
file://0001-config.mak.uname-do-not-force-RHEL-7-specific-build-.patch \
+ file://0001-http-handle-curl-stripping-creds-from-effective-url.patch \
"
SRC_URI:append:class-nativesdk = " \
diff --git a/meta/recipes-support/curl/curl/CVE-2026-94266_p1.patch b/meta/recipes-support/curl/curl/CVE-2026-94266_p1.patch
new file mode 100644
index 0000000000..8b27e2859e
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-94266_p1.patch
@@ -0,0 +1,225 @@
+From 7a6bd027d0de2b6857472d35da090ef60cc29f19 Mon Sep 17 00:00:00 2001
+From: Daniel Stenberg <daniel@haxx.se>
+Date: Mon, 21 Sep 2026 11:13:13 +0200
+Subject: [PATCH] getinfo: make sure CURLINFO_EFFECTIVE_URL does not contain
+ creds
+
+Reported-by: Joshua Rogers
+
+Verified in test 1876
+
+Closes #23016
+
+CVE: CVE-2026-94266
+Upstream-Status: Backport [https://github.com/curl/curl/commit/7a6bd027d0de2b6857472d35da090ef60cc29f19]
+
+Backport Changes:
+- Use Curl_safefree() in initialization, transfer reset and cleanup;
+ curl 8.19.0 does not have the newer curlx_safefree() helper.
+- Register test1876 in the older grouped TESTCASES list instead of
+ upstream's one-entry-per-line list.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/getinfo.c | 47 +++++++++++++++++++++++++----
+ lib/transfer.c | 1 +
+ lib/url.c | 1 +
+ lib/urldata.h | 1 +
+ tests/data/Makefile.am | 2 +-
+ tests/data/test1876 | 67 ++++++++++++++++++++++++++++++++++++++++++
+ 6 files changed, 113 insertions(+), 6 deletions(-)
+ create mode 100644 tests/data/test1876
+
+diff --git a/lib/getinfo.c b/lib/getinfo.c
+index dedafd382e..920f1f3a0c 100644
+--- a/lib/getinfo.c
++++ b/lib/getinfo.c
+@@ -72,6 +72,7 @@ void Curl_initinfo(struct Curl_easy *data)
+
+ curlx_free(info->wouldredirect);
+ info->wouldredirect = NULL;
++ Curl_safefree(info->effective_url);
+
+ memset(&info->primary, 0, sizeof(info->primary));
+ info->retry_after = 0;
+@@ -84,15 +85,51 @@ void Curl_initinfo(struct Curl_easy *data)
+ #endif
+ }
+
++static CURLcode effective_url(struct Curl_easy *data,
++ const char **param_charp)
++{
++ const char *s = Curl_bufref_ptr(&data->state.url);
++ *param_charp = s ? s : "";
++ if(s) {
++ char *url = NULL;
++ /* Make a copy of the URL without credentials */
++ CURLU *u = curl_url();
++ CURLUcode uc;
++ if(!u)
++ return CURLE_OUT_OF_MEMORY;
++
++ uc = curl_url_set(u, CURLUPART_URL, s,
++ CURLU_NON_SUPPORT_SCHEME |
++ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
++ if(!uc)
++ uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
++ if(!uc)
++ uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
++ if(!uc)
++ uc = curl_url_get(u, CURLUPART_URL, &url, CURLU_GET_EMPTY);
++
++ curl_url_cleanup(u);
++
++ if(uc || !url)
++ return CURLE_OUT_OF_MEMORY;
++ if(strcmp(url, s)) {
++ /* they are different */
++ curlx_free(data->info.effective_url);
++ data->info.effective_url = url;
++ *param_charp = url;
++ }
++ else
++ curl_free(url);
++ }
++ return CURLE_OK;
++}
++
+ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
+ const char **param_charp)
+ {
+ switch(info) {
+- case CURLINFO_EFFECTIVE_URL: {
+- const char *s = Curl_bufref_ptr(&data->state.url);
+- *param_charp = s ? s : "";
+- }
+- break;
++ case CURLINFO_EFFECTIVE_URL:
++ return effective_url(data, param_charp);
+ case CURLINFO_EFFECTIVE_METHOD: {
+ const char *m = data->set.str[STRING_CUSTOMREQUEST];
+ if(!m) {
+diff --git a/lib/transfer.c b/lib/transfer.c
+index 9b55913265..fc7a5177d1 100644
+--- a/lib/transfer.c
++++ b/lib/transfer.c
+@@ -534,6 +534,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
+ data->state.authhost.want = data->set.httpauth;
+ data->state.authproxy.want = data->set.proxyauth;
+ Curl_safefree(data->info.wouldredirect);
++ Curl_safefree(data->info.effective_url);
+ Curl_data_priority_clear_state(data);
+ if(data->set.http_auto_referer)
+ Curl_bufref_free(&data->state.referer);
+diff --git a/lib/url.c b/lib/url.c
+index 4a01bafae1..ba3cb7f0b6 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -284,6 +284,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
+ Curl_safefree(data->state.most_recent_ftp_entrypath);
+ Curl_safefree(data->info.contenttype);
+ Curl_safefree(data->info.wouldredirect);
++ Curl_safefree(data->info.effective_url);
+
+ data_priority_cleanup(data);
+
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 0bc9ade696..8b18ce590a 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -756,6 +756,7 @@ struct PureInfo {
+ uint32_t httpauthpicked; /* selected host auth type */
+ char *contenttype; /* the content type of the object */
+ char *wouldredirect; /* URL this would have been redirected to if asked to */
++ char *effective_url; /* when needed */
+ curl_off_t retry_after; /* info from Retry-After: header */
+ uint32_t header_size; /* size of read header(s) in bytes */
+
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index 08ece8bc1c..d6aaa71f5d 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -232,7 +232,7 @@ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
+ test1708 test1709 test1710 test1711 test1712 \
+ test1718 \
+ \
+-test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1862 \
++test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1862 test1876 \
+ \
+ test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \
+ test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \
+diff --git a/tests/data/test1876 b/tests/data/test1876
+new file mode 100644
+index 0000000000..4fc3b9fd02
+--- /dev/null
++++ b/tests/data/test1876
+@@ -0,0 +1,67 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++netrc
++</keywords>
++</info>
++
++# Server-side
++<reply>
++<data crlf="headers" nocheck="yes">
++HTTP/1.1 301 redir
++Content-Length: 0
++Content-Type: text/html
++Location: %TESTNUMBER0002
++
++</data>
++
++<data2 crlf="headers">
++HTTP/1.1 200 OK
++Content-Length: 0
++Content-Type: text/html
++
++</data2>
++
++</reply>
++
++# Client-side
++<client>
++<server>
++http
++</server>
++<name>
++verify no credentials present in effective URL after redirect
++</name>
++<file name="%LOGDIR/netrc">
++machine %HOSTIP
++login netrcuser
++password netrcSECRET123
++</file>
++
++<command option="no-include">
++http://%HOSTIP:%HTTPPORT/%TESTNUMBER --netrc-optional --netrc-file %LOGDIR/netrc -L -w 'EFFECTIVE_URL=%{url_effective}\n'
++</command>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++<protocol crlf="headers">
++GET /%TESTNUMBER HTTP/1.1
++Host: %HOSTIP:%HTTPPORT
++Authorization: Basic %b64[netrcuser:netrcSECRET123]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++
++GET /%TESTNUMBER0002 HTTP/1.1
++Host: %HOSTIP:%HTTPPORT
++Authorization: Basic %b64[netrcuser:netrcSECRET123]b64%
++User-Agent: curl/%VERSION
++Accept: */*
++
++</protocol>
++<stdout>
++EFFECTIVE_URL=http://%HOSTIP:%HTTPPORT/%TESTNUMBER0002
++</stdout>
++</verify>
++</testcase>
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl/CVE-2026-94266_p2.patch b/meta/recipes-support/curl/curl/CVE-2026-94266_p2.patch
new file mode 100644
index 0000000000..54d10dbb0f
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-94266_p2.patch
@@ -0,0 +1,235 @@
+From 589c35f0cb47266394367659648bf3efc03e3790 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A1t=C3=A9=20Szab=C3=B3?= <mszabo@slack-corp.com>
+Date: Fri, 16 Oct 2026 22:23:57 +0200
+Subject: [PATCH] getinfo: clarify invalid URL handling for
+ CURLINFO_EFFECTIVE_URL
+
+Since 7a6bd02 `CURLINFO_EFFECTIVE_URL` returns `CURLE_OUT_OF_MEMORY` if
+the effective URL couldn't be parsed, which surprised some code that
+expected it to be mostly always present in normal operation (we interact
+with `libcurl` over HHVM's wrapper, which, akin to PHP, wraps[1]
+easy_getinfo and returns a dict with optional entries, so it's been easy
+for things to assume that `url` would always be set).
+
+Clarify the new behaviour in docs and have `CURLINFO_EFFECTIVE_URL`
+return the relevant parsing error code rather than `CURLE_OUT_OF_MEMORY`
+for invalid URLs.
+
+[1] https://github.com/facebook/hhvm/blob/c83584cad90cc9327d0b75175135d46882d8dc1f/hphp/runtime/ext/curl/ext_curl.cpp#L208
+
+Closes #23444
+
+CVE: CVE-2026-94266
+Upstream-Status: Backport [https://github.com/curl/curl/commit/589c35f0cb47266394367659648bf3efc03e3790]
+
+Backport Changes:
+- Register test1990 without indentation in curl 8.19.0's grouped
+ TESTCASES list; upstream indents its entry by two spaces.
+- Add <tool>lib1990</tool> to test1990. The 8.19.0 runner does not
+ select a library tool from the libtest keyword, as upstream does.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md | 8 +++
+ lib/getinfo.c | 10 +++-
+ tests/data/Makefile.am | 1 +
+ tests/data/test1990 | 31 ++++++++++
+ tests/libtest/Makefile.inc | 1 +
+ tests/libtest/lib1990.c | 63 +++++++++++++++++++++
+ 6 files changed, 111 insertions(+), 3 deletions(-)
+ create mode 100644 tests/data/test1990
+ create mode 100644 tests/libtest/lib1990.c
+
+diff --git a/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md b/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md
+index f7adcd810b..12347bfd5f 100644
+--- a/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md
++++ b/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md
+@@ -32,6 +32,14 @@ Pass in a pointer to a char pointer and get the last used effective URL.
+ In cases when you have asked libcurl to follow redirects, it may not be the same
+ value you set with CURLOPT_URL(3).
+
++Any credentials (username and password) present in the URL are removed from
++the returned string.
++
++If no URL has been used yet, **urlp** points to an empty string.
++
++If the URL set with CURLOPT_URL(3) cannot be parsed as a valid URL, **urlp** is
++set to NULL and an error is returned, typically *CURLE_URL_MALFORMAT*.
++
+ The **urlp** pointer is NULL or points to private memory. You **must not**
+ free it. It memory gets freed automatically when you call curl_easy_cleanup(3)
+ on the corresponding curl handle.
+diff --git a/lib/getinfo.c b/lib/getinfo.c
+index 920f1f3a0c..ce4ac77f52 100644
+--- a/lib/getinfo.c
++++ b/lib/getinfo.c
+@@ -29,6 +29,7 @@
+ #include "vtls/vtls.h"
+ #include "connect.h" /* Curl_getconnectinfo() */
+ #include "bufref.h"
++#include "url.h" /* Curl_uc_to_curlcode() */
+ #include "curlx/strparse.h"
+
+ /*
+@@ -89,7 +90,8 @@ static CURLcode effective_url(struct Curl_easy *data,
+ const char **param_charp)
+ {
+ const char *s = Curl_bufref_ptr(&data->state.url);
+- *param_charp = s ? s : "";
++ /* never return the URL unfiltered */
++ *param_charp = s ? NULL : "";
+ if(s) {
+ char *url = NULL;
+ /* Make a copy of the URL without credentials */
+@@ -111,15 +113,17 @@ static CURLcode effective_url(struct Curl_easy *data,
+ curl_url_cleanup(u);
+
+ if(uc || !url)
+- return CURLE_OUT_OF_MEMORY;
++ return uc ? Curl_uc_to_curlcode(uc) : CURLE_OUT_OF_MEMORY;
+ if(strcmp(url, s)) {
+ /* they are different */
+ curlx_free(data->info.effective_url);
+ data->info.effective_url = url;
+ *param_charp = url;
+ }
+- else
++ else {
+ curl_free(url);
++ *param_charp = s;
++ }
+ }
+ return CURLE_OK;
+ }
+diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
+index d6aaa71f5d..b348f37bf8 100644
+--- a/tests/data/Makefile.am
++++ b/tests/data/Makefile.am
+@@ -243,6 +243,7 @@ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
+ test1955 test1956 test1957 test1958 test1959 test1960 test1964 \
+ test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \
+ test1978 test1979 test1980 test1981 \
++test1990 \
+ \
+ test2000 test2001 test2002 test2003 test2004 test2005 test2006 \
+ test2009 test2010 test2011 \
+diff --git a/tests/data/test1990 b/tests/data/test1990
+new file mode 100644
+index 0000000000..61ef3ca95d
+--- /dev/null
++++ b/tests/data/test1990
+@@ -0,0 +1,31 @@
++<?xml version="1.0" encoding="US-ASCII"?>
++<testcase>
++<info>
++<keywords>
++CURLINFO_EFFECTIVE_URL
++libtest
++</keywords>
++</info>
++
++# Client-side
++<client>
++<tool>
++lib1990
++</tool>
++<name>
++CURLINFO_EFFECTIVE_URL with no URL and an invalid URL
++</name>
++<command>
++-
++</command>
++</client>
++
++# Verify data after the test has been "shot"
++<verify>
++<stdout>
++getinfo: 0, url: ''
++perform: 3
++getinfo: 3, url: (NULL)
++</stdout>
++</verify>
++</testcase>
+diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
+index e494189f69..fc8534234d 100644
+--- a/tests/libtest/Makefile.inc
++++ b/tests/libtest/Makefile.inc
+@@ -111,6 +111,7 @@ TESTS_C = \
+ lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \
+ lib1964.c lib1970.c \
+ lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \
++ lib1990.c \
+ lib2023.c lib2032.c lib2082.c \
+ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \
+ lib2402.c lib2404.c lib2405.c \
+diff --git a/tests/libtest/lib1990.c b/tests/libtest/lib1990.c
+new file mode 100644
+index 0000000000..96e18d4033
+--- /dev/null
++++ b/tests/libtest/lib1990.c
+@@ -0,0 +1,63 @@
++/***************************************************************************
++ * _ _ ____ _
++ * Project ___| | | | _ \| |
++ * / __| | | | |_) | |
++ * | (__| |_| | _ <| |___
++ * \___|\___/|_| \_\_____|
++ *
++ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
++ *
++ * This software is licensed as described in the file COPYING, which
++ * you should have received as part of this distribution. The terms
++ * are also available at https://curl.se/docs/copyright.html.
++ *
++ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
++ * copies of the Software, and permit persons to whom the Software is
++ * furnished to do so, under the terms of the COPYING file.
++ *
++ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
++ * KIND, either express or implied.
++ *
++ * SPDX-License-Identifier: curl
++ *
++ ***************************************************************************/
++#include "first.h"
++
++static void show_effective_url(CURL *curl)
++{
++ char untouched[] = "untouched";
++ char *url = untouched;
++ CURLcode result = curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
++ if(url)
++ curl_mprintf("getinfo: %d, url: '%s'\n", (int)result, url);
++ else
++ curl_mprintf("getinfo: %d, url: (NULL)\n", (int)result);
++}
++
++static CURLcode test_lib1990(const char *URL)
++{
++ CURL *curl;
++ CURLcode result;
++
++ (void)URL;
++ curl_global_init(CURL_GLOBAL_ALL);
++ curl = curl_easy_init();
++ if(!curl) {
++ curl_global_cleanup();
++ return TEST_ERR_EASY_INIT;
++ }
++
++ /* no URL used yet */
++ show_effective_url(curl);
++
++ /* an invalid URL with credentials must not be returned */
++ curl_easy_setopt(curl, CURLOPT_URL, "http://user:secret@[invalid/");
++ result = curl_easy_perform(curl);
++ curl_mprintf("perform: %d\n", (int)result);
++ show_effective_url(curl);
++
++ curl_easy_cleanup(curl);
++ curl_global_cleanup();
++
++ return CURLE_OK;
++}
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 71b7c06e71..a4f7b466c3 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -68,6 +68,8 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-92388_p1.patch \
file://CVE-2026-92388_p2.patch \
file://CVE-2026-92392.patch \
+ file://CVE-2026-94266_p1.patch \
+ file://CVE-2026-94266_p2.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 8/9] curl: fix CVE-2026-107929
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (6 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 7/9] curl: fix CVE-2026-94266 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 9/9] curl: fix CVE-2026-87866 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 9:00 ` [OE-core][wrynose][PATCH 0/9] curl: Security fixes Yoann Congal
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Keep the complete CURLOPT_INTERFACE option in the connection and
compare it when choosing a connection to reuse. This includes if!,
host! and ifhost! prefixes, so a transfer cannot silently inherit an
older source binding. Parse that owned option again when binding the
socket and release temporary strings on success and failure.
Adapt the old string helpers, setopt parameters and nested device-bind
check while preserving the complete upstream matching and cleanup.
[1] https://github.com/curl/curl/commit/d37d79df1e01066417aff0a9e6bc675edad3edba
[2] https://curl.se/docs/CVE-2026-107929.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-107929.patch | 470 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 471 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-107929.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-107929.patch b/meta/recipes-support/curl/curl/CVE-2026-107929.patch
new file mode 100644
index 0000000000..873b03fd5b
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-107929.patch
@@ -0,0 +1,470 @@
+From d37d79df1e01066417aff0a9e6bc675edad3edba Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Fri, 9 Oct 2026 09:51:12 +0200
+Subject: [PATCH] url: CURLOPT_INTERFACE fix connection reuse match
+
+Remember the full option string at the connection for use in reuse
+matching and cf-socket binding.
+
+Parse the string when set for syntax check, but remember the unparsed
+version and use that in connection matching.
+
+Parse that string again when the socket is set up.
+
+Closes #23326
+
+CVE: CVE-2026-107929
+Upstream-Status: Backport [https://github.com/curl/curl/commit/d37d79df1e01066417aff0a9e6bc675edad3edba]
+
+Backport Changes:
+- Use curl 8.19.0's data->set.str[], Curl_setstropt() and
+ Curl_safefree() instead of the newer string and free helpers.
+- Change the older setstropt_interface() parameters and its caller
+ to pass the easy handle; remove the obsolete output assertions.
+- Keep bindlocal()'s older nested SO_BINDTODEVICE test. Route its
+ success path through cleanup and keep two blank separators around
+ the new option parsing and binding checks.
+- Replace the older braced local-bind comparison and its split
+ comment ending with upstream's new matching expression.
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/cf-socket.c | 135 +++++++++++++++++++++++++++++++-----------------
+ lib/setopt.c | 34 +++---------
+ lib/url.c | 48 +++++++++--------
+ lib/urldata.h | 6 +--
+ 4 files changed, 126 insertions(+), 97 deletions(-)
+
+diff --git a/lib/cf-socket.c b/lib/cf-socket.c
+index 2ebbe4d..f3978e2 100644
+--- a/lib/cf-socket.c
++++ b/lib/cf-socket.c
+@@ -464,11 +464,13 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
+ * len [in] - length of the input string.
+ * dev [in/out] - address where a pointer to newly allocated memory
+ * holding the interface-or-host will be stored upon
+- * completion.
++ * completion or NULL if the caller is not interested.
+ * iface [in/out] - address where a pointer to newly allocated memory
+- * holding the interface will be stored upon completion.
++ * holding the interface will be stored upon completion
++ * or NULL if the caller is not interested.
+ * host [in/out] - address where a pointer to newly allocated memory
+- * holding the host will be stored upon completion.
++ * holding the host will be stored upon completion
++ * or NULL if the caller is not interested.
+ *
+ * Returns CURLE_OK on success.
+ */
+@@ -480,27 +482,36 @@ CURLcode Curl_parse_interface(const char *input,
+ static const char if_host_prefix[] = "ifhost!";
+ size_t len;
+
+- DEBUGASSERT(dev);
+- DEBUGASSERT(iface);
+- DEBUGASSERT(host);
+-
+ len = strlen(input);
+ if(len > 512)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
++ if(dev)
++ *dev = NULL;
++ if(iface)
++ *iface = NULL;
++ if(host)
++ *host = NULL;
++
+ if(!strncmp(if_prefix, input, strlen(if_prefix))) {
+ input += strlen(if_prefix);
+ if(!*input)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- *iface = curlx_memdup0(input, len - strlen(if_prefix));
+- return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ if(iface) {
++ *iface = curlx_memdup0(input, len - strlen(if_prefix));
++ return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ }
++ return CURLE_OK;
+ }
+ else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
+ input += strlen(host_prefix);
+ if(!*input)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- *host = curlx_memdup0(input, len - strlen(host_prefix));
+- return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ if(host) {
++ *host = curlx_memdup0(input, len - strlen(host_prefix));
++ return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ }
++ return CURLE_OK;
+ }
+ else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
+ const char *host_part;
+@@ -509,23 +520,30 @@ CURLcode Curl_parse_interface(const char *input,
+ host_part = memchr(input, '!', len);
+ if(!host_part || !*(host_part + 1))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- *iface = curlx_memdup0(input, host_part - input);
+- if(!*iface)
+- return CURLE_OUT_OF_MEMORY;
++ if(iface) {
++ *iface = curlx_memdup0(input, host_part - input);
++ if(!*iface)
++ return CURLE_OUT_OF_MEMORY;
++ }
+ ++host_part;
+- *host = curlx_memdup0(host_part, len - (host_part - input));
+- if(!*host) {
+- curlx_free(*iface);
+- *iface = NULL;
+- return CURLE_OUT_OF_MEMORY;
++ if(host) {
++ *host = curlx_memdup0(host_part, len - (host_part - input));
++ if(!*host) {
++ if(iface)
++ Curl_safefree(*iface);
++ return CURLE_OUT_OF_MEMORY;
++ }
+ }
+ return CURLE_OK;
+ }
+
+ if(!*input)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- *dev = curlx_memdup0(input, len);
+- return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ if(dev) {
++ *dev = curlx_memdup0(input, len);
++ return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
++ }
++ return CURLE_OK;
+ }
+
+ #ifndef CURL_DISABLE_BINDLOCAL
+@@ -545,27 +563,38 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ "random" */
+ /* how many port numbers to try to bind to, increasing one at a time */
+ int portnum = data->set.localportrange;
+- const char *dev = data->set.str[STRING_DEVICE];
+- const char *iface_input = data->set.str[STRING_INTERFACE];
+- const char *host_input = data->set.str[STRING_BINDHOST];
+- const char *iface = iface_input ? iface_input : dev;
+- const char *host = host_input ? host_input : dev;
++ char *dev_in = NULL, *iface_in = NULL, *host_in = NULL;
++ const char *iface = NULL, *host = NULL;
+ int error;
+ #ifdef IP_BIND_ADDRESS_NO_PORT
+ int on = 1;
+ #endif
++ CURLcode result = CURLE_OK;
++
+ #ifndef USE_IPV6
+ (void)scope;
+ #endif
+
+- /*************************************************************
+- * Select device to bind socket to
+- *************************************************************/
++ if(conn->interface_opts) {
++ /*************************************************************
++ * Select device to bind socket to
++ *************************************************************/
++ result = Curl_parse_interface(conn->interface_opts,
++ &dev_in, &iface_in, &host_in);
++ if(result)
++ goto out;
++ /* This is how we use the values */
++ iface = iface_in ? iface_in : dev_in;
++ host = host_in ? host_in : dev_in;
++ }
++
+ if(!iface && !host && !port)
+- /* no local kind of binding was requested */
+- return CURLE_OK;
+- else if(iface && (strlen(iface) >= 255))
+- return CURLE_BAD_FUNCTION_ARGUMENT;
++ goto out;
++
++ if(iface && (strlen(iface) >= 255)) {
++ result = CURLE_BAD_FUNCTION_ARGUMENT;
++ goto out;
++ }
+
+ memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
+
+@@ -593,14 +622,15 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ * succeeds it means the parameter was a valid interface and not an IP
+ * address. Return immediately.
+ */
+- if(!host_input) {
++ if(!host_in) {
+ infof(data, "socket successfully bound to interface '%s'", iface);
+- return CURLE_OK;
++ result = CURLE_OK;
++ goto out;
+ }
+ }
+ }
+ #endif
+- if(!host_input) {
++ if(!host_in) {
+ /* Discover IP from input device, then bind to it */
+ if2ip_result = Curl_if2ip(af,
+ #ifdef USE_IPV6
+@@ -610,18 +640,20 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ }
+ switch(if2ip_result) {
+ case IF2IP_NOT_FOUND:
+- if(iface_input && !host_input) {
++ if(iface_in && !host_in) {
+ /* Do not fall back to treating it as a hostname */
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "Could not bind to interface '%s' with errno %d: %s",
+ iface, error, curlx_strerror(error, buffer, sizeof(buffer)));
+- return CURLE_INTERFACE_FAILED;
++ result = CURLE_INTERFACE_FAILED;
++ goto out;
+ }
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ /* Signal the caller to try another address family if available */
+- return CURLE_UNSUPPORTED_PROTOCOL;
++ result = CURLE_UNSUPPORTED_PROTOCOL;
++ goto out;
+ case IF2IP_FOUND:
+ /*
+ * We now have the numerical IP address in the 'myhost' buffer
+@@ -632,7 +664,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ done = 1;
+ break;
+ }
+- if(!iface_input || host_input) {
++ if(!iface_in || host_in) {
+ /*
+ * This was not an interface, resolve the name as a hostname
+ * or IP number
+@@ -659,7 +691,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ if(af != h_af) {
+ /* bad IP version combo, signal the caller to try another address
+ family if available */
+- return CURLE_UNSUPPORTED_PROTOCOL;
++ result = CURLE_UNSUPPORTED_PROTOCOL;
++ goto out;
+ }
+ done = 1;
+ }
+@@ -692,8 +725,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ present, is known to be numeric */
+ curl_off_t scope_id;
+ if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
+- &scope_id, UINT_MAX))
+- return CURLE_UNSUPPORTED_PROTOCOL;
++ &scope_id, UINT_MAX)) {
++ result = CURLE_UNSUPPORTED_PROTOCOL;
++ goto out;
++ }
+ si6->sin6_scope_id = (unsigned int)scope_id;
+ }
+ #endif
+@@ -720,7 +755,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "Could not bind to '%s' with errno %d: %s", host,
+ error, curlx_strerror(error, buffer, sizeof(buffer)));
+- return CURLE_INTERFACE_FAILED;
++ result = CURLE_INTERFACE_FAILED;
++ goto out;
+ }
+ }
+ else {
+@@ -747,7 +783,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ /* we succeeded to bind */
+ infof(data, "Local port: %hu", port);
+ conn->bits.bound = TRUE;
+- return CURLE_OK;
++ result = CURLE_OK;
++ goto out;
+ }
+
+ if(--portnum > 0) {
+@@ -773,7 +810,13 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ error, curlx_strerror(error, buffer, sizeof(buffer)));
+ }
+
+- return CURLE_INTERFACE_FAILED;
++ result = CURLE_INTERFACE_FAILED;
++
++out:
++ curlx_free(dev_in);
++ curlx_free(iface_in);
++ curlx_free(host_in);
++ return result;
+ }
+ #endif
+
+diff --git a/lib/setopt.c b/lib/setopt.c
+index ae77ae8..4c634a8 100644
+--- a/lib/setopt.c
++++ b/lib/setopt.c
+@@ -160,33 +160,18 @@ static CURLcode setstropt_userpwd(const char *option, char **userp,
+ return CURLE_OK;
+ }
+
+-static CURLcode setstropt_interface(char *option, char **devp,
+- char **ifacep, char **hostp)
++static CURLcode setstropt_interface(struct Curl_easy *data, char *option)
+ {
+- char *dev = NULL;
+- char *iface = NULL;
+- char *host = NULL;
+- CURLcode result;
+-
+- DEBUGASSERT(devp);
+- DEBUGASSERT(ifacep);
+- DEBUGASSERT(hostp);
+-
++ if(option && !option[0])
++ option = NULL;
+ if(option) {
+- /* Parse the interface details if set, otherwise clear them all */
+- result = Curl_parse_interface(option, &dev, &iface, &host);
++ /* Parse for checking syntax */
++ CURLcode result = Curl_parse_interface(option, NULL, NULL, NULL);
+ if(result)
+ return result;
++ return Curl_setstropt(&data->set.str[STRING_INTERFACE_OPT], option);
+ }
+- curlx_free(*devp);
+- *devp = dev;
+-
+- curlx_free(*ifacep);
+- *ifacep = iface;
+-
+- curlx_free(*hostp);
+- *hostp = host;
+-
++ Curl_safefree(data->set.str[STRING_INTERFACE_OPT]);
+ return CURLE_OK;
+ }
+
+@@ -2267,10 +2252,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
+ * Set what interface or address/hostname to bind the socket to when
+ * performing an operation and thus what from-IP your connection will use.
+ */
+- return setstropt_interface(ptr,
+- &s->str[STRING_DEVICE],
+- &s->str[STRING_INTERFACE],
+- &s->str[STRING_BINDHOST]);
++ return setstropt_interface(data, ptr);
+ case CURLOPT_ISSUERCERT:
+ /*
+ * Set Issuer certificate file
+diff --git a/lib/url.c b/lib/url.c
+index 4a01baf..1b6e9e3 100644
+--- a/lib/url.c
++++ b/lib/url.c
+@@ -550,7 +550,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
+ Curl_safefree(conn->host.rawalloc); /* hostname buffer */
+ Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
+ Curl_safefree(conn->secondaryhostname);
+- Curl_safefree(conn->localdev);
++ Curl_safefree(conn->interface_opts);
+ Curl_ssl_conn_config_cleanup(conn);
+
+ #ifdef USE_UNIX_SOCKETS
+@@ -773,24 +773,29 @@ static bool url_match_connect_config(struct connectdata *conn,
+ m->data->set.ipver != conn->ip_version)
+ return FALSE;
+
+- if(m->needle->localdev || m->needle->localport) {
++ /* When needle has specific local bind settings, those have to match.
++ * If it does not, any other connection might do when it comes to ports. */
++ if(m->needle->localport &&
++ ((conn->localport != m->needle->localport) ||
++ (conn->localportrange != m->needle->localportrange)))
++ return FALSE;
++ /* For interface related bind options, we require a full match
++ * as connection might go totally different ways (VPNs, for example). */
++ if((m->needle->interface_opts != conn->interface_opts) &&
++ (!m->needle->interface_opts || !conn->interface_opts ||
++ strcmp(conn->interface_opts, m->needle->interface_opts)))
++ return FALSE;
++
++ if((m->needle->interface_opts || m->needle->localport) &&
+ /* If we are bound to a specific local end (IP+port), we must not reuse a
+ random other one, although if we did not ask for a particular one we
+- can reuse one that was bound.
+-
+- This comparison is a bit rough and too strict. Since the input
+- parameters can be specified in numerous ways and still end up the same
+- it would take a lot of processing to make it really accurate. Instead,
+- this matching will assume that reuses of bound connections will most
+- likely also reuse the exact same binding parameters and missing out a
+- few edge cases should not hurt anyone much.
+- */
+- if((conn->localport != m->needle->localport) ||
+- (conn->localportrange != m->needle->localportrange) ||
+- (m->needle->localdev &&
+- (!conn->localdev || strcmp(conn->localdev, m->needle->localdev))))
+- return FALSE;
+- }
++ can reuse one that was bound. */
++ ((conn->localport != m->needle->localport) ||
++ (conn->localportrange != m->needle->localportrange) ||
++ (m->needle->interface_opts &&
++ (!conn->interface_opts ||
++ strcmp(conn->interface_opts, m->needle->interface_opts)))))
++ return FALSE;
+
+ if(m->needle->bits.conn_to_host != conn->bits.conn_to_host)
+ /* do not mix connections that use the "connect to host" feature and
+@@ -1443,9 +1448,10 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
+ conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
+
+ /* Store the local bind parameters that will be used for this connection */
+- if(data->set.str[STRING_DEVICE]) {
+- conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]);
+- if(!conn->localdev)
++ if(data->set.str[STRING_INTERFACE_OPT]) {
++ conn->interface_opts =
++ curlx_strdup(data->set.str[STRING_INTERFACE_OPT]);
++ if(!conn->interface_opts)
+ goto error;
+ }
+ #ifndef CURL_DISABLE_BINDLOCAL
+@@ -1465,7 +1471,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
+ return conn;
+ error:
+
+- curlx_free(conn->localdev);
++ curlx_free(conn->interface_opts);
+ curlx_free(conn);
+ return NULL;
+ }
+diff --git a/lib/urldata.h b/lib/urldata.h
+index 0bc9ade..327c9f4 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -694,7 +694,7 @@ struct connectdata {
+ made and will serve the purpose of being used for comparison reasons so
+ that subsequent bound-requested connections are not accidentally reusing
+ wrong connections. */
+- char *localdev;
++ char *interface_opts;
+ uint16_t localportrange;
+ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
+ int socks5_gssapi_enctype;
+@@ -1167,9 +1167,7 @@ enum dupstring {
+ #endif
+ STRING_CUSTOMREQUEST, /* HTTP/FTP/RTSP request/method to use */
+ STRING_DEFAULT_PROTOCOL, /* Protocol to use when the URL does not specify */
+- STRING_DEVICE, /* local network interface/address to use */
+- STRING_INTERFACE, /* local network interface to use */
+- STRING_BINDHOST, /* local address to use */
++ STRING_INTERFACE_OPT, /* CURLOPT_INTERFACE config */
+ STRING_ENCODING, /* Accept-Encoding string */
+ #ifndef CURL_DISABLE_FTP
+ STRING_FTP_ACCOUNT, /* ftp account data */
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index a4f7b466c3..026b5a3638 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -70,6 +70,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-92392.patch \
file://CVE-2026-94266_p1.patch \
file://CVE-2026-94266_p2.patch \
+ file://CVE-2026-107929.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* [OE-core][wrynose][PATCH 9/9] curl: fix CVE-2026-87866
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (7 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 8/9] curl: fix CVE-2026-107929 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 7:19 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
2026-11-06 9:00 ` [OE-core][wrynose][PATCH 0/9] curl: Security fixes Yoann Congal
9 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-06 7:19 UTC (permalink / raw)
To: openembedded-core; +Cc: Darsh Kelaiya
From: Darsh Kelaiya <dkelaiya@cisco.com>
This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].
Require the two certificate validity time fields to consume their
complete ASN.1 element before parsing the subject and public key.
This prevents wolfSSL pinning from accepting a different embedded key
than the key used for the TLS connection.
Carry the shared parser's byte-pointer updates and all TLS caller
changes so downstream layers enabling wolfSSL receive the source fix.
Retain the valid and malformed certificate regression in unit1657.
Adapt the older conversion helpers and unit-test entry point. Document
the omitted type-only updates to three tests absent from curl 8.19.0.
[1] https://github.com/curl/curl/commit/ed131747c1a3dc7f816d26724c44537ac8acba44
[2] https://curl.se/docs/CVE-2026-87866.html
Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
---
.../curl/curl/CVE-2026-87866.patch | 750 ++++++++++++++++++
meta/recipes-support/curl/curl_8.19.0.bb | 1 +
2 files changed, 751 insertions(+)
create mode 100644 meta/recipes-support/curl/curl/CVE-2026-87866.patch
diff --git a/meta/recipes-support/curl/curl/CVE-2026-87866.patch b/meta/recipes-support/curl/curl/CVE-2026-87866.patch
new file mode 100644
index 0000000000..c42aee1f0f
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-87866.patch
@@ -0,0 +1,750 @@
+From ed131747c1a3dc7f816d26724c44537ac8acba44 Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Wed, 2 Sep 2026 12:25:50 +0200
+Subject: [PATCH] x509: stricter parsing
+
+Check that the "validity" ASN.1 field has a length matching the
+contained date elements and fail the parsing otherwise.
+
+This is a precaution against malformed cert data that should have been
+caught by the TLS backend already. A bit added defense.
+
+Closes #22799
+
+CVE: CVE-2026-87866
+Upstream-Status: Backport [https://github.com/curl/curl/commit/ed131747c1a3dc7f816d26724c44537ac8acba44]
+
+Backport Changes:
+- Apply the byte-pointer updates to curl 8.19.0's older ASN.1 helpers.
+ Retain its Boolean, bit-string, integer and OID conversion logic,
+ including OID2str()'s symbolic argument in its updated signature.
+- Keep getASN1Element_()'s older flat length branches while applying
+ the byte reads and bounds comparison.
+- Expose getASN1Element() with UNITTEST and remove the older
+ Curl_x509_getASN1Element() wrapper. Add the private test prototype
+ to x509asn1.h: the old generator extracts only single-line prototypes.
+ Guard the production forward declaration to avoid a duplicate in
+ unit-test builds; retain WARN_UNUSED_RESULT on both declarations.
+- Adapt test1657 from the old CURLcode wrapper to the parser's pointer
+ result with the same success/error expectations. Rename its
+ inactive-build message to match the parser name.
+- Omit the type-only updates to unit1666, unit1667 and unit1676. These
+ tests are absent in 8.19.0; [1], [2] and [3] introduced them for OID
+ conversion, ASN.1-to-string conversion and DH public-key extraction.
+
+[1] https://github.com/curl/curl/commit/673e14cd33ef720b8c2b7e3485084e6e41a2c9dd
+[2] https://github.com/curl/curl/commit/14782b36fea2a5f69029b2b898302b6ba5aef838
+[3] https://github.com/curl/curl/commit/61d59c9e39b451b30feb431b780a5cc325757921
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/vtls/gtls.c | 4 +-
+ lib/vtls/mbedtls.c | 5 +-
+ lib/vtls/rustls.c | 13 ++---
+ lib/vtls/schannel.c | 10 ++--
+ lib/vtls/wolfssl.c | 4 +-
+ lib/vtls/x509asn1.c | 124 +++++++++++++++++++++---------------------
+ lib/vtls/x509asn1.h | 22 ++++----
+ tests/unit/unit1651.c | 6 +-
+ tests/unit/unit1657.c | 79 ++++++++++++++++++++++++---
+ 9 files changed, 163 insertions(+), 104 deletions(-)
+
+diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
+index b87b27d864..02a8147520 100644
+--- a/lib/vtls/gtls.c
++++ b/lib/vtls/gtls.c
+@@ -1626,8 +1626,8 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf,
+ goto out;
+
+ for(i = 0; i < chain.num_certs; i++) {
+- const char *beg = (const char *)chain.certs[i].data;
+- const char *end = beg + chain.certs[i].size;
++ const uint8_t *beg = chain.certs[i].data;
++ const uint8_t *end = beg + chain.certs[i].size;
+
+ result = Curl_extract_certinfo(data, (int)i, beg, end);
+ if(result)
+diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
+index d2199df884..cf7ac29594 100644
+--- a/lib/vtls/mbedtls.c
++++ b/lib/vtls/mbedtls.c
+@@ -431,9 +431,8 @@ static void mbed_extract_certinfo(struct Curl_easy *data,
+ result = Curl_ssl_init_certinfo(data, cert_count);
+
+ for(i = 0, cur = crt; result == CURLE_OK && cur; ++i, cur = cur->next) {
+- const char *beg = (const char *)cur->raw.p;
+- const char *end = beg + cur->raw.len;
+- result = Curl_extract_certinfo(data, i, beg, end);
++ result = Curl_extract_certinfo(data, i, cur->raw.p,
++ cur->raw.p + cur->raw.len);
+ }
+ }
+
+diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
+index 89619432b8..017e7ddcd8 100644
+--- a/lib/vtls/rustls.c
++++ b/lib/vtls/rustls.c
+@@ -1225,15 +1225,10 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
+ (int)errorlen, errorbuf);
+ return map_error(rresult);
+ }
+- {
+- const char *beg;
+- const char *end;
+- beg = (const char *)der_data;
+- end = (const char *)(der_data + der_len);
+- result = Curl_extract_certinfo(data, (int)i, beg, end);
+- if(result)
+- return result;
+- }
++ result = Curl_extract_certinfo(data, (int)i,
++ der_data, der_data + der_len);
++ if(result)
++ return result;
+ }
+ }
+
+diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
+index a4f57c7c0a..643b6d510c 100644
+--- a/lib/vtls/schannel.c
++++ b/lib/vtls/schannel.c
+@@ -1108,7 +1108,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+
+ do {
+ SECURITY_STATUS sspi_status;
+- const char *x509_der;
++ uint8_t *x509_der;
+ DWORD x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+@@ -1129,7 +1129,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+ (pCertContextServer->cbCertEncoded > 0)))
+ break;
+
+- x509_der = (const char *)pCertContextServer->pbCertEncoded;
++ x509_der = (uint8_t *)pCertContextServer->pbCertEncoded;
+ x509_der_len = pCertContextServer->cbCertEncoded;
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+@@ -1543,12 +1543,12 @@ static bool add_cert_to_certinfo(const CERT_CONTEXT *ccert_context,
+ struct Adder_args *args = (struct Adder_args *)raw_arg;
+ args->result = CURLE_OK;
+ if(valid_cert_encoding(ccert_context)) {
+- const char *beg = (const char *)ccert_context->pbCertEncoded;
+- const char *end = beg + ccert_context->cbCertEncoded;
+ int insert_index = reverse_order ? (args->certs_count - 1) - args->idx :
+ args->idx;
+ args->result = Curl_extract_certinfo(args->data, insert_index,
+- beg, end);
++ ccert_context->pbCertEncoded,
++ ccert_context->pbCertEncoded +
++ ccert_context->cbCertEncoded);
+ args->idx++;
+ }
+ return args->result == CURLE_OK;
+diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
+index c901ebfe1e..5a2967d275 100644
+--- a/lib/vtls/wolfssl.c
++++ b/lib/vtls/wolfssl.c
+@@ -1545,7 +1545,7 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+
+ if(pinnedpubkey) {
+ #ifdef KEEP_PEER_CERT
+- const char *x509_der;
++ const uint8_t *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+@@ -1558,7 +1558,7 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ goto end;
+ }
+
+- x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
++ x509_der = wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ goto end;
+diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c
+index b687a60193..65f2d9229d 100644
+--- a/lib/vtls/x509asn1.c
++++ b/lib/vtls/x509asn1.c
+@@ -160,17 +160,19 @@ static const struct Curl_OID OIDtable[] = {
+ * Please note there is no pretension here to rewrite a full SSL library.
+ */
+
+-static const char *getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
++#ifndef UNITTESTS
++static const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
+ WARN_UNUSED_RESULT;
++#endif
+
+ #define CURL_ASN1_MAX_RECURSIONS 16
+
+-static const char *getASN1Element_(struct Curl_asn1Element *elem,
+- const char *beg, const char *end,
++static const uint8_t *getASN1Element_(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end,
+ size_t lvl)
+ {
+- unsigned char b;
++ uint8_t b;
+ size_t len;
+ struct Curl_asn1Element lelem;
+
+@@ -185,7 +187,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+
+ /* Process header byte. */
+ elem->header = beg;
+- b = (unsigned char)*beg++;
++ b = *beg++;
+ elem->constructed = (b & 0x20) != 0;
+ elem->eclass = (b >> 6) & 3;
+ b &= 0x1F;
+@@ -196,7 +198,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ /* Process length. */
+ if(beg >= end)
+ return NULL;
+- b = (unsigned char)*beg++;
++ b = *beg++;
+ if(!(b & 0x80))
+ len = b;
+ else if(!(b &= 0x7F)) {
+@@ -215,7 +217,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ elem->end = beg;
+ return beg + 1;
+ }
+- else if((unsigned)b > (size_t)(end - beg))
++ else if(b > (size_t)(end - beg))
+ return NULL; /* Does not fit in source. */
+ else {
+ /* Get long length. */
+@@ -223,7 +225,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ do {
+ if(len & 0xFF000000L)
+ return NULL; /* Lengths > 32 bits are not supported. */
+- len = (len << 8) | (unsigned char) *beg++;
++ len = (len << 8) | *beg++;
+ } while(--b);
+ }
+ if(len > (size_t)(end - beg))
+@@ -233,8 +235,11 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ return elem->end;
+ }
+
+-static const char *getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
++/*
++ * @unittest 1657
++ */
++UNITTEST const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ return getASN1Element_(elem, beg, end, 0);
+ }
+@@ -255,17 +260,6 @@ static const struct Curl_OID *searchOID(const char *oid)
+ return NULL;
+ }
+
+-#ifdef UNITTESTS
+-/* used by unit1657.c */
+-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
+-{
+- if(getASN1Element(elem, beg, end))
+- return CURLE_OK;
+- return CURLE_BAD_FUNCTION_ARGUMENT;
+-}
+-#endif
+-
+ /*
+ * Convert an ASN.1 Boolean value into its string representation.
+ *
+@@ -273,7 +267,7 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ */
+
+ static CURLcode bool2str(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ if(end - beg != 1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+@@ -286,17 +280,18 @@ static CURLcode bool2str(struct dynbuf *store,
+ * Return error code.
+ */
+ static CURLcode octet2str(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ CURLcode result = CURLE_OK;
+
+ while(!result && beg < end)
+- result = curlx_dyn_addf(store, "%02x:", (unsigned char)*beg++);
++ result = curlx_dyn_addf(store, "%02x:", *beg++);
+
+ return result;
+ }
+
+-static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end)
++static CURLcode bit2str(struct dynbuf *store,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ /* Convert an ASN.1 bit string to a printable string. */
+
+@@ -310,7 +305,8 @@ static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end)
+ *
+ * Returns error.
+ */
+-static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
++static CURLcode int2str(struct dynbuf *store,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ unsigned int val = 0;
+ size_t n = end - beg;
+@@ -326,7 +322,7 @@ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
+ val = ~val;
+
+ do
+- val = (val << 8) | *(const unsigned char *)beg++;
++ val = (val << 8) | *beg++;
+ while(beg < end);
+ return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+ }
+@@ -339,8 +335,8 @@ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
+ *
+ * Returns error.
+ */
+-static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+- const char *end)
++static CURLcode utf8asn1str(struct dynbuf *to, int type,
++ const uint8_t *from, const uint8_t *end)
+ {
+ size_t inlength = end - from;
+ int size = 1;
+@@ -381,14 +377,14 @@ static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+
+ switch(size) {
+ case 4:
+- wc = (wc << 8) | *(const unsigned char *)from++;
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
++ wc = (wc << 8) | *from++;
+ FALLTHROUGH();
+ case 2:
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
+ FALLTHROUGH();
+ default: /* case 1: */
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
+ }
+ if(wc >= 0x00000080) {
+ if(wc >= 0x00000800) {
+@@ -422,14 +418,14 @@ static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+ * Return error code.
+ */
+ static CURLcode encodeOID(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ unsigned int x;
+ unsigned int y;
+ CURLcode result = CURLE_OK;
+
+ /* Process the first two numbers. */
+- y = *(const unsigned char *)beg++;
++ y = *beg++;
+ x = y / 40;
+ y -= x * 40;
+
+@@ -445,7 +441,7 @@ static CURLcode encodeOID(struct dynbuf *store,
+ return CURLE_OK;
+ else if(beg == end)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- y = *(const unsigned char *)beg++;
++ y = *beg++;
+ x = (x << 7) | (y & 0x7F);
+ } while(y & 0x80);
+ result = curlx_dyn_addf(store, ".%u", x);
+@@ -460,7 +456,7 @@ static CURLcode encodeOID(struct dynbuf *store,
+ */
+
+ static CURLcode OID2str(struct dynbuf *store,
+- const char *beg, const char *end, bool symbolic)
++ const uint8_t *beg, const uint8_t *end, bool symbolic)
+ {
+ CURLcode result = CURLE_OK;
+ if(beg < end) {
+@@ -650,10 +646,12 @@ static CURLcode ASN1tostr(struct dynbuf *store,
+ result = OID2str(store, elem->beg, elem->end, TRUE);
+ break;
+ case CURL_ASN1_UTC_TIME:
+- result = UTime2str(store, elem->beg, elem->end);
++ result = UTime2str(store, (const char *)elem->beg,
++ (const char *)elem->end);
+ break;
+ case CURL_ASN1_GENERALIZED_TIME:
+- result = GTime2str(store, elem->beg, elem->end);
++ result = GTime2str(store, (const char *)elem->beg,
++ (const char *)elem->end);
+ break;
+ case CURL_ASN1_UTF8_STRING:
+ case CURL_ASN1_NUMERIC_STRING:
+@@ -681,10 +679,10 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ struct Curl_asn1Element atv;
+ struct Curl_asn1Element oid;
+ struct Curl_asn1Element value;
+- const char *p1;
+- const char *p2;
+- const char *p3;
+- const char *str;
++ const uint8_t *p1;
++ const uint8_t *p2;
++ const uint8_t *p3;
++ const uint8_t *str;
+ CURLcode result = CURLE_OK;
+ bool added = FALSE;
+ struct dynbuf temp;
+@@ -716,7 +714,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ if(result)
+ goto error;
+
+- str = curlx_dyn_ptr(&temp);
++ str = curlx_dyn_uptr(&temp);
+
+ if(!str) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+@@ -737,7 +735,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ }
+
+ /* Encode attribute name. */
+- result = curlx_dyn_add(store, str);
++ result = curlx_dyn_add(store, (const char *)str);
+ if(result)
+ goto error;
+
+@@ -769,13 +767,14 @@ error:
+ * See RFC 5280.
+ */
+ int Curl_parseX509(struct Curl_X509certificate *cert,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element tbsCertificate;
+- const char *ccp;
+- static const char defaultVersion = 0; /* v1. */
++ const uint8_t *ccp;
++ static const uint8_t defaultVersion = 0; /* v1. */
+
++ memset(cert, 0, sizeof(*cert));
+ cert->certificate.header = NULL;
+ cert->certificate.beg = beg;
+ cert->certificate.end = end;
+@@ -831,6 +830,9 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+ return -1;
+ if(!getASN1Element(&cert->notAfter, ccp, elem.end))
+ return -1;
++ /* These two fields MUST consume all of the validity elem */
++ if(elem.end != cert->notAfter.end)
++ return -1;
+ /* Get subject. */
+ beg = getASN1Element(&cert->subject, beg, end);
+ if(!beg)
+@@ -851,10 +853,10 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
+ cert->extensions.tag = elem.tag = 0;
+ cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
+- cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
+- cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
++ cert->issuerUniqueID.beg = cert->issuerUniqueID.end = beg;
++ cert->subjectUniqueID.beg = cert->subjectUniqueID.end = beg;
+ cert->extensions.header = NULL;
+- cert->extensions.beg = cert->extensions.end = "";
++ cert->extensions.beg = cert->extensions.end = beg;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+@@ -888,7 +890,7 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+
+ static CURLcode dumpAlgo(struct dynbuf *store,
+ struct Curl_asn1Element *param,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ struct Curl_asn1Element oid;
+
+@@ -901,7 +903,7 @@ static CURLcode dumpAlgo(struct dynbuf *store,
+ param->tag = 0;
+ param->beg = param->end = end;
+ if(beg < end) {
+- const char *p = getASN1Element(param, beg, end);
++ const uint8_t *p = getASN1Element(param, beg, end);
+ if(!p)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+@@ -972,7 +974,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ {
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element pk;
+- const char *p;
++ const uint8_t *p;
+
+ /* Generate all information records for the public key. */
+
+@@ -1004,7 +1006,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ return 1;
+
+ if(curl_strequal(algo, "rsaEncryption")) {
+- const char *q;
++ const uint8_t *q;
+ size_t len;
+
+ p = getASN1Element(&elem, pk.beg, pk.end);
+@@ -1017,7 +1019,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ len = ((elem.end - q) * 8);
+ if(len) {
+ unsigned int i;
+- for(i = *(const unsigned char *)q; !(i & 0x80); i <<= 1)
++ for(i = *q; !(i & 0x80); i <<= 1)
+ len--;
+ }
+ if(len > 32)
+@@ -1083,8 +1085,8 @@ static CURLcode DNtostr(struct dynbuf *store, struct Curl_asn1Element *dn)
+
+ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ int certnum,
+- const char *beg,
+- const char *end)
++ const uint8_t *beg,
++ const uint8_t *end)
+ {
+ struct Curl_X509certificate cert;
+ struct Curl_asn1Element param;
+@@ -1093,7 +1095,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ struct dynbuf out;
+ CURLcode result = CURLE_OK;
+ unsigned int version;
+- const char *ptr;
++ const uint8_t *ptr;
+ int rc;
+
+ if(!data->set.ssl.certinfo)
+@@ -1132,7 +1134,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ /* Version (always fits in less than 32 bits). */
+ version = 0;
+ for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
+- version = (version << 8) | *(const unsigned char *)ptr;
++ version = (version << 8) | *ptr;
+ if(data->set.ssl.certinfo) {
+ result = curlx_dyn_addf(&out, "%x", version);
+ if(result)
+diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h
+index 829dfd884f..e5374108e5 100644
+--- a/lib/vtls/x509asn1.h
++++ b/lib/vtls/x509asn1.h
+@@ -38,11 +38,11 @@
+
+ /* ASN.1 parsed element. */
+ struct Curl_asn1Element {
+- const char *header; /* Pointer to header byte. */
+- const char *beg; /* Pointer to element data. */
+- const char *end; /* Pointer to 1st byte after element. */
+- unsigned char eclass; /* ASN.1 element class. */
+- unsigned char tag; /* ASN.1 element tag. */
++ const uint8_t *header; /* Pointer to header byte. */
++ const uint8_t *beg; /* Pointer to element data. */
++ const uint8_t *end; /* Pointer to 1st byte after element. */
++ uint8_t eclass; /* ASN.1 element class. */
++ uint8_t tag; /* ASN.1 element tag. */
+ BIT(constructed); /* Element is constructed. */
+ };
+
+@@ -70,22 +70,22 @@ struct Curl_X509certificate {
+ */
+
+ int Curl_parseX509(struct Curl_X509certificate *cert,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+
+ #ifdef UNITTESTS
++const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
++ WARN_UNUSED_RESULT;
+ #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \
+ defined(USE_RUSTLS)
+
+ /* used by unit1656.c */
+ CURLcode Curl_x509_GTime2str(struct dynbuf *store,
+ const char *beg, const char *end);
+-/* used by unit1657.c */
+-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end);
+ #endif /* USE_GNUTLS || USE_SCHANNEL || USE_MBEDTLS || RUSTLS */
+ #endif /* UNITTESTS */
+
+diff --git a/tests/unit/unit1651.c b/tests/unit/unit1651.c
+index 10fc979724..ce8cbaedc5 100644
+--- a/tests/unit/unit1651.c
++++ b/tests/unit/unit1651.c
+@@ -33,7 +33,7 @@ static CURLcode test_unit1651(const char *arg)
+
+ /* cert captured from gdb when connecting to curl.se on October 26
+ 2018. */
+- static unsigned char cert[] = {
++ static uint8_t cert[] = {
+ 0x30, 0x82, 0x0F, 0x5B, 0x30, 0x82, 0x0E, 0x43, 0xA0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0C, 0x08, 0x77, 0x99, 0x2C, 0x6B, 0x67, 0xE1, 0x18, 0xD6,
+ 0x66, 0x66, 0x9E, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+@@ -365,8 +365,8 @@ static CURLcode test_unit1651(const char *arg)
+ };
+
+ CURLcode result;
+- const char *beg = (const char *)&cert[0];
+- const char *end = (const char *)&cert[sizeof(cert)];
++ const uint8_t *beg = &cert[0];
++ const uint8_t *end = &cert[sizeof(cert)];
+ struct Curl_easy *data;
+ int i;
+ int byte;
+diff --git a/tests/unit/unit1657.c b/tests/unit/unit1657.c
+index 7c769e78da..716df1856c 100644
+--- a/tests/unit/unit1657.c
++++ b/tests/unit/unit1657.c
+@@ -23,7 +23,8 @@
+ ***************************************************************************/
+ #include "unitcheck.h"
+
+-#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS)
++#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
++ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+
+ #include "vtls/x509asn1.h"
+
+@@ -38,8 +39,8 @@ static CURLcode make1657_nested(const struct test1657_spec *spec,
+ {
+ CURLcode result;
+ size_t i;
+- unsigned char open_undef[] = { 0x32, 0x80 };
+- unsigned char close_undef[] = { 0x00, 0x00 };
++ uint8_t open_undef[] = { 0x32, 0x80 };
++ uint8_t close_undef[] = { 0x00, 0x00 };
+
+ for(i = 0; i < spec->n; ++i) {
+ result = curlx_dyn_addn(buf, open_undef, sizeof(open_undef));
+@@ -66,7 +67,8 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ {
+ CURLcode result;
+ struct Curl_asn1Element elem;
+- const char *in;
++ const uint8_t *in;
++ const uint8_t *ptr;
+
+ memset(&elem, 0, sizeof(elem));
+ curlx_dyn_reset(buf);
+@@ -75,8 +77,9 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ curl_mfprintf(stderr, "test %zu: error setting buf %d\n", i, result);
+ return FALSE;
+ }
+- in = curlx_dyn_ptr(buf);
+- result = Curl_x509_getASN1Element(&elem, in, in + curlx_dyn_len(buf));
++ in = curlx_dyn_uptr(buf);
++ ptr = getASN1Element(&elem, in, in + curlx_dyn_len(buf));
++ result = ptr ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
+ if(result != spec->result_exp) {
+ curl_mfprintf(stderr, "test %zu: expect result %d, got %d\n",
+ i, spec->result_exp, result);
+@@ -85,6 +88,59 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ return TRUE;
+ }
+
++struct test1657_cert_spec {
++ const char * const pem;
++ bool exp_success;
++};
++
++static const struct test1657_cert_spec test1657_certs[] = {
++ { /* a valid certificate */
++ "MIIBGTCBzKADAgECAhQ8Ssn8BaVsqlKKccv81NFuKY1vATAFBgMrZXAwADAeFw0y"
++ "NjA5MDIwNDU0MjZaFw0yNjEwMDIwNDU0MjZaMAAwKjAFBgMrZXADIQBeG0S343s9"
++ "5yRmIK8xZmGvUmiiYvRH5ZMcPq6rvHHKwqNYMFYwNQYDKgMEBC4wADAqMAUGAytl"
++ "cAMhAPP5TXSe2WHt/bebbP8eHPXrG3LNxYz8TnHNgpwTFsT7MB0GA1UdDgQWBBR6"
++ "aqL9LrZiIpgYCy0X4fNrhgFQsTAFBgMrZXADQQALU0OHGcIvfTwyin/+u9csJ9hZ"
++ "W0c+XdHJY5uDdCR1O92K4Vnozf/QCe7ITOJ8aUnCs7Jgf5xrDgyXZNdum04A",
++ TRUE
++ },
++ { /* a certificate with "validity" element that has too long length */
++ "MIIBGTCBzKADAgECAhQ8Ssn8BaVsqlKKccv81NFuKY1vATAFBgMrZXAwADBZFw0y"
++ "NjA5MDIwNDU0MjZaFw0yNjEwMDIwNDU0MjZaMAAwKjAFBgMrZXADIQBeG0S343s9"
++ "5yRmIK8xZmGvUmiiYvRH5ZMcPq6rvHHKwqNYMFYwNQYDKgMEBC4wADAqMAUGAytl"
++ "cAMhAPP5TXSe2WHt/bebbP8eHPXrG3LNxYz8TnHNgpwTFsT7MB0GA1UdDgQWBBR6"
++ "aqL9LrZiIpgYCy0X4fNrhgFQsTAFBgMrZXADQQALU0OHGcIvfTwyin/+u9csJ9hZ"
++ "W0c+XdHJY5uDdCR1O92K4Vnozf/QCe7ITOJ8aUnCs7Jgf5xrDgyXZNdum04A",
++ FALSE
++ }
++};
++
++static bool test1657_parse_cert(size_t i,
++ const struct test1657_cert_spec *spec)
++{
++ struct Curl_X509certificate cert;
++ CURLcode result;
++ uint8_t *cert_der;
++ size_t cert_der_len;
++ bool success;
++
++ result = curlx_base64_decode(spec->pem, &cert_der, &cert_der_len);
++ if(result) {
++ curl_mfprintf(stderr, "cert %zu: base64 decoding failed: %d\n",
++ i, (int)result);
++ return FALSE;
++ }
++
++ success = !Curl_parseX509(&cert, cert_der, cert_der + cert_der_len);
++ if(success != spec->exp_success) {
++ curl_mfprintf(stderr, "cert %zu: parsing DER %sfailed\n", i,
++ spec->exp_success ? "" : "should have ");
++ return FALSE;
++ }
++
++ curlx_free(cert_der);
++ return TRUE;
++}
++
+ static CURLcode test_unit1657(const char *arg)
+ {
+ UNITTEST_BEGIN_SIMPLE
+@@ -104,7 +160,14 @@ static CURLcode test_unit1657(const char *arg)
+ if(!do_test1657(&test1657_specs[i], i, &dbuf))
+ all_ok = FALSE;
+ }
+- fail_unless(all_ok, "some tests of Curl_x509_getASN1Element() fails");
++ fail_unless(all_ok, "some getASN1Element() tests failed");
++
++ all_ok = TRUE;
++ for(i = 0; i < CURL_ARRAYSIZE(test1657_certs); ++i) {
++ if(!test1657_parse_cert(i, &test1657_certs[i]))
++ all_ok = FALSE;
++ }
++ fail_unless(all_ok, "some certificate tests failed");
+
+ curlx_dyn_free(&dbuf);
+ curl_global_cleanup();
+@@ -117,7 +180,7 @@ static CURLcode test_unit1657(const char *arg)
+ static CURLcode test_unit1657(const char *arg)
+ {
+ UNITTEST_BEGIN_SIMPLE
+- puts("not tested since Curl_x509_getASN1Element() is not built in");
++ puts("not tested since getASN1Element() is not built in");
+ UNITTEST_END_SIMPLE
+ }
+
+--
+2.35.6
diff --git a/meta/recipes-support/curl/curl_8.19.0.bb b/meta/recipes-support/curl/curl_8.19.0.bb
index 026b5a3638..51479d8b6a 100644
--- a/meta/recipes-support/curl/curl_8.19.0.bb
+++ b/meta/recipes-support/curl/curl_8.19.0.bb
@@ -71,6 +71,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-94266_p1.patch \
file://CVE-2026-94266_p2.patch \
file://CVE-2026-107929.patch \
+ file://CVE-2026-87866.patch \
"
SRC_URI:append:class-nativesdk = " \
--
2.35.6
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [OE-core][wrynose][PATCH 0/9] curl: Security fixes
2026-11-06 7:19 [OE-core][wrynose][PATCH 0/9] curl: Security fixes Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
` (8 preceding siblings ...)
2026-11-06 7:19 ` [OE-core][wrynose][PATCH 9/9] curl: fix CVE-2026-87866 Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
@ 2026-11-06 9:00 ` Yoann Congal
2026-11-11 10:20 ` [wrynose][PATCH " Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
9 siblings, 1 reply; 12+ messages in thread
From: Yoann Congal @ 2026-11-06 9:00 UTC (permalink / raw)
To: dkelaiya, openembedded-core
Hello,
On Fri Nov 6, 2026 at 8:19 AM CET, Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) via openembedded-core wrote:
> From: Darsh Kelaiya <dkelaiya@cisco.com>
>
> Backport nine additional security fixes to curl 8.19.0 on Wrynose.
>
> This series applies on top of my earlier curl CVE series [1],
> from CVE-2026-86414 through CVE-2026-18924.
May I ask that you use unique subjects for the cover letters?
Either put the date or maybe the CVE range if that makes sense.
This will make it easier to track them.
Thanks!
--
Yoann Congal
Smile ECS
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [wrynose][PATCH 0/9] curl: Security fixes
2026-11-06 9:00 ` [OE-core][wrynose][PATCH 0/9] curl: Security fixes Yoann Congal
@ 2026-11-11 10:20 ` Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco)
0 siblings, 0 replies; 12+ messages in thread
From: Darsh Kelaiya -X (dkelaiya - E INFOCHIPS PRIVATE LIMITED at Cisco) @ 2026-11-11 10:20 UTC (permalink / raw)
To: openembedded-core
[-- Attachment #1: Type: text/plain, Size: 788 bytes --]
Hi Yoann,
Thanks for the feedback. Will keep that in mind for upcoming series.
Regards,
Darsh
On Fri, Nov 6, 2026 at 02:30 PM, Yoann Congal wrote:
>
> Hello,
>
> On Fri Nov 6, 2026 at 8:19 AM CET, Darsh Kelaiya -X (dkelaiya - E
> INFOCHIPS PRIVATE LIMITED at Cisco) via openembedded-core wrote:
>
>> From: Darsh Kelaiya <dkelaiya@cisco.com>
>>
>> Backport nine additional security fixes to curl 8.19.0 on Wrynose.
>>
>> This series applies on top of my earlier curl CVE series [1],
>> from CVE-2026-86414 through CVE-2026-18924.
>
> May I ask that you use unique subjects for the cover letters?
> Either put the date or maybe the CVE range if that makes sense.
> This will make it easier to track them.
>
> Thanks!
> --
> Yoann Congal
> Smile ECS
[-- Attachment #2: Type: text/html, Size: 904 bytes --]
^ permalink raw reply [flat|nested] 12+ messages in thread