# Cloudreve: Storage-quota TOCTOU race allows quota bypass and storage-based denial of service

Cloudreve v4 PrepareUpload checks quota from a stale in-memory snapshot then charges storage in a separate UPDATE, letting concurrent uploads exceed MaxStorage and fill the disk.

- Payout: Research
- Severity: high
- Program: Cloudreve
- Found by: newugly
- Disclosed: 2026-09-22
- CVE: CVE-2026-77633
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-xj3h-wwxq-gfcj
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-xj3h-wwxq-gfcj
- Tags: area:web domain:oss domain:web impact:dos impact:integrity kind:research platform:ghsa sev:high tech:go technique:race-window vuln:business-logic vuln:dos vuln:race-condition

**Target:** File upload session endpoint (PrepareUpload) in Cloudreve v4 DBFS
**Root cause:** Quota check reads `used` from a request-start in-memory user snapshot with no lock or reservation, while the charge is a separate unconditional `storage = storage + diff` UPDATE outside the check's transaction, so concurrent requests all pass against the same stale value.
**Steps:**
1. Authenticate as an unprivileged `User`-group account with `Files.Write` permission.
2. Send N concurrent `PrepareUpload` requests (tested 20-way) each declaring a `size` that individually fits the remaining quota.
3. Each request reads the same stale `capacity.Used` at validateUserCapacity and passes the check.
4. Each then executes `UPDATE users SET storage = storage + diff`, committing N×size against MaxStorage.
5. Optionally complete chunked uploads to materialise the reserved bytes and fill host disk.
**How found:** Code reading of PrepareUpload: noticed the quota check uses an in-memory `f.user.Storage` snapshot with no SELECT/lock, and the charge is a plain additive UPDATE in a separate step, with pending sessions uncounted.
**Exploitation:** A 1 GiB-quota account reserved 17 GiB in a single 20-way burst; completing chunked uploads writes the excess bytes to disk, and amplification to host free space denies uploads for all instance users.
**Escalation:** Quota bypass amplified into storage-based DoS against the whole instance by materialising reservations to exhaust physical disk.
**Insight:** When a quota check reads from a hydrated in-memory user object rather than a fresh locked SELECT, and the charge is a bare `col = col + diff` UPDATE, every concurrent request sees the same stale value — look for check/charge split across a long code path (policy load, path gen, tx start) as the race window.

