Where
-Infinity
0
Severity
6.2
AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

A flaw was found in libmodulemd. When parsing module metadata, the xmd field is converted into nested GVariant values through recursive YAML mapping and sequence helpers without a recursion-depth or node-count limit. An attacker who can supply crafted modulemd YAML with deeply nested xmd content can cause stack exhaustion or excessive resource consumption, resulting in a denial of service.

1 / 2
Source: MITRE
First published (updated )
Severity
4

AIONLYREPORT package: libmodulemd-2.15.2-7.1.hum1 ------ Summary: Unbounded recursion while parsing attacker-controlled XMD YAML into nested GVariant: denial of service is possible when deeply nested xmd YAML is recursively converted into GVariant values without recursion-depth or node-count limits, leading to stack exhaustion and excessive parser resource consumption. Requirements to exploit: The attacker must be able to cause an application or tool using libmodulemd-2.15.2-7.1.hum1 to parse attacker-controlled modulemd YAML containing a deeply nested xmd field. This is reachable through public parsing interfaces such as modulemdmoduleindexupdatefromstring(), modulemdmoduleindexupdatefromstream(), modulemdmoduleindexupdatefromcustom(), or tooling such as modulemd-validator. No privileges are required, but environments that only ingest trusted metadata materially reduce exposure. Component affected: libmodulemd-2.15.2-7.1.hum1, primarily modulemd/modulemd-yaml-util.c (mmdparsexmd(), mmdvariantfrommapping(), mmdvariantfromsequence()), reached from the xmd handling in modulemd/modulemd-module-stream-v1.c, modulemd/modulemd-module-stream-v2.c, and modulemd/modulemd-packager-v3.c. 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: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 5.5 (MEDIUM) AV:L - The evidence shows a library/parser issue triggered when a local consumer parses attacker-controlled modulemd YAML; the available materials do not establish a network-facing service in this package. AC:L - A sufficiently deep xmd nesting structure is enough to drive the vulnerable recursion. PR:N - No privileges are required to supply malicious metadata to a parser that accepts attacker-controlled input. UI:N - No separate human action is required beyond the normal parsing of the supplied document. S:U - The failure remains within the security scope of the parsing process. C:N - The available evidence does not show confidentiality impact. I:N - The available evidence does not show integrity impact. A:H - The parser can crash, abort, or consume substantial resources, causing a denial of service. Impact: Moderate. Based on the Red Hat severity guidance, the supported impact is availability loss rather than code execution or data exposure. The issue can be serious for consumers that parse untrusted module metadata, but exploitability depends on attacker control over the parsed YAML and is therefore deployment-dependent rather than an easily exploitable remote service case. Embargo: no Reason: The available evidence supports a moderate, availability-only parser denial of service. Exploitation depends on a consumer accepting attacker-controlled modulemd content, and straightforward operational mitigations exist until a code fix is shipped. Acknowledgement: Aisle Research Vulnerability Details: libmodulemd-2.15.2-7.1.hum1 parses xmd content in module stream v1, module stream v2, and packager v3 documents by calling mmdparsexmd() when the xmd key is encountered. mmdparsexmd() dispatches YAML mappings to mmdvariantfrommapping(), and the conversion helpers recurse again whenever nested mappings or sequences are seen. The available code does not show a recursion-depth limit or a total-node limit in this path. The following excerpt from modulemd/modulemd-yaml-util.c shows the recursive descent in mmdvariantfrommapping(): c ... case YAMLMAPPINGSTARTEVENT: value = mmdvariantfrommapping (parser, &nestederror); if (!value) { gpropagateerror (error, gstealpointer (&nestederror)); return NULL; } break; case YAMLSEQUENCESTARTEVENT: value = mmdvariantfromsequence (parser, &nestederror); if (!value) { gpropagateerror (error, gstealpointer (&nestederror)); return NULL; } break; ... mmdvariantfromsequence() contains the same recursive pattern for nested mappings and nested sequences. Because this descent is unbounded, sufficiently deep attacker-controlled xmd YAML can exhaust stack space and may also create severe parse-time CPU or memory pressure. The clearest supported outcome is denial of service. The available evidence does not establish confidentiality, integrity, or code execution impact. Steps to reproduce: 1. Build libmodulemd and modulemd-validator with sanitizers if available, and set a typical stack limit such as ulimit -s 8192. 2. Create a modulemd v2 document with deeply nested xmd content, for example: yaml document: modulemd version: 2 data: name: demo stream: s version: 1 context: c0 arch: x8664 summary: s description: d license: { module: [MIT] } xmd: {a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{a:{}}}}}}}}}}}}}}}}}}}} 3. Parse the file with modulemd-validator <file.yaml>, or feed the same content to modulemdmoduleindexupdatefromstring(). 4. Increase the nesting depth until parsing fails. The expected result is a crash, abort, stack overflow, or severe parse-time resource pressure, depending on the runtime environment and stack limit. 5. A backtrace should show the failure through mmdparsexmd() into the mmdvariantfrommapping() / mmdvariantfromsequence() recursion chain. Mitigation: Until a fix is available, avoid parsing untrusted modulemd documents containing attacker-controlled xmd data. If such input must be accepted, reject unusually deep or large xmd structures before handing them to libmodulemd and isolate parsing in a process with strict stack, CPU, and memory limits. If supported by the YAML stack, parser-side depth or alias limits should also be enabled. Proposed Fix: Enforce both a maximum recursion depth and a maximum parsed-node count in the xmd conversion path, and return an error when either limit is exceeded. diff diff --git a/modulemd/modulemd-yaml-util.c b/modulemd/modulemd-yaml-util.c @@ +#define MMDXMDMAXDEPTH 128 +#define MMDXMDMAXNODES 100000 + +static GVariant mmdvariantfrommappinglimited ( + yamlparsert parser, GError error, guint depth, guint nodes); +static GVariant mmdvariantfromsequencelimited ( + yamlparsert parser, GError error, guint depth, guint nodes); @@ -GVariant mmdvariantfrommapping (yamlparsert parser, GError error) +static GVariant mmdvariantfrommappinglimited ( + yamlparsert parser, GError error, guint depth, guint nodes) { + if (depth > MMDXMDMAXDEPTH || ++(nodes) > MMDXMDMAXNODES) + MMDYAMLERROREVENTEXIT (error, event, "xmd exceeds safety limits"); @@ value = mmdvariantfrommapping (parser, &nestederror); + value = mmdvariantfrommappinglimited ( + parser, &nestederror, depth + 1, nodes); @@

