Bug 71109

Summary: [pmap] [patch] Possible race conditions in pmap.c
Product: Base System Reporter: gemini
Component: kernAssignee: Alan Cox <alc>
Status: Closed FIXED    
Severity: Affects Only Me CC: gemini
Priority: Normal    
Version: 4.5-RELEASE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
pmap.c.diff none

Description gemini 2004-08-29 18:10:28 UTC
pmap_allocpte() and pmap_enter_quick() in 'src/sys/i386/i386/pmap.c'
both call pmap_page_lookup() and _pmap_allocpte().  The latter two
functions may return NULL, which can be an indicator that the
respective function has been blocking.  The caller is then supposed
to start over again since the current operation at this level can
no longer be considered atomic.

Now, in pmap_allocpte() the return value of pmap_page_lookup()
isn't checked for NULL, and in pmap_enter_quick() the same is
true for _pmap_allocpte().  These flaws cause race conditions
that can result in a kernel panic.

Fix: Please consider adopting the patch below.  It applies cleanly to
RELENG_4 as of today, and, as far as I can tell, also applies to
the Alpha architecture (possibly with minor tweaks).

Apart from adding the missing NULL checking we also optimize the
other, already existing NULL check in pmap_enter_quick() in that
we do it only after pmap_page_lookup() has been called, because
'mpte' cannot be NULL in case the first part of the if-else clause
gets executed.

This patch also removes a superfluous call of VM_WAIT in
_pmap_allocpte().  The preceding function vm_page_grab() already
does the appropriate sleeping before returning NULL, even if called
without the VM_ALLOC_RETRY flag.  So we can return from
_pmap_allocpte() right away.

How-To-Repeat: The problem becomes apparent when looking at the relevant source
code.
Comment 1 Alan L. Cox 2004-12-05 20:12:00 UTC
The VM_WAIT is, indeed, unnecessary.  I suspect, but haven't verified, 
that it is an artifact of merging PAE support.

There is not, however, a race.  The reason is that we never call 
pmap_page_lookup() on a missing or busy page table page.  So, the check 
for a busy page is pointless and confusing.  (HEAD and RELENG_5 have 
remedied this.)

To see why, observe that pmap_page_lookup() is called only after 
inspection of the page directory entry ("pde") has shown that the page 
table page exists.  So, unless the page directory entry has been 
corrupted by a hardware error or different bug, the pmap_page_lookup() 
will succeed in finding a page.

A page table is only marked busy for a short time in
_pmap_unwire_pte_hold(), pmap_release() and _pmap_allocpte().  Since
the kernel is non-preemptive, the busy flag should be cleared before
it is tested anywhere.  For a problem to occur, an interrupt handler 
would have to free a page that is mapped into a user address space. 
That is not supposed to happen (and would itself be an error).

I'm happy to elaborate on any of these points if you like.

Regards,
Alan
Comment 2 Alan Cox freebsd_committer freebsd_triage 2004-12-05 20:15:52 UTC
Responsible Changed
From-To: freebsd-bugs->alc
Comment 3 Alan Cox freebsd_committer freebsd_triage 2006-08-26 03:56:06 UTC
State Changed
From-To: open->closed

The hypothesized race condition does not exist.