linux/mm
Mike Kravetz 60454d9304 hugetlbfs: fix kernel BUG at fs/hugetlbfs/inode.c:444!
commit 5e41540c8a upstream.

This bug has been experienced several times by the Oracle DB team.  The
BUG is in remove_inode_hugepages() as follows:

	/*
	 * If page is mapped, it was faulted in after being
	 * unmapped in caller.  Unmap (again) now after taking
	 * the fault mutex.  The mutex will prevent faults
	 * until we finish removing the page.
	 *
	 * This race can only happen in the hole punch case.
	 * Getting here in a truncate operation is a bug.
	 */
	if (unlikely(page_mapped(page))) {
		BUG_ON(truncate_op);

In this case, the elevated map count is not the result of a race.
Rather it was incorrectly incremented as the result of a bug in the huge
pmd sharing code.  Consider the following:

 - Process A maps a hugetlbfs file of sufficient size and alignment
   (PUD_SIZE) that a pmd page could be shared.

 - Process B maps the same hugetlbfs file with the same size and
   alignment such that a pmd page is shared.

 - Process B then calls mprotect() to change protections for the mapping
   with the shared pmd. As a result, the pmd is 'unshared'.

 - Process B then calls mprotect() again to chage protections for the
   mapping back to their original value. pmd remains unshared.

 - Process B then forks and process C is created. During the fork
   process, we do dup_mm -> dup_mmap -> copy_page_range to copy page
   tables. Copying page tables for hugetlb mappings is done in the
   routine copy_hugetlb_page_range.

In copy_hugetlb_page_range(), the destination pte is obtained by:

	dst_pte = huge_pte_alloc(dst, addr, sz);

If pmd sharing is possible, the returned pointer will be to a pte in an
existing page table.  In the situation above, process C could share with
either process A or process B.  Since process A is first in the list,
the returned pte is a pointer to a pte in process A's page table.

However, the check for pmd sharing in copy_hugetlb_page_range is:

	/* If the pagetables are shared don't copy or take references */
	if (dst_pte == src_pte)
		continue;

Since process C is sharing with process A instead of process B, the
above test fails.  The code in copy_hugetlb_page_range which follows
assumes dst_pte points to a huge_pte_none pte.  It copies the pte entry
from src_pte to dst_pte and increments this map count of the associated
page.  This is how we end up with an elevated map count.

To solve, check the dst_pte entry for huge_pte_none.  If !none, this
implies PMD sharing so do not copy.

Link: http://lkml.kernel.org/r/20181105212315.14125-1-mike.kravetz@oracle.com
Fixes: c5c99429fa ("fix hugepages leak due to pagetable page sharing")
Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com>
Reviewed-by: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Hugh Dickins <hughd@google.com>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Prakash Sangappa <prakash.sangappa@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
History 2019-02-11 17:54:05 +00:00
..
backing-dev.c block: fix bdi vs gendisk lifetime mismatch 2016-11-20 01:17:05 +00:00
balloon_compaction.c mm/balloon_compaction: fix deflation when compaction is disabled 2016-06-15 21:29:35 +01:00
bootmem.c mem-hotplug: reset node managed pages when hot-adding a new pgdat 2014-11-27 11:21:50 +00:00
cleancache.c mm: dump page when hitting a VM_BUG_ON using VM_BUG_ON_PAGE 2014-01-23 16:36:50 -08:00
compaction.c mm/balloon_compaction: redesign ballooned pages management 2016-06-15 21:29:35 +01:00
debug-pagealloc.c mm, x86: Remove debug_pagealloc_enabled 2011-12-06 09:24:07 +01:00
dmapool.c Fix unbalanced mutex in dma_pool_create(). 2014-10-05 13:41:09 -07:00
early_ioremap.c mm: create generic early_ioremap() support 2014-04-07 16:36:15 -07:00
fadvise.c teach SYSCALL_DEFINE<n> how to deal with long long/unsigned long long 2013-03-03 22:46:22 -05:00
failslab.c switch debugfs to umode_t 2012-01-03 22:54:56 -05:00
filemap.c mm/filemap.c: fix NULL pointer in page_cache_tree_insert() 2018-10-21 08:45:34 +01:00
filemap_xip.c mm: drop vm_ops->remap_pages and generic_file_remap_pages() stub 2018-10-03 04:09:51 +01:00
frontswap.c mm: frontswap: invalidate expired data on a dup-store failure 2014-12-10 17:49:41 +00:00
gup.c mm: remove rest usage of VM_NONLINEAR and pte_file() 2018-10-03 04:09:52 +01:00
highmem.c Some nice cleanups, and even a patch my wife did as a "live" demo for 2012-12-20 08:37:05 -08:00
huge_memory.c mremap: properly flush TLB before releasing the page 2018-12-16 22:09:45 +00:00
hugetlb.c hugetlbfs: fix kernel BUG at fs/hugetlbfs/inode.c:444! 2019-02-11 17:54:05 +00:00
hugetlb_cgroup.c cgroup: remove css_parent() 2014-05-16 13:22:48 -04:00
hwpoison-inject.c mm/hwpoison: add '#' to hwpoison_inject 2014-01-21 16:19:48 -08:00
init-mm.c mm: Add a user_ns owner to mm_struct and fix ptrace permission checks 2018-01-01 20:52:10 +00:00
internal.h mm, compaction: properly signal and act upon lock and need_sched() contention 2014-06-04 16:54:11 -07:00
interval_tree.c mm: replace vma->sharead.linear with vma->shared 2018-10-03 04:09:52 +01:00
iov_iter.c Fix thinko in iov_iter_single_seg_count 2014-11-27 11:21:48 +00:00
Kconfig mm/zsmalloc: make zsmalloc module-buildable 2014-06-04 16:54:14 -07:00
Kconfig.debug mm: more intensive memory corruption debugging 2012-01-10 16:30:42 -08:00
kmemcheck.c kmemcheck: Fix build errors due to missing slab.h 2010-03-30 22:02:32 +09:00
kmemleak-test.c mm/kmemleak-test.c: use pr_fmt for logging 2014-06-06 16:08:18 -07:00
kmemleak.c mm: kmemleak: allow safe memory scanning during kmemleak disabling 2015-07-15 10:00:54 +01:00
ksm.c mm/ksm.c: ignore STABLE_FLAG of rmap_item->address in rmap_walk_ksm() 2018-11-20 18:05:14 +00:00
list_lru.c mm: keep page cache radix tree nodes in check 2014-04-03 16:21:01 -07:00
maccess.c mm: Map most files to use export.h instead of module.h 2011-10-31 09:20:12 -04:00
madvise.c mm: madvise(MADV_DODUMP): allow hugetlbfs pages 2018-12-16 22:09:38 +00:00
Makefile mm: replace remap_file_pages() syscall with emulation 2018-10-03 04:09:50 +01:00
memblock.c memblock, memhotplug: fix wrong type in memblock_find_in_range_node(). 2014-10-05 13:41:03 -07:00
memcontrol.c mm: remove rest usage of VM_NONLINEAR and pte_file() 2018-10-03 04:09:52 +01:00
memory-failure.c mm/memory-failure.c: use compound_head() flags for huge pages 2017-09-15 18:30:12 +01:00
memory.c mm/tlb: Remove tlb_remove_table() non-concurrent condition 2018-12-16 22:08:52 +00:00
memory_hotplug.c memory_hotplug: cond_resched in __remove_pages 2019-02-11 17:53:46 +00:00
mempolicy.c mm/mempolicy.c: avoid use uninitialized preferred_node 2018-06-16 22:22:47 +01:00
mempool.c mm/mempool.c: update the kmemleak stack trace for mempool allocations 2014-06-06 16:08:17 -07:00
migrate.c rmap: drop support of non-linear mappings 2018-10-03 04:09:51 +01:00
mincore.c mm: remove rest usage of VM_NONLINEAR and pte_file() 2018-10-03 04:09:52 +01:00
mlock.c mm: do not bug_on on incorrect length in __mm_populate() 2018-11-20 18:05:41 +00:00
mm_init.c mm: bring back /sys/kernel/mm 2014-01-27 21:02:39 -08:00
mmap.c mm: do not bug_on on incorrect length in __mm_populate() 2018-11-20 18:05:41 +00:00
mmu_context.c mm/mmu_context, sched/core: Fix mmu_context.h assumption 2018-01-09 00:35:08 +00:00
mmu_notifier.c mm: audit/fix non-modular users of module_init in core code 2014-01-23 16:36:52 -08:00
mmzone.c mm: numa: Change page last {nid,pid} into {cpu,pid} 2013-10-09 14:47:45 +02:00
mprotect.c x86/speculation/l1tf: Disallow non privileged high MMIO PROT_NONE mappings 2018-10-03 04:10:02 +01:00
mremap.c mremap: properly flush TLB before releasing the page 2018-12-16 22:09:45 +00:00
msync.c mm: remove rest usage of VM_NONLINEAR and pte_file() 2018-10-03 04:09:52 +01:00
nobootmem.c mem-hotplug: reset node managed pages when hot-adding a new pgdat 2014-11-27 11:21:50 +00:00
nommu.c mm: replace remap_file_pages() syscall with emulation 2018-10-03 04:09:50 +01:00
oom_kill.c OOM, PM: OOM killed task shouldn't escape PM suspend 2014-11-05 11:43:09 +00:00
page-writeback.c writeback: use |1 instead of +1 to protect against div by zero 2015-05-20 13:25:57 +01:00
page_alloc.c mm, page_alloc: do not break __GFP_THISNODE by zonelist reset 2018-11-20 18:05:12 +00:00
page_cgroup.c swap: cond_resched in swap_cgroup_prepare() 2017-09-15 18:30:12 +01:00
page_io.c fix __swap_writepage() compile failure on old gcc versions 2014-06-14 19:30:48 -05:00
page_isolation.c mm: fix invalid node in alloc_migrate_target() 2016-05-01 00:06:08 +02:00
pagewalk.c pagewalk: improve vma handling 2018-10-03 04:10:02 +01:00
percpu-km.c percpu: clear memory allocated with the km allocator 2010-10-02 10:28:42 +03:00
percpu-vm.c percpu: perform tlb flush after pcpu_map_pages() failure 2014-10-05 13:41:06 -07:00
percpu.c Revert "percpu: free percpu allocation info for uniprocessor system" 2014-11-14 15:20:06 +00:00
pgtable-generic.c mm: fix TLB flush race between migration, and change_protection_range 2013-12-18 19:04:51 -08:00
process_vm_access.c ptrace: use fsuid, fsgid, effective creds for fs access checks 2017-09-15 18:30:17 +01:00
quicklist.c mm: delete various needless include <linux/module.h> 2011-10-31 09:20:11 -04:00
readahead.c mm/readahead.c: inline ra_submit 2014-04-07 16:35:58 -07:00
rmap.c ksm: add cond_resched() to the rmap_walks 2018-11-20 18:05:14 +00:00
shmem.c mm: shmem.c: Correctly annotate new inodes for lockdep 2018-12-16 22:09:24 +00:00
slab.c cpuset: PF_SPREAD_PAGE and PF_SPREAD_SLAB should be atomic flags 2017-10-12 15:28:22 +01:00
slab.h memcg, slab: merge memcg_{bind,release}_pages to memcg_{un}charge_slab 2014-06-04 16:54:01 -07:00
slab_common.c Merge branch 'slab/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg/linux into for-3.16-rcX 2014-07-22 18:38:27 -04:00
slob.c slab: get_online_mems for kmem_cache_{create,destroy,shrink} 2014-06-04 16:53:59 -07:00
slub.c slub/memcg: cure the brainless abuse of sysfs attributes 2017-09-15 18:29:54 +01:00
sparse-vmemmap.c mm/sparse: use memblock apis for early memory allocations 2014-01-21 16:19:47 -08:00
sparse.c mm: use macros from compiler.h instead of __attribute__((...)) 2014-04-07 16:35:54 -07:00
swap.c rmap: drop support of non-linear mappings 2018-10-03 04:09:51 +01:00
swap_state.c mm: page_alloc: convert hot/cold parameter and immediate callers to bool 2014-06-04 16:54:09 -07:00
swapfile.c mm/swapfile.c: fix swap_count comment about nonexistent SWAP_HAS_CONT 2018-11-20 18:05:14 +00:00
truncate.c mm: cleancache: fix corruption on missed inode invalidation 2018-12-16 22:09:45 +00:00
util.c fs/proc: Stop trying to report thread stacks 2018-11-20 18:05:58 +00:00
vmacache.c mm: get rid of vmacache_flush_all() entirely 2018-09-25 23:47:35 +01:00
vmalloc.c mm/vmalloc.c: export unmap_kernel_range() 2014-06-04 16:54:14 -07:00
vmpressure.c mm: vmpressure: fix sending wrong events on underflow 2017-06-05 21:17:16 +01:00
vmscan.c mm: fix the NULL mapping case in __isolate_lru_page() 2018-06-16 22:22:04 +01:00
vmstat.c mm/vmstat.c: skip NR_TLB_REMOTE_FLUSH* properly 2018-12-16 22:09:37 +00:00
workingset.c mm: workingset: fix crash in shadow node shrinker caused by replace_page_cache_page() 2016-11-20 01:17:36 +00:00
zbud.c mm/zbud.c: make size unsigned like unique callsite 2014-06-04 16:54:13 -07:00
zsmalloc.c zsmalloc: fixup trivial zs size classes value in comments 2014-06-04 16:54:13 -07:00
zswap.c mm/zswap: NUMA aware allocation for zswap_dstmem 2014-06-04 16:54:14 -07:00