libc6: Bug (+fix) in readdir() due to getdents()
Bug #11685 reported by
Debian Bug Importer
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
GLibC |
Invalid
|
Critical
|
|||
glibc (Debian) |
Fix Released
|
Unknown
|
|||
glibc (Ubuntu) |
Fix Released
|
Medium
|
Jeff Bailey |
Bug Description
Automatically imported from Debian bug report #288948 http://
http://
Changed in glibc: | |
status: | Unconfirmed → Confirmed |
Changed in glibc: | |
importance: | Unknown → Critical |
Changed in glibc (Debian): | |
status: | New → Fix Released |
Changed in glibc: | |
status: | Confirmed → Invalid |
To post a comment you must log in.
- This bug report was already posted in <email address hidden>
I'm resubmitting it here at the request of Andreas Jaeger.
- All the following relates to the file:
glibc- 2.3.2/sysdeps/ unix/sysv/ linux/getdents. c
I report more than one bug in getdents() so please bare with me :> (the ically) enter an endless-loop:
description is actually much longer than the patch). In a nutshell, the
following program might (nondeterminist
DIR *dir = opendir( "some-autofs- directory" );
printf( "ino=%d name=%s\n", e->d_ino, e->d_name)
struct dirent *e;
while( (e = readdir(dir)) != NULL ) // might never return null
- In the implementation of getdents(), the `d_off' field (belonging to the
linux kernel's dirent structure) is falsely assumed to contain the *byte*
offset to the next dirent.
Note that the linux manual of the readdir system-call states that d_off
is the "offset to *this* dirent" while glibc's getdents treats it as the
offset to the *next* dirent.
- In practice, both of the above are wrong/misleading. The `d_off' field
may contain illegal negative values, 0 (should also never happen as the
"next" dirent's offset must always be bigger then 0), or positive values
that are bigger than the size of the directory-file itself:
o We're not sure what the Linux kernel intended to place in this field,
but our experience shows that on "real" file systems (that actually
reside on some disk) the offset seems to be a simple (not necessarily
continuous) counter: e.g. first entry may have d_off=1, second: d_off=2,
third: d_off=4096, fourth=d_off=4097 etc. We conjecture this is the
serial of the dirent record within the directory (and so, this is indeed
the "offset", but counted in records out of which some were already
removed).
o For file systems that are maintained by the amd automounter (automount,
directories) the d_off seems to be arbitrary (and may be negative, zero
or beyond the scope of a 32bit integer). We conjecture the amd doesn't
assign this field and the received values are simply garbage.
- Fortunately, there is actually no need to use d_off as all the needed conditions that might trigger conditions are
information is embedded in the d_reclen field (dirents are consecutive
and the "next" dirent is found exactly after the "current" dirent).
And indeed, glibc's getdents() uses the above technique most of the time.
But, there are certain exceptional-
getdents() to stop using on the dependable d_reclen and start using
the unreliable d_off (Furthermore, these exceptional-
identified based on the unreliable information held by d_off).
- One aspect of this bug was actually reported in 2001: mail.gnu. org/archive/ html/bug- glibc/2001- 03/msg00048. html
http://
but was wrongly (IMHO) dismissed by Ulrich Drepper who claimed that the
bug is actually in the linux-kernel. The linux-kernel folks do not intend
to change the data of their 'struct dirent'. They want it just the way it
is and don't care if the glibc folks "wrongly" interpret the `d_off' field.
- The bug in glibc only occurs when an "overflow" is *wrongly* detecte...