mrb's blog

getdents() And lstat() Race Condition

Keywords: amusing linux

I never thought I would ever witness this, as an end-user of a Linux system, from a normal shell session:

$ ls -l
ls: cannot access index.pdf.part: No such file or directory
total 2672
drwx------  2 mrb mrb      91 Aug 13 20:27 .
drwxr-xr-x 53 mrb mrb    4096 Aug 13 20:26 ..
-rw-------  1 mrb mrb 2610918 Aug 13 20:27 index.pdf
??????????  ? ?   ?         ?            ? index.pdf.part
$ ls -l
total 2672
drwx------  2 mrb mrb      91 Aug 13 20:27 .
drwxr-xr-x 53 mrb mrb    4096 Aug 13 20:26 ..
-rw-------  1 mrb mrb 2610918 Aug 13 20:27 index.pdf

This is a race condition between getdents() and lstat() ! I was using Firefox to download a file called index.pdf. The browser creates a 0-byte index.pdf file, then downloads the data to the temporary file index.pdf.part and, when done, renames it to index.pdf. I just happened to run "ls -l" right before the rename, so ls obtained the 2 directory entries via getdents(), but by the time ls tried to lstat() the second file, the rename had already been performed, so the lstat() call failed with ENOENT.

The first thought that flashed to my mind was filesystem corruption, but I immediately re-ran ls and it ran fine. This race condition is probably easy to reproduce with an automated tool, especially on a loaded system under a random I/O workload, but the fact it happened to me on a virtually idle system is very surprising :-)

Comments

Stan Schwertly wrote: Interesting write-up! This is one of those things you notice happen and want to tell someone. I wouldn't have immediately noticed the race operation -- I would have worried about corruption too hard! :) 20 Feb 2012 17:34 UTC