Module Name: src Committed By: ad Date: Tue Mar 17 22:37:05 UTC 2020
Modified Files: src/sys/arch/x86/x86: pmap.c Log Message: - pmap_enter(): under low memory conditions, if PTP allocation succeeded and then PV entry allocation failed, PTP pages were being freed without their struct pmap_page being reset back to the non-PTP setup, which then caused havoc with pmap_page_removed(). Fix it. - pmap_enter_pv(): don't do the PV check if memory allocation failed. Reported-by: syzbot+d9b42238107c155ca...@syzkaller.appspotmail.com Reported-by: syzbot+80cf4850dc1cf2990...@syzkaller.appspotmail.com To generate a diff of this commit: cvs rdiff -u -r1.374 -r1.375 src/sys/arch/x86/x86/pmap.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/x86/pmap.c diff -u src/sys/arch/x86/x86/pmap.c:1.374 src/sys/arch/x86/x86/pmap.c:1.375 --- src/sys/arch/x86/x86/pmap.c:1.374 Tue Mar 17 22:29:19 2020 +++ src/sys/arch/x86/x86/pmap.c Tue Mar 17 22:37:05 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pmap.c,v 1.374 2020/03/17 22:29:19 ad Exp $ */ +/* $NetBSD: pmap.c,v 1.375 2020/03/17 22:37:05 ad Exp $ */ /* * Copyright (c) 2008, 2010, 2016, 2017, 2019, 2020 The NetBSD Foundation, Inc. @@ -130,7 +130,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.374 2020/03/17 22:29:19 ad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.375 2020/03/17 22:37:05 ad Exp $"); #include "opt_user_ldt.h" #include "opt_lockdebug.h" @@ -635,30 +635,6 @@ pmap_compare_key(void *context, const vo } /* - * pmap_ptp_init: initialize new page table page - */ -static inline void -pmap_ptp_init(struct vm_page *ptp) -{ - - ptp->uanon = (struct vm_anon *)(vaddr_t)~0L; - rb_tree_init(&VM_PAGE_TO_PP(ptp)->pp_rb, &pmap_rbtree_ops); - PMAP_CHECK_PP(VM_PAGE_TO_PP(ptp)); -} - -/* - * pmap_ptp_fini: finalize a page table page - */ -static inline void -pmap_ptp_fini(struct vm_page *ptp) -{ - - KASSERT(RB_TREE_MIN(&VM_PAGE_TO_PP(ptp)->pp_rb) == NULL); - PMAP_CHECK_PP(VM_PAGE_TO_PP(ptp)); - ptp->uanon = NULL; -} - -/* * pmap_ptp_range_set: abuse ptp->uanon to record minimum VA of PTE */ static inline void @@ -2158,7 +2134,9 @@ pmap_enter_pv(struct pmap *pmap, struct LIST_INSERT_HEAD(&pp->pp_pvlist, pve, pve_list); } mutex_spin_exit(&pp->pp_lock); - pmap_check_pv(pmap, ptp, pp, va, true); + if (error == 0) { + pmap_check_pv(pmap, ptp, pp, va, true); + } return error; } @@ -2252,13 +2230,15 @@ pmap_freepage(struct pmap *pmap, struct int lidx; KASSERT(ptp->wire_count == 1); + PMAP_CHECK_PP(VM_PAGE_TO_PP(ptp)); lidx = level - 1; pmap_stats_update(pmap, -1, 0); if (pmap->pm_ptphint[lidx] == ptp) pmap->pm_ptphint[lidx] = NULL; ptp->wire_count = 0; - pmap_ptp_fini(ptp); + ptp->uanon = NULL; + KASSERT(RB_TREE_MIN(&VM_PAGE_TO_PP(ptp)->pp_rb) == NULL); /* * Enqueue the PTP to be freed by pmap_update(). We can't remove @@ -2357,19 +2337,21 @@ pmap_get_ptp(struct pmap *pmap, struct p if (pt->pg[i] == NULL) { pt->pg[i] = uvm_pagealloc(obj, off, NULL, aflags); - pt->alloced[i] = true; - if (pt->pg[i] != NULL) { - pmap_ptp_init(pt->pg[i]); - } + pt->alloced[i] = (pt->pg[i] != NULL); } else if (pt->pg[i]->wire_count == 0) { /* This page was queued to be freed; dequeue it. */ LIST_REMOVE(pt->pg[i], mdpage.mp_pp.pp_link); - pmap_ptp_init(pt->pg[i]); + pt->alloced[i] = true; } PMAP_DUMMY_UNLOCK(pmap); if (pt->pg[i] == NULL) { pmap_unget_ptp(pmap, pt); return ENOMEM; + } else { + pt->pg[i]->uanon = (struct vm_anon *)(vaddr_t)~0L; + rb_tree_init(&VM_PAGE_TO_PP(pt->pg[i])->pp_rb, + &pmap_rbtree_ops); + PMAP_CHECK_PP(VM_PAGE_TO_PP(pt->pg[i])); } } ptp = pt->pg[2]; @@ -2464,22 +2446,12 @@ pmap_unget_ptp(struct pmap *pmap, struct KASSERT(mutex_owned(&pmap->pm_lock)); for (i = PTP_LEVELS; i > 1; i--) { - if (pt->pg[i] == NULL) { - break; - } if (!pt->alloced[i]) { continue; } KASSERT(pt->pg[i]->wire_count == 0); PMAP_CHECK_PP(VM_PAGE_TO_PP(pt->pg[i])); - /* pmap zeros all pages before freeing. */ - pt->pg[i]->flags |= PG_ZERO; - pmap_ptp_fini(pt->pg[i]); - PMAP_DUMMY_LOCK(pmap); - uvm_pagefree(pt->pg[i]); - PMAP_DUMMY_UNLOCK(pmap); - pt->pg[i] = NULL; - pmap->pm_ptphint[0] = NULL; + pmap_freepage(pmap, pt->pg[i], i - 1); } } @@ -5232,6 +5204,7 @@ pmap_update(struct pmap *pmap) mutex_enter(&pmap->pm_lock); while ((ptp = LIST_FIRST(&pmap->pm_gc_ptp)) != NULL) { KASSERT(ptp->wire_count == 0); + KASSERT(ptp->uanon == NULL); LIST_REMOVE(ptp, mdpage.mp_pp.pp_link); pp = VM_PAGE_TO_PP(ptp); LIST_INIT(&pp->pp_pvlist);