linux Wrong length units lead to buffer overflow and cause kernel panic

Field `len' units are bytes while field `pos' is an array index; thus, units are bytes/sizeof(pagemap_entry_t). Pagemap walks are using `len' as the upper limit for `pos' to iterate the buffer, but clearly `len' is larger than the actual buffer length. If a function tries to fill the buffer taking `len' as a boundary, that will cause a buffer overflow. Note that PROC_PAGE_MONITOR enables monitoring of memory utilization via the /proc file system, one of these files is /proc/pid/pagemap.
Bug fixed by commit 8c8296223f3
Type BufferOverflow
Config "PROC_PAGE_MONITOR" (1st degree)
Fix-in code
Location fs/proc/
#include <errno.h>
#include <stdlib.h>

#ifdef CONFIG_PROC_PAGE_MONITOR

#define PM_ENTRY_BYTES sizeof(long)
#define PM_END_OF_BUFFER 1
#define NUM_ENTRIES 10

long *pm_buffer;
unsigned long pm_pos, pm_len;

static int add_to_pagemap()
{
	pm_buffer[pm_pos++] = 0; // (5,8) ERROR
	if (pm_pos >= pm_len) // (6) comparing array positions against bytes !!!
		return PM_END_OF_BUFFER;
	return 0;
}

static int pagemap_pte_range()
{
	int err = 0;

	while (1) {
		err = add_to_pagemap(); // (4,7)
		if (err)
			return err;
	}

	return err;
}

int walk_page_range()
{
	return pagemap_pte_range(); // (3)
}

static int pagemap_read() {
  pm_len = PM_ENTRY_BYTES * NUM_ENTRIES;
  pm_buffer = malloc(pm_len);

  if (!pm_buffer)
    return -ENOMEM;

  pm_pos = 0;
  walk_page_range(); // (2): note that pm_len units are bytes

  return 0;
}

#endif /* CONFIG_PROC_PAGE_MONITOR */

int main(int argc, char** argv) {
#ifdef CONFIG_PROC_PAGE_MONITOR
  pagemap_read(); // (1)
#endif
  return 0;
}
diff --git a/simple/8c82962.c b/simple/8c82962.c
--- a/simple/8c82962.c
+++ b/simple/8c82962.c
@@ -38,8 +42,8 @@
 }
 
 static int pagemap_read() {
-  pm_len = PM_ENTRY_BYTES * NUM_ENTRIES;
-  pm_buffer = malloc(pm_len);
+  pm_len = NUM_ENTRIES;
+  pm_buffer = malloc(pm_len * PM_ENTRY_BYTES);
 
   if (!pm_buffer)
     return -ENOMEM;
#include <errno.h>
#include <stdlib.h>

#ifdef CONFIG_PROC_PAGE_MONITOR

#define PM_ENTRY_BYTES sizeof(long)
#define PM_END_OF_BUFFER 1
#define NUM_ENTRIES 10

long *pm_buffer;
unsigned long pm_pos, pm_len;
#endif

int main(int argc, char** argv) {
#ifdef CONFIG_PROC_PAGE_MONITOR
//  pagemap_read();
  pm_len = PM_ENTRY_BYTES * NUM_ENTRIES;
  pm_buffer = malloc(pm_len);

  if (!pm_buffer)
    return -ENOMEM;

  pm_pos = 0;
  int err = 0;

  while (1) {
    //err = add_to_pagemap();
    pm_buffer[pm_pos++] = 0; // ERROR
    if (pm_pos >= pm_len)
      err = PM_END_OF_BUFFER;
    if (err)
      return err;
  }
#endif
  return 0;
}
. call fs/proc/task_mmu.c:1103:pagemap_read()
. 1130: pm.len = PM_ENTRY_BYTES * (PAGEMAP_WALK_SIZE >> PAGE_SHIFT);
// pm.len is set to the size of pm.buffer in bytes
// PM_ENTRY_BYTES = sizeof(pagemap_entry_t)
// PAGEMAP_WALK_SIZE = PMD_SIZE
// PMD_SIZE is the size of a page table, while 2^PAGE_SHIFT is the size of a page, therefore PMD_SIZE / 2^PAGE_SHIFT is the number of page entries in a page table
. 1131: pm.buffer = kmalloc(pm.len, GFP_TEMPORARY);
. 1165: while (count && (start_vaddr < end_vaddr)) {
. 1175: ret = walk_page_range(start_vaddr, end, &pagemap_walk);
.. call mm/pagewalk.c:167:walk_page_range()
.. 241: err = walk_pud_range(pgd, addr, next, walk);
... call mm/pagewalk.c:72:walk_pud_range()
... 92: err = walk_pmd_range(pud, addr, next, walk);
.... call mm/pagewalk.c:27:walk_pmd_range()
.... 50: err = walk->pmd_entry(pmd, addr, next, walk);
..... dyn-call fs/proc/task_mmu.c:986:pagemap_pte_range()
..... 1007: err = add_to_pagemap(addr, &pme, pm);
// add_to_pagemap() 905 is the iterator step function for setting entries by walking through the buffer
...... call fs/proc/task_mmu.c:905:add_to_pagemap()
...... 908: pm->buffer[pm->pos++] = *pme;
...... 909: if (pm->pos >= pm->len)
// it compares pos and len, but pos is the index in the array while len is the size of the array in bytes
...... ERROR 908: pm->buffer[pm->pos++] = *pme;
// eventually we get a buffer overflow, when pos is incremented beyond the array bounds