REDHAT-BUG-2525602: Medium severity libsolv libsolv vulnerability
libsolv is a dependency-resolution library used by dnf/libdnf, zypper/libzypp, and other RPM-based package managers. It serializes parsed repository metadata into a binary cache format (.solv files) to avoid re-parsing repository XML on every run. repowrite() (src/repowrite.c) rewrites such a cache from an in-memory Repo/Repodata, in two passes: pass 1 (collectneededcb) collects which directory ids are used, and pass 2 (collectdatacb / collectfilelistcb) emits the serialized data.
Root cause: in collectneededcb() (src/repowrite.c:614-683), for REPOKEYTYPEDIR / REPOKEYTYPEDIRNUMNUMARRAY / REPOKEYTYPEDIRSTRARRAY keys, the code executes cbdata->dirused[id] = 1; at line 651, where id = kv->id comes directly from the KeyValue produced while iterating repodata via repodatasearch(). dirused is allocated at src/repowrite.c:1525 as solvcalloc(dirpool->ndirs, sizeof(Id)), i.e. sized to the number of known directories. For solvable data that is stored incore, the eager decoder (src/reposolv.c) validates every id against a maximum via datareadidmax()/SOLVERRORIDRANGE (e.g. reposolv.c:252-263) before it is used. However, for data stored KEYSTORAGEVERTICALOFFSET (used for large per-package data such as SOLVABLEFILELIST / REPOKEYTYPEDIRSTRARRAY), the bytes are lazily paged in and decoded on demand by datafetch() (src/repopack.h:131-211), which reads the id with datareadid()/datareadideof() and performs no range check at all. If the underlying .solv file's vertical filelist section contains a directory id greater than or equal to dirpool->ndirs (e.g. from a torn/corrupted cache after an unclean shutdown, or a hand-crafted .solv file), that raw, unvalidated id flows into collectneededcb() and is used directly as an array index into dirused, producing an out-of-bounds write of the constant value 1 far outside the allocated buffer. The reporter observed kv->id = 65137234 against dirpool->ndirs = 2737 in a field crash, i.e. a write roughly 260 MB past the end of the buffer.
The same unvalidated id is read back (not just written) without a bounds check at repowrite.c:844, 856, 865, and 917 (id = cbdata->dirused[id];) during the second serialization pass -- these are additional out-of-bounds reads beyond what the reporter's report explicitly enumerated. Independently, repodatadir2str() (src/repodata.c:356-410) walks the directory pool via dirpoolcompid()/dirpoolparent() (src/dirpool.h:34-41, 81-85), both of which index dp->dirs[did] with no range check against dp->ndirs; this function is reachable from readers such as dumpsolv and repoquery -l, giving a second, independent OOB-read primitive on the same class of corrupted/malicious input.
Reporter: Dinko Georgiev (StorPool), reported via PSIRT ticket PSIRTSUPT-22561 on 2026-08-26. No upstream fix version or commit is available at time of this analysis (not yet fixed upstream as of libsolv master 1b580675; this analysis used a shallow/depth-1 clone, so the reporter's claim that the code has been 'unchanged since d9ebbcf1 (2018-09-29)' could NOT be independently verified -- flagged as unverifiable, not confirmed or denied). The reporter's claim relating this to CVE-2026-9149 (a 0.7.38 hardening of repoaddsolv robustness against corrupt files) is noted as reporter-supplied prior art; this analysis could not verify the relationship or the CVE's exact fix commit against git history in the shallow clone, and treats this new bug as a distinct, unpatched code path (the vertical/paged filelist decoder), not covered by that prior hardening.
Independent verification performed via sandboxed static source review (see PSIRTSUPT-22561 for full verification report); CVSS independently scored at 5.5 (MODERATE), matching the reporter's own DoS/availability-only framing rather than an inflated rating.
Affected Software
Event History
Frequently Asked Questions
Which package-management stacks may be exposed to this issue?
libsolv is used by dnf/libdnf and zypper/libzypp, as well as other RPM-based package managers that depend on libsolv. Exposure depends on whether the affected libsolv cache-writing path is used.
What data condition triggers the vulnerable write?
The issue occurs when repo_write() processes directory-related keys, including REPOKEY_TYPE_DIR, REPOKEY_TYPE_DIRNUMNUMARRAY, or REPOKEY_TYPE_DIRSTRARRAY, and uses a directory ID from KeyValue data without confirming that it is within the allocated dirused array.
How does validation differ between the affected and protected data paths?
Incore solvable data is eagerly decoded and validates IDs with data_read_id_max() before use. The affected path involves data stored using KEY_STORAGE_VERTICAL, where the described directory ID reaches collect_needed_cb() directly from repodata_search().