linux
Assertion violation when walking through system memory
kpageflags_read() walks through system memory, reading pages flags and writing them to an output buffer. For each pfn it calls pfn_valid(), which in parisc architectures with DISCONTIGMEM, relies on pfn_to_nid(). The pfn_to_nid() function incorrectly bugs on pages that are not physically available.
Bug fixed by commit ae249b5fa27
Type | FatalAssertionViolation |
Config | "PARISC && DISCONTIGMEM && PROC_PAGE_MONITOR" (3rd degree) |
Fix-in | code |
Location | arch/parisc/ |
#include <stdbool.h> #ifdef CONFIG_DISCONTIGMEM #include <assert.h> #include <string.h> #endif #define PFNNID_MAP_MAX 512 /* support 512GB */ unsigned char pfnnid_map[PFNNID_MAP_MAX]; unsigned long max_pfn = PFNNID_MAP_MAX; #ifdef CONFIG_DISCONTIGMEM int pfn_to_nid(unsigned long pfn) { unsigned char r; assert(pfn < PFNNID_MAP_MAX); r = pfnnid_map[pfn]; assert(r != 0xff); // (6) ERROR: all positions set to 0xff by (2) return (int)r; } int pfn_valid(int pfn) { int nid = pfn_to_nid(pfn); // (5) return (nid >= 0); } #endif #ifndef CONFIG_DISCONTIGMEM #define pfn_valid(pfn) (true) #endif /* CONFIG_DISCONTIGMEM */ #ifdef CONFIG_PROC_PAGE_MONITOR int kpageflags_read() { unsigned int pfn = 0; for (;pfn<max_pfn;pfn++) { if (pfn_valid(pfn)) // (4) ; } return 0; } #endif void setup_bootmem(void) { #ifdef CONFIG_DISCONTIGMEM memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); // (2) pfnnid_map[*] = 0xff #endif } int main() { setup_bootmem(); // (1) #ifdef CONFIG_PROC_PAGE_MONITOR kpageflags_read(); // (3) #endif return 0; }
diff --git a/simple/ae249b5.c b/simple/ae249b5.c --- a/simple/ae249b5.c +++ b/simple/ae249b5.c @@ -13,13 +13,8 @@ #ifdef CONFIG_DISCONTIGMEM int pfn_to_nid(unsigned long pfn) { - unsigned char r; - assert(pfn < PFNNID_MAP_MAX); - r = pfnnid_map[pfn]; - assert(r != 0xff); // ERROR - - return (int)r; + return (int)pfnnid_map[pfn]; } int pfn_valid(int pfn)
#include <stdbool.h> #ifdef CONFIG_DISCONTIGMEM #include <assert.h> #include <string.h> #endif #define PFNNID_MAP_MAX 512 /* support 512GB */ unsigned char pfnnid_map[PFNNID_MAP_MAX]; unsigned long max_pfn = PFNNID_MAP_MAX; int main() { // setup_bootmem(); #ifdef CONFIG_DISCONTIGMEM memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); #endif #ifdef CONFIG_PROC_PAGE_MONITOR // kpageflags_read(); unsigned int pfn = 0; for (;pfn<max_pfn;pfn++) { #ifdef CONFIG_DISCONTIGMEM unsigned char r; assert(pfn < PFNNID_MAP_MAX); r = pfnnid_map[pfn]; assert(r != 0xff); // ERROR int nid = (int)r; return (nid >= 0); #endif if (pfn_valid(pfn)) ; } #endif return 0; }
// setup . call arch/parisc/mm/init.c:116:setup_bootmem() . 273: for (i = 0; i < MAX_PHYSMEM_RANGES; i++) { // MAX_PHYSMEM_RANGES is hardcoded to 8 . 274: memset(NODE_DATA(i), 0, sizeof(pg_data_t)); . [DISCONTIGMEM] 277: memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); // since pfnnid_map is unsigned char[], this is (unsigned char)255 // /proc/kpageflags . call fs/proc/page.c:173:kpageflags_read() . 188: if (pfn_valid(pfn)) .. [DISCONTIGMEM] call arch/parisc/include/asm/mmzone.h:55:pfn_valid() .. 57: int nid = pfn_to_nid(pfn); ... [DISCONTIGMEM] call arch/parisc/include/asm/mmzone.h:39:pfn_to_nid() ... 49: r = pfnnid_map[i]; // 0xff is the mark for non-physically available pages ... ERROR 50: BUG_ON(r == 0xff);