value = mmdvariantfromsequence (parser, &nestederror); + value = mmdvariantfromsequencelimited ( + parser, &nestederror, depth + 1, nodes); @@ -GVariant mmdvariantfromsequence (yamlparsert parser, GError error) +static GVariant mmdvariantfromsequencelimited ( + yamlparsert parser, GError error, guint depth, guint nodes) { + if (depth > MMDXMDMAXDEPTH || ++(nodes) > MMDXMDMAXNODES) + MMDYAMLERROREVENTEXIT (error, event, "xmd exceeds safety limits"); @@

value = mmdvariantfrommapping (parser, &nestederror); + value = mmdvariantfrommappinglimited ( + parser, &nestederror, depth + 1, nodes); @@

value = mmdvariantfromsequence (parser, &nestederror); + value = mmdvariantfromsequencelimited ( + parser, &nestederror, depth + 1, nodes); @@ GVariant mmdparsexmd (yamlparsert parser, GError error) { + guint nodes = 0; @@

variant = mmdvariantfrommapping (parser, &nestederror); + variant = mmdvariantfrommappinglimited ( + parser, &nestederror, 1, &nodes);

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

First published (updated )
Severity
6.2
AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

A flaw was found in libmodulemd. In the module stream v2 rpm-map emission path, an inner loop incorrectly uses the count of digest algorithms as its bound instead of the number of checksums in the current digest table. Crafted modulemd v2 metadata with multiple digest types can trigger an out-of-bounds read during YAML serialization, typically causing a crash and resulting in a denial of service.

1 / 3
Source: OSV
First published (updated )
Severity
4

AIONLYREPORT 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, modulemdmodulestreamv2emitrpmmap() 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 modulemdmodulestreamv2emitrpmmap(), the outer loop iterates over the top-level digest algorithms in self->rpmartifactmap. Inside that loop, the code creates a checksums array for the current digesttable, but the inner loop incorrectly uses digests->len as its bound instead of checksums->len: c digests = modulemdorderedstrkeys (self->rpmartifactmap, modulemdstrcmpsort); ... checksums = modulemdorderedstrkeys (digesttable, modulemdstrcmpsort); for (guint j = 0; j < digests->len; j++) { checksum = gptrarrayindex (checksums, j); EMITSCALAR (emitter, error, checksum); entry = ghashtablelookup (digesttable, checksum); if (!modulemdrpmmapentryemityaml (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, gptrarrayindex (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 YAMLSCALAREVENT: / Each key in this map is a checksum with the value being a ModulemdRpmMapEntry / checksum = (const gchar )event.data.scalar.value;

entry = modulemdrpmmapentryparseyaml (parser, strict, &nestederror); if (!entry) { gpropagateerror (error, gstealpointer (&nestederror)); return FALSE; } modulemdmodulestreamv2setrpmartifactmapentry ( 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 modulemdmodulestreamreadstring(). 3. Add the resulting stream to an index and serialize it with modulemdmoduleindexdumptostring() or another path that reaches modulemdmodulestreamv2emitrpmmap(). 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.x8664 rpm-map: sha256: aaa: name: pkg epoch: 0 version: "1" release: "1" arch: x8664 nevra: pkg-0:1-1.x8664 sha1: bbb: name: pkg epoch: 0 version: "1" release: "1" arch: x8664 nevra: pkg-0:1-1.x8664 ...

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 @@ modulemdmodulestreamv2emitrpmmap (ModulemdModuleStreamV2 self, checksums = modulemdorderedstrkeys (digesttable, modulemdstrcmpsort); for (guint j = 0; j < digests->len; j++) + for (guint j = 0; j < checksums->len; j++) { checksum = gptrarrayindex (checksums, j); EMITSCALAR (emitter, error, checksum);

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

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203