du(1)/fts(3) integer overflow

Peter Jeremy PeterJeremy at optushome.com.au
Mon Dec 13 10:57:42 PST 2004


On Mon, 2004-Dec-13 11:42:17 -0000, henry wrote:
>I have noticed a problem with the fts(3) library or the way du(1) interacts
>with it.
>
>A 3.2TiB file gives the following output:
>> du -cs /fs/file
>3408720016      /fs/file
>-886247279      total
>
>This is because while stat(2) reports blocks as a 64bit number du(1) uses
>the 32bit value FTSENT.fts_number to store the result:

I think the large filesystem support is insufficiently exercised and
there will probably be more of these sort of things lurking around.

>The simplest change appears to be to make fts_number 64bit however this
>changes the fts(3) abi, so I am not sure if this is acceptable.

For 6.x, the ABI isn't fixed so fts_number can be changed to int64_t.
4.x doesn't really support large filesystems due to integer overflow
issues in UFS/FFS code so it's not really a problem there.

The 5.x ABI is fixed so there's no simple solution there.  Possible hacks
would be:
- Add a new 'fts_number64' at the end of FTSENT.  Since FTSENT is always
  managed by fts(3) and the documentation allows for undocumented fields,
  this should be permitted, though a "new" du(1) with an "old" libc
  would break badly.
- Move fts_number to the end of FTSENT and leave a 'long' hole where the
  existing fts_number is.  This changes the ABI but old programs remain
  compatible with the new fts.  (Though new programs break with the
  old fts).
- <Severe_kludge_alert>Have du(1) treat fts_pointer as an integer that
  it can concatenate to fts_number on 32-bit architectures:
    int64_t x = (int64_t)(ulong)p->fts_parent->fts_number |
	((int64_t)(ulong)p->fts_parent->fts_pointer) << 32;
    x += p->fts_statp->st_blocks;
    p->fts_parent->fts_number = (long)x;
    p->fts_parent->fts_pointer = (void *)(long)(x >> 32);
  etc. </Severe_kludge_alert>

-- 
Peter Jeremy


More information about the freebsd-hackers mailing list