This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]