---
id: ee5121b8-e3da-4667-88b7-243e11ec27f6
title: 'serve: git upload-pack has no timeout, no concurrency cap, and fully buffers the pack'
state: closed
labels:
- security
assignees: []
milestone: null
created: 2026-10-27T14:53:54.653824678Z
updated: 2026-10-27T15:48:01.072598268Z
---
`/repo.bundle/git-upload-pack` spawns `git upload-pack` per POST with no timeout, no concurrency limit, and buffers the entire pack in RAM (`wait_with_output()` → `Vec<u8>` → `respond`).

Problems:
- **No timeout**: nothing bounds the child. The 5 s socket read timeout applies to our reads from the client, not to git finishing; a client that dribbles its want-list or stalls mid-pack keeps a thread + a `git` process alive for as long as it likes. The 1 MiB request cap only bounds the *request*.
- **No concurrency cap**: N parallel clones = N `git upload-pack` processes + N full packs in memory. Two or three concurrent clones of a large repo can OOM the container (default Docker memory limits are generous, Cloudflare-free-boxes are not).
- **Whole-response buffering**: `respond()` takes `&[u8]`, so the pack is fully materialized before the first byte is written — no streaming, no `Content-Length`-free chunked path available in the current `respond`.
- Client disconnects are not detected until wait() returns.

Note this is the *smart* path and is serve-only (dumb `/repo.git` is static and unaffected), so a deployment that never advertises `/repo.bundle` still exposes it — the route exists whenever `www/repo.bundle` exists, which `build` always writes.

Fix direction:
- spawn `upload-pack`, stream stdout to the socket while writing the pack (drop `respond`'s buffering for this route), kill the child on write error
- enforce a wall-clock timeout (e.g. 60 s) and a concurrency semaphore; 503 when full
- consider gating the route behind a flag (it is a convenience, not the canonical clone path since #54)

Acceptance:
- [ ] a stalled client cannot keep a git process alive past the timeout
- [ ] concurrent clones are capped and the cap is documented
- [ ] memory use does not scale with pack size for a single clone
