[Bug 206579] Multiple vulnerabilities in AMR ioctl handler

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Sun Jan 24 15:51:03 UTC 2016


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=206579

            Bug ID: 206579
           Summary: Multiple vulnerabilities in AMR ioctl handler
           Product: Base System
           Version: 11.0-CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs at FreeBSD.org
          Reporter: ecturt at gmail.com

The `amr_ioctl` handler contains userland dereferences, and no bound checks on
user supplied sizes.

The only time where the `addr` is correctly accessed by `copyin` is in the
Linux emulation commands, like `0xc06e6d00`:

    error = copyin(addr, &ali, sizeof(ali));

The rest of the commands use a union called `arg` is setup to make incorrectly
dealing with `addr` easier:

    union {
        void                    *_p;
        struct amr_user_ioctl   *au;
    #ifdef AMR_IO_COMMAND32
        struct amr_user_ioctl32 *au32;
    #endif
        int                     *result;
    } arg;

    ...

    arg._p = (void *)addr;

The most serious issue is the `AMR_IO_VERSION` command, writing its output
directly without using `copyout`:

case AMR_IO_VERSION:
        debug(1, "AMR_IO_VERSION");
        *arg.result = AMR_IO_VERSION_NUMBER;
        return(0);

The address of this write is completely user controlled, and can be used to
write arbitrary kernel memory.

Another issue stems from supplying the `AMR_IO_COMMAND` command. A user
supplied size will be fetched (without `copyin`):

    au_length = arg.au->au_length;

Which is then used by `malloc` and `copyin` without any boundary checks:

/* handle inbound data buffer */
real_length = amr_ioctl_buffer_length(au_length);
dp = malloc(real_length, M_AMR, M_WAITOK|M_ZERO);
if (au_length != 0 && au_cmd[0] != 0x06) {
        if ((error = copyin(au_buffer, dp, au_length)) != 0) {
                free(dp, M_AMR);
                return (error);
        }
        debug(2, "copyin %ld bytes from %p -> %p", au_length, au_buffer, dp);
}

On FreeBSD 9, we could abuse the 32bit size truncation in `uma_large_malloc` to
get a heap overflow from this. On later versions, allocating large sizes can
probably only be used to DoS the system.

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list