about summary refs log tree commit diff stats
path: root/cache.c
diff options
context:
space:
mode:
authorJohn Keeping2018-06-20 07:29:14 +0200
committerJason A. Donenfeld2018-06-27 18:13:03 +0200
commitb31e99887b17f513289fb11227b2484504e85b6c (patch)
treefd4b868e415db4de0577495eabf48e6bf814d408 /cache.c
parentgit: update to v2.18.0 (diff)
downloadcgit-b31e99887b17f513289fb11227b2484504e85b6c.tar.gz
cgit-b31e99887b17f513289fb11227b2484504e85b6c.zip
cache: close race window when unlocking slots
We use POSIX advisory record locks to control access to cache slots, but
these have an unhelpful behaviour in that they are released when any
file descriptor referencing the file is closed by this process.

Mostly this is okay, since we know we won't be opening the lock file
anywhere else, but there is one place that it does matter: when we
restore stdout we dup2() over a file descriptor referring to the file,
thus closing that descriptor.

Since we restore stdout before unlocking the slot, this creates a window
during which the slot content can be overwritten.  The fix is reasonably
straightforward: simply restore stdout after unlocking the slot, but the
diff is a bit bigger because this requires us to move the temporary
stdout FD into struct cache_slot.

Signed-off-by: John Keeping <john@keeping.me.uk>
Reviewed-by: Christian Hesse <mail@eworm.de>
Diffstat (limited to 'cache.c')
-rw-r--r--cache.c37
1 files changed, 14 insertions, 23 deletions
diff --git a/cache.c b/cache.c index 0901e6e..2c70be7 100644 --- a/cache.c +++ b/cache.c
@@ -29,6 +29,7 @@ struct cache_slot {
29 cache_fill_fn fn; 29 cache_fill_fn fn;
30 int cache_fd; 30 int cache_fd;
31 int lock_fd; 31 int lock_fd;
32 int stdout_fd;
32 const char *cache_name; 33 const char *cache_name;
33 const char *lock_name; 34 const char *lock_name;
34 int match; 35 int match;
@@ -197,6 +198,13 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
197 else 198 else
198 err = unlink(slot->lock_name); 199 err = unlink(slot->lock_name);
199 200
201 /* Restore stdout and close the temporary FD. */
202 if (slot->stdout_fd >= 0) {
203 dup2(slot->stdout_fd, STDOUT_FILENO);
204 close(slot->stdout_fd);
205 slot->stdout_fd = -1;
206 }
207
200 if (err) 208 if (err)
201 return errno; 209 return errno;
202 210
@@ -208,42 +216,24 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
208 */ 216 */
209static int fill_slot(struct cache_slot *slot) 217static int fill_slot(struct cache_slot *slot)
210{ 218{
211 int tmp;
212
213 /* Preserve stdout */ 219 /* Preserve stdout */
214 tmp = dup(STDOUT_FILENO); 220 slot->stdout_fd = dup(STDOUT_FILENO);
215 if (tmp == -1) 221 if (slot->stdout_fd == -1)
216 return errno; 222 return errno;
217 223
218 /* Redirect stdout to lockfile */ 224 /* Redirect stdout to lockfile */
219 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1) { 225 if (dup2(slot->lock_fd, STDOUT_FILENO) == -1)
220 close(tmp);
221 return errno; 226 return errno;
222 }
223 227
224 /* Generate cache content */ 228 /* Generate cache content */
225 slot->fn(); 229 slot->fn();
226 230
227 /* Make sure any buffered data is flushed to the file */ 231 /* Make sure any buffered data is flushed to the file */
228 if (fflush(stdout)) { 232 if (fflush(stdout))
229 close(tmp);
230 return errno; 233 return errno;
231 }
232 234
233 /* update stat info */ 235 /* update stat info */
234 if (fstat(slot->lock_fd, &slot->cache_st)) { 236 if (fstat(slot->lock_fd, &slot->cache_st))
235 close(tmp);
236 return errno;
237 }
238
239 /* Restore stdout */
240 if (dup2(tmp, STDOUT_FILENO) == -1) {
241 close(tmp);
242 return errno;
243 }
244
245 /* Close the temporary filedescriptor */
246 if (close(tmp))
247 return errno; 237 return errno;
248 238
249 return 0; 239 return 0;
@@ -393,6 +383,7 @@ int cache_process(int size, const char *path, const char *key, int ttl,
393 strbuf_addstr(&lockname, ".lock"); 383 strbuf_addstr(&lockname, ".lock");
394 slot.fn = fn; 384 slot.fn = fn;
395 slot.ttl = ttl; 385 slot.ttl = ttl;
386 slot.stdout_fd = -1;
396 slot.cache_name = filename.buf; 387 slot.cache_name = filename.buf;
397 slot.lock_name = lockname.buf; 388 slot.lock_name = lockname.buf;
398 slot.key = key; 389 slot.key = key;