Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: libmodulemd-2.15.2-7.1.hum1 ------ Summary: Incorrect inner loop bound in rpm-map emission causes out-of-bounds read: crafted modulemd v2 metadata with multiple digest types can trigger an out-of-bounds read and likely crash during YAML serialization. Requirements to exploit: An attacker must supply crafted modulemd v2 input to a libmodulemd consumer that parses `data.artifacts.rpm-map` and later serializes the resulting object. No privileges are needed beyond control of that input. Remote reachability depends on the consuming application; the library itself does not expose a network service. Component affected: `libmodulemd-2.15.2-7.1.hum1`, `modulemd/modulemd-module-stream-v2.c`, `modulemd_module_stream_v2_emit_rpm_map()` Version affected: `libmodulemd-2.15.2-7.1.hum1` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: This report serves as the initial notification. CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 6.2 (MEDIUM) AV:L - The vulnerable behavior is in a library code path reached when a local caller supplies crafted module metadata; the library does not itself provide a network-facing service. AC:L - The trigger condition is simple: at least two digest algorithms in `rpm-map`, with fewer checksum entries in one per-digest map than the total digest count. PR:N - No privileges are required beyond the ability to provide the malformed modulemd document to an affected consumer. UI:N - Once the document is parsed and later serialized, the fault occurs automatically. S:U - The impact is limited to the consuming process. C:N - No confidentiality impact is established. I:N - No integrity impact is established. A:H - The out-of-bounds read occurs in the emit path and is likely to abort or crash the process. Impact: Moderate. Under Red Hat guidance, Important is generally reserved for issues that easily compromise confidentiality, integrity, or availability, including easy remote denial of service. Here the established impact is denial of service in a library consumer, but exploitation depends on a specific parse-then-emit workflow rather than a default exposed service. That makes Moderate a better fit than Important, while Low would understate a real memory-safety flaw with a concrete crash path. Embargo: no Reason: This is an availability-focused library flaw with a straightforward one-line fix and usage-dependent reachability. The current evidence does not support an easily exploitable default remote compromise scenario. Acknowledgement: Aisle Research Vulnerability Details: In `modulemd_module_stream_v2_emit_rpm_map()`, the outer loop iterates over the top-level digest algorithms in `self->rpm_artifact_map`. Inside that loop, the code creates a `checksums` array for the current `digest_table`, but the inner loop incorrectly uses `digests->len` as its bound instead of `checksums->len`: ```c digests = modulemd_ordered_str_keys (self->rpm_artifact_map, modulemd_strcmp_sort); ... checksums = modulemd_ordered_str_keys (digest_table, modulemd_strcmp_sort); for (guint j = 0; j < digests->len; j++) { checksum = g_ptr_array_index (checksums, j); EMIT_SCALAR (emitter, error, checksum); entry = g_hash_table_lookup (digest_table, checksum); if (!modulemd_rpm_map_entry_emit_yaml (entry, emitter, error)) { return FALSE; } } ``` `digests` is global across digest types, while `checksums` belongs only to the current digest table. If a document contains two or more digest algorithms but a given digest map has fewer checksum keys than the total number of digest algorithms, `g_ptr_array_index (checksums, j)` reads past the end of `checksums`. The parse path accepts checksum keys directly from YAML and stores them under the selected digest: ```c case YAML_SCALAR_EVENT: /* Each key in this map is a checksum with the value being a ModulemdRpmMapEntry */ checksum = (const gchar *)event.data.scalar.value; entry = modulemd_rpm_map_entry_parse_yaml (parser, strict, &nested_error); if (!entry) { g_propagate_error (error, g_steal_pointer (&nested_error)); return FALSE; } modulemd_module_stream_v2_set_rpm_artifact_map_entry ( modulestream, entry, digest, checksum); ``` No matching cardinality check is visible before the object is later emitted. The established consequence is an out-of-bounds read in a core serialization path, with likely denial-of-service impact. Confidentiality, integrity, and code-execution impact were not established. The flaw is present in `libmodulemd-2.15.2-7.1.hum1`; a broader affected range is not asserted here without authoritative history. Steps to reproduce: 1. Create a modulemd v2 document with at least two keys under `data.artifacts.rpm-map`, while leaving only one checksum entry under each digest key. 2. Parse the document with `modulemd_module_stream_read_string()`. 3. Add the resulting stream to an index and serialize it with `modulemd_module_index_dump_to_string()` or another path that reaches `modulemd_module_stream_v2_emit_rpm_map()`. 4. The serializer will iterate `j < digests->len` while indexing the smaller `checksums` array. The expected result is an out-of-bounds read, typically causing a crash or abort. Minimal trigger YAML: ```yaml — document: modulemd version: 2 data: name: demo stream: stable version: 1 context: c0ffee43 summary: s description: d license: module: [MIT] artifacts: rpms: pkg-0:1-1.x86_64 rpm-map: sha256: aaa: name: pkg epoch: 0 version: "1" release: "1" arch: x86_64 nevra: pkg-0:1-1.x86_64 sha1: bbb: name: pkg epoch: 0 version: "1" release: "1" arch: x86_64 nevra: pkg-0:1-1.x86_64 ... ``` Mitigation: Until a fixed package is available, avoid round-tripping untrusted modulemd v2 documents that include `artifacts.rpm-map`. If untrusted metadata must be accepted, reject or strip `rpm-map` content with multiple digest algorithms before serializing the parsed object. Proposed Fix: The inner loop should be bounded by the per-digest `checksums` array rather than the global `digests` array. Adding a regression test for multiple digest algorithms with fewer checksum keys in at least one digest map would help prevent recurrence. ```diff diff --git a/modulemd/modulemd-module-stream-v2.c b/modulemd/modulemd-module-stream-v2.c — a/modulemd/modulemd-module-stream-v2.c +++ b/modulemd/modulemd-module-stream-v2.c @@ -3267,7 +3267,7 @@ modulemd_module_stream_v2_emit_rpm_map (ModulemdModuleStreamV2 *self, checksums = modulemd_ordered_str_keys (digest_table, modulemd_strcmp_sort); for (guint j = 0; j < digests->len; j++) + for (guint j = 0; j < checksums->len; j++) { checksum = g_ptr_array_index (checksums, j); EMIT_SCALAR (emitter, error, checksum); ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use