Re: [PATCH] fs/quota: fix the mismatch of data type

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Jan 11, 2021 at 12:35 PM Zhengyuan Liu
<liuzhengyuan@xxxxxxxxxxxxx> wrote:
>
> From: Zhengyuan Liu <liuzhengyuan@xxxxxxxxxx>
>
> When doing fuzzing test to quota, an error occurred due to the
> mismatch of data type:
>
>     Quota error (device loop0): qtree_write_dquot: Error -1244987383 occurred while creating quota
>     Unable to handle kernel paging request at virtual address ffffffffb5cb0071
>     Mem abort info:
>       ESR = 0x96000006
>       EC = 0x25: DABT (current EL), IL = 32 bits
>       SET = 0, FnV = 0
>       EA = 0, S1PTW = 0
>     Data abort info:
>       ISV = 0, ISS = 0x00000006
>       CM = 0, WnR = 0
>     swapper pgtable: 64k pages, 48-bit VAs, pgdp=0000000023980000
>     [ffffffffb5cb0071] pgd=00000000243f0003, p4d=00000000243f0003, pud=00000000243f0003, pmd=0000000000000000
>     Internal error: Oops: 96000006 [#1] SMP
>     Modules linked in: nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6 nft_reject nftn
>     CPU: 1 PID: 1256 Comm: a.out Not tainted 5.10.0 #31
>     Hardware name: XXXX XXXX/Kunpeng Desktop Board D920L11K, BIOS 0.23 07/22/2020
>     pstate: 00400009 (nzcv daif +PAN -UAO -TCO BTYPE=--)
>     pc : dquot_add_space+0x30/0x258
>     lr : __dquot_alloc_space+0x22c/0x358
>     sp : ffff80001c10f660
>     x29: ffff80001c10f660 x28: 0000000000000001
>     x27: 0000000000000000 x26: 0000000000000000
>     x25: ffff800011add9c8 x24: ffff0020a2110470
>     x23: 0000000000000400 x22: 0000000000000400
>     x21: 0000000000000000 x20: 0000000000000400
>     x19: ffffffffb5cb0009 x18: 0000000000000000
>     x17: 0000000000000020 x16: 0000000000000000
>     x15: 0000000000000010 x14: 65727471203a2930
>     x13: 706f6f6c20656369 x12: 0000000000000020
>     x11: 000000000000000a x10: 0000000000000400
>     x9 : ffff8000103afb5c x8 : 0000000800000000
>     x7 : 0000000000002000 x6 : 000000000000000f
>     x5 : ffffffffb5cb0009 x4 : ffff80001c10f728
>     x3 : 0000000000000001 x2 : 0000000000000000
>     x1 : 0000000000000400 x0 : ffffffffb5cb0009
>     Call trace:
>      dquot_add_space+0x30/0x258
>      __dquot_alloc_space+0x22c/0x358
>      ext4_mb_new_blocks+0x100/0xe88
>      ext4_new_meta_blocks+0xb4/0x110
>      ext4_xattr_block_set+0x4ec/0xce8
>      ext4_xattr_set_handle+0x400/0x528
>      ext4_xattr_set+0xc4/0x170
>      ext4_xattr_security_set+0x30/0x40
>      __vfs_setxattr+0x7c/0xa0
>      __vfs_setxattr_noperm+0x88/0x218
>      __vfs_setxattr_locked+0xf8/0x120
>      vfs_setxattr+0x6c/0x100
>      setxattr+0x148/0x240
>      path_setxattr+0xc4/0xd8
>      __arm64_sys_setxattr+0x2c/0x40
>      el0_svc_common.constprop.4+0x94/0x178
>      do_el0_svc+0x78/0x98
>      el0_svc+0x20/0x30
>      el0_sync_handler+0x90/0xb8
>      el0_sync+0x158/0x180
>
> In this test case, the return value from get_free_dqblk() could be
> info->dqi_free_blk, which is defined as unsigned int, but we use
> type int in do_insert_tree to check the return value, and therefor we
> may get a negative duo to the transformation. This negative(as aboved
> said -1244987383) then can transmit to dquots in __dquot_initialize(),
> and once we access there can trigger above panic.
>
>         __dquot_initialize():
>                 dquot = dqget(sb, qid);
>                 if (IS_ERR(dquot)) {
>                         /* We raced with somebody turning quotas off... */
>                         if (PTR_ERR(dquot) != -ESRCH) {
>                                 ret = PTR_ERR(dquot);
>                                 goto out_put;
>                         }
>                         dquot = NULL;
>                 }
>                 got[cnt] = dquot;
>
> Try to fix this problem by making the data type consistent.
>
> Signed-off-by: Zhengyuan Liu <liuzhengyuan@xxxxxxxxxx>
> ---
>  fs/quota/quota_tree.c | 13 +++++++------
>  1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
> index c5562c871c8b..f898a550a3ee 100644
> --- a/fs/quota/quota_tree.c
> +++ b/fs/quota/quota_tree.c
> @@ -81,11 +81,11 @@ static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf)
>  }
>
>  /* Remove empty block from list and return it */
> -static int get_free_dqblk(struct qtree_mem_dqinfo *info)
> +static ssize_t get_free_dqblk(struct qtree_mem_dqinfo *info)
>  {
>         char *buf = getdqbuf(info->dqi_usable_bs);
>         struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
> -       int ret, blk;
> +       ssize_t ret, blk;
>
>         if (!buf)
>                 return -ENOMEM;
> @@ -295,11 +295,12 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
>  }
>
>  /* Insert reference to structure into the trie */
> -static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
> +static ssize_t do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
>                           uint *treeblk, int depth)
>  {
>         char *buf = getdqbuf(info->dqi_usable_bs);
> -       int ret = 0, newson = 0, newact = 0;
> +       int newson = 0, newact = 0;
> +       ssize_t ret = 0;
>         __le32 *ref;
>         uint newblk;
>
> @@ -335,7 +336,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
>                         goto out_buf;
>                 }
>  #endif
> -               newblk = find_free_dqentry(info, dquot, &ret);
> +               newblk = find_free_dqentry(info, dquot, (int*)&ret);
>         } else {
>                 ret = do_insert_tree(info, dquot, &newblk, depth+1);
>         }
> @@ -352,7 +353,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
>  }
>
>  /* Wrapper for inserting quota structure into tree */
> -static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
> +static inline ssize_t dq_insert_tree(struct qtree_mem_dqinfo *info,
>                                  struct dquot *dquot)
>  {
>         int tmp = QT_TREEOFF;
> --
> 2.20.1
>
>
>
The fuzzing test program showed as following:

#define _GNU_SOURCE

#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

#include <linux/loop.h>

static unsigned long long procid;

struct fs_image_segment {
        void* data;
        uintptr_t size;
        uintptr_t offset;
};

#define IMAGE_MAX_SEGMENTS 4096
#define IMAGE_MAX_SIZE (129 << 20)

#define sys_memfd_create 319

static unsigned long fs_image_segment_check(unsigned long size,
unsigned long nsegs, struct fs_image_segment* segs)
{
        if (nsegs > IMAGE_MAX_SEGMENTS)
                nsegs = IMAGE_MAX_SEGMENTS;
        for (size_t i = 0; i < nsegs; i++) {
                if (segs[i].size > IMAGE_MAX_SIZE)
                        segs[i].size = IMAGE_MAX_SIZE;
                segs[i].offset %= IMAGE_MAX_SIZE;
                if (segs[i].offset > IMAGE_MAX_SIZE - segs[i].size)
                        segs[i].offset = IMAGE_MAX_SIZE - segs[i].size;
                if (size < segs[i].offset + segs[i].offset)
                        size = segs[i].offset + segs[i].offset;
        }
        if (size > IMAGE_MAX_SIZE)
                size = IMAGE_MAX_SIZE;
        return size;
}
static int setup_loop_device(long unsigned size, long unsigned nsegs,
struct fs_image_segment* segs, const char* loopname, int* memfd_p,
int* loopfd_p)
{
        int err = 0, loopfd = -1;
        size = fs_image_segment_check(size, nsegs, segs);
        int memfd = syscall(sys_memfd_create, "syzkaller", 0);
        if (memfd == -1) {
                err = errno;
                goto error;
        }
        if (ftruncate(memfd, size)) {
                err = errno;
                goto error_close_memfd;
        }
        for (size_t i = 0; i < nsegs; i++) {
                if (pwrite(memfd, segs[i].data, segs[i].size,
segs[i].offset) < 0) {
                }
        }
        loopfd = open(loopname, O_RDWR);
        if (loopfd == -1) {
                err = errno;
                goto error_close_memfd;
        }
        if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
                if (errno != EBUSY) {
                        err = errno;
                        goto error_close_loop;
                }
                ioctl(loopfd, LOOP_CLR_FD, 0);
                usleep(1000);
                if (ioctl(loopfd, LOOP_SET_FD, memfd)) {
                        err = errno;
                        goto error_close_loop;
                }
        }
        *memfd_p = memfd;
        *loopfd_p = loopfd;
        return 0;

error_close_loop:
        close(loopfd);
error_close_memfd:
        close(memfd);
error:
        errno = err;
        return -1;
}

