DjVuLibre 3.5.29 Patches Heap Overflow Via CVE-2025-53367

DjVuLibre 3.5.29 has been released to address CVE-2025-53367 (GHSL-2025-055), an out-of-bounds write vulnerability in the MMRDecoder::scanruns method. The flaw can potentially be exploited to achieve arbitrary code execution on Linux desktop systems when a user opens a specially crafted document.

DjVu is a document format used for similar purposes to PDF and is supported by both Evince and Papers, the default document viewers on many Linux distributions. Notably, even if a DjVu file uses a .pdf extension, these viewers automatically detect the actual format and invoke DjVuLibre to decode it.

The vulnerability was uncovered through fuzzing during research into the Evince document reader. Researchers at GitHub Security Lab have since developed a working proof-of-concept exploit, demonstrated against a fully patched Ubuntu 25.04 (x86_64) system with all standard security protections enabled.

Practical Exploitation

The exploit demonstration walks through a realistic attack scenario:

  1. The user clicks on a malicious file named poc.pdf in ~/Downloads, which is actually a DjVu document.
  2. The default viewer, /usr/bin/papers, loads the file, identifies its true format, and delegates to DjVuLibre for decoding.
  3. The file triggers the out-of-bounds write, eventually invoking system("google-chrome https://www.youtube.com/…").

The proof of concept is able to bypass ASLR, though it remains somewhat unreliable—it can work repeatedly and then become inactive for minutes at a time. However, the researchers believe greater reliability is achievable with further refinement. The choice of launching a YouTube video rather than a standalone executable stems from the AppArmor profile applied to /usr/bin/papers. That profile blocks arbitrary process launches but creates an exception for google-chrome. The AppArmor restrictions, however, are far from robust: they still permit arbitrary file writes to the user's home directory (except for obvious targets like ~/.bashrc), meaning a determined attacker could achieve full code execution despite the sandboxing.

Root Cause in MMRDecoder

The defect lies in MMRDecoder::scanruns, which writes run-length encoded data into two buffers: lineruns and prevruns.

//libdjvu/MMRDecoder.h
class DJVUAPI MMRDecoder : public GPEnabled
{
...
public:

  unsigned short *lineruns;
...
  unsigned short *prevruns;
...
}

The pointers pr and xr track current positions in those buffers respectively, but scanruns fails to verify that these pointers stay within allocated bounds.

//libdjvu/MMRDecoder.cpp
const unsigned short *
MMRDecoder::scanruns(const unsigned short **endptr)
{
...
  // Swap run buffers
  unsigned short *pr = lineruns;
  unsigned short *xr = prevruns;
  prevruns = pr;
  lineruns = xr;
...
  for(a0=0,rle=0,b1=*pr++;a0 < width;)
    {
     ...
            *xr = rle; xr++; rle = 0;
     ...
            *xr = rle; xr++; rle = 0;
 ...
          *xr = inc+rle-a0;
          xr++;
}

The result is a heap corruption condition from writes past the buffer edge; an out-of-bounds read via pr is also possible for the same underlying reason.

Responsible Disclosure

Maintainers Léon Bottou and Bill Riemers responded quickly to the July 1, 2025 report, with a fix committed the following day and applied in the 3.5.29 release on July 3. The proof-of-concept exploit source code is slated for publication in the GitHub Security Lab repository in the coming weeks.