CVE-2014-1739: Kernel Infoleak vulnerability in media_enum_entities()

Summary

CVE CVE-2014-1739
Author Salva Peiró
Date April 2014 - Discovery of the vulnerability.
Impact The vulnerability discloses 200 bytes of kernel process stack.
Affected Versions From linux-2.6.38 to linux-3.15-rc3
Bug Timespan 3 years: 2011-03-23 to 2014-04-29 commit 1651333b

Description

During a code review of the kernel sources we found an infoleak vulnerability in the ioctl media_enum_entities() that allows to disclose 200 bytes the kernel process' stack. The vulnerability is exploitable on versions up to linux-3.15-rc3 by local users with read access to /dev/media0. Linux distributions ship with chmod 600 /dev/media0 preventing unprivileged local users from exploiting the vulnerability. However, some Android devices are known to be shipped with both read and/or write permissions for all: chmod 666 /dev/media0.

Impact

The vulnerability was introduced in linux-2.6.38 and has been fixed in linux-3.15-rc3, therefore, all kernel versions in between are affected. The kernel subsystem affected is the media device that provides the enumeration of media devices such as: V4L, ALSA, DBV, and FB.

Vulnerability

The vulnerability leaks the contents of the kernel process stack through the reserved and raw fields of the struct media_entity_desc uent;. The cause of the infoleak is the missing initialisation of the struct media_device_entity; uent before being copied to userland. This causes 200 bytes of kernel process' stack to be disclosed to userland.

Proof of Concept code

The PoC code media-enum-poc.c triggers the vulnerability by issuing the ioctl(fd, MEDIA_IOC_ENUM_ENTITIES) request and checking the data returned.

box $ ./media-enum-poc /dev/media0
[*] CVE-2014-1739: Infoleak PoC in media_device_enum_entities() leaking 200 kstack bytes:
    00000000: 0x00001000 0xc104d114 0x00010000 0xf6744514
    00000004: 0x00000051 0x00000000 0x00000000 0xc1420b91
    00000008: 0xf2e31dc8 0xc12fe9be 0xf86ddab0 0xf6744514
    00000012: 0xf29cc140 0xf2e31dc8 0x00000000 0xc115086d
    00000016: 0x00000000 0xf29cc140 0xf6744514 0xf29cc148
    00000020: 0x00000000 0xc114a77a 0xc11f2581 0x00000002
    00000024: 0xf6744514 0xf3ea7690 0xf29cc440 0x000000cf
    00000028: 0xf3ea7760 0xc10fcb11 0xc116680d 0x00000000
    00000032: 0xf5929d50 0xf3ea7690 0xf29cc440 0xf3ea775c
    00000036: 0xf6f92960 0xc10fe4bb 0x00000081 0x00ffffff
    00000040: 0x00000000 0x000000a8 0x000000cf 0xe8defeb8
    00000044: 0xf5929d40 0xc104d2d4 0x0000003c 0xfffff000
    00000048: 0x00000001 0xc104d114

The Vulnerability Fix

After verification the patch that memsets(0) the whole struct media_device_entity uent; has been submitted to the kernel: [PATCH] drivers/media: Fix infoleak in ioctl media_enum_entities()

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index d5a7a13..703560f 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -93,6 +93,7 @@ static long media_device_enum_entities(struct media_device *mdev,
    struct media_entity *ent;
    struct media_entity_desc u_ent;

+   memset(&u_ent, 0, sizeof(u_ent));
    if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id)))
        return -EFAULT;

File: 0001-drivers-media-Fix-infoleak-in-ioctl-media_enum_entit.patch

Update

After looking at the code fixing the infoleak one notices that that when a ent->name is provided u_ent.name is memset(0) twice; first at the global memset() of the u_ent and afterwards at u_ent.name. The next patch addresses this issue by removing the memset(u_ent.name, 0, ...).

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 703560f..3729d63 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -105,9 +105,6 @@ static long media_device_enum_entities(struct media_device *mdev,
    u_ent.id = ent->id;
    if (ent->name) {
        strncpy(u_ent.name, ent->name, sizeof(u_ent.name));
        u_ent.name[sizeof(u_ent.name) - 1] = '\0';
-   } else {
-       memset(u_ent.name, 0, sizeof(u_ent.name));
    }
    u_ent.type = ent->type;
    u_ent.revision = ent->revision;

File: 0001-media-device-Remove-duplicated-memset-in-media_enum_.patch