Please report privately through GitHub's Security → Report a vulnerability form on this repository, which opens a private advisory visible only to the maintainer. Do not open a public issue for a memory-safety or archive-parsing bug.
Include the gleipnir --version output, the platform and compiler, and — most
usefully — the archive or input that triggers it. A file under a megabyte that
reproduces the fault is worth more than a description of it.
Expect an acknowledgement within a week. There is no bounty; this is a single-maintainer project.
The decompressor treats every archive as untrusted input, so anything that breaks that assumption is in scope:
- Memory unsafety while parsing a header, index, trailer, segment or recovery block — out-of-bounds read or write, overflow in a length or offset, wild pointer, use-after-free.
- A crafted archive causing a crash, a hang, or unbounded allocation.
- Path traversal on extract. Member names that are absolute, carry a drive
letter, contain a
..component, or hold control characters are refused, and extraction must not write outside the destination directory. - Silent wrong output — any input for which
gleipnirexits 0 and produces bytes that are not what was compressed. Every field the reader parses is bounds-checked, and a damaged archive is required to produce a diagnostic and exit 2, never a crash, a hang, or plausible-looking wrong bytes.
scripts/gfuzz.py exercises the storage-decay half of this threat model — bit
flips, decayed sectors, truncation, splices, zero holes, trailing junk and pure
noise — and asserts that every outcome is either byte-exact output or a
non-zero exit.
scripts/sfuzz.py exercises the deliberate half, which random damage does not
reach: archives whose magic, version, header hash and index hash are all
correct, so they pass every structural check, but whose segment fields
contradict each other. That is where a crafted archive attacks, and it is
where the bugs fixed in the September 2026 audit lived — undetected by the
three suites that existed at the time, all of which passed clean both before
and after.
scripts/wfuzz.py covers the word transform, whose segments none of the
other suites' inputs are large enough to produce: text carrying the flag and
escape bytes the transform must escape, bytes above 0x7F, every case form,
and words either side of its 32-letter limit, all round-tripped byte for byte.
The transform's decoder indexes a dictionary read out of the archive, so its
malformed-segment cases live in sfuzz.py with the rest.
A case that defeats any of them is a good report.
These are documented properties, not defects:
- There is no encryption, and none is planned.
gleipnirprovides integrity, not confidentiality. Encrypt the archive afterwards if the data needs it, and note that doing so removes the ability to scrub or repair it, since neither the checksums nor the parity blocks are readable through ciphertext. - Compression is not constant-time and its timing and output size depend on the input. Do not compress attacker-controlled data together with secrets and expect the size not to leak — the CRIME/BREACH class of attack applies here as it does to every general-purpose compressor.
- Memory use is large and preset-dependent, and scales with
-tbecause each worker owns a complete model. In 1.2 a-9worker budgets about 1.5 GB (1.1: 1.1 GB), so-9 -t8needs about 12 GB;gleipnircaps-tto installed RAM. An out-of-memory condition from settings chosen by the operator is a capacity question, covered in USAGE.md. - The whole input is buffered in RAM. A large input needs roughly its own size plus tables plus an output buffer, which is a documented limit rather than a resource-exhaustion bug.
- Anything requiring the attacker to already have write access to the machine
running
gleipnir, or to the archive being read, without a crafted-input step.
The latest release is supported. Fixes land on main and go out in the next
release; there is no backport branch.