Skip to content

Reject compressed YAML files which decompress to much - #644

Open
ppisar wants to merge 1 commit into
fedora-modularity:mainfrom
ppisar:CVE-2026-92823
Open

ppisar wants to merge 1 commit into
fedora-modularity:mainfrom
ppisar:CVE-2026-92823

Conversation

@ppisar

@ppisar ppisar commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Small compressed files can decompress to very large files. Then processing the very large decompressed data consumes unexpectedly large amount of memory. This amplification of use of resources can be interpreted as a denial of service.

While libmodulemd uses a streaming YAML parser, effectivelly skipping large sequences of white spaces, constructing YAML scalars from very long sequences of the same character succesfully propages to higher level data structures.

For example this YAML document:

document: modulemd
version: 2
data:
  name: test
  stream: a
  version: 1
  context: c
  arch: x86_64
  summary: Text
  description: Text
  license:
    module:
      - MITA...A

where "MITA...A" is a sequence of 2^26 "A" characters occupies roughly 64 MB uncompressed, but only 64 KB after compressing with gzip. When processing the document with this library 265 MB of memory is allocated:

$ ls -l /tmp/input.yaml.gz
-rw-r--r--. 1 test test 65297 Aug  6 13:10 /tmp/input.yaml.gz
$ /usr/bin/time -f %M modulemd-validator /tmp/input.yaml.gz
/tmp/input.yaml.gz validated successfully
271688

To avoid these decompression bombs, this patch adds a limit for decompressed data. The limit is 64 MiB for small files, 64 times the compressed size for large files. 64 MiB is 10 times more than the largest modulemd YAML files observed in the wild and the factor of 64 is a doubled factor of XZ compression ratio on the largest observed file.

Reaching the limit is explained with a Glib warning and reported with FALSE return value from the public function (i.e.
modulemd_module_index_update_from_file()). The warning is used because libyaml and libmodulemd do not support reporting a detailed cause of a libyaml reader callback's error. Implementing the proper error reporting is out of scope of this decompression bomb mitigation.

Implementation note: This patch adds a test which cannot check for modulemd_module_index_update_from_file() return value because the reader callback emits a warning which Glib unhelpully turns into a SIGABORT if G_DEBUG=fatal-warning enviroment variable is set. Instead, the test checks for the abortion. This is not a different from reporting standard I/O error in libmodulemd.

This flaw was added with aad63a5 commit ("Add helpers for reading compressed files") and first released in 2.8.0 version.

Acknowledgement: Aisle Research
Resolves: CVE-2026-92823

@ppisar

ppisar commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator Author

I developed the code on Fedora 46. It seems that valgrind is more hungry on older Fedoras. I will look what can be improved.

@ppisar

ppisar commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator Author

I developed the code on Fedora 46. It seems that valgrind is more hungry on older Fedoras. I will look what can be improved.

The cause was CI valgrind tests configuring libmodulemd without compression support. I excluded the tests on those builds.

Small compressed files can decompress to very large files. Then
processing the very large decompressed data consumes unexpectedly
large amount of memory. This amplification of use of resources can
be interpreted as a denial of service.

While libmodulemd uses a streaming YAML parser, effectivelly skipping
large sequences of white spaces, constructing YAML scalars from very long
sequences of the same character succesfully propages to higher level
data structures.

For example this YAML document:

    document: modulemd
    version: 2
    data:
      name: test
      stream: a
      version: 1
      context: c
      arch: x86_64
      summary: Text
      description: Text
      license:
        module:
          - MITA...A

where "MITA...A" is a sequence of 2^26 "A" characters occupies roughly
64 MB uncompressed, but only 64 KB after compressing with gzip. When
processing the document with this library 265 MB of memory is
allocated:

    $ ls -l /tmp/input.yaml.gz
    -rw-r--r--. 1 test test 65297 Aug  6 13:10 /tmp/input.yaml.gz
    $ /usr/bin/time -f %M modulemd-validator /tmp/input.yaml.gz
    /tmp/input.yaml.gz validated successfully
    271688

To avoid these decompression bombs, this patch adds a limit for
decompressed data. The limit is 64 MiB for small files, 64 times the
compressed size for large files. 64 MiB is 10 times more than the
largest modulemd YAML files observed in the wild and the factor of 64
is a doubled factor of XZ compression ratio on the largest observed
file.

Reaching the limit is explained with a Glib warning and reported with
FALSE return value from the public function (i.e.
modulemd_module_index_update_from_file()). The warning is used because
libyaml and libmodulemd do not support reporting a detailed cause of
a libyaml reader callback's error. Implementing the proper error
reporting is out of scope of this decompression bomb mitigation.

Implementation note: This patch adds a test which cannot check for
modulemd_module_index_update_from_file() return value because the
reader callback emits a warning which Glib unhelpully turns into
a SIGABORT if G_DEBUG=fatal-warning enviroment variable is set.
Instead, the test checks for the abortion. This is not a different
from reporting standard I/O error in libmodulemd.

This flaw was added with aad63a5
commit ("Add helpers for reading compressed files") and first released
in 2.8.0 version.

Acknowledgement: Aisle Research
Resolves: CVE-2026-92823
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant