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);