NetBSD-Bugs archive

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

kern/55664: rump race condition



>Number:         55664
>Category:       kern
>Synopsis:       rump race condition
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Sep 16 22:10:00 +0000 2020
>Originator:     Ruslan Nikolaev
>Release:        master
>Organization:
Virginia Tech
>Environment:
>Description:
While working on the SMP version of rumprun (https://github.com/ssrg-vt/rumprun-smp), we found several issues:

1. A race condition (bug) in sys/rump/librump/rumpkern/intr.c since rumpuser_cv_signal() is called without holding a mutex

2. sleepq is implemented using a single (global) conditional variable; that should be done per each sleepq separately

Below I attach a patch (relocated) for the master branch. Please let us know what you think. We also have some other fixes for rump but they should probably be considered separately.
>How-To-Repeat:

>Fix:
diff --git a/sys/kern/kern_condvar.c b/sys/kern/kern_condvar.c
index cb58236fcb05..460e077b526f 100644
--- a/sys/kern/kern_condvar.c
+++ b/sys/kern/kern_condvar.c
@@ -102,6 +102,7 @@ void
 cv_destroy(kcondvar_t *cv)
 {
 
+	sleepq_destroy(CV_SLEEPQ(cv));
 #ifdef DIAGNOSTIC
 	KASSERT(cv_is_valid(cv));
 	KASSERT(!cv_has_waiters(cv));
diff --git a/sys/rump/include/rump-sys/kern.h b/sys/rump/include/rump-sys/kern.h
index cac0b96d0698..a54012df9d01 100644
--- a/sys/rump/include/rump-sys/kern.h
+++ b/sys/rump/include/rump-sys/kern.h
@@ -174,6 +174,7 @@ void	rump_syscall_boot_establish(const struct rump_onesyscall *, size_t);
 void	rump_schedlock_cv_wait(struct rumpuser_cv *);
 int	rump_schedlock_cv_timedwait(struct rumpuser_cv *,
 				    const struct timespec *);
+void	rump_schedlock_cv_signal(struct cpu_info *, struct rumpuser_cv *);
 
 void	rump_user_schedule(int, void *);
 void	rump_user_unschedule(int, int *, void *);
diff --git a/sys/rump/librump/rumpkern/intr.c b/sys/rump/librump/rumpkern/intr.c
index 651665ff11bb..829154f187b2 100644
--- a/sys/rump/librump/rumpkern/intr.c
+++ b/sys/rump/librump/rumpkern/intr.c
@@ -464,7 +464,7 @@ rump_softint_run(struct cpu_info *ci)
 
 	for (i = 0; i < SOFTINT_COUNT; i++) {
 		if (!TAILQ_EMPTY(&si_lvl[i].si_pending))
-			rumpuser_cv_signal(si_lvl[i].si_cv);
+			rump_schedlock_cv_signal(ci, si_lvl[i].si_cv);
 	}
 }
 
diff --git a/sys/rump/librump/rumpkern/scheduler.c b/sys/rump/librump/rumpkern/scheduler.c
index 8b4a519c8cda..231cacba3ac9 100644
--- a/sys/rump/librump/rumpkern/scheduler.c
+++ b/sys/rump/librump/rumpkern/scheduler.c
@@ -179,6 +179,16 @@ rump_scheduler_init(int numcpu)
 	mutex_init(&unruntime_lock, MUTEX_DEFAULT, IPL_SCHED);
 }
 
+void
+rump_schedlock_cv_signal(struct cpu_info *ci, struct rumpuser_cv *cv)
+{
+	struct rumpcpu *rcpu = cpuinfo_to_rumpcpu(ci);
+
+	rumpuser_mutex_enter_nowrap(rcpu->rcpu_mtx);
+	rumpuser_cv_signal(cv);
+	rumpuser_mutex_exit(rcpu->rcpu_mtx);
+}
+
 /*
  * condvar ops using scheduler lock as the rumpuser interlock.
  */
