2021-07-21 21:04:17

by butt3rflyh4ck

[permalink] [raw]
Subject: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

Hi, there was a shift-out-bounds bug in minix_statfs in
fs/minix/inode.c founded by my custom syzkaller and reproduced in
linux-5.13.0-rc6+.

####
Simply analyze the vulnerability principle, First, mount a minix file
system by minix_fill_super() and initialize some custom basic data.

the code is as follows:
```
static int minix_fill_super(struct super_block *s, void *data, int silent)
{
struct buffer_head *bh;
struct buffer_head **map;
struct minix_super_block *ms;
struct minix3_super_block *m3s = NULL;
unsigned long i, block;
struct inode *root_inode;
struct minix_sb_info *sbi;
int ret = -EINVAL;

sbi = kzalloc(sizeof(struct minix_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
s->s_fs_info = sbi;

BUILD_BUG_ON(32 != sizeof (struct minix_inode));
BUILD_BUG_ON(64 != sizeof(struct minix2_inode));

if (!sb_set_blocksize(s, BLOCK_SIZE))
goto out_bad_hblock;

if (!(bh = sb_bread(s, 1))) /// -----------------> get
minix_super_block's data from super_block
goto out_bad_sb;

ms = (struct minix_super_block *) bh->b_data; /// --------------> set
minix_super_block pointer
sbi->s_ms = ms;
sbi->s_sbh = bh;
sbi->s_mount_state = ms->s_state;
sbi->s_ninodes = ms->s_ninodes;
sbi->s_nzones = ms->s_nzones;
sbi->s_imap_blocks = ms->s_imap_blocks;
sbi->s_zmap_blocks = ms->s_zmap_blocks;
sbi->s_firstdatazone = ms->s_firstdatazone;
sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
set sbi->s_log_zone_size
s->s_maxbytes = ms->s_max_size;
s->s_magic = ms->s_magic;
```
Set bh->b_data to sbi. Initialize minix_sb_info by minix_super_block ’s data

After the file system is mounted, we can call the statfs syscall and
it could invoke the minix_statfs function. the code is as follows:
```
static int minix_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *sb = dentry->d_sb;
struct minix_sb_info *sbi = minix_sb(sb);
u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
buf->f_type = sb->s_magic;
buf->f_bsize = sb->s_blocksize;
buf->f_blocks = (sbi->s_nzones - sbi->s_firstdatazone) <<
sbi->s_log_zone_size; // -----> shift left
buf->f_bfree = minix_count_free_blocks(sb);
buf->f_bavail = buf->f_bfree;
buf->f_files = sbi->s_ninodes;
buf->f_ffree = minix_count_free_inodes(sb);
buf->f_namelen = sbi->s_namelen;
buf->f_fsid = u64_to_fsid(id);

return 0;
}
```
if set sbi->s_log_zone_size as a lager num, the
(sbi->s_nzones-sbi->s_firstdatazone) will be shift left out of bounds
from the 64-bit type 'long unsigned int'.

####
crash logs is as follows:
```
[ 1512.826425][ T8010] loop0: detected capacity change from 0 to 16
[ 1512.829202][ T8010]
================================================================================
[ 1512.830892][ T8010] UBSAN: shift-out-of-bounds in fs/minix/inode.c:380:57
[ 1512.851019][ T8010] shift exponent 1024 is too large for 64-bit
type 'long unsigned int'
[ 1512.852875][ T8010] CPU: 0 PID: 8010 Comm: minix_statfs Not tainted
5.13.0-rc6+ #21
[ 1512.854333][ T8010] Hardware name: QEMU Standard PC (i440FX + PIIX,
1996), BIOS 1.13.0-1ubuntu1 04/01/2014
[ 1512.856165][ T8010] Call Trace:
[ 1512.856809][ T8010] dump_stack+0x7f/0xad
[ 1512.857629][ T8010] ubsan_epilogue+0x5/0x40
[ 1512.858417][ T8010] __ubsan_handle_shift_out_of_bounds.cold+0x61/0x10e
[ 1512.859634][ T8010] ? __lock_acquire+0x3b6/0x2680
[ 1512.860566][ T8010] minix_statfs.cold+0x16/0x1f
[ 1512.861453][ T8010] statfs_by_dentry+0x48/0x70
[ 1512.862314][ T8010] vfs_statfs+0x11/0xc0
[ 1512.863095][ T8010] fd_statfs+0x29/0x60
[ 1512.863860][ T8010] __do_sys_fstatfs+0x20/0x50
[ 1512.864733][ T8010] do_syscall_64+0x3a/0xb0
[ 1512.865820][ T8010] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 1512.866948][ T8010] RIP: 0033:0x44e74d
[ 1512.867804][ T8010] Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3
0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b
4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff 8
[ 1512.871739][ T8010] RSP: 002b:00007ffe06c55808 EFLAGS: 00000217
ORIG_RAX: 000000000000008a
[ 1512.873195][ T8010] RAX: ffffffffffffffda RBX: 0000000000400530
RCX: 000000000044e74d
[ 1512.874585][ T8010] RDX: 000000000044d9f7 RSI: 0000000000000000
RDI: 0000000000000005
[ 1512.875939][ T8010] RBP: 00007ffe06c55820 R08: 00007ffe06c55664
R09: 0000000000000000
[ 1512.877284][ T8010] R10: 00007ffe06c556e0 R11: 0000000000000217
R12: 0000000000403750
[ 1512.878665][ T8010] R13: 0000000000000000 R14: 00000000004c6018
R15: 0000000000000000
[ 1512.881676][ T8010]
================================================================================
[ 1512.883289][ T8010] Kernel panic - not syncing: panic_on_warn set ...
[ 1512.884457][ T8010] CPU: 0 PID: 8010 Comm: minix_statfs Not tainted
5.13.0-rc6+ #21
[ 1512.885851][ T8010] Hardware name: QEMU Standard PC (i440FX + PIIX,
1996), BIOS 1.13.0-1ubuntu1 04/01/2014
[ 1512.887598][ T8010] Call Trace:
[ 1512.888186][ T8010] dump_stack+0x7f/0xad
[ 1512.888935][ T8010] panic+0x147/0x31a
[ 1512.889623][ T8010] ubsan_epilogue+0x3f/0x40
[ 1512.890392][ T8010] __ubsan_handle_shift_out_of_bounds.cold+0x61/0x10e
[ 1512.891527][ T8010] ? __lock_acquire+0x3b6/0x2680
[ 1512.892353][ T8010] minix_statfs.cold+0x16/0x1f
[ 1512.893202][ T8010] statfs_by_dentry+0x48/0x70
[ 1512.894033][ T8010] vfs_statfs+0x11/0xc0
[ 1512.894759][ T8010] fd_statfs+0x29/0x60
[ 1512.895483][ T8010] __do_sys_fstatfs+0x20/0x50
[ 1512.896298][ T8010] do_syscall_64+0x3a/0xb0
[ 1512.897103][ T8010] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 1512.898136][ T8010] RIP: 0033:0x44e74d
[ 1512.898823][ T8010] Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3
0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b
4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 c0 ff ff 8
[ 1512.902284][ T8010] RSP: 002b:00007ffe06c55808 EFLAGS: 00000217
ORIG_RAX: 000000000000008a
[ 1512.903771][ T8010] RAX: ffffffffffffffda RBX: 0000000000400530
RCX: 000000000044e74d
[ 1512.905263][ T8010] RDX: 000000000044d9f7 RSI: 0000000000000000
RDI: 0000000000000005
[ 1512.906723][ T8010] RBP: 00007ffe06c55820 R08: 00007ffe06c55664
R09: 0000000000000000
[ 1512.908181][ T8010] R10: 00007ffe06c556e0 R11: 0000000000000217
R12: 0000000000403750
[ 1512.909661][ T8010] R13: 0000000000000000 R14: 00000000004c6018
R15: 0000000000000000
[ 1512.911356][ T8010] Kernel Offset: disabled
[ 1512.912216][ T8010] Rebooting in 86400 seconds..
```
The attachment is a reproduction.


