2001-02-14 13:09:04

by Mark Hemment

[permalink] [raw]
Subject: [PATCH] vmalloc fault race

Hi,

If two processes, sharing the same page tables, hit an unloaded vmalloc
address in the kernel at the same time, one of the processes is killed
(with the message "Unable to handle kernel paging request").

This occurs because the test on a vmalloc fault is too tight. On x86,
it contains;
if (pmd_present(*pmd) || !pmd_present(*pmd_k))
goto bad_area_nosemaphore;

If two processes are racing, chances are that "pmd_present(*pmd)" will
be true for one of them.

It appears that; alpha, x86, arm, cris and sparc all have the
same/similar test.
I've included a patch for all the above archs, but have only tested on
the x86 (so no guarantee it is correct for other archs).

Note: Even though this race can only occur on SMP (assuming cannot
context switch on entering a page-fault), I've unconditionally remove the
test against pmd. It could be argued that it should be left in for UP...

Patch is againt 2.4.2pre3.

Mark


diff -urN -X dontdiff vxfs-2.4.2pre3/arch/alpha/mm/fault.c markhe-2.4.2pre3/arch/alpha/mm/fault.c
--- vxfs-2.4.2pre3/arch/alpha/mm/fault.c Fri Dec 29 22:07:19 2000
+++ markhe-2.4.2pre3/arch/alpha/mm/fault.c Wed Feb 14 12:10:21 2001
@@ -223,7 +223,7 @@

pgd = current->active_mm->pgd + offset;
pgd_k = swapper_pg_dir + offset;
- if (!pgd_present(*pgd) && pgd_present(*pgd_k)) {
+ if (pgd_present(*pgd_k)) {
pgd_val(*pgd) = pgd_val(*pgd_k);
return;
}
diff -urN -X dontdiff vxfs-2.4.2pre3/arch/arm/mm/fault-common.c markhe-2.4.2pre3/arch/arm/mm/fault-common.c
--- vxfs-2.4.2pre3/arch/arm/mm/fault-common.c Mon Feb 12 10:10:27 2001
+++ markhe-2.4.2pre3/arch/arm/mm/fault-common.c Wed Feb 14 12:12:13 2001
@@ -185,8 +185,6 @@
goto bad_area;

pmd = pmd_offset(pgd, addr);
- if (!pmd_none(*pmd))
- goto bad_area;
set_pmd(pmd, *pmd_k);
return 1;

diff -urN -X dontdiff vxfs-2.4.2pre3/arch/cris/mm/fault.c markhe-2.4.2pre3/arch/cris/mm/fault.c
--- vxfs-2.4.2pre3/arch/cris/mm/fault.c Mon Feb 12 10:10:27 2001
+++ markhe-2.4.2pre3/arch/cris/mm/fault.c Wed Feb 14 12:13:10 2001
@@ -381,7 +381,7 @@
pmd = pmd_offset(pgd, address);
pmd_k = pmd_offset(pgd_k, address);

- if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ if (!pmd_present(*pmd_k))
goto bad_area_nosemaphore;
set_pmd(pmd, *pmd_k);
return;
diff -urN -X dontdiff vxfs-2.4.2pre3/arch/i386/mm/fault.c markhe-2.4.2pre3/arch/i386/mm/fault.c
--- vxfs-2.4.2pre3/arch/i386/mm/fault.c Sun Nov 12 03:01:11 2000
+++ markhe-2.4.2pre3/arch/i386/mm/fault.c Wed Feb 14 12:14:13 2001
@@ -340,7 +340,7 @@
pmd = pmd_offset(pgd, address);
pmd_k = pmd_offset(pgd_k, address);

- if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ if (!pmd_present(*pmd_k))
goto bad_area_nosemaphore;
set_pmd(pmd, *pmd_k);
return;
diff -urN -X dontdiff vxfs-2.4.2pre3/arch/sparc/mm/fault.c markhe-2.4.2pre3/arch/sparc/mm/fault.c
--- vxfs-2.4.2pre3/arch/sparc/mm/fault.c Mon Jan 1 18:37:41 2001
+++ markhe-2.4.2pre3/arch/sparc/mm/fault.c Wed Feb 14 12:17:36 2001
@@ -378,7 +378,7 @@
pmd = pmd_offset(pgd, address);
pmd_k = pmd_offset(pgd_k, address);

- if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ if (!pmd_present(*pmd_k))
goto bad_area_nosemaphore;
pmd_val(*pmd) = pmd_val(*pmd_k);
return;