diff --git a/sys/rump/librump/rumpkern/sleepq.c b/sys/rump/librump/rumpkern/sleepq.c
index 4ce5142226cb..b047a03166b8 100644
--- a/sys/rump/librump/rumpkern/sleepq.c
+++ b/sys/rump/librump/rumpkern/sleepq.c
@@ -40,25 +40,20 @@ __KERNEL_RCSID(0, "$NetBSD: sleepq.c,v 1.20 2020/04/25 15:42:15 bouyer Exp $");
 #include <rump-sys/kern.h>
 
 syncobj_t sleep_syncobj;
-static kcondvar_t sq_cv;
 
-static int
-sqinit1(void)
+void
+sleepq_init(sleepq_t *sq)
 {
 
-	cv_init(&sq_cv, "sleepq");
-
-	return 0;
+	LIST_INIT(sq);
+	cv_init(&sq->sq_cv, "sleepq");
 }
 
 void
-sleepq_init(sleepq_t *sq)
+sleepq_destroy(sleepq_t *sq)
 {
-	static ONCE_DECL(sqctl);
-
-	RUN_ONCE(&sqctl, sqinit1);
 
-	LIST_INIT(sq);
+	cv_destroy(&sq->sq_cv);
 }
 
 void
@@ -83,7 +78,7 @@ sleepq_block(int timo, bool catch)
 
 	while (l->l_wchan) {
 		l->l_mutex = mp; /* keep sleepq lock until woken up */
-		error = cv_timedwait(&sq_cv, mp, timo);
+		error = cv_timedwait(&l->l_sleepq->sq_cv, mp, timo);
 		if (error == EWOULDBLOCK || error == EINTR) {
 			if (l->l_wchan) {
 				LIST_REMOVE(l, l_sleepchain);
@@ -118,7 +113,7 @@ sleepq_wake(sleepq_t *sq, wchan_t wchan, u_int expected, kmutex_t *mp)
 		}
 	}
 	if (found)
-		cv_broadcast(&sq_cv);
+		cv_broadcast(&sq->sq_cv);
 
 	mutex_spin_exit(mp);
 }
@@ -130,7 +125,7 @@ sleepq_unsleep(struct lwp *l, bool cleanup)
 	l->l_wchan = NULL;
 	l->l_wmesg = NULL;
 	LIST_REMOVE(l, l_sleepchain);
-	cv_broadcast(&sq_cv);
+	cv_broadcast(&l->l_sleepq->sq_cv);
 
 	if (cleanup) {
 		mutex_spin_exit(l->l_mutex);
diff --git a/sys/sys/sleepq.h b/sys/sys/sleepq.h
index c721837bb635..c3f502cf0246 100644
--- a/sys/sys/sleepq.h
+++ b/sys/sys/sleepq.h
@@ -45,13 +45,21 @@
  * Generic sleep queues.
  */
 
+#ifdef _RUMPKERNEL
+# include <sys/condvar.h>
+struct sleepq {
+	LIST_HEAD(, lwp);	/* anonymous struct */
+	kcondvar_t sq_cv;
+};
+#else
+LIST_HEAD(sleepq, lwp);
+#endif
+
 #define	SLEEPTAB_HASH_SHIFT	7
 #define	SLEEPTAB_HASH_SIZE	(1 << SLEEPTAB_HASH_SHIFT)
 #define	SLEEPTAB_HASH_MASK	(SLEEPTAB_HASH_SIZE - 1)
 #define	SLEEPTAB_HASH(wchan)	(((uintptr_t)(wchan) >> 8) & SLEEPTAB_HASH_MASK)
 
-LIST_HEAD(sleepq, lwp);
-
 typedef struct sleepq sleepq_t;
 
 typedef struct sleeptab {
@@ -72,6 +80,16 @@ void	sleepq_changepri(lwp_t *, pri_t);
 void	sleepq_lendpri(lwp_t *, pri_t);
 int	sleepq_block(int, bool);
 
+#ifdef _RUMPKERNEL
+void
+sleepq_destroy(sleepq_t *);
+#else
+static inline void
+sleepq_destroy(sleepq_t *sq)
+{
+}
+#endif
+
 void	sleeptab_init(sleeptab_t *);
 
 extern sleeptab_t	sleeptab;



Home | Main Index | Thread Index | Old Index