| author | Jason A. Donenfeld | 2026-04-13 19:02:31 +0200 |
|---|---|---|
| committer | Jason A. Donenfeld | 2026-04-13 19:04:38 +0200 |
| commit | 829eb0711305e8946fa2f4a1c57c43354f35e208 (patch) | |
| tree | af4ba00100ac7ce398f6654c693a2f367945d443 | |
| parent | 0d8e5fbc31e1082063bfb5155c35b7869721152b (diff) | |
| download | cgit-829eb0711305e8946fa2f4a1c57c43354f35e208.tar.gz | |
cache: truncate lock file before filling
lockslot() opens the lock file with ORDWR|OCREAT but without OTRUNC. If a previous cgit process was killed between lockslot() and unlockslot() (e.g. by a CGI timeout or OOM), the stale lock file remains on disk with the old content, and the kernel releases the fcntl lock.
The next process to claim the same cache slot then opens this stale lock file, acquires the fcntl lock, writes its key and generated content on top of the old bytes. If the new response is shorter than what was previously in the file, trailing bytes from the old response survive beyond the end of the new content. fstat() in fillslot() reports the total file size (including the stale tail), and printslot() faithfully sends all of it – producing a response that is the correct page followed by a fragment of whatever previously occupied that lock file.
Fix this by truncating the lock file after acquiring the lock and before writing the new key.
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
| -rw-r--r-- | cache.c | 2 |
@@ -185,6 +185,8 @@ static int lock_slot(struct cache_slot *slot) slot->lock_fd = -1; return saved_errno; } + if (ftruncate(slot->lock_fd, 0) < 0) + return errno; if (xwrite(slot->lock_fd, slot->key, slot->keylen + 1) < 0) return errno; return 0; |