From 1d45989765dccffaf57fc53d285b2b76b9fa2297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= Date: Wed, 27 May 2026 16:04:54 +0200 Subject: [PATCH] Fix a buffer overflow when decompressing solv pages (CVE-2026-48864) Resolves: RHEL-178970 --- ...ress_buf-and-repopagestore_decompres.patch | 152 ++++++++++++++++++ ...erflow-when-decompressing-solv-pages.patch | 62 +++++++ libsolv.spec | 14 +- 3 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 0006-Add-check_decompress_buf-and-repopagestore_decompres.patch create mode 100644 0007-Fix-a-buffer-overflow-when-decompressing-solv-pages.patch diff --git a/0006-Add-check_decompress_buf-and-repopagestore_decompres.patch b/0006-Add-check_decompress_buf-and-repopagestore_decompres.patch new file mode 100644 index 0000000..c3762bb --- /dev/null +++ b/0006-Add-check_decompress_buf-and-repopagestore_decompres.patch @@ -0,0 +1,152 @@ +From 1595089d2944cb6a96d8a0ce816fbba329642d19 Mon Sep 17 00:00:00 2001 +From: Michael Schroeder +Date: Fri, 25 Feb 2022 17:21:17 +0100 +Subject: [PATCH] Add check_decompress_buf() and + repopagestore_decompress_page() + +Upstream commit: 85ced9742b10cd2ce9c43dc8722dbd6aa84662ac ("solv +format: support storing of package dependencies in a compressed +block") + +Petr Pisar: check_decompress_buf() and repopagestore_decompress_page() +ported to 0.7.20. Also a cosmetic correction of an artihmetics in +unchecked_decompress_buf() ported to align with +check_decompress_buf(). +--- + src/repopage.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++--- + src/repopage.h | 2 ++ + 2 files changed, 86 insertions(+), 5 deletions(-) + +diff --git a/src/repopage.c b/src/repopage.c +index 9e9694f..971de77 100644 +--- a/src/repopage.c ++++ b/src/repopage.c +@@ -466,14 +466,12 @@ unchecked_decompress_buf(const unsigned char *in, unsigned int in_len, + { + o = in[0] | (in[1] << 8); + in += 2; +- first = first & 31; +- first += 3; ++ first = (first & 15) + 3; + break; + } + case 15: +- /* f1 1111llll <8o> <8o> <8l> */ +- /* f2 11110lll <8o> <8o> <8l> */ +- /* g 11111lll <8o> <8o> <8o> <8l> */ ++ /* f2 11110lll <8l> <8o> <8o> */ ++ /* g 11111lll <8l> <8o> <8o> <8o> */ + { + first = first & 15; + if (first >= 8) +@@ -557,6 +555,77 @@ unchecked_decompress_buf(const unsigned char *in, unsigned int in_len, + return out - orig_out; + } + ++static unsigned int ++check_decompress_buf(const unsigned char *in, unsigned int in_len) ++{ ++ unsigned int out_len = 0; ++ const unsigned char *in_end = in + in_len; ++ while (in < in_end) ++ { ++ unsigned int first = *in++; ++ int o; ++ switch (first >> 4) ++ { ++ default: ++ /* This default case can't happen, but GCCs VRP is not strong ++ enough to see this, so make this explicitely not fall to ++ the end of the switch, so that we don't have to initialize ++ o above. */ ++ continue; ++ case 0: case 1: ++ case 2: case 3: ++ case 4: case 5: ++ case 6: case 7: ++ out_len++; ++ continue; ++ case 8: case 9: ++ /* b 100lllll */ ++ first = (first & 31) + 1; ++ in += first; ++ out_len += first; ++ continue; ++ case 10: case 11: ++ /* c 101oolll <8o> */ ++ o = (first & (3 << 3)) << 5 | *in++; ++ first = (first & 7) + 2; ++ break; ++ case 12: case 13: ++ /* d 110lllll <8o> */ ++ o = *in++; ++ first = (first & 31) + 10; ++ break; ++ case 14: ++ /* e 1110llll <8o> <8o> */ ++ o = in[0] | (in[1] << 8); ++ in += 2; ++ first = (first & 15) + 3; ++ break; ++ case 15: ++ /* f1 1111llll <8l> <8o> <8o> */ ++ /* g 11111lll <8l> <8o> <8o> <8o> */ ++ first = first & 15; ++ if (first >= 8) ++ { ++ first = (((first - 8) << 8) | in[0]) + 5; ++ o = in[1] | (in[2] << 8) | (in[3] << 16); ++ in += 4; ++ } ++ else ++ { ++ first = ((first << 8) | in[0]) + 19; ++ o = in[1] | (in[2] << 8); ++ in += 3; ++ } ++ break; ++ } ++ /* fprintf(stderr, "ref: %d @ %d\n", first, o); */ ++ if (o >= out_len) ++ return 0; ++ out_len += first; ++ } ++ return out_len; ++} ++ + /**********************************************************************/ + + void repopagestore_init(Repopagestore *store) +@@ -757,6 +826,16 @@ repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char + return compress_buf(page, len, cpage, max); + } + ++unsigned int ++repopagestore_decompress_page(const unsigned char *cpage, unsigned int len, unsigned char *page, unsigned int max) ++{ ++ unsigned int l = check_decompress_buf(cpage, len); ++ if (l == 0 || l > max) ++ return 0; ++ return unchecked_decompress_buf(cpage, len, page, max); ++} ++ ++ + #define SOLV_ERROR_EOF 3 + #define SOLV_ERROR_CORRUPT 6 + +diff --git a/src/repopage.h b/src/repopage.h +index b5f2eee..9fb84f0 100644 +--- a/src/repopage.h ++++ b/src/repopage.h +@@ -53,6 +53,8 @@ unsigned char *repopagestore_load_page_range(Repopagestore *store, unsigned int + + /* compress a page, return compressed len */ + unsigned int repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max); ++/* uncompress a page, return uncompressed len */ ++unsigned int repopagestore_decompress_page(const unsigned char *cpage, unsigned int len, unsigned char *page, unsigned int max); + + /* setup page data for repodata_load_page_range */ + int repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz); +-- +2.54.0 + diff --git a/0007-Fix-a-buffer-overflow-when-decompressing-solv-pages.patch b/0007-Fix-a-buffer-overflow-when-decompressing-solv-pages.patch new file mode 100644 index 0000000..ccbf761 --- /dev/null +++ b/0007-Fix-a-buffer-overflow-when-decompressing-solv-pages.patch @@ -0,0 +1,62 @@ +From 6ba8fbf6603a7fcfdb1744df52d6f0c291f7b29f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= +Date: Tue, 5 May 2026 10:52:20 +0200 +Subject: [PATCH] Fix a buffer overflow when decompressing solv pages + +repopagestore_load_page_range() and +repopagestore_read_or_setup_pages() functions called +unchecked_decompress_buf() on compressed data which were just loaded +from a solv file without validating them with check_decompress_buf(). + +If the solv file was maliously crafted to decompress beyond +REPOPAGE_BLOBSIZE-byte-sized stack-allocated buffer (e.g. 100lllll +byte with high lllll counter at the end of the buffer), or +a backreference was pointing out of the output buffer (e.g. +a reference at the beginning of the buffer with high offset pointing +before a start of the buffer), unchecked_decompress_buf() would read +or write out of the output buffer, causing a buffer overflow. + +Trival fix would be calling check_decompress_buf() before +unchecked_decompress_buf() as repopagestore_decompress_page() already +does. + +Instead, this patch uses repopagestore_decompress_page() to do the +check and decompression in a single step. + +Acknowledgement: Aisle Research + +CVE-2026-48864 +https://bugzilla.redhat.com/show_bug.cgi?id=2460425 +--- + src/repopage.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/repopage.c b/src/repopage.c +index a9a1c074..0967979b 100644 +--- a/src/repopage.c ++++ b/src/repopage.c +@@ -779,8 +779,8 @@ repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigne + if (compressed) + { + unsigned int out_len; +- out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE); +- if (out_len != REPOPAGE_BLOBSIZE && pnum < store->num_pages - 1) ++ out_len = repopagestore_decompress_page(buf, in_len, dest, REPOPAGE_BLOBSIZE); ++ if (out_len == 0 || (out_len != REPOPAGE_BLOBSIZE && pnum < store->num_pages - 1)) + { + #ifdef DEBUG_PAGING + fprintf(stderr, "can't decompress\n"); +@@ -947,8 +947,8 @@ repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int p + } + if (compressed) + { +- out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE); +- if (out_len != REPOPAGE_BLOBSIZE && i < npages - 1) ++ out_len = repopagestore_decompress_page(buf, in_len, dest, REPOPAGE_BLOBSIZE); ++ if (out_len == 0 || (out_len != REPOPAGE_BLOBSIZE && i < npages - 1)) + { + return SOLV_ERROR_CORRUPT; + } +-- +2.54.0 + diff --git a/libsolv.spec b/libsolv.spec index 1de2291..4129047 100644 --- a/libsolv.spec +++ b/libsolv.spec @@ -37,7 +37,7 @@ Name: lib%{libname} Version: 0.7.20 -Release: 6%{?dist} +Release: 7%{?dist} Summary: Package dependency solver License: BSD @@ -54,6 +54,14 @@ Patch3: 0003-Allow-accessing-toolversion-at-runtime-and-increase-.patch Patch4: 0004-Treat-condition-both-as-positive-and-negative-litera.patch Patch5: 0005-Allow_break_arch_lock_step_on_erase.patch +# 1/2 Fix a buffer overflow when decompressing solv pages (CVE-2026-48864), +# RHEL-178970, in upstream 0.7.21 +Patch6: 0006-Add-check_decompress_buf-and-repopagestore_decompres.patch +# 2/2 Fix a buffer overflow when decompressing solv pages (CVE-2026-48864), +# RHEL-178970, rejected by upstream, +# . +Patch7: 0007-Fix-a-buffer-overflow-when-decompressing-solv-pages.patch + BuildRequires: cmake BuildRequires: gcc-c++ BuildRequires: ninja-build @@ -281,6 +289,10 @@ export LD_LIBRARY_PATH=%{buildroot}%{_libdir} %endif %changelog +* Thu May 28 2026 Petr Pisar - 0.7.20-7 +- Fix a buffer overflow when decompressing solv pages (CVE-2026-48864) + (RHEL-178970) + * Wed Jun 21 2023 Jaroslav Rohel - 0.7.20-6 - Backport Allow to break arch lock-step on erase operations (RhBug:2172288,2172292)