• Kirill A. Shutemov's avatar
    thp: fix split_huge_page() after mremap() of THP · bd56086f
    Kirill A. Shutemov authored
    Sasha Levin has reported KASAN out-of-bounds bug[1].  It points to "if
    (!is_swap_pte(pte[i]))" in unfreeze_page_vma() as a problematic access.
    
    The cause is that split_huge_page() doesn't handle THP correctly if it's
    not allingned to PMD boundary.  It can happen after mremap().
    
    Test-case (not always triggers the bug):
    
    	#define _GNU_SOURCE
    	#include <stdio.h>
    	#include <stdlib.h>
    	#include <sys/mman.h>
    
    	#define MB (1024UL*1024)
    	#define SIZE (2*MB)
    	#define BASE ((void *)0x400000000000)
    
    	int main()
    	{
    		char *p;
    
    		p = mmap(BASE, SIZE, PROT_READ | PROT_WRITE,
    				MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,
    				-1, 0);
    		if (p == MAP_FAILED)
    			perror("mmap"), exit(1);
    		p = mremap(BASE, SIZE, SIZE, MREMAP_FIXED | MREMAP_MAYMOVE,
    				BASE + SIZE + 8192);
    		if (p == MAP_FAILED)
    			perror("mremap"), exit(1);
    		system("echo 1 > /sys/kernel/debug/split_huge_pages");
    		return 0;
    	}
    
    The patch fixes freeze and unfreeze paths to handle page table boundary
    crossing.
    
    It also makes mapcount vs count check in split_huge_page_to_list()
    stricter:
     - after freeze we don't expect any subpage mapped as we remove them
       from rmap when setting up migration entries;
     - count must be 1, meaning only caller has reference to the page;
    
    [1] https://gist.github.com/sashalevin/c67fbea55e7c0576972aSigned-off-by: default avatarKirill A.  Shutemov <kirill.shutemov@linux.intel.com>
    Reported-by: default avatarSasha Levin <sasha.levin@oracle.com>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    bd56086f
huge_memory.c 91.1 KB