Regards,
butt3rflyh4ck

--
Active Defense Lab of Venustech


Attachments:
repro.cprog (14.56 kB)

2021-07-21 21:05:08

by Matthew Wilcox

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> minix_super_block pointer
> sbi->s_ms = ms;
> sbi->s_sbh = bh;
> sbi->s_mount_state = ms->s_state;
> sbi->s_ninodes = ms->s_ninodes;
> sbi->s_nzones = ms->s_nzones;
> sbi->s_imap_blocks = ms->s_imap_blocks;
> sbi->s_zmap_blocks = ms->s_zmap_blocks;
> sbi->s_firstdatazone = ms->s_firstdatazone;
> sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> set sbi->s_log_zone_size

So what you're saying is that if you construct a malicious minix image,
you can produce undefined behaviour? That's not something we're
traditionally interested in, unless the filesystem is one customarily
used for data interchange (like FAT or iso9660).

2021-07-21 21:05:38

by Darrick J. Wong

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Wed, Jul 21, 2021 at 06:37:23PM +0100, Matthew Wilcox wrote:
> On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> > ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> > minix_super_block pointer
> > sbi->s_ms = ms;
> > sbi->s_sbh = bh;
> > sbi->s_mount_state = ms->s_state;
> > sbi->s_ninodes = ms->s_ninodes;
> > sbi->s_nzones = ms->s_nzones;
> > sbi->s_imap_blocks = ms->s_imap_blocks;
> > sbi->s_zmap_blocks = ms->s_zmap_blocks;
> > sbi->s_firstdatazone = ms->s_firstdatazone;
> > sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> > set sbi->s_log_zone_size
>
> So what you're saying is that if you construct a malicious minix image,
> you can produce undefined behaviour? That's not something we're
> traditionally interested in, unless the filesystem is one customarily
> used for data interchange (like FAT or iso9660).

Sounds to me like butt3rflyh4ck is volunteering to rebuild fs/minix with
proper ondisk metadata buffer verifiers.

--D

2021-07-22 02:44:59

by butt3rflyh4ck

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Thu, Jul 22, 2021 at 1:37 AM Matthew Wilcox <[email protected]> wrote:
>
> On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> > ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> > minix_super_block pointer
> > sbi->s_ms = ms;
> > sbi->s_sbh = bh;
> > sbi->s_mount_state = ms->s_state;
> > sbi->s_ninodes = ms->s_ninodes;
> > sbi->s_nzones = ms->s_nzones;
> > sbi->s_imap_blocks = ms->s_imap_blocks;
> > sbi->s_zmap_blocks = ms->s_zmap_blocks;
> > sbi->s_firstdatazone = ms->s_firstdatazone;
> > sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> > set sbi->s_log_zone_size
>

> So what you're saying is that if you construct a malicious minix image,
> you can produce undefined behaviour?

Yes, the attachment is a reproduction. just compile it and run.

>That's not something we're
> traditionally interested in, unless the filesystem is one customarily
> used for data interchange (like FAT or iso9660).
>

These file systems are my fuzzing targets.


Regards,
butt3rflyh4ck.


--
Active Defense Lab of Venustech

2021-07-22 02:54:52

by Matthew Wilcox

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Thu, Jul 22, 2021 at 10:43:10AM +0800, butt3rflyh4ck wrote:
> On Thu, Jul 22, 2021 at 1:37 AM Matthew Wilcox <[email protected]> wrote:
> >That's not something we're
> > traditionally interested in, unless the filesystem is one customarily
> > used for data interchange (like FAT or iso9660).
>
> These file systems are my fuzzing targets.

You're not the first.

https://lore.kernel.org/linux-fsdevel/[email protected]/T/#u

2021-07-22 08:12:01

by Dan Carpenter

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Wed, Jul 21, 2021 at 06:37:23PM +0100, Matthew Wilcox wrote:
> On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> > ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> > minix_super_block pointer
> > sbi->s_ms = ms;
> > sbi->s_sbh = bh;
> > sbi->s_mount_state = ms->s_state;
> > sbi->s_ninodes = ms->s_ninodes;
> > sbi->s_nzones = ms->s_nzones;
> > sbi->s_imap_blocks = ms->s_imap_blocks;
> > sbi->s_zmap_blocks = ms->s_zmap_blocks;
> > sbi->s_firstdatazone = ms->s_firstdatazone;
> > sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> > set sbi->s_log_zone_size
>
> So what you're saying is that if you construct a malicious minix image,
> you can produce undefined behaviour? That's not something we're
> traditionally interested in, unless the filesystem is one customarily
> used for data interchange (like FAT or iso9660).

Someone had the idea what we should make these things only compile for
usermode linux. It's kind of a hassle to copy things from UML but if
people really wanted to we could write a program to handle it.

regards,
dan carpenter

2021-07-22 22:01:42

by Theodore Ts'o

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Wed, Jul 21, 2021 at 06:37:23PM +0100, Matthew Wilcox wrote:
> On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> > ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> > minix_super_block pointer
> > sbi->s_ms = ms;
> > sbi->s_sbh = bh;
> > sbi->s_mount_state = ms->s_state;
> > sbi->s_ninodes = ms->s_ninodes;
> > sbi->s_nzones = ms->s_nzones;
> > sbi->s_imap_blocks = ms->s_imap_blocks;
> > sbi->s_zmap_blocks = ms->s_zmap_blocks;
> > sbi->s_firstdatazone = ms->s_firstdatazone;
> > sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> > set sbi->s_log_zone_size
>
> So what you're saying is that if you construct a malicious minix image,
> you can produce undefined behaviour? That's not something we're
> traditionally interested in, unless the filesystem is one customarily
> used for data interchange (like FAT or iso9660).

It's going to depend on the file system maintainer. The traditional
answer is that block device is part of the Trusted Computing Base, and
malicious file system images are not considered part of the threat
model. A system adminstration or developer which allows potentially
malicious agents to mount file system agents are cray-cray.

Unfortunately, those developers are also known as "Linux desktop devs"
(who implement unprivileged mounts of USB cards) or "container
evangelists" who think containers should be treated as being Just As
Good as VM's From A Security Perspective.

So I do care about this for ext4, although I don't guarantee immediate
response, as it's something that I usually end up doing on my own
time. I do get cranky that Syzkaller makes it painful to extract out
the fuzzed file system image, and I much prefer those fuzzing systems
which provide the file system image and the C program used to trigger
the failre as two seprate files. Or failing that, if there was some
trivial way to get the syzkaller reproducer program to disgorge the
file system image to a specified output file. As a result, if I have
a choice of spending time investigating fuzzing report from a more
file-system friendly fuzzing program and syzkaller, I'll tend choose
to spend my time dealing with other file system fuzzing reports first.

The problem for Minix is that it does not have an active maintainer.
So if you submit fuzzing reports for Minix, it's unlikely anyone will
spend time working on it. But if you submit a patch, it can go in,
probably via Andrew Morton. (Recent Minix fixes that have gone in
this way: 0a12c4a8069 and 32ac86efff9)

Cheers,

- Ted

2021-07-22 22:36:38

by Randy Dunlap

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On 7/22/21 2:58 PM, Theodore Ts'o wrote:
...

>
> So I do care about this for ext4, although I don't guarantee immediate
> response, as it's something that I usually end up doing on my own
> time. I do get cranky that Syzkaller makes it painful to extract out
> the fuzzed file system image, and I much prefer those fuzzing systems
> which provide the file system image and the C program used to trigger
> the failre as two seprate files. Or failing that, if there was some

gosh yes. I have added a patch to the syzkaller C reproducer multiple times
so that it would write out the fs image and then I could just use that
with 'mount' etc. instead of running the (unreadable) C reproducer.

> trivial way to get the syzkaller reproducer program to disgorge the
> file system image to a specified output file. As a result, if I have
> a choice of spending time investigating fuzzing report from a more
> file-system friendly fuzzing program and syzkaller, I'll tend choose
> to spend my time dealing with other file system fuzzing reports first.



--
~Randy

2021-07-23 09:24:05

by Christian Brauner

[permalink] [raw]
Subject: Re: A shift-out-of-bounds in minix_statfs in fs/minix/inode.c

On Thu, Jul 22, 2021 at 05:58:23PM -0400, Theodore Ts'o wrote:
> On Wed, Jul 21, 2021 at 06:37:23PM +0100, Matthew Wilcox wrote:
> > On Thu, Jul 22, 2021 at 01:14:06AM +0800, butt3rflyh4ck wrote:
> > > ms = (struct minix_super_block *) bh->b_data; /// --------------> set
> > > minix_super_block pointer
> > > sbi->s_ms = ms;
> > > sbi->s_sbh = bh;
> > > sbi->s_mount_state = ms->s_state;
> > > sbi->s_ninodes = ms->s_ninodes;
> > > sbi->s_nzones = ms->s_nzones;
> > > sbi->s_imap_blocks = ms->s_imap_blocks;
> > > sbi->s_zmap_blocks = ms->s_zmap_blocks;
> > > sbi->s_firstdatazone = ms->s_firstdatazone;
> > > sbi->s_log_zone_size = ms->s_log_zone_size; // ------------------>
> > > set sbi->s_log_zone_size
> >
> > So what you're saying is that if you construct a malicious minix image,
> > you can produce undefined behaviour? That's not something we're
> > traditionally interested in, unless the filesystem is one customarily
> > used for data interchange (like FAT or iso9660).
>
> It's going to depend on the file system maintainer. The traditional
> answer is that block device is part of the Trusted Computing Base, and
> malicious file system images are not considered part of the threat
> model. A system adminstration or developer which allows potentially
> malicious agents to mount file system agents are cray-cray.
>
> Unfortunately, those developers are also known as "Linux desktop devs"
> (who implement unprivileged mounts of USB cards) or "container

That has always been a weird approach for sure.

> evangelists" who think containers should be treated as being Just As
> Good as VM's From A Security Perspective.

Mounting virtual filesystems like procfs, sysfs, cgroupfs, devpts,
binderfs and so on in unprivileged containers makes a lot of sense but
for filesystems like ext4, xfs, or btrfs making them mountable inside
unprivileged containers aka user namespaces never made a lot of sense to
me.

Most users don't really want to or need to expose a whole filesystem
image to their containers anyway. It's much more common that they want
to expose a part of an existing filesystem that has been mounted by an
administrator before. Which means they create bind mounts of the
directories that they want to expose. And as soon as the filesystem
supports idmapped mounts like ext4 does you can then serve all of those
use-cases including creating an idmapped mount of the whole filesystem
itself. And then you shouldn't need to require that the filesystem must
be able to mount untrusted images.

>
> So I do care about this for ext4, although I don't guarantee immediate
> response, as it's something that I usually end up doing on my own
> time. I do get cranky that Syzkaller makes it painful to extract out
> the fuzzed file system image, and I much prefer those fuzzing systems
> which provide the file system image and the C program used to trigger
> the failre as two seprate files. Or failing that, if there was some
> trivial way to get the syzkaller reproducer program to disgorge the
> file system image to a specified output file. As a result, if I have
> a choice of spending time investigating fuzzing report from a more
> file-system friendly fuzzing program and syzkaller, I'll tend choose
> to spend my time dealing with other file system fuzzing reports first.
>
> The problem for Minix is that it does not have an active maintainer.
> So if you submit fuzzing reports for Minix, it's unlikely anyone will
> spend time working on it. But if you submit a patch, it can go in,
> probably via Andrew Morton. (Recent Minix fixes that have gone in
> this way: 0a12c4a8069 and 32ac86efff9)
>
> Cheers,
>
> - Ted>