Re: [PATCH] fix bitmap corruption on close_range() with CLOSE_RANGE_UNSHARE

Al Viro
2024-08-04T04:17:35Z

BTW, the current description I've got next to struct fdtable definition:

/*
* fdtable's capacity is stored in ->max_fds; it is set when instance is
* created and never changed after that. It is always a positive multiple
* of BITS_PER_LONG (i.e. no less than BITS_PER_LONG). It can't exceed
* MAX_INT - userland often stores descriptors as int, treating anything
* negative as an error, so the numbers past MAX_INT are definitely
* not usable as descriptors.
*
* if fdt->max_fds is equal to N, fdt can hold descriptors in range 0..N-1.
*
* ->fd points to an N-element array of file pointers. If descriptor #k is
* open, ->fd[k] points to the file associated it; otherwise it's NULL.
*
* both ->open_fds and ->close_on_exec point to N-bit bitmaps.
* Since N is a multiple of BITS_PER_LONG, those are arrays of unsigned long
* with N / BITS_PER_LONG elements and all bits are in use.
* Bit #k in ->open_fds is set iff descriptor #k has been claimed.
* In that case the corresponding bit in ->close_on_exec determines whether
* it should be closed on execve(). If descriptor is *not* claimed, the
* value of corresponding bit in ->close_on_exec is undetermined.
*
* ->full_fds_bits points to a N/BITS_PER_LONG-bit bitmap, i.e. to an array of
* unsigned long with BITS_TO_LONGS(N / BITS_PER_LONG) elements. Note that
* N is *not* promised to be a multiple of BITS_PER_LONG^2, so there may be
* unused bits in the last word. Its contents is a function of ->open_fds -
* bit #k is set in it iff all bits stored in ->open_fds[k] are set (i.e. if
* ->open_fds[k] == ~0ul). All unused bits in the last word must be clear.
* That bitmap is used for cheaper search for unclaimed descriptors.
*
* Note that all of those are pointers - arrays themselves are elsewhere.
* The whole thing is reached from a files_struct instance; the reason
* we do not keep those pointers in files_struct itself is that on resize
* we want to flip all of them at once *and* we want the readers to be
* able to work locklessly. This is a device for switching all of those
* in one store operation. Freeing the replaced instance is RCU-delayed;
* ->rcu is used to do that.
*/