Bug 1599168 (CVE-2018-13406)

Summary: CVE-2018-13406 kernel: Integer overflow in drivers/video/fbdev/uvesafb.c:uvesafb_setcmap() allows for potential denial of service
Product: [Other] Security Response Reporter: Sam Fowler <sfowler>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: abhgupta, airlied, aquini, bhu, blc, bskeggs, dbaker, dhoward, esammons, ewk, fhrbata, hdegoede, hkrzesin, hwkernel-mgr, iboverma, ichavero, itamar, jarodwilson, jforbes, jglisse, jkacur, john.j5live, jokerman, jonathan, josef, jross, jwboyer, kernel-maint, kernel-mgr, labbott, lgoncalv, linville, lwang, matt, mchehab, mcressma, mguzik, mjg59, mlangsdo, nmurray, plougher, rt-maint, rvrbovsk, skozina, steved, sthangav, trankin, vdronov, williams, yozone
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: kernel 4.17.4 Doc Type: If docs needed, set a value
Doc Text:
The Linux kernel was found vulnerable to an integer overflow in the drivers/video/fbdev/uvesafb.c:uvesafb_setcmap() function. The vulnerability could result in local attackers being able to crash the kernel or potentially elevate privileges.
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-08-03 12:04:01 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1599169, 1599170    
Bug Blocks: 1599177    

Description Sam Fowler 2018-07-09 06:11:34 UTC
The Linux kernel was found vulnerable to an integer overflow in the drivers/video/fbdev/uvesafb.c:uvesafb_setcmap() function that could result in local attackers being able to crash the kernel or potentially elevate privileges.

References:

https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.17.4

An upstream patch:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9f645bcc566a1e9f921bdae7528a01ced5bc3713

Comment 1 Sam Fowler 2018-07-09 06:12:37 UTC
Created kernel tracking bugs for this issue:

Affects: fedora-all [bug 1599169]

Comment 5 Vladis Dronov 2018-08-03 12:04:01 UTC
Notes:

the flaw is not triggerable in the Red Hat products.

1) an attacker must be able to do ioctl() to /dev/fb0 and most probably he can not due to the permissions. namespaces or containers won't help attacker with this.

$ ls -l /dev/fb0
crw-rw---- 1 root video 29, 0 Jul 28 19:35 /dev/fb0

2) for the overflow to happen size_t must be 32-bit, as cmap->len is __u32:

[include/uapi/linux/fb.h]
struct fb_cmap {
        __u32 start;                    /* First entry  */
        __u32 len;                      /* Number of entries */

and kmalloc() accepts size_t and type of sizeof() is size_t too:

void *kmalloc(size_t size, gfp_t flags)

[drivers/video/fbdev/uvesafb.c]
entries = kmalloc(sizeof(*entries) * cmap->len, GFP_KERNEL);

if size_t is 64-bit then multiplication is also 64-bit and there is no overflow. this means that only 32-bit systems are vulnerable.

3) even with a 32-bit system there is a check in fb_set_user_cmap():

[drivers/video/fbdev/core/fbcmap.c]
int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
{
        int rc, size = cmap->len * sizeof(u16);
        if (size < 0 || size < cmap->len)
                return -E2BIG;

this especially limits cmap->len to be less-or-equal to UINT_MAX/4 i.e. 0x3fffffff. so later when multipying it to sizeof(*entries) == 4 it can be as maximum as:

3FFFFFFF * 4 = FFFFFFFC i.e. no 32-bit overflow.

4) the check above is from the upstream commit 1e7c7804884fc which is present since v2.6.37-rc4 and so is present in all versions of RHEL-7 and later but not in RHEL-6.

still, fb_set_user_cmap() calls fb_alloc_cmap():

[drivers/video/fbcmap.c]
int fb_set_user_cmap(struct fb_cmap_user *cmap, struct fb_info *info)
{
        int rc, size = cmap->len * sizeof(u16);
        rc = fb_alloc_cmap(&umap, cmap->len, cmap->transp != NULL);

int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
{
    int size = len*sizeof(u16);
    if (!(cmap->red = kmalloc(size, GFP_ATOMIC)))
        goto fail;

and kmalloc() will fail for the sizes > 32 Mb (theoretically) and > 2^22 = 4Mb (in practice):

[include/linux/slab.h]
/* The largest kmalloc size supported by the slab allocators is
   32 megabyte (2^25) or the maximum allocatable page order if that is
   less than 32 MB. */
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
                           (MAX_ORDER + PAGE_SHIFT - 1) : 25)
#define KMALLOC_MAX_SIZE (1UL << KMALLOC_SHIFT_HIGH)

[include/linux/mmzone.h]
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif

$ grep CONFIG_FORCE_MAX_ZONEORDER *
config-2.6.32-754.el6.ppc64:CONFIG_FORCE_MAX_ZONEORDER=9
config-2.6.32-754.el6.s390x:CONFIG_FORCE_MAX_ZONEORDER=9

Comment 6 Hans de Goede 2018-08-04 10:40:18 UTC
Note that for Fedora the long analysis is not needed, Fedora is not vulnerable because the uvesafb driver is not enabled:

[hans@shalem ~]$ grep CONFIG_FB_UVESA /boot/config-4.18.0-0.rc7.git1.1.fc29.x86_64
# CONFIG_FB_UVESA is not set