xorl %eax, %eax

CVE-2009-1192: Linux kernel AGP Information Leak

leave a comment »

This bug was fixed in 2.6.30-rc3 according to its ChangeLog file, it was reported by Shaohua Li of Intel and patched on 23 April 2009. The code presented here is from 2.6.29 release of the Linux kernel:

1216 /*
1217  * Basic Page Allocation Routines -
1218  * These routines handle page allocation and by default they reserve the allocated
1219  * memory.  They also handle incrementing the current_memory_agp value, Which is checked
1220  * against a maximum value.
1221  */
1222
1223 int agp_generic_alloc_pages(struct agp_bridge_data *bridge, struct agp_memory *mem, size_t num_pages)
1224 {
1225        struct page * page;
1226        int i, ret = -ENOMEM;
1227
1228        for (i = 0; i < num_pages; i++) {
1229                page = alloc_page(GFP_KERNEL | GFP_DMA32);
1230                /* agp_free_memory() needs gart address */
1231                if (page == NULL)
1232                        goto out;
        ...
1252        return ret;
1253 }
1254 EXPORT_SYMBOL(agp_generic_alloc_pages);

This function is part of the generic AGP driver located at drivers/char/agp/generic.c. The bug is at line 1229 where it attempts to allocate a page using alloc_page() macro of include/linux/gfp.h. Even though it’s doing it correctly, it only uses the following two flags:
GFP_KERNEL
which means that it includes these three attributes:
1) Can wait and reschedule
2) Can start physical IO
3) Can call down to low-level FS
GFP_DMA32
which stands for 4GB DMA available on some platforms
Those pages contain uninitialized memory which could result in kernel information leaks under certain conditions. This was patched by adding another flag from gfp.h to zero out the requested pages like this:

     for (i = 0; i < num_pages; i++) {
-        page = alloc_page(GFP_KERNEL | GFP_DMA32);
+        page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
         /* agp_free_memory() needs gart address */

Another similar information leak vulnerability was a few lines later in this function:

1256 void *agp_generic_alloc_page(struct agp_bridge_data *bridge)
1257 {
1258        struct page * page;
1259
1260        page = alloc_page(GFP_KERNEL | GFP_DMA32);
1261        if (page == NULL)
1262                return NULL;
1263
1264        map_page_into_agp(page);
1265
1266        get_page(page);
1267        atomic_inc(&agp_bridge->current_memory_agp);
1268        return page_address(page);
1269 }
1270 EXPORT_SYMBOL(agp_generic_alloc_page);

It’s easy to spot at line 1260. This was also fixed using the same approach:

-    page = alloc_page(GFP_KERNEL | GFP_DMA32);
+    page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
     if (page == NULL)

As Eugene Teo pointed out some of the AGP driver’s pages might eventually mapped at the user space and thus lead into kernel information leak because of this missing check.

Written by xorl

April 23, 2009 at 14:57

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s