linux
Array is accessed out of its bounds in some configurations
The length of `kmem_caches' is KMALLOC_SHIFT_HIGH + 1, while init_node_lock_keys() iterates on this array using the value PAGE_SHIFT + MAX_ORDER as upper-bound. Actually, KMALLOC_SHIFT_HIGH is a configuration-dependent value defined as min((MAX_ORDER + PAGE_SHIFT - 1),25). For configurations where MAX_ORDER + PAGE_SHIFT - 1 > 25, the length of `kmem_caches' is cut to 26, and init_node_lock_keys() will access this array out of its bounds. The bug shows up for LOCKDEP && SLAB, if some additional configuration like PPC_256K_PAGES && FORCE_MAX_ZONEORDER=11 makes MAX_ORDER + PAGE_SHIFT - 1 > 25.
Bug fixed by commit 0f8f8094d28
| Type | OutOfBoundsRead |
| Config | "LOCKDEP && SLAB && !SLOB && PPC_256K_PAGES && FORCE_MAX_ZONEORDER=11" (5th degree) |
| Fix-in | code |
| Location | mm/ |
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
#if defined(CONFIG_PPC_256K_PAGES)
#define PAGE_SHIFT 18
#elif defined(CONFIG_PPC_64K_PAGES)
#define PAGE_SHIFT 16
#elif defined(CONFIG_PPC_16K_PAGES)
#define PAGE_SHIFT 14
#else
#define PAGE_SHIFT 12
#endif
#ifdef CONFIG_SLAB
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
(MAX_ORDER + PAGE_SHIFT - 1) : 25)
#else
#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1)
#endif
#ifndef CONFIG_SLOB
int* kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
#endif
#ifdef CONFIG_LOCKDEP
static void init_node_lock_keys()
{
int i;
for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++)
{
int* cache = kmalloc_caches[i]; // (4) ERROR
if (!cache)
continue;
int n = *cache;
}
}
void init_lock_keys(void)
{
init_node_lock_keys(); // (3)
}
#else
init_node_lock_keys()
{
}
void init_lock_keys(void)
{
}
#endif
void kmem_cache_init_late(void)
{
init_lock_keys(); // (2)
}
int main(void)
{
#ifndef CONFIG_SLOB
kmem_cache_init_late(); // (1)
#endif
return 0;
}
diff --git a/simple/0f8f809.c b/simple/0f8f809.c
--- a/simple/0f8f809.c
+++ b/simple/0f8f809.c
@@ -32,7 +32,7 @@
{
int i;
- for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++)
+ for (i = 1; i < KMALLOC_SHIFT_HIGH; i++)
{
int* cache = &kmalloc_caches[i]; // (4) ERROR
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
#if defined(CONFIG_PPC_256K_PAGES)
#define PAGE_SHIFT 18
#elif defined(CONFIG_PPC_64K_PAGES)
#define PAGE_SHIFT 16
#elif defined(CONFIG_PPC_16K_PAGES)
#define PAGE_SHIFT 14
#else
#define PAGE_SHIFT 12
#endif
#ifdef CONFIG_SLAB
#define KMALLOC_SHIFT_HIGH ((MAX_ORDER + PAGE_SHIFT - 1) <= 25 ? \
(MAX_ORDER + PAGE_SHIFT - 1) : 25)
#else
#define KMALLOC_SHIFT_HIGH (PAGE_SHIFT + 1)
#endif
#ifndef CONFIG_SLOB
int kmalloc_caches[KMALLOC_SHIFT_HIGH + 1];
#endif
int main(int argc, char** argv)
{
#ifndef CONFIG_SLOB
// kmem_cache_init_late();
#ifdef CONFIG_LOCKDEP
int i;
for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) {
int* cache = &kmalloc_caches[i]; // ERROR
if (!cache)
continue;
int n = *cache;
}
#else
#endif
#endif
return 0;
}
. call mm/slab.c:1631:kmem_cache_init_late()
. 1645: init_lock_keys();
.. [LOCKDEP] call mm/slab.c:602:init_lock_keys()
.. 607: init_node_lock_keys(node);
... [LOCKDEP] call mm/slab.c:561:init_node_lock_keys()
... 568: for (i = 1; i < PAGE_SHIFT + MAX_ORDER; i++) {
// length(kmalloc_caches) == KMALLOC_SHIFT_HIGH+1
... 570: struct kmem_cache *cache = kmalloc_caches[i];
... ERROR 575: n = cache->node[q];