Get started
CVE-2026-4430: LibreOffice OOXML overflow breaks headless DOCX-to-PDF

CVE-2026-4430: LibreOffice OOXML overflow breaks headless DOCX-to-PDF

CVE-2026-4430 is an OOXML salt-mismatch out-of-bounds write in LibreOffice, fixed in 26.2.3 / 25.8.7. Why headless soffice in production needs sandboxing now.

11 min read

CVE-2026-4430 is an out-of-bounds write in LibreOffice triggered by OOXML documents with mismatched encryption salt parameters. The bug sits in the encryption-handling path and is reachable from soffice --headless --convert-to pdf, which is the exact production pattern most teams use to turn user-uploaded DOCX into PDF. Fixed in LibreOffice 26.2.3 and 25.8.7, published by The Document Foundation on May 6, 2026. Affected versions: 26.2 before 26.2.3, and 25.8 before 25.8.7. If your backend accepts user-uploaded Office files and shells out to soffice, your converter host is on the blast radius. Patching is half the answer. Sandboxing the headless converter is the other half, and it is the only durable defense against the next OOXML CVE.

What CVE-2026-4430 actually is

CVE-2026-4430 is a heap buffer overflow in LibreOffice's OOXML encryption-parameter handling, reachable via crafted Office Open XML documents with mismatched encryption salt parameters. The Document Foundation describes it as "an out-of-bounds write when processing documents with inconsistent encryption salt values" on the official LibreOffice security page. The advisory index lists it as a high-severity bug with no CVSS score published at the time of writing.

The mechanics are concrete. OOXML's encryption block stores a salt used to derive the file encryption key. The format embeds the salt twice: once in the EncryptionInfo stream and once in the data descriptor that prefaces the encrypted package. LibreOffice's parser reads the salt length from one location and the salt bytes from another, then copies the bytes into a heap buffer sized from the first read. A crafted document with a small declared salt length but a large actual salt payload causes the parser to write past the end of the heap allocation. The write is attacker-controlled in both length and content, which is the textbook primitive for heap corruption.

The vulnerable code path runs during the initial document open, before any rendering or layout. That matters because it means the bug fires on soffice --headless --convert-to pdf input.docx exactly as it does on a double-click in the GUI. There is no need for the attacker to trigger any post-open action. Submitting the crafted DOCX to a conversion pipeline is enough. Independent tracking on Security Vulnerability corroborates the headless reachability.

If your backend accepts user-uploaded DOCX, XLSX, or PPTX files and converts them with LibreOffice 26.2.0 through 26.2.2 or 25.8.0 through 25.8.6, every conversion request is an attacker-controlled parse. The OOXML encryption-salt path is reachable headlessly. Patch to 26.2.3 or 25.8.7 this week, and sandbox soffice on the checklist below regardless.

Why headless soffice in production is in scope

The typical DOCX-to-PDF backend pattern is the reason this CVE matters. A user uploads a Word document through a web form, the backend writes it to a scratch directory, calls soffice --headless --convert-to pdf /tmp/input.docx --outdir /tmp/out, and streams the resulting PDF back. The pipeline ingests attacker-controlled bytes by definition, and the conversion tool parses the full file format including the encryption block.

The pattern in Node:

import { execFile } from "node:child_process";
import { promisify } from "node:util";
 
const execFileAsync = promisify(execFile);
 
export async function docxToPdf(inputPath, outputDir) {
  await execFileAsync("soffice", [
    "--headless",
    "--convert-to", "pdf",
    "--outdir", outputDir,
    inputPath,
  ], { timeout: 30_000 });
}

And in Python:

import subprocess
 
def docx_to_pdf(input_path: str, output_dir: str) -> None:
    subprocess.run(
        ["soffice", "--headless", "--convert-to", "pdf",
         "--outdir", output_dir, input_path],
        check=True,
        timeout=30,
    )

Both snippets are reasonable production code. Both are also fully exposed to CVE-2026-4430. The soffice binary parses every byte of the input file, the OOXML encryption path runs before any sandboxing the application layer might add, and a crafted DOCX with the mismatched salt fires the bug regardless of the calling language. The vulnerability is in the parser, not in the wrapper.

The exposure is broadest for SaaS products that let any signed-up user submit a document for conversion. It is narrower but still real for internal tools that convert documents from trusted employees, because phishing-style payloads delivered as innocuous-looking attachments are a known vector. The right planning assumption is that every DOCX hitting soffice is hostile.

Patch and detect

The fix is in LibreOffice 26.2.3 (for the 26.2 line) and 25.8.7 (for the 25.8 line). The Document Foundation published both releases on May 6, 2026, alongside the advisory. Older LibreOffice branches (24.8, 24.2, and earlier) are out of upstream support and will not receive a backport. Distros that ship those older branches need to either pull the fix forward themselves or move users to a supported branch.

Update commands per OS:

# Debian / Ubuntu (once the security update lands in the distro)
sudo apt update
sudo apt install --only-upgrade libreoffice libreoffice-common libreoffice-core
 
# Fedora / RHEL / Rocky / Alma
sudo dnf upgrade libreoffice libreoffice-core libreoffice-headless
 
# Alpine (edge)
apk update
apk upgrade libreoffice
 
# macOS (Homebrew Cask)
brew upgrade --cask libreoffice

For Docker pipelines, rebuild with the patched version pinned. The two most common base images:

# Debian-based, explicit version pin
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      libreoffice-core=1:26.2.3-* \
      libreoffice-writer=1:26.2.3-* \
      && rm -rf /var/lib/apt/lists/*
# Alpine-based, edge branch
FROM alpine:edge
RUN apk add --no-cache libreoffice>=26.2.3

Verify the active version before reopening traffic:

soffice --version
# Expected: LibreOffice 26.2.3.x or 25.8.7.x or newer

Distros lag upstream by days to weeks. If your package manager has not received the patched build yet, the next two sections explain what to do in the interim. Pin the LibreOffice version in your image build so a rebuild does not silently regress when the registry caches a stale tag.

Sandboxing checklist for soffice --headless

The CVE patch fixes one bug. Sandboxing fixes the entire class. Every document parser ships memory-corruption bugs eventually, and headless converters that handle untrusted input need defense in depth regardless of whether today's CVE is patched.

LayerWhat it doesHow to enable
Non-root userLimits filesystem reach if exploit landsUSER 1001 in Dockerfile, or runAsUser: 1001 in Kubernetes
Read-only root filesystemStops the exploit from persisting malware on diskreadOnlyRootFilesystem: true in Kubernetes, or --read-only in Docker
Scratch tmpfsBind-mount conversion working directory with noexec,nodev,nosuidtmpfs: { /work: noexec,nodev,nosuid,size=512M } in compose
SeccompDrops network syscalls and other primitives soffice never needsCustom seccomp profile or Kubernetes seccompProfile.type: RuntimeDefault
Egress denySoffice has no reason to phone home during a convertNetworkPolicy egress: [], or Docker --network none after copying the input file in
Memory capBounds blast radius of a malloc storm--memory=512m in Docker, or resources.limits.memory: 512Mi in K8s
CPU capBounds CPU-bomb impact--cpus=1.0 in Docker, or resources.limits.cpu: "1" in K8s
TimeoutKills hung or looping conversionsWall-clock kill from the caller process (timeout flag in Node or Python)
PID namespace + reaperReaps orphan child processes from soffice--init in Docker, or shareProcessNamespace: false plus tini in K8s
User namespaceMaps root in the container to a non-root host UIDuserns-remap: default on the Docker daemon, or runAsNonRoot: true in K8s

A minimal Docker run that ticks most of the boxes:

docker run --rm \
  --read-only \
  --network none \
  --memory 512m \
  --cpus 1.0 \
  --user 1001 \
  --tmpfs /work:noexec,nodev,nosuid,size=512M \
  --security-opt no-new-privileges \
  --init \
  -v /host/input.docx:/work/input.docx:ro \
  pdf-converter:patched \
  soffice --headless --convert-to pdf --outdir /work /work/input.docx

The --network none flag is the highest-value single switch. A headless conversion has no legitimate need to make outbound DNS queries or HTTP requests. Cutting the network eliminates the most common post-exploitation primitive, which is a beacon callback to attacker infrastructure.

Defense in depth: a single CVE patch is not enough

CVE-2026-4430 is the second LibreOffice OOXML memory-corruption advisory in twelve months. The previous one was patched in late 2025, and the pattern across the LibreOffice security advisory index is clear: large C++ parsers with decades of inherited code reliably produce a memory-corruption bug every six to twelve months. The patch fixes the specific bug, not the class.

The durable defense is to assume the parser will be exploited and design the surrounding system to contain the impact. The sandboxing layers above are the standard answer. Three principles tie them together:

Least privilege at the process level. Soffice does not need to be root, does not need to read /etc/shadow, does not need to write outside its scratch directory, and does not need to open outbound sockets. Every privilege not granted is a primitive the exploit cannot use.

Ephemeral execution. A converter container that lives for one conversion and dies is harder to pivot through than a long-running daemon. Container-per-conversion patterns add latency, but Kubernetes Jobs or AWS Lambda-style executions bound the blast radius to one document.

Observability on the parse. Conversions that take much longer than baseline, produce empty output, or trigger segfaults are signal. Log them, alert on them, and correlate with the submitting tenant. Memory-corruption exploits are noisy in the right metrics even when the application logs are silent.

Alternatives for DOCX/XLSX-to-PDF

When the patch lands and the sandbox is in place, headless LibreOffice is still a defensible choice for many workloads. For workloads where the threat model does not justify the operational burden, here are the practical alternatives.

OptionProsConsBest for
Headless LibreOffice (patched + sandboxed)Free, mature, broad format support, full DOCX fidelityLarge C++ parser, periodic memory-corruption CVEs, operational overhead of sandboxingSelf-hosted converters with engineering bandwidth for the sandbox
Aspose.Words (commercial)Managed-memory parser (.NET / Java), no shell-out, high DOCX fidelity, vendor patchesPer-conversion license cost, runtime-specific deployment, smaller communityTeams paying for a vendor-supported parser, regulated industries
Hosted DOCX-to-PDF APIZero operational burden, vendor handles patching and sandboxingRecurring cost, data leaves your infrastructure, vendor lock-inTeams that prefer to outsource the entire parsing problem
Browser-rendered HTML-to-PDFNo Office parser involved, smaller attack surface, fastOnly works if source is HTML or Markdown, not DOCXPipelines where the source format is HTML or Markdown
Pandoc via LaTeXDifferent code path entirely, robust against Office CVEsPoor fidelity for complex DOCX layouts, LaTeX toolchain operational burdenAcademic publishing, technical documentation, simple DOCX
Microsoft Graph / Office 365 APIMicrosoft handles the parser, official Office fidelityRequires Office 365 tenant, throttling, latency, data residencyTeams already on Office 365 with low conversion volume

The right pick is a function of the source format. If your input is genuinely DOCX from arbitrary external users, headless LibreOffice with the sandbox is the realistic choice for most teams. If your input is HTML or Markdown, switching to a browser-rendered pipeline removes the entire Office-parser attack surface and replaces it with a smaller one. If your input is mostly DOCX from a small set of trusted tenants, a commercial library or a hosted API trades cost for reduced operational risk.

How PDF4.dev handles this class

PDF4.dev does not run soffice in its render path. The PDF4.dev API accepts HTML, not user-uploaded DOCX or XLSX. PDFs are rendered through a browser-based pipeline (Playwright + Chromium) which has a different attack surface than an Office-format parser. Teams that need DOCX-to-PDF on PDF4.dev convert the source document client-side or through a separate Office-specific provider, then submit the resulting HTML to the PDF4.dev API. CVE-2026-4430 has no exposure on the PDF4.dev API surface.

The architectural choice generalizes. The cheapest way to be safe from Office-parser CVEs is to not run an Office parser. The second-cheapest way is to run one inside a sandbox that assumes the parser is hostile. The most expensive way is to run one as root on a shared host and hope the patches keep up.

Frequently asked questions

The FAQ above covers the recurring questions on this CVE. The short version: patch LibreOffice to 26.2.3 or 25.8.7, sandbox soffice regardless, and audit your logs for the OOXML encryption-path signals while the deploy rolls out.

For developers shipping document-conversion pipelines, the durable move is the same one CVE-2026-42593 in Gotenberg called for last month: assume the parser will eventually be exploited, design the surrounding system to contain the impact, and treat each new advisory as a trigger to rerun the sandbox checklist rather than a one-time emergency.

Start generating PDFs

Build PDF templates with a visual editor. Render them via API from any language in ~300ms.