tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

netbsd32: race condition in swapctl()



Hi,
the 32bit version of swapctl() calls uvm_swap_stats() without locking
'swap_syscall_lock'. Which means that if you perform a swapctl() call
and at the same time update/add/delete a swap device, you may end up
with a memory corruption.

Here is a patch. Tested on amd64 (with a 32bit binary).

Ok?

Index: compat/netbsd32/netbsd32_netbsd.c
===================================================================
RCS file: /cvsroot/src/sys/compat/netbsd32/netbsd32_netbsd.c,v
retrieving revision 1.195
diff -u -r1.195 netbsd32_netbsd.c
--- compat/netbsd32/netbsd32_netbsd.c	16 Jun 2015 10:42:38 -0000	1.195
+++ compat/netbsd32/netbsd32_netbsd.c	21 Jun 2015 09:13:03 -0000
@@ -1747,11 +1747,16 @@

 	if (count < 0)
 		return EINVAL;
-	if (count == 0 || uvmexp.nswapdev == 0)
-		return 0;
-	/* Make sure userland cannot exhaust kernel memory */
+
+	swapsys_lock(RW_WRITER);
+
 	if ((size_t)count > (size_t)uvmexp.nswapdev)
 		count = uvmexp.nswapdev;
+	if (count == 0) {
+		/* No swap device */
+		swapsys_unlock();
+		return 0;
+	}

 	ksep_len = sizeof(*ksep) * count;
 	ksep = kmem_alloc(ksep_len, KM_SLEEP);
@@ -1760,6 +1765,8 @@
 	uvm_swap_stats(SWAP_STATS, ksep, count, retval);
 	count = *retval;

+	swapsys_unlock();
+
 	for (i = 0; i < count; i++) {
 		se32.se_dev = ksep[i].se_dev;
 		se32.se_flags = ksep[i].se_flags;
Index: uvm/uvm_swap.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.c,v
retrieving revision 1.172
diff -u -r1.172 uvm_swap.c
--- uvm/uvm_swap.c	25 Jul 2014 08:10:40 -0000	1.172
+++ uvm/uvm_swap.c	21 Jun 2015 09:13:03 -0000
@@ -430,6 +430,15 @@
 	return NULL;
 }

+void swapsys_lock(krw_t op)
+{
+	rw_enter(&swap_syscall_lock, op);
+}
+
+void swapsys_unlock(void)
+{
+	rw_exit(&swap_syscall_lock);
+}

 /*
  * sys_swapctl: main entry point for swapctl(2) system call
@@ -741,6 +750,8 @@
 	struct swapdev *sdp;
 	int count = 0;

+	KASSERT(rw_lock_held(&swap_syscall_lock));
+
 	LIST_FOREACH(spp, &swap_priority, spi_swappri) {
 		TAILQ_FOREACH(sdp, &spp->spi_swapdev, swd_next) {
 			int inuse;
Index: uvm/uvm_swap.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_swap.h,v
retrieving revision 1.20
diff -u -r1.20 uvm_swap.h
--- uvm/uvm_swap.h	3 Feb 2014 13:20:21 -0000	1.20
+++ uvm/uvm_swap.h	21 Jun 2015 09:13:03 -0000
@@ -48,7 +48,10 @@
 void	uvm_swap_free(int, int);
 void	uvm_swap_markbad(int, int);
 bool	uvm_swapisfull(void);
+void	swapsys_lock(krw_t);
+void	swapsys_unlock(void);
 void	uvm_swap_stats(int, struct swapent *, int, register_t *);
+
 #else /* defined(VMSWAP) */
 #define	uvm_swapisfull()	true
 #define uvm_swap_stats(c, sep, count, retval) { *retval = 0; }


Home | Main Index | Thread Index | Old Index