Bug 2460380 (CVE-2026-9149) - CVE-2026-9149 libsolv: Heap buffer overflow in libsolv repo_add_solv via negative maxsize from crafted .solv file
Summary: CVE-2026-9149 libsolv: Heap buffer overflow in libsolv repo_add_solv via nega...
Keywords:
Status: NEW
Alias: CVE-2026-9149
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-04-21 21:20 UTC by OSIDB Bzimport
Modified: 2026-05-21 08:29 UTC (History)
22 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Github openSUSE libsolv pull 617 0 None Merged Cope with integer overflow in data size arithmetics in repo_add_solv() 2026-05-21 08:29:38 UTC

Description OSIDB Bzimport 2026-04-21 21:20:24 UTC
MANUALLY_VERIFIED_REPORT
package: libsolv-0.7.33-2.el10
------

[Security] Heap Buffer Overflow in repo_add_solv via Negative maxsize

Summary:  Heap buffer overflow in `repo_add_solv` when parsing attacker-controlled `.solv` files; large encoded `maxsize`/`allsize` header values can decode to negative signed `Id` values, leading to undersized heap allocation while a subsequent `fread` uses `DATA_READ_CHUNK` (8192) bytes.

Requirements to exploit: Ability to supply a crafted `.solv` file that a victim processes with libsolv (directly or via a consumer such as `dumpsolv` or an application that calls `repo_add_solv` on untrusted input).

Component affected: libsolv

Version affected: <= 0.7.36

Version fixed (if any already): >= TBD

CVSS:  6.5 (Medium) — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H

Impact: Moderate (proposed). Per https://access.redhat.com/security/updates/classification this is memory corruption reachable via untrusted `.solv` ingestion and can at least cause a denial of service; it is not clearly "High" because an attacker typically needs the victim to process attacker-controlled input (configuration/user action) and reliable system compromise is not demonstrated here. This may be "Low" instead in product contexts where the vulnerable path is not used by default, is only reachable via uncommon workflows, or is effectively mitigated (e.g., only trusted solvdb is processed).

Embargo: no

Acknowledgement: Aisle Research

Steps to reproduce if available: See "Reproduction steps" below.

Mitigation if available: Prefer only consuming trusted `.solv` / solvdb inputs; avoid parsing untrusted `.solv` files until patched.

Original report:

Hello libsolv maintainers,

We believe that we have discovered a potential security vulnerability in `repo_add_solv` when parsing attacker-controlled `.solv` files.

### Vulnerability details

`read_id` decodes into an unsigned value and returns `Id` (signed `int`), so large encoded values can become negative after conversion:

```c
/* src/repo_solv.c */
static Id
read_id(Repodata *data, Id max)
{
  unsigned int x = 0;
  ...
  return x;
}
```

In `repo_add_solv`, `maxsize` and `allsize` are read with `max=0` (no bounds check), then used for allocation and read length:

```c
/* src/repo_solv.c */
maxsize = read_id(&data, 0);
allsize = read_id(&data, 0);
maxsize += 5;
if (maxsize > allsize)
  maxsize = allsize;

buf = solv_calloc(maxsize + DATA_READ_CHUNK + 4, 1);

l = maxsize;
if (l < DATA_READ_CHUNK)
  l = DATA_READ_CHUNK;
if (l > allsize)
  l = allsize;
if (!l || fread(buf, l, 1, data.fp) != 1)
```

If `maxsize` is negative, `solv_calloc(maxsize + 8192 + 4, 1)` can allocate a much smaller buffer, but `l` is then raised to `8192`, and `fread` writes `8192` bytes into that undersized heap buffer.

Most relevant CWEs:
- `CWE-122` (Heap-based Buffer Overflow): direct overflow sink.
- `CWE-20` (Improper Input Validation): negative header fields are accepted.
- `CWE-195` (Signed to Unsigned Conversion Error): signed `int` values flow into allocation sizing.

### Reproduction steps

1. Build libsolv with ASAN (or run a consumer binary that calls `repo_add_solv` on `.solv` input, e.g. `dumpsolv`).
2. Run the parser on this file (`dumpsolv crafted.solv` or equivalent).

