linux
NULL function pointer dereferenced if SHMEM is enabled but not TMPFS
The `readpage' field of `shmem_aops' only points to a concrete function when TMPFS is enabled, otherwise it points to NULL. GPU driver for Intel i915 depends on SHMEM but not on TMPFS, and makes it possible for a NULL readpage() to be dereferenced by read_cache_page(). Related to commit ca9ab10033d190c1ede85fdf456307bdfdabf079
Bug fixed by commit f7ab9b407b3
Type | NullDereference |
Config | "DRM_I915 && SHMEM && !TMPFS" (3rd degree) |
C-features | FunctionPointers, PointerAliasing, Structs |
Fix-in | model |
Location | drivers/gpu/drm/ |
#ifdef CONFIG_TMPFS #define CONFIG_SHMEM #endif #define NULL ((void*)0) typedef int filler_t(int); int some_fun(int n) { return n; } #ifdef CONFIG_TMPFS filler_t *f = &some_fun; #else filler_t *f = NULL; // (1) #endif #ifdef CONFIG_SHMEM filler_t* shmem_get_inode() { return f; // (11) } #else filler_t* ramfs_get_inode() { return &some_fun; } #define shmem_get_inode() ramfs_get_inode() #endif /* CONFIG_SHMEM */ filler_t* shmem_file_setup() { return shmem_get_inode(); // (10) } #ifdef CONFIG_DRM_I915 int drm_gem_object_init(filler_t **readpage) { *readpage = shmem_file_setup(); // (9) return 0; } void i915_gem_alloc_object(filler_t **readpage) { if (drm_gem_object_init(readpage) != 0) // (8) ; } void do_read_cache_page(filler_t *filler) { filler(0); // ERROR (18) } void read_cache_page_gfp(filler_t **readpage) { filler_t *filler = *readpage; // (15) filler = NULL do_read_cache_page(filler); // (17) } int i915_gem_object_get_pages_gtt(filler_t **readpage) { read_cache_page_gfp(readpage); // (15) return 0; } int i915_gem_object_bind_to_gtt(filler_t **readpage) { return i915_gem_object_get_pages_gtt(readpage); // (14) } int i915_gem_object_pin(filler_t **readpage) { return i915_gem_object_bind_to_gtt(readpage); // (13) } int intel_init_ring_buffer(filler_t **readpage) { i915_gem_alloc_object(readpage); // (7) *readpage = NULL return i915_gem_object_pin(readpage); // (12) } int intel_init_render_ring_buffer(filler_t **readpage) { return intel_init_ring_buffer(readpage); // (6) } int i915_gem_init_ringbuffer(filler_t **readpage) { return intel_init_render_ring_buffer(readpage); // (5) } int i915_load_modeset_init(filler_t **readpage) { return i915_gem_init_ringbuffer(readpage); // (4) } int i915_driver_load() { filler_t *readpage; return i915_load_modeset_init(&readpage); // (3) } #endif int main() { #ifdef CONFIG_DRM_I915 i915_driver_load(); // (2) #endif return 0; }
diff --git a/simple/f7ab9b4.c b/simple/f7ab9b4.c --- a/simple/f7ab9b4.c +++ b/simple/f7ab9b4.c @@ -1,5 +1,6 @@ -#ifdef CONFIG_TMPFS +#ifdef CONFIG_DRM_I915 +#define CONFIG_TMPFS #define CONFIG_SHMEM #endif
#ifdef CONFIG_TMPFS #define CONFIG_SHMEM #endif #include <stdlib.h> typedef int filler_t(int); int some_fun(int n) { return n; } #ifdef CONFIG_TMPFS filler_t *f = &some_fun; #else filler_t *f = NULL; #endif int main() { #ifdef CONFIG_DRM_I915 // i915_driver_load(); filler_t *readpage; // return i915_load_modeset_init(&readpage); // i915_gem_alloc_object(readpage); #ifdef CONFIG_SHMEM *readpage = f; #else *readpage = &some_fun; #endif int i = 0; if (i != 0) ; // return i915_gem_object_pin(readpage); filler_t *filler = **readpage; filler(0); // ERROR #endif return 0; }
// shmem_aops only has a function assigned to the readpage field // if TMPFS is enabled, otherwise it is NULL // see mm/shmem.c:2454 . call drivers/gpu/drm/i915/i915_dma.c:1862:i915_driver_load() . 2011: ret = i915_load_modeset_init(dev); .. call drivers/gpu/drm/i915/i915_dma.c:1167:i915_load_modeset_init() .. 1192: ret = i915_gem_init_ringbuffer(dev); ... call drivers/gpu/drm/i915/i915_gem.c:3649:i915_gem_init_ringbuffer() ... 3654: ret = intel_init_render_ring_buffer(dev); .... call drivers/gpu/drm/i915/intel_ringbuffer.c:1271:intel_init_render_ring_buffer() .... 1291: return intel_init_ring_buffer(dev, ring); ..... call drivers/gpu/drm/i915/intel_ringbuffer.c:807:intel_init_ring_buffer() // setup: function pointer `readpage' set to NULL ..... 827: obj = i915_gem_alloc_object(dev, ring->size); ...... call drivers/gpu/drm/i915/i915_gem.c:3518:i915_gem_alloc_object() ...... 3528: if (drm_gem_object_init(dev, &obj->base, size) != 0) ....... call drivers/gpu/drm/drm_gem.c:134:drm_gem_object_init() ....... 140: obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); ........ call mm/shmem.c:2719:shmem_file_setup() ........ 2748: inode = shmem_get_inode(root->d_sb, NULL, S_IFREG | S_IRWXUGO, 0, flags); ......... [SHMEM] call mm/shmem.c:1577:shmem_get_inode() ......... 1608: inode->i_mapping->a_ops = &shmem_aops; ..... 836: ret = i915_gem_object_pin(obj, PAGE_SIZE, true); ...... call drivers/gpu/drm/i915/i915_gem.c:3246:i915_gem_object_pin() ...... 3274: ret = i915_gem_object_bind_to_gtt(obj, alignment, ....... call drivers/gpu/drm/i915/i915_gem.c:2705:i915_gem_object_bind_to_gtt() ....... 2779: ret = i915_gem_object_get_pages_gtt(obj, gfpmask); ........ call drivers/gpu/drm/i915/i915_gem.c:1488:i915_gem_object_get_pages_gtt() ........ 1508: page = read_cache_page_gfp(mapping, i, ......... call mm/filemap.c:1815:read_cache_page_gfp() ......... 1819: filler_t *filler = (filler_t *)mapping->a_ops->readpage; // so filler is an alias for readpage, which is NULL ......... 1821: return wait_on_page_read(do_read_cache_page(mapping, index, filler, NULL, gfp)); .......... call mm/filemap.c:1728:do_read_cache_page() .......... ERROR 1755: err = filler(data, page) // filler is pointing to NULL !