static long syz_mount_image(volatile long fsarg, volatile long dir,
volatile unsigned long size, volatile unsigned long nsegs, volatile
long segments, volatile long flags, volatile long optsarg)
{
        struct fs_image_segment* segs = (struct fs_image_segment*)segments;
        int res = -1, err = 0, loopfd = -1, memfd = -1,
need_loop_device = !!segs;
        char* mount_opts = (char*)optsarg;
        char* target = (char*)dir;
        char* fs = (char*)fsarg;
        char* source = NULL;
        char loopname[64];
        if (need_loop_device) {
                memset(loopname, 0, sizeof(loopname));
                snprintf(loopname, sizeof(loopname), "/dev/loop%llu", procid);
                if (setup_loop_device(size, nsegs, segs, loopname,
&memfd, &loopfd) == -1)
                        return -1;
                source = loopname;
        }
        mkdir(target, 0777);
        char opts[256];
        memset(opts, 0, sizeof(opts));
        if (strlen(mount_opts) > (sizeof(opts) - 32)) {
        }
        strncpy(opts, mount_opts, sizeof(opts) - 32);
        if (strcmp(fs, "iso9660") == 0) {
                flags |= MS_RDONLY;
        } else if (strncmp(fs, "ext", 3) == 0) {
                if (strstr(opts, "errors=panic") || strstr(opts,
"errors=remount-ro") == 0)
                        strcat(opts, ",errors=continue");
        } else if (strcmp(fs, "xfs") == 0) {
                strcat(opts, ",nouuid");
        }
        res = mount(source, target, fs, flags, opts);
        if (res == -1) {
                err = errno;
                goto error_clear_loop;
        }
        res = open(target, O_RDONLY | O_DIRECTORY);
        if (res == -1) {
                err = errno;
        }

error_clear_loop:
        if (need_loop_device) {
                ioctl(loopfd, LOOP_CLR_FD, 0);
                close(loopfd);
                close(memfd);
        }
        errno = err;
        return res;
}

int main(void)
{
         syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
        syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
        syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);

memcpy((void*)0x20000000, "ext4\000", 5);
memcpy((void*)0x20000100, "./file0\000", 8);
*(uint64_t*)0x20000200 = 0x20010000;
memcpy((void*)0x20010000,
"\x20\x00\x00\x00\x00\x01\x00\x00\x0c\x00\x00\x00\xce\x00\x00\x00\x0f\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x20\x00\x00\x20\x00\x00\x00\xd2\xf4\x65\x5f\xd2\xf4\x65\x5f\x01\x00\xff\xff\x53\xef\x01\x00\x01\x00\x00\x00\xd1\xf4\x65\x5f\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x80\x00\x00\x00\x08\x00\x00\x00\x52\x47\x00\x00\x62\x01",
102);
*(uint64_t*)0x20000208 = 0x66;
*(uint64_t*)0x20000210 = 0x400;
*(uint64_t*)0x20000218 = 0x20010200;
memcpy((void*)0x20010200, "\x01\x00\x00\x00\x00\x00\x05\x00\x11", 9);
*(uint64_t*)0x20000220 = 9;
*(uint64_t*)0x20000228 = 0x560;
*(uint64_t*)0x20000230 = 0x20010300;
memcpy((void*)0x20010300, "\x03", 1);
*(uint64_t*)0x20000238 = 1;
*(uint64_t*)0x20000240 = 0x640;
*(uint64_t*)0x20000248 = 0x20010400;
memcpy((void*)0x20010400,
"\x03\x00\x00\x00\x13\x00\x00\x00\x23\x00\x00\x00\xce", 13);
*(uint64_t*)0x20000250 = 0xd;
*(uint64_t*)0x20000258 = 0x800;
*(uint64_t*)0x20000260 = 0;
*(uint64_t*)0x20000268 = 0;
*(uint64_t*)0x20000270 = 0xc00;
*(uint64_t*)0x20000278 = 0x20011600;
memcpy((void*)0x20011600, "\x50\x4d\x4d\x00\x50\x4d\x4d\xff", 8);
*(uint64_t*)0x20000280 = 8;
*(uint64_t*)0x20000288 = 0x4400;
*(uint64_t*)0x20000290 = 0x20000040;
memcpy((void*)0x20000040,
"\x11\x1f\xc0\xd9\x01\x00\x00\x00\x3a\x68\xc0\xa7\x79\x5f\xd6\x68\xd3\x22\xa7\xe4\x75\x56\x83\xe4\x09\x00\xcb\xb5\x8f\x17\xc7\x37\x03\x00\x00\x00",
36);
*(uint64_t*)0x20000298 = 0x24;
*(uint64_t*)0x200002a0 = 0x4800;
*(uint64_t*)0x200002a8 = 0x20012b00;
memcpy((void*)0x20012b00,
"\xed\x41\x00\x00\x00\x04\x00\x00\xd1\xf4\x65\x5f\xd2\xf4\x65\x5f\xd2\xf4\x65\x5f\x00\x00\x00\x00\x00\x00\x04\x00\x02",
29);
*(uint64_t*)0x200002b0 = 0x1d;
*(uint64_t*)0x200002b8 = 0x8c80;
*(uint64_t*)0x200002c0 = 0x20012c00;
memcpy((void*)0x20012c00,
"\x80\x81\x00\x00\x00\x18\x00\x00\xd1\xf4\x65\x5f\xd1\xf4\x65\x5f\xd1\xf4\x65\x5f\x00\x00\x00\x00\x00\x00\x01\x00\x0c\x00\x00\x00\x10\x00\x08\x00\x00\x00\x00\x00\x0a\xf3\x03\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x12\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x18\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x14",
85);
*(uint64_t*)0x200002c8 = 0x55;
*(uint64_t*)0x200002d0 = 0x8d00;
*(uint8_t*)0x20013800 = 0;
syz_mount_image(0x20000000, 0x20000100, 0x40000, 9, 0x20000200, 0, 0x20013800);
memcpy((void*)0x20000b00, "./file0\000", 8);
memcpy((void*)0x20000b40, "security.ima\000", 13);
        syscall(__NR_setxattr, 0x20000b00ul, 0x20000b40ul,
0x20000b80ul, 0xeul, 0ul);
        return 0;
}




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux