package/rauc: add patch for CVE-2026-34155

This fixes the following vulnerability:

    RAUC bundles using the 'plain' format exceeding a payload size of 2 GiB
    cause an integer overflow which results in a signature which covers only
    the first few bytes of the payload. Given such a bundle with a legitimate
    signature, an attacker can modify the part of the payload which is not
    covered by the signature. Bundles using the recommended 'verity' or
    'crypt' formats are not affected. They are supported from v1.5
    (released 2020-12-14) and v1.7 (released 2022-06-03) respectively.
    If all signed and published bundles were smaller than 2GiB,
    the vulnerability cannot be exploited.
    https://github.com/rauc/rauc/security/advisories/GHSA-6hj7-q844-m2hx

Signed-off-by: Titouan Christophe <titouan.christophe@mind.be>
(cherry picked from commit 6e4a136363)
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
This commit is contained in:
Titouan Christophe
2026-03-31 09:09:14 +02:00
committed by Thomas Perale
parent 6e84b56f4d
commit c9f7b876ee

View File

@@ -0,0 +1,219 @@
From f482e9f2ce34697844332db531b4b8be1e4b6043 Mon Sep 17 00:00:00 2001
From: Titouan Christophe <titouan.christophe@mind.be>
Date: Tue, 31 Mar 2026 08:49:13 +0200
Subject: [PATCH] Fix CVE-2026-34155
This is the concatenation of the 2 upstream commits that make the actual
change between rauc v1.15.1 and 1.15.2; rebased on top of rauc v1.13
===============================================================================
[1/2] src/signature: protect against integer overflows with BIO_new_mem_buf()
BIO_new_mem_buf()'s len argument is of type int, so it cannot support
lengths exceeding 2 GiB. Reject larger lengths and additionally check
that we have created the BIO correctly.
These are only internal checks, GError handling will be added in a later
commit.
Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
Upstream: https://github.com/rauc/rauc/commit/bbdf9f04a96a7094e11cfa822d695bc7f33dbf61
===============================================================================
[2/2] src/signature: reject plain bundles with payload exceeding 2 GiB
Due to BIO_new_mem_buf() only supporting buffers of up to 2 GiB, we
cannot sign or verify bundles with a payload larger than that.
This fixes an integer overflow which would lead to calling
BIO_new_mem_buf() with a negative len, which will cause it to use
strlen() to to determine the buffer size automatically. As a result,
the buffer is truncated at the first '\0' byte in the SquashFS header.
The first 4 bytes are a fixed magic number ("hsqs"), and since a '\0'
byte appears within the inode count field at bytes 5-8 in most SquashFS
images, the resulting signature will only cover this part of the
SquashFS header.
As we sign or verify the payload directly via OpenSSL only for the
'plain' format, the 'verity' and 'crypt' formats are not affected.
Fix this by checking the content size before calling cms_sign_file() or
cms_verify_bytes(), which call BIO_new_mem_buf() via bytes_as_bio().
Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
Upstream: https://github.com/rauc/rauc/commit/662a212fc16b736bfed163ebb7349a53881803cd
===============================================================================
CVE: CVE-2026-34155
Signed-off-by: Titouan Christophe <titouan.christophe@mind.be>
---
src/signature.c | 32 ++++++++++++++++++++++++++++++++
test/signature.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+)
diff --git a/src/signature.c b/src/signature.c
index 88f6e790..ca06bfa6 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -414,11 +414,19 @@ static BIO *bytes_as_bio(GBytes *bytes)
g_error("bytes_as_bio: no data");
if (size == 0)
g_error("bytes_as_bio: size is zero");
+ if (size > INT_MAX)
+ g_error("bytes_as_bio: size is too large for BIO_new_mem_buf");
bio = BIO_new_mem_buf(data, size);
if (!bio)
g_error("bytes_as_bio: BIO_new_mem_buf() failed");
+ /* ensure that we've passed the data correctly */
+ const BUF_MEM *bio_mem_buf = NULL;
+ BIO_get_mem_ptr(bio, &bio_mem_buf);
+ g_assert(bio_mem_buf->data == data);
+ g_assert(bio_mem_buf->length == size);
+
return bio;
}
@@ -1420,6 +1428,7 @@ GBytes *cms_sign_file(const gchar *filename, const gchar *certfile, const gchar
GError *ierror = NULL;
g_autoptr(GMappedFile) file = NULL;
g_autoptr(GBytes) content = NULL;
+ gsize content_size = 0;
GBytes *sig = NULL;
g_return_val_if_fail(filename != NULL, FALSE);
@@ -1434,6 +1443,17 @@ GBytes *cms_sign_file(const gchar *filename, const gchar *certfile, const gchar
}
content = g_mapped_file_get_bytes(file);
+ G_STATIC_ASSERT(INT_MAX >= INT32_MAX);
+ content_size = g_bytes_get_size(content);
+ if (content_size > INT32_MAX) {
+ g_set_error(
+ error,
+ R_SIGNATURE_ERROR,
+ R_SIGNATURE_ERROR_LOAD_FAILED,
+ "Bundle payload size %"G_GSIZE_FORMAT " exceeds maximum for bundles using plain format (2 GiB)", content_size);
+ goto out;
+ }
+
sig = cms_sign(content, TRUE, certfile, keyfile, interfiles, &ierror);
if (sig == NULL) {
g_propagate_error(error, ierror);
@@ -1479,6 +1499,7 @@ gboolean cms_verify_fd(gint fd, GBytes *sig, goffset limit, X509_STORE *store, C
GError *ierror = NULL;
g_autoptr(GMappedFile) file = NULL;
g_autoptr(GBytes) content = NULL;
+ gsize content_size = 0;
gboolean res = FALSE;
g_return_val_if_fail(fd >= 0, FALSE);
@@ -1515,6 +1536,17 @@ gboolean cms_verify_fd(gint fd, GBytes *sig, goffset limit, X509_STORE *store, C
content = tmp;
}
+ G_STATIC_ASSERT(INT_MAX >= INT32_MAX);
+ content_size = g_bytes_get_size(content);
+ if (content_size > INT32_MAX) {
+ g_set_error(
+ error,
+ R_SIGNATURE_ERROR,
+ R_SIGNATURE_ERROR_LOAD_FAILED,
+ "Bundle payload size %"G_GSIZE_FORMAT " exceeds maximum for bundles using plain format (2 GiB)", content_size);
+ goto out;
+ }
+
res = cms_verify_bytes(content, sig, store, cms, NULL, &ierror);
if (!res) {
g_propagate_error(error, ierror);
diff --git a/test/signature.c b/test/signature.c
index 7fc9a4f3..7a16c1b1 100644
--- a/test/signature.c
+++ b/test/signature.c
@@ -10,6 +10,7 @@
#include "common.h"
typedef struct {
+ gchar *tmpdir;
GBytes *content;
GBytes *sig;
GError *error;
@@ -23,6 +24,8 @@ static void signature_set_up(SignatureFixture *fixture,
{
r_context_conf();
+ fixture->tmpdir = g_dir_make_tmp("rauc-XXXXXX", NULL);
+ g_assert_nonnull(fixture->tmpdir);
fixture->content = read_file("test/openssl-ca/manifest", NULL);
g_assert_nonnull(fixture->content);
fixture->sig = NULL;
@@ -39,6 +42,10 @@ static void signature_set_up(SignatureFixture *fixture,
static void signature_tear_down(SignatureFixture *fixture,
gconstpointer user_data)
{
+ if (fixture->tmpdir)
+ g_assert_true(rm_tree(fixture->tmpdir, NULL));
+
+ g_free(fixture->tmpdir);
g_bytes_unref(fixture->content);
g_bytes_unref(fixture->sig);
g_clear_error(&fixture->error);
@@ -275,6 +282,44 @@ static void signature_verify_file(SignatureFixture *fixture,
g_clear_error(&fixture->error);
}
+static void signature_too_large(SignatureFixture *fixture,
+ gconstpointer user_data)
+{
+ gboolean res = FALSE;
+
+ g_autofree gchar *payloadname = write_random_file(fixture->tmpdir, "payload", 1024, 1234);
+ g_assert_nonnull(payloadname);
+ fixture->sig = read_file("test/openssl-ca/manifest-r1.sig", NULL);
+ g_assert_nonnull(fixture->sig);
+
+ goffset large_size = (goffset)INT32_MAX+1;
+
+ g_assert_cmpint(truncate(payloadname, large_size), ==, 0);
+ g_autoptr(GBytes) sig = cms_sign_file(payloadname,
+ "test/openssl-ca/rel/release-1.cert.pem",
+ "test/openssl-ca/rel/private/release-1.pem",
+ NULL,
+ &fixture->error);
+ g_assert_null(sig);
+ g_assert_error(fixture->error, R_SIGNATURE_ERROR, R_SIGNATURE_ERROR_LOAD_FAILED);
+ g_clear_error(&fixture->error);
+
+ g_assert_cmpint(truncate(payloadname, large_size + 1024), ==, 0);
+ gint fd = g_open(payloadname, O_RDONLY|O_CLOEXEC, 0);
+ g_assert_cmpint(fd, >=, 0);
+ res = cms_verify_fd(fd,
+ fixture->sig,
+ large_size,
+ fixture->store,
+ &fixture->cms,
+ &fixture->error);
+ g_assert_false(res);
+ g_assert_error(fixture->error, R_SIGNATURE_ERROR, R_SIGNATURE_ERROR_LOAD_FAILED);
+ g_assert_null(fixture->cms);
+ g_clear_error(&fixture->error);
+ g_close(fd, NULL);
+}
+
static void signature_loopback_detached(SignatureFixture *fixture,
gconstpointer user_data)
{
@@ -828,6 +873,7 @@ int main(int argc, char *argv[])
g_test_add("/signature/verify_valid", SignatureFixture, NULL, signature_set_up, signature_verify_valid, signature_tear_down);
g_test_add("/signature/verify_invalid", SignatureFixture, NULL, signature_set_up, signature_verify_invalid, signature_tear_down);
g_test_add("/signature/verify_file", SignatureFixture, NULL, signature_set_up, signature_verify_file, signature_tear_down);
+ g_test_add("/signature/too_large", SignatureFixture, NULL, signature_set_up, signature_too_large, signature_tear_down);
g_test_add("/signature/loopback_detached", SignatureFixture, NULL, signature_set_up, signature_loopback_detached, signature_tear_down);
g_test_add("/signature/loopback_inline", SignatureFixture, NULL, signature_set_up, signature_loopback_inline, signature_tear_down);
g_test_add("/signature/get_cert_chain", SignatureFixture, NULL, signature_set_up, signature_get_cert_chain, signature_tear_down);
--
2.53.0