Re: [PATCH] stat: fix inconsistency between struct stat and struct compat_stat

From: Mikulas Patocka
Date: Tue Apr 12 2022 - 13:42:24 EST




On Tue, 12 Apr 2022, Linus Torvalds wrote:

> On Mon, Apr 11, 2022 at 11:41 PM Mikulas Patocka <mpatocka@xxxxxxxxxx> wrote:
> >
> > Also, if the st_dev and st_rdev values are 32-bit, we don't have to use
> > old_valid_dev to test if the value fits into them. This fixes -EOVERFLOW
> > on filesystems that are on NVMe because NVMe uses the major number 259.
>
> The problem with this part of the patch is that this:
>
> > @@ -353,7 +352,7 @@ static int cp_new_stat(struct kstat *sta
> > #endif
> >
> > INIT_STRUCT_STAT_PADDING(tmp);
> > - tmp.st_dev = encode_dev(stat->dev);
> > + tmp.st_dev = new_encode_dev(stat->dev);
>
> completely changes the format of that st_dev field.

we have these definitions:

static __always_inline u16 old_encode_dev(dev_t dev)
{
return (MAJOR(dev) << 8) | MINOR(dev);
}

static __always_inline u32 new_encode_dev(dev_t dev)
{
unsigned major = MAJOR(dev);
unsigned minor = MINOR(dev);
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
}

As long as both major and minor numbers are less than 256, these functions
return equivalent results. So, I think it's safe to replace old_encode_dev
with new_encode_dev.

old_encode_dev shouldn't be called with minor >= 256, because it blends
the upper minor bits into the major field - the kernel doesn't do this and
checks the value with old_valid_dev before calling old_encode_dev. But
when old_valid_dev returns true, it doesn't matter if you use
old_encode_dev or new_encode_dev - both give equivalent results.

When I tested it, both gcc and openwatcom return st_dev 0x10301, which is
the expected value (the NVMe device has major 259 and minor 1).

> (b) we could just hide the bits in upper bits instead.
>
> So what I suggest we do is to make old_encode_dev() put the minor bits
> in bits 0..7 _and_ 16..23, and the major bits in 8..15 _and_ 24..32.

new_encode_dev puts the minor value into bits 0..7, 20..31 and the major
value into bits 8..19

So, we can use this instead of inventing a new format.

Mikulas