Linux getdents.c is not aliasing safe
Highlights:
char *kbuf = buf;
size_t kbytes = nbytes;
if (offsetof (DIRENT_TYPE, d_name)
< offsetof (struct kernel_dirent64, d_name)
&& nbytes <= sizeof (DIRENT_TYPE))
{
kbytes = nbytes + offsetof (struct kernel_dirent64, d_name)
- offsetof (DIRENT_TYPE, d_name);
kbuf = __alloca(kbytes);
}
dp = (DIRENT_TYPE *)buf;
kdp = (struct kernel_dirent64 *) kbuf;
uint64_t d_ino = kdp->d_ino;
int64_t d_off = kdp->d_off;
unsigned char d_type = kdp->d_type;
DIRENT_SET_DP_INO (dp, d_ino);
dp->d_off = d_off;
GCC is perfectly free to re-order the stores and loads here; if it does,
when buf and kbuf are pointing at the same thing (which they usually are),
then storing to dp->d_off (at offset 4 in a 32-bit dirent) corrupts d_ino
(at offset 0 and size 8 in a kernel_dirent64). This is why Debian was
seeing a broken ldconfig.
Not sure how to fix this while still editing the buffer in-place. It seems
like we want the equivalent of:
union {
DIRENT_TYPE dpbuf[];
struct kernel_dirent64[];
}
but that's not legal C.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
Reply to: