Vulnerability Description
The function c4_ioctl() (listed below) writes data from user in ifr->ifr_data
to the kernel struct data arg, without performing any bounds checking.
This allows using a crafted iocmd to write outside of the struct data arg,
where iolen = IOC_SIZE(iocmd) can specify a maximum write size up to 2^14 bytes.
Triggering the write requires CAP_SYS_ADMIN capability
but even in the case of having admin rights it should be disallowed.
Update
Starting with gcc-4.0, the gcc compiler allows to retrieve the size of an object
GCC Object Size.
The kernel uses the builtin_object_size() to implement checks on the copy
size to prevent this class of vulnerabilities at
copy_from_user_overflow() at arch/x86/include/asm/uaccess.h:690.
static inline unsigned long __must_check
688 copy_from_user(void *to, const void __user *from, unsigned long n)
689 {
690 int sz = __compiletime_object_size(to);
691
692 might_fault();
693
694 /*
695 * While we would like to have the compiler do the checking for us
696 * even in the non-constant size case, any false positives there are
697 * a problem (especially when DEBUG_STRICT_USER_COPY_CHECKS, but even
698 * without - the [hopefully] dangerous looking nature of the warning
699 * would make people go look at the respecitive call sites over and
700 * over again just to find that there's no problem).
701 *
702 * And there are cases where it's just not realistic for the compiler
703 * to prove the count to be in range. For example when multiple call
704 * sites of a helper function - perhaps in different source files -
705 * all doing proper range checking, yet the helper function not doing
706 * so again.
707 *
708 * Therefore limit the compile time checking to the constant size
709 * case, and do only runtime checking for non-constant sizes.
710 */
711
712 if (likely(sz < 0 || sz >= n))
713 n = _copy_from_user(to, from, n);
714 else if(__builtin_constant_p(n))
715 copy_from_user_overflow();
716 else
717 __copy_from_user_overflow(sz, n);
718
719 return n;
Fixing the Vulnerability
The proposed fix is to do bounds-checking of the iolen write size before
performing the copy_from_user() to avoid the out-of-bounds data write.
The patch fixing the vulnerability has been submitted to the Linux kernel:
Listing: c4_ioctl() function from drivers/staging/cxt1e1/linux.c
commit 0414855f.
static status_t
c4_ioctl (struct net_device *ndev, struct ifreq *ifr, int cmd)
{
ci_t *ci;
void *data;
int iocmd, iolen;
status_t ret;
static struct data arg;
if (!capable (CAP_SYS_ADMIN))
return -EPERM;
...
if (copy_from_user (&iocmd, ifr->ifr_data, sizeof (iocmd)))
return -EFAULT;
iolen = _IOC_SIZE (iocmd);
data = ifr->ifr_data + sizeof (iocmd);
if (copy_from_user (&arg, data, iolen)) // XXX iolen from user without bounds checks
return -efault;