### Crash:
[root@c28a4ffb0823 workspace]# ./build-asan/tools/dumpsolv ./vuln_1_101_1_negative_maxsize.solv
=================================================================
==542==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x5020000000b1 at pc 0x00000041fb3c bp 0x7ffffffc5fd0 sp 0x7ffffffc5798
WRITE of size 8192 at 0x5020000000b1 thread T0
    #0 0x00000041fb3b  (/workspace/build-asan/tools/dumpsolv+0x41fb3b) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)
    #1 0x7fffff662147  (/workspace/build-asan/src/libsolv.so.1+0x57147) (BuildId: ebfff12c035b97f95b2d532a1d6d237ac31e770a)
    #2 0x0000004e45fe  (/workspace/build-asan/tools/dumpsolv+0x4e45fe) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)
    #3 0x7fffff2f0447  (/lib64/libc.so.6+0x3447) (BuildId: dae6ae6929d69dca842288f5300af5a33d1bdcd7)
    #4 0x7fffff2f050a  (/lib64/libc.so.6+0x350a) (BuildId: dae6ae6929d69dca842288f5300af5a33d1bdcd7)
    #5 0x000000401514  (/workspace/build-asan/tools/dumpsolv+0x401514) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)

0x5020000000b1 is located 0 bytes after 1-byte region [0x5020000000b0,0x5020000000b1)
allocated by thread T0 here:
    #0 0x0000004a1343  (/workspace/build-asan/tools/dumpsolv+0x4a1343) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)
    #1 0x7fffff6b6be6  (/workspace/build-asan/src/libsolv.so.1+0xabbe6) (BuildId: ebfff12c035b97f95b2d532a1d6d237ac31e770a)
    #2 0x7fffff662100  (/workspace/build-asan/src/libsolv.so.1+0x57100) (BuildId: ebfff12c035b97f95b2d532a1d6d237ac31e770a)
    #3 0x0000004e45fe  (/workspace/build-asan/tools/dumpsolv+0x4e45fe) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)
    #4 0x7fffff2f0447  (/lib64/libc.so.6+0x3447) (BuildId: dae6ae6929d69dca842288f5300af5a33d1bdcd7)
    #5 0x7fffff2f050a  (/lib64/libc.so.6+0x350a) (BuildId: dae6ae6929d69dca842288f5300af5a33d1bdcd7)
    #6 0x000000401514  (/workspace/build-asan/tools/dumpsolv+0x401514) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/workspace/build-asan/tools/dumpsolv+0x41fb3b) (BuildId: 3a1e71d74bd4d38c896ffc899393aedf86bf1cfc) 
Shadow bytes around the buggy address:
  0x501ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x502000000000: fa fa 00 00 fa fa 04 fa fa fa 00 00 fa fa 04 fa
=>0x502000000080: fa fa 04 fa fa fa[01]fa fa fa fa fa fa fa fa fa
  0x502000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  
### Proposed fix

```diff
diff --git a/src/repo_solv.c b/src/repo_solv.c
@@
   maxsize = read_id(&data, 0);
   allsize = read_id(&data, 0);
+  if (maxsize < 0 || allsize < 0)
+    {
+      data.error = pool_error(pool, SOLV_ERROR_CORRUPT, "negative data size in solv header");
+      id = 0;
+      goto data_error;
+    }
+  if (maxsize > INT_MAX - 5)
+    {
+      data.error = pool_error(pool, SOLV_ERROR_OVERFLOW, "data size overflow in solv header");
+      id = 0;
+      goto data_error;
+    }
   maxsize += 5;  /* so we can read the next schema of an array */
   if (maxsize > allsize)
     maxsize = allsize;
@@
   if (keydepth)
     data.error = pool_error(pool, SOLV_ERROR_EOF, "unexpected EOF, depth = %d", keydepth);
@@
+data_error:
```

Best wishes,
Aisle Research

------
This report was generated using AI technology. Always review AI-generated content prior to use


Note You need to log in before you can comment on or make changes to this bug.