linux Function incorrectly returning (int)255 on failure causes kernel panic

The value 0xff is used to mark pfnnid_map pages that are not physically available. But, since pfnnid_map is an array of unsigned values, 0xff is interpreted as 255 and not as -1. As a result, pfn_to_nid() is returning nid=255 for those pages that are not available in physical memory. This value is being used by pfn_valid() to index the node_data array, thus reading data out of node_data bounds.
Bug fixed by commit 91ea8207168
Type OutOfBoundsRead
Config "PARISC && DISCONTIGMEM && PROC_PAGE_MONITOR" (3rd degree)
Fix-in code
Location arch/parisc/
#include <assert.h>
#include <string.h>

#ifdef CONFIG_NODES_SHIFT
// NB: parisc's CONFIG_NODES_SHIFT default is 3
#define NODES_SHIFT     CONFIG_NODES_SHIFT
#else
#define NODES_SHIFT     0
#endif

#define MAX_NUMNODES    (1 << NODES_SHIFT)

#define node_end_pfn(nid) NODE_DATA(nid)

#define MAX_PHYSMEM_RANGES 8 /* Fix the size for now (current known max is 3) */

#ifdef CONFIG_DISCONTIGMEM

int node_data[MAX_NUMNODES];

#define NODE_DATA(nid)          (node_data[nid])

#define PFNNID_MAP_MAX  512     /* support 512GB */
unsigned char pfnnid_map[PFNNID_MAP_MAX];
unsigned long max_pfn = PFNNID_MAP_MAX;

int pfn_to_nid(unsigned int pfn)
{
	assert(pfn < PFNNID_MAP_MAX);
	return (int)pfnnid_map[pfn];
}

int pfn_valid(unsigned int pfn)
{
	int nid = pfn_to_nid(pfn);

	if (nid >= 0)
		return (pfn < node_end_pfn(nid)); // ERROR
	return 0;
}

#endif

#ifndef CONFIG_DISCONTIGMEM
#define max_pfn 0
#define pfn_valid(pfn)		(1)
#endif /* CONFIG_DISCONTIGMEM */

void setup_bootmem(void)
{
#ifdef CONFIG_DISCONTIGMEM
  memset(pfnnid_map, 0xff, sizeof(pfnnid_map));
#endif
}

#ifdef CONFIG_PROC_PAGE_MONITOR
int kpageflags_read()
{
  unsigned int pfn = 0;

  for (;pfn<max_pfn;pfn++) {
    if (pfn_valid(pfn))
      ;
  }

  return 0;
}
#endif

int main()
{
  setup_bootmem();
#ifdef CONFIG_PROC_PAGE_MONITOR
  kpageflags_read();
#endif
  return 0;
}

diff --git a/simple/91ea820.c b/simple/91ea820.c
--- a/simple/91ea820.c
+++ b/simple/91ea820.c
@@ -22,13 +22,13 @@
 #define NODE_DATA(nid)          (node_data[nid])
 
 #define PFNNID_MAP_MAX  512     /* support 512GB */
-unsigned char pfnnid_map[PFNNID_MAP_MAX];
+signed char pfnnid_map[PFNNID_MAP_MAX];
 unsigned long max_pfn = PFNNID_MAP_MAX;
 
 int pfn_to_nid(unsigned int pfn)
 {
 	assert(pfn < PFNNID_MAP_MAX);
-	return (int)pfnnid_map[pfn];
+	return pfnnid_map[pfn];
 }
 
 int pfn_valid(unsigned int pfn)
#include <assert.h>
#include <string.h>

#ifdef CONFIG_NODES_SHIFT
// NB: parisc's CONFIG_NODES_SHIFT default is 3
#define NODES_SHIFT     CONFIG_NODES_SHIFT
#else
#define NODES_SHIFT     0
#endif

#define MAX_NUMNODES    (1 << NODES_SHIFT)

#define node_end_pfn(nid) NODE_DATA(nid)

#define MAX_PHYSMEM_RANGES 8 /* Fix the size for now (current known max is 3) */

#ifdef CONFIG_DISCONTIGMEM

int node_data[MAX_NUMNODES];
#define NODE_DATA(nid)          (node_data[nid])
#define PFNNID_MAP_MAX  512     /* support 512GB */
unsigned char pfnnid_map[PFNNID_MAP_MAX];
unsigned long max_pfn = PFNNID_MAP_MAX;

#endif

#ifndef CONFIG_DISCONTIGMEM
#define max_pfn 0
#define pfn_valid(pfn)		(1)
#endif /* CONFIG_DISCONTIGMEM */

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++) {
    assert(pfn < PFNNID_MAP_MAX);
    int nid = (int)pfnnid_map[pfn];
    if (nid >= 0)
      return (pfn < node_end_pfn(nid)); // ERROR
  }
#endif
  return 0;
}

. 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

. call fs/proc/page.c:173:kpageflags_read()
. 188: if (pfn_valid(pfn))
.. call arch/parisc/include/asm/mmzone.h:52:pfn_valid()
.. 54: int nid = pfn_to_nid(pfn);
... call arch/parisc/include/asm/mmzone.h:39:pfn_to_nid()
... 49: return (int)pfnnid_map[i];
// if a page is not physically available we will get (int)255 instead of (int)-1
.. 56: if (nid >= 0)
.. 57: return (pfn < node_end_pfn(nid));
... call include/linux/mmzone.h:759:node_end_pfn()
.... call arch/parisc/include/asm/mmzone.h:16:NODE_DATA()
.... ERROR 16: &node_data[nid].pg_data
// node_data[255] may be reading out of bounds