[igt-dev] [PATCH] lib/igt_dummyload: Use timerfd rather than SIGEV_THREAD

Michał Winiarski michal at hardline.pl
Tue Mar 31 11:19:11 UTC 2020


From: Michał Winiarski <michal.winiarski at intel.com>

Since timer_delete doesn't give us any guarantees that the thread and
its notify_function isn't currently running, we can hit a use-after-free
in a race condition scenario.
This causes a seemingly random segfault when igt_spin_end from notify
thread is called after igt_spin_free was already called from the main
thread.
Let's fix that by using timerfd and managing the timer thread ourselves.

Signed-off-by: Michał Winiarski <michal.winiarski at intel.com>
Cc: Chris Wilson <chris at chris-wilson.co.uk>
---
 lib/igt_dummyload.c | 42 +++++++++++++++++++++++++-----------------
 lib/igt_dummyload.h |  3 ++-
 2 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index 657c3c64..694d907d 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -26,6 +26,7 @@
 #include <signal.h>
 #include <pthread.h>
 #include <sys/poll.h>
+#include <sys/timerfd.h>
 
 #include <i915_drm.h>
 
@@ -394,11 +395,20 @@ igt_spin_factory(int fd, const struct igt_spin_factory *opts)
 	return spin;
 }
 
-static void notify(union sigval arg)
+static void *timer_thread(void *data)
 {
-	igt_spin_t *spin = arg.sival_ptr;
+	igt_spin_t *spin = data;
+	struct pollfd pfd;
+	int ret;
 
-	igt_spin_end(spin);
+	pfd.fd = spin->timerfd;
+	pfd.events = POLLIN;
+
+	ret = poll(&pfd, 1, -1);
+	if (ret >= 0)
+		igt_spin_end(spin);
+
+	return NULL;
 }
 
 /**
@@ -412,29 +422,24 @@ static void notify(union sigval arg)
  */
 void igt_spin_set_timeout(igt_spin_t *spin, int64_t ns)
 {
-	timer_t timer;
-	struct sigevent sev;
 	struct itimerspec its;
+	int timerfd;
 
 	igt_assert(ns > 0);
 	if (!spin)
 		return;
 
-	igt_assert(!spin->timer);
+	igt_assert(!spin->timerfd);
+	timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
+	igt_assert(timerfd >= 0);
+	spin->timerfd = timerfd;
 
-	memset(&sev, 0, sizeof(sev));
-	sev.sigev_notify = SIGEV_THREAD;
-	sev.sigev_value.sival_ptr = spin;
-	sev.sigev_notify_function = notify;
-	igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
-	igt_assert(timer);
+	pthread_create(&spin->timer_thread, NULL, timer_thread, spin);
 
 	memset(&its, 0, sizeof(its));
 	its.it_value.tv_sec = ns / NSEC_PER_SEC;
 	its.it_value.tv_nsec = ns % NSEC_PER_SEC;
-	igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
-
-	spin->timer = timer;
+	igt_assert(timerfd_settime(timerfd, 0, &its, NULL) == 0);
 }
 
 /**
@@ -484,8 +489,11 @@ void igt_spin_free(int fd, igt_spin_t *spin)
 	igt_list_del(&spin->link);
 	pthread_mutex_unlock(&list_lock);
 
-	if (spin->timer)
-		timer_delete(spin->timer);
+	if (spin->timerfd) {
+		pthread_cancel(spin->timer_thread);
+		igt_assert(pthread_join(spin->timer_thread, NULL) == 0);
+		close(spin->timerfd);
+	}
 
 	igt_spin_end(spin);
 	gem_munmap((void *)((unsigned long)spin->condition & (~4095UL)),
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index cb696009..dfba123c 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -34,7 +34,8 @@
 
 typedef struct igt_spin {
 	unsigned int handle;
-	timer_t timer;
+	int timerfd;
+	pthread_t timer_thread;
 	struct igt_list_head link;
 
 	uint32_t *condition;
-- 
2.26.0



More information about the igt-dev mailing list