OpenBSD mallocarray

Warner Losh imp at bsdimp.com
Thu Feb 11 21:36:55 UTC 2016


> On Feb 11, 2016, at 2:21 PM, C Turt <cturt at hardenedbsd.org> wrote:
> 
> I just wanted to post some real world examples of bugs which could be
> mitigated with `mallocarray` to attract more interest.

Let’s play devil’s advocate: since you have to make code changes, how
would mallocarray() be superior to the various MALLOC_OVERFLOW
macro suggestions from earlier in the thread? Given that kernel code is
somewhat different in what it needs to support?

> My most meritable example of a real world attack from this behaviour would
> be the sys_dynlib_prepare_dlclose kernel exploit for PS4 (PS4 OS is based
> on FreeBSD 9.0). You may read my write-up of this exploit here:
> http://cturt.github.io/dlclose-overflow.html
> 
> The significance of this example is that if a `mallocarray` wrapper was
> available, and used here, the bug would not have been exploitable, because
> it would have intentionally panicked instead of continuing with an under
> allocated buffer.

Except that you’d need to change all the code that was imported into
the kernel to use mallocarray. The code Sony imported didn’t have it
to start with.

> You may think that this is clearly Sony's fault for not checking the count
> at all, and that FreeBSD kernel code would never have a bug like this,
> which is why I would like to mention that similar overflows can be possible
> even if there initially appear to be sufficient bound checks performed.
> 
> There are several pieces of vulnerable code present in FreeBSD kernel
> (albeit most of them are triggerable only as root so are not critical),
> however I will use the file `/sys/kern/kern_alq.c` to demonstrate. There
> are some checks on counts and sizes, but they are insufficient:
> 
> [CODE]int
> alq_open(struct alq **alqp, const char *file, struct ucred *cred, int cmode,
>  int size, int count)
> {
>   int ret;
> 
>   KASSERT((count >= 0), ("%s: count < 0", __func__));
> 
>   if (count > 0) {
>     if ((ret = alq_open_flags(alqp, file, cred, cmode,
>      size*count, 0)) == 0) {
>       (*alqp)->aq_flags |= AQ_LEGACY;
>       (*alqp)->aq_entmax = count;
>       (*alqp)->aq_entlen = size;
>     }
> 
> ...
> 
> int
> alq_open_flags(struct alq **alqp, const char *file, struct ucred *cred, int
> cmode,
>  int size, int flags)
> {
>   ...
>   KASSERT((size > 0), ("%s: size <= 0", __func__));
>   ...
>   alq->aq_buflen = size;
>   ...
>   alq->aq_entbuf = malloc(alq->aq_buflen, M_ALD, M_WAITOK|M_ZERO);[/CODE]
> 
> The check on `count` being greater than or equal to 0 in `alq_open`, and
> the check for `size` being greater than 0 in `alq_open_flags` are cute, but
> they don't check for an overflow of `size*count`.
> 
> This code path is reachable in several places, such as
> `sysctl_debug_ktr_alq_enable`:
> 
> [CODE]static int
> sysctl_debug_ktr_alq_enable(SYSCTL_HANDLER_ARGS)
> {
>     ...
>     error = alq_open(&ktr_alq, (const char *)ktr_alq_file,
>      req->td->td_ucred, ALQ_DEFAULT_CMODE,
>      sizeof(struct ktr_entry), ktr_alq_depth);
> [/CODE]
> 
> With `ktr_alq_depth` being controllable from userland (but only as root):
> 
> [CODE]SYSCTL_INT(_debug_ktr, OID_AUTO, alq_depth, CTLFLAG_RW,
> &ktr_alq_depth, 0, "Number of items in the write buffer");[/CODE]
> 
> `sizeof(struct ktr_entry)` is 88 bytes. So theoretically if `ktr_alq_depth`
> is set to `48806447`, we'll get an allocation of `0x100000028`, which
> truncates to 0x28 = 40 bytes. A heap overflow would then possible when this
> buffer is iterated over with `aq_entmax` and `aq_entlen`.

These are all good finds. And they are all mitigable with the MALLOC_OVERFLOW
macro that was suggested earlier in this thread. Given the unique demands of the
kernel, why should that not be the preferred method of dealing with this stuff?

Warner


> On Mon, Feb 1, 2016 at 7:57 PM, C Turt <cturt at hardenedbsd.org> wrote:
> 
>> I've recently started browsing the OpenBSD kernel source code, and have
>> found the mallocarray function positively wonderful. I would like to
>> discuss the possibility of getting this into FreeBSD kernel.
>> 
>> For example, many parts of kernel code in FreeBSD use something like
>> malloc(xxx * sizeof(struct xxx)). If xxx is 64bit and controllable by user,
>> this allocation can easily overflow, resulting in a heap overflow later on.
>> 
>> The mallocarray is a wrapper for malloc which can be used in this
>> situations to detect an integer overflow before allocating:
>> 
>> /*
>> * Copyright (c) 2008 Otto Moerbeek <otto at drijf.net>
>> *
>> * Permission to use, copy, modify, and distribute this software for any
>> * purpose with or without fee is hereby granted, provided that the above
>> * copyright notice and this permission notice appear in all copies.
>> *
>> * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
>> * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
>> * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
>> * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
>> * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
>> * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
>> * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>> */
>> 
>> /*
>> * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
>> * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
>> */
>> #define MUL_NO_OVERFLOW    (1UL << (sizeof(size_t) * 4))
>> 
>> void *
>> mallocarray(size_t nmemb, size_t size, int type, int flags)
>> {
>>    if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
>>        nmemb > 0 && SIZE_MAX / nmemb < size) {
>>        if (flags & M_CANFAIL)
>>            return (NULL);
>>        panic("mallocarray: overflow %zu * %zu", nmemb, size);
>>    }
>>    return (malloc(size * nmemb, type, flags));
>> }
>> 
>> 
> _______________________________________________
> freebsd-arch at freebsd.org mailing list
> https://lists.freebsd.org/mailman/listinfo/freebsd-arch
> To unsubscribe, send any mail to "freebsd-arch-unsubscribe at freebsd.org"

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 842 bytes
Desc: Message signed with OpenPGP using GPGMail
URL: <http://lists.freebsd.org/pipermail/freebsd-arch/attachments/20160211/d54a73ca/attachment.sig>


More information about the freebsd-arch mailing list