Re: [PATCH] blktrace: put bounds on BLKTRACESETUP buf_size and buf_nr

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

 



On Fri, Jun 5, 2020 at 6:43 AM Bart Van Assche <bvanassche@xxxxxxx> wrote:
>
> On 2020-06-04 22:02, harshad shirwadkar wrote:
> > Hi Bart,
> >
> > On Thu, Jun 4, 2020 at 9:31 PM Bart Van Assche <bvanassche@xxxxxxx> wrote:
> >>
> >> On 2020-06-03 22:44, Harshad Shirwadkar wrote:
> >>> Make sure that user requested memory via BLKTRACESETUP is within
> >>> bounds. This can be easily exploited by setting really large values
> >>> for buf_size and buf_nr in BLKTRACESETUP ioctl.
> >>>
> >>> blktrace program has following hardcoded values for bufsize and bufnr:
> >>> BUF_SIZE=(512 * 1024)
> >>> BUF_NR=(4)
> >>
> >> Where is the code that imposes these limits? I haven't found it. Did I
> >> perhaps overlook something?
> >
> > From what I understand, these are not limits. These are hardcoded
> > values used by blktrace userspace when it calls BLKTRACESETUP ioctl.
> > Inside the kernel, before this patch there is no checking in the linux
> > kernel.
>
> Please do not move limits from userspace tools into the kernel. Doing so
> would break other user space software (not sure if there is any) that
> uses the same ioctl but different limits.
Ack.
>
> >>> diff --git a/include/uapi/linux/blktrace_api.h b/include/uapi/linux/blktrace_api.h
> >>> index 690621b610e5..4d9dc44a83f9 100644
> >>> --- a/include/uapi/linux/blktrace_api.h
> >>> +++ b/include/uapi/linux/blktrace_api.h
> >>> @@ -129,6 +129,9 @@ enum {
> >>>  };
> >>>
> >>>  #define BLKTRACE_BDEV_SIZE   32
> >>> +#define BLKTRACE_MAX_BUFSIZ  (1024 * 1024)
> >>> +#define BLKTRACE_MAX_BUFNR   16
> >>> +#define BLKTRACE_MAX_ALLOC   ((BLKTRACE_MAX_BUFNR) * (BLKTRACE_MAX_BUFNR))
> >>
> >> Adding these constants to the user space API is a very inflexible
> >> approach. There must be better approaches to export constants like these
> >> to user space, e.g. through sysfs attributes.
> >>
> >> It probably will be easier to get a patch like this one upstream if the
> >> uapi headers are not modified.
> >
> > Thanks, makes sense. I'll do this in the V2. If we do that, we still
> > need to define the default values for these limits. Do the values
> > chosen for MAX_BUFSIZE and MAX_ALLOC look reasonable?
>
> We typically do not implement arbitrary limits in the kernel. So I'd
> prefer not to introduce any artificial limits.
I see. But my worry is that if we don't check for bounds in the kernel
in this case, a non-root user who has access to any block device (even
a simple loop device) can make the kernel go out of memory.
---
...
int main(int argc, char *argv[])
{
        struct blk_user_trace_setup buts;
        int fd;
        int ret;

        fd = open(argv[1], O_RDONLY|O_NONBLOCK);

        memset(&buts, 0, sizeof(buts));
        buts.buf_size = ~0;
        buts.buf_nr = 1;
        buts.act_mask = 65535;
        return ioctl(fd, BLKTRACESETUP, &buts);
}
---

I understand that introducing artificial hard-coded limits is probably
not the best approach. However, not having a reasonable sanity
checking on the IOCTL arguments, especially when the IOCTL itself
doesn't require any special capability checks and the kernel uses
those arguments almost as-is for allocating memory seems like a
vulnerability that attackers can exploit.

>
> >>
> >>>  /*
> >>>   * User setup structure passed with BLKTRACESETUP
> >>> diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c
> >>> index ea47f2084087..b3b0a8164c05 100644
> >>> --- a/kernel/trace/blktrace.c
> >>> +++ b/kernel/trace/blktrace.c
> >>> @@ -482,6 +482,9 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
> >>>       if (!buts->buf_size || !buts->buf_nr)
> >>>               return -EINVAL;
> >>>
> >>> +     if (buts->buf_size * buts->buf_nr > BLKTRACE_MAX_ALLOC)
> >>> +             return -E2BIG;
> >>> +
> >>>       if (!blk_debugfs_root)
> >>>               return -ENOENT;
> >>
> >> Where do the overflow(s) happen if buts->buf_size and buts->buf_nr are
> >> large? Is the following code from relay_open() the only code that can
> >> overflow?
> >>
> >>         chan->alloc_size = PAGE_ALIGN(subbuf_size * n_subbufs);
> >
> > Good catch, I'm not sure if the overflows are checked for, but I maybe
> > wrong. Given the possibility of overflows, perhaps we should be
> > checking for individual upper bounds instead of the product?
>
> How about using check_mul_overflow() to catch overflows of this
> multiplication? There may be other statements that need protection
> against overflow.

Makes sense. I can add that check.

Thanks,
Harshad.

>
> Thanks,
>
> Bart.



[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux