Subject: Re: [bug#43591] [PATCH core-updates] gnu: glibc-final: Catch all cases of a glibc user not requesting 64-bit offsets and then using readdir.

From: Danny Milosavljevic <[email-masked]>
Date: Fri, 25 Sep 2020 17:33:20 +0200

Hi,

I wrote a FUSE filesystem to test what happens with big d_off (I just
hard-or-ed a bitmask) and ran it on a real ARMHF machine, then made the program
from before([1] from before) look into that directory.

Result (on ARMHF, so real 32 bit machine!):

$ gcc --version
gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
$ gcc a00.c 
$ ./a00 
1737031971 .
1737032035 ..
1737032035 hello
$ gcc -D_FILE_OFFSET_BITS=64 a00.c 
$ ./a.out 
320255973458211 .
320255973458275 ..
320255973458275 hello

(Note: Guix gcc-toolchain 10 on ARMHF is still building from source--and
will continue to do so for some hours I guess)

I only had to patch fuse 2.9.4 (lib/fuse_lowlevel.c) to do this:

char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf,
                      off_t off)
{
        unsigned namelen = strlen(name);
        unsigned entlen = FUSE_NAME_OFFSET + namelen;
        unsigned entsize = fuse_dirent_size(namelen);
        unsigned padlen = entsize - entlen;
        struct fuse_dirent *dirent = (struct fuse_dirent *) buf;

        dirent->ino = stbuf->st_ino;
        dirent->off = off | 0x1234567890123; // !!!!
        dirent->namelen = namelen;
        dirent->type = (stbuf->st_mode & 0170000) >> 12;
        strncpy(dirent->name, name, namelen);
        if (padlen)
                memset(buf + entlen, 0, padlen);

        return buf + entsize;
}

(I DID NOT have to patch the kernel or even have root)

So it can happen that you get 64 bit d_off even on real 32 bit machines!

That's what I thought--but I still wanted to make sure.

And the same on Guix i686 (a00 is [1] from my previous e-mail):

$ ./a00-i686
readdir: Value too large for defined data type
$ ./a00-i686_flag_32 
readdir: Value too large for defined data type
$ ./a00-i686_flag_64
320255973458211
320255973458275
320255973458275

So there you have it, even on i686--without emulating anything--you can get a
64 bit d_off value.