CVE-2026-92822: Libmodulemd: libmodulemd: unbounded recursion when parsing nested xmd yaml
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.
Other sources
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
— Red Hat
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
Enforce a maximum recursion depth of 128 and a maximum parsed-node count of 100000 when converting nested xmd YAML; return an error when either limit is exceeded.
libmodulemd xmd conversion path maximum recursion depth and maximum parsed-node count = 128 and 100000 - Compensating control
Until a code fix is shipped, avoid parsing untrusted modulemd content; if untrusted input must be accepted, reject unusually deep or large xmd structures before passing them to libmodulemd.
- Compensating control
Isolate parsing of attacker-controlled modulemd in a separate process with strict stack, CPU, and memory limits; a typical stack limit mentioned in the guidance is ulimit -s 8192.