diff mbox series

[scarthgap,6/7] curl: fix CVE-2026-92391

Message ID 20261106144134.1370928-7-joaomarcos.costa@bootlin.com
State Under Review
Delegated to: Jérémy ROSEN
Headers show
Series curl: fix High scored CVEs | expand

Commit Message

João Marcos Costa (Schneider Electric) Nov. 6, 2026, 2:41 p.m. UTC
This vulnerability, marked as High [1], was fixed upstream [2] since
v8.23.0.

Backport the fix to curl v8.7.1.

[1] https://nvd.nist.gov/vuln/detail/cve-2026-92391
[2] https://curl.se/docs/CVE-2026-92391.html

Signed-off-by: João Marcos Costa (Schneider Electric) <joaomarcos.costa@bootlin.com>
---
 .../curl/curl/CVE-2026-92391.patch            | 185 ++++++++++++++++++
 meta/recipes-support/curl/curl_8.7.1.bb       |   1 +
 2 files changed, 186 insertions(+)
 create mode 100644 meta/recipes-support/curl/curl/CVE-2026-92391.patch
diff mbox series

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..d7d9c3e18a
--- /dev/null
+++ b/meta/recipes-support/curl/curl/CVE-2026-92391.patch
@@ -0,0 +1,185 @@ 
+From 7ba6d6df009e5dba8cced2607005825a142ff525 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
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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]
+
+Signed-off-by: João Marcos Costa <joaomarcos.costa@bootlin.com>
+---
+ lib/mime.c              | 28 +++++++++++++----
+ tests/data/Makefile.inc |  2 +-
+ tests/data/test3595     | 21 +++++++++++++
+ tests/libtest/lib3595.c | 70 +++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 114 insertions(+), 7 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 5bc6d38931..254d6c93aa 100644
+--- a/lib/mime.c
++++ b/lib/mime.c
+@@ -1180,20 +1180,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's 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);
+       free(part);
+     }
+-    free(mime);
++    else {
++      curl_mime *parent = (curl_mime *)mime->parent;
++      free(mime);
++      mime = parent;
++    }
+   }
+ }
+ 
+diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
+index 614b6baf4b..9efae9cefe 100644
+--- a/tests/data/Makefile.inc
++++ b/tests/data/Makefile.inc
+@@ -262,4 +262,4 @@ test3024 test3025 test3026 test3027 test3028 test3029 test3030 \
+ \
+ test3100 test3101 test3102 test3103 \
+ test3200 \
+-test3201 test3202 test3500
++test3201 test3202 test3500 test3595
+diff --git a/tests/data/test3595 b/tests/data/test3595
+new file mode 100644
+index 0000000000..b2b1dd26c2
+--- /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/lib3595.c b/tests/libtest/lib3595.c
+new file mode 100644
+index 0000000000..1f343f503e
+--- /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
++}
diff --git a/meta/recipes-support/curl/curl_8.7.1.bb b/meta/recipes-support/curl/curl_8.7.1.bb
index 39e804a1c3..3bd1b92d44 100644
--- a/meta/recipes-support/curl/curl_8.7.1.bb
+++ b/meta/recipes-support/curl/curl_8.7.1.bb
@@ -48,6 +48,7 @@  SRC_URI = "https://curl.se/download/${BP}.tar.xz \
            file://CVE-2026-87865.patch \
            file://CVE-2026-92386.patch \
            file://CVE-2026-92389.patch \
+           file://CVE-2026-92391.patch \
            "
 
 SRC_URI:append:class-nativesdk = " \