# Gotenberg has a Race Condition via Multipart `downloadFrom` Handling

Gotenberg's multipart downloadFrom handler spawns concurrent goroutines writing shared context maps unsynchronized, letting an unauthenticated attacker crash the process via concurrent map writes.

- Payout: Research
- Severity: high
- Program: Gotenberg
- Found by: uokik
- Disclosed: 2026-05-29
- CVE: CVE-2026-45742
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-vp73-vjw8-8f32
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-vp73-vjw8-8f32
- Tags: area:web domain:api domain:web impact:dos kind:research platform:ghsa sev:high tech:go technique:race-window technique:source-review vuln:dos vuln:race-condition

**Target:** multipart conversion API endpoint (downloadFrom form field)
**Root cause:** newContext processes each downloadFrom entry in a separate errgroup goroutine that writes to shared request-context maps (ctx.files, ctx.diskToOriginal, ctx.filesByField) without synchronization; Go maps/slices are not safe for concurrent writes.
**Steps:**
1. Send a multipart POST to a conversion endpoint (e.g. `/forms/libreoffice/convert`) with a `downloadFrom` field containing a JSON array of many entries, each with `Url` and `Field`
2. Point each `Url` at a server that delays responses until all N requests arrive, so goroutines finish simultaneously
3. All goroutines write `ctx.files[filename]`, `ctx.diskToOriginal[path]`, `ctx.filesByField[...]` concurrently at context.go:395/396/401
4. Process terminates with `fatal error: concurrent map writes`; race detector confirms DATA RACE at those lines
**How found:** Code review of pkg/modules/api/context.go: noticed newContext parses downloadFrom before the handler and fans out per-entry downloads via errgroup.Go(), then each goroutine mutates shared context maps — a classic unsynchronized shared-resource pattern.
**Exploitation:** Unauthenticated remote attacker sends one crafted multipart request with many downloadFrom entries whose URLs respond in sync, forcing simultaneous map writes and crashing the Gotenberg process (default config: downloadFrom enabled, auth disabled).
**Insight:** When a framework pre-processes request fields by fanning out goroutines (errgroup.Go) before the handler, check whether each goroutine writes to shared request-scoped maps/slices; a single request with many entries plus a slow/synchronizing upstream turns it into a reliable crash.

## Proof of concept (copied exactly from the original report)

**Race detector test command**

```
GOTOOLCHAIN=go1.26.2 go test -race -tags security_poc ./pkg/modules/api -run TestSecurityPoCDownloadFromConcurrentMapWrites -count=1
```

**Non-race crash reproduction command**

```
GOTOOLCHAIN=go1.26.2 go test -tags security_poc ./pkg/modules/api -run TestSecurityPoCDownloadFromConcurrentMapWrites -count=20
```

