Blame SOURCES/0101-all-only-request-the-userspace-notification-fd-once.patch

2f610f
From bbf4ae95742662e641e0a456951b912024433435 Mon Sep 17 00:00:00 2001
2f610f
From: Paul Moore <paul@paul-moore.com>
2f610f
Date: Sun, 26 Jul 2020 11:01:49 -0400
2f610f
Subject: [PATCH 101/102] all: only request the userspace notification fd once
2f610f
2f610f
It turns out that requesting the seccomp userspace notifcation fd
2f610f
more than once is a bad thing which causes the kernel to complain
2f610f
(rightfully so for a variety of reasons).  Unfortunately as we were
2f610f
always requesting the notification fd whenever possible this results
2f610f
in problems at filter load time.
2f610f
2f610f
Our solution is to move the notification fd out of the filter context
2f610f
and into the global task context, using a newly created task_state
2f610f
structure.  This allows us to store, and retrieve the notification
2f610f
outside the scope of an individual filter context.  It also provides
2f610f
some implementation improvements by giving us a convenient place to
2f610f
stash all of the API level related support variables.  We also extend
2f610f
the seccomp_reset() API call to reset this internal global state when
2f610f
passed a NULL filter context.
2f610f
2f610f
There is one potential case which we don't currently handle well:
2f610f
threads.  At the moment libseccomp is thread ignorant, and that works
2f610f
well as the only global state up to this point was the currently
2f610f
supported API level information which was common to all threads in a
2f610f
process.  Unfortunately, it appears that the notification fd need not
2f610f
be common to all threads in a process, yet this patch treats it as if
2f610f
it is common.  I suspect this is a very unusual use case so I decided
2f610f
to keep this patch simple and ignore this case, but in the future if
2f610f
we need to support this properly we should be able to do so without
2f610f
API changes by keeping an internal list of notification fds indexed
2f610f
by gettid(2).
2f610f
2f610f
This fixes the GitHub issue below:
2f610f
* https://github.com/seccomp/libseccomp/issues/273
2f610f
2f610f
Reported-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2f610f
Acked-by: Tom Hromatka <tom.hromatka@oracle.com>
2f610f
Signed-off-by: Paul Moore <paul@paul-moore.com>
2f610f
---
2f610f
 doc/man/man3/seccomp_init.3         |  10 +-
2f610f
 doc/man/man3/seccomp_notify_alloc.3 |   3 +-
2f610f
 src/api.c                           |  19 ++-
2f610f
 src/db.c                            |   1 -
2f610f
 src/db.h                            |   3 +-
2f610f
 src/system.c                        | 204 ++++++++++++++++++----------
2f610f
 src/system.h                        |   3 +
2f610f
 tests/11-basic-basic_errors.c       |   9 +-
2f610f
 tests/51-live-user_notification.c   |  21 +++
2f610f
 tests/51-live-user_notification.py  |   4 +
2f610f
 10 files changed, 187 insertions(+), 90 deletions(-)
2f610f
2f610f
diff --git a/doc/man/man3/seccomp_init.3 b/doc/man/man3/seccomp_init.3
2f610f
index 3ab68fe..87520cd 100644
2f610f
--- a/doc/man/man3/seccomp_init.3
2f610f
+++ b/doc/man/man3/seccomp_init.3
2f610f
@@ -36,7 +36,15 @@ The
2f610f
 function releases the existing filter context state before reinitializing it
2f610f
 and can only be called after a call to
2f610f
 .BR seccomp_init ()
2f610f
-has succeeded.
2f610f
+has succeeded.  If
2f610f
+.BR seccomp_reset ()
2f610f
+is called with a NULL filter, it resets the library's global task state;
2f610f
+normally this is not needed, but it may be required to continue using the
2f610f
+library after a
2f610f
+.BR fork ()
2f610f
+or
2f610f
+.BR clone ()
2f610f
+call to ensure the API level and user notification state is properly reset.
2f610f
 .P
2f610f
 When the caller is finished configuring the seccomp filter and has loaded it
2f610f
 into the kernel, the caller should call
2f610f
diff --git a/doc/man/man3/seccomp_notify_alloc.3 b/doc/man/man3/seccomp_notify_alloc.3
2f610f
index 50c8970..cb1c048 100644
2f610f
--- a/doc/man/man3/seccomp_notify_alloc.3
2f610f
+++ b/doc/man/man3/seccomp_notify_alloc.3
2f610f
@@ -59,7 +59,8 @@ returns the notification fd of a filter after it has been loaded.
2f610f
 .\" //////////////////////////////////////////////////////////////////////////
2f610f
 The
2f610f
 .BR seccomp_notify_fd ()
2f610f
-returns the notification fd of the loaded filter.
2f610f
+returns the notification fd of the loaded filter, -1 if a notification fd has
2f610f
+not yet been created, and -EINVAL if the filter context is invalid.
2f610f
 .P
2f610f
 The
2f610f
 .BR seccomp_notify_id_valid ()
2f610f
diff --git a/src/api.c b/src/api.c
2f610f
index 00975ad..5cec088 100644
2f610f
--- a/src/api.c
2f610f
+++ b/src/api.c
2f610f
@@ -301,10 +301,18 @@ API int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action)
2f610f
 {
2f610f
 	struct db_filter_col *col = (struct db_filter_col *)ctx;
2f610f
 
2f610f
-	/* use a NULL filter collection here since we are resetting it */
2f610f
-	if (ctx == NULL || db_col_action_valid(NULL, def_action) < 0)
2f610f
+	/* a NULL filter context indicates we are resetting the global state */
2f610f
+	if (ctx == NULL) {
2f610f
+		/* reset the global state and redetermine the api level */
2f610f
+		sys_reset_state();
2f610f
+		_seccomp_api_update();
2f610f
+		return _rc_filter(0);
2f610f
+	}
2f610f
+	/* ensure the default action is valid */
2f610f
+	if (db_col_action_valid(NULL, def_action) < 0)
2f610f
 		return _rc_filter(-EINVAL);
2f610f
 
2f610f
+	/* reset the filter */
2f610f
 	return _rc_filter(db_col_reset(col, def_action));
2f610f
 }
2f610f
 
2f610f
@@ -675,16 +683,17 @@ API int seccomp_notify_id_valid(int fd, uint64_t id)
2f610f
 /* NOTE - function header comment in include/seccomp.h */
2f610f
 API int seccomp_notify_fd(const scmp_filter_ctx ctx)
2f610f
 {
2f610f
-	struct db_filter_col *col;
2f610f
+	/* NOTE: for historical reasons, and possibly future use, we require a
2f610f
+	 * valid filter context even though we don't actual use it here; the
2f610f
+	 * api update is also not strictly necessary, but keep it for now */
2f610f
 
2f610f
 	/* force a runtime api level detection */
2f610f
 	_seccomp_api_update();
2f610f
 
2f610f
 	if (_ctx_valid(ctx))
2f610f
 		return _rc_filter(-EINVAL);
2f610f
-	col = (struct db_filter_col *)ctx;
2f610f
 
2f610f
-	return _rc_filter(col->notify_fd);
2f610f
+	return _rc_filter(sys_notify_fd());
2f610f
 }
2f610f
 
2f610f
 /* NOTE - function header comment in include/seccomp.h */
2f610f
diff --git a/src/db.c b/src/db.c
2f610f
index 4a87ea3..836171a 100644
2f610f
--- a/src/db.c
2f610f
+++ b/src/db.c
2f610f
@@ -1057,7 +1057,6 @@ int db_col_reset(struct db_filter_col *col, uint32_t def_action)
2f610f
 	if (col->filters)
2f610f
 		free(col->filters);
2f610f
 	col->filters = NULL;
2f610f
-	col->notify_fd = -1;
2f610f
 
2f610f
 	/* set the endianess to undefined */
2f610f
 	col->endian = 0;
2f610f
diff --git a/src/db.h b/src/db.h
2f610f
index b96b104..765c607 100644
2f610f
--- a/src/db.h
2f610f
+++ b/src/db.h
2f610f
@@ -160,8 +160,7 @@ struct db_filter_col {
2f610f
 	/* transaction snapshots */
2f610f
 	struct db_filter_snap *snapshots;
2f610f
 
2f610f
-	/* notification fd that was returned from seccomp() */
2f610f
-	int notify_fd;
2f610f
+	/* userspace notification */
2f610f
 	bool notify_used;
2f610f
 };
2f610f
 
2f610f
diff --git a/src/system.c b/src/system.c
2f610f
index 6cdfc16..3b43b2a 100644
2f610f
--- a/src/system.c
2f610f
+++ b/src/system.c
2f610f
@@ -40,16 +40,61 @@
2f610f
  *       our next release we may have to enable the allowlist */
2f610f
 #define SYSCALL_ALLOWLIST_ENABLE	0
2f610f
 
2f610f
-static int _nr_seccomp = -1;
2f610f
-static int _support_seccomp_syscall = -1;
2f610f
-static int _support_seccomp_flag_tsync = -1;
2f610f
-static int _support_seccomp_flag_log = -1;
2f610f
-static int _support_seccomp_action_log = -1;
2f610f
-static int _support_seccomp_kill_process = -1;
2f610f
-static int _support_seccomp_flag_spec_allow = -1;
2f610f
-static int _support_seccomp_flag_new_listener = -1;
2f610f
-static int _support_seccomp_user_notif = -1;
2f610f
-static int _support_seccomp_flag_tsync_esrch = -1;
2f610f
+/* task global state */
2f610f
+struct task_state {
2f610f
+	/* seccomp(2) syscall */
2f610f
+	int nr_seccomp;
2f610f
+
2f610f
+	/* userspace notification fd */
2f610f
+	int notify_fd;
2f610f
+
2f610f
+	/* runtime support flags */
2f610f
+	int sup_syscall;
2f610f
+	int sup_flag_tsync;
2f610f
+	int sup_flag_log;
2f610f
+	int sup_action_log;
2f610f
+	int sup_kill_process;
2f610f
+	int sup_flag_spec_allow;
2f610f
+	int sup_flag_new_listener;
2f610f
+	int sup_user_notif;
2f610f
+	int sup_flag_tsync_esrch;
2f610f
+};
2f610f
+static struct task_state state = {
2f610f
+	.nr_seccomp = -1,
2f610f
+
2f610f
+	.notify_fd = -1,
2f610f
+
2f610f
+	.sup_syscall = -1,
2f610f
+	.sup_flag_tsync = -1,
2f610f
+	.sup_flag_log = -1,
2f610f
+	.sup_action_log = -1,
2f610f
+	.sup_kill_process = -1,
2f610f
+	.sup_flag_spec_allow = -1,
2f610f
+	.sup_flag_new_listener = -1,
2f610f
+	.sup_user_notif = -1,
2f610f
+	.sup_flag_tsync_esrch = -1,
2f610f
+};
2f610f
+
2f610f
+/**
2f610f
+ * Reset the task state
2f610f
+ *
2f610f
+ * This function fully resets the library's global "system task state".
2f610f
+ *
2f610f
+ */
2f610f
+void sys_reset_state(void)
2f610f
+{
2f610f
+	state.nr_seccomp = -1;
2f610f
+	state.notify_fd = -1;
2f610f
+	state.sup_syscall = -1;
2f610f
+	state.sup_flag_tsync = -1;
2f610f
+	state.sup_flag_log = -1;
2f610f
+	state.sup_action_log = -1;
2f610f
+	state.sup_kill_process = -1;
2f610f
+	state.sup_flag_spec_allow = -1;
2f610f
+	state.sup_flag_new_listener = -1;
2f610f
+	state.sup_user_notif = -1;
2f610f
+	state.sup_flag_tsync_esrch = -1;
2f610f
+}
2f610f
 
2f610f
 /**
2f610f
  * Check to see if the seccomp() syscall is supported
2f610f
@@ -68,8 +113,8 @@ int sys_chk_seccomp_syscall(void)
2f610f
 	/* NOTE: it is reasonably safe to assume that we should be able to call
2f610f
 	 *       seccomp() when the caller first starts, but we can't rely on
2f610f
 	 *       it later so we need to cache our findings for use later */
2f610f
-	if (_support_seccomp_syscall >= 0)
2f610f
-		return _support_seccomp_syscall;
2f610f
+	if (state.sup_syscall >= 0)
2f610f
+		return state.sup_syscall;
2f610f
 
2f610f
 #if SYSCALL_ALLOWLIST_ENABLE
2f610f
 	/* architecture allowlist */
2f610f
@@ -100,11 +145,11 @@ int sys_chk_seccomp_syscall(void)
2f610f
 		goto supported;
2f610f
 
2f610f
 unsupported:
2f610f
-	_support_seccomp_syscall = 0;
2f610f
+	state.sup_syscall = 0;
2f610f
 	return 0;
2f610f
 supported:
2f610f
-	_nr_seccomp = nr_seccomp;
2f610f
-	_support_seccomp_syscall = 1;
2f610f
+	state.nr_seccomp = nr_seccomp;
2f610f
+	state.sup_syscall = 1;
2f610f
 	return 1;
2f610f
 }
2f610f
 
2f610f
@@ -118,7 +163,7 @@ supported:
2f610f
  */
2f610f
 void sys_set_seccomp_syscall(bool enable)
2f610f
 {
2f610f
-	_support_seccomp_syscall = (enable ? 1 : 0);
2f610f
+	state.sup_syscall = (enable ? 1 : 0);
2f610f
 }
2f610f
 
2f610f
 /**
2f610f
@@ -132,16 +177,16 @@ void sys_set_seccomp_syscall(bool enable)
2f610f
 int sys_chk_seccomp_action(uint32_t action)
2f610f
 {
2f610f
 	if (action == SCMP_ACT_KILL_PROCESS) {
2f610f
-		if (_support_seccomp_kill_process < 0) {
2f610f
+		if (state.sup_kill_process < 0) {
2f610f
 			if (sys_chk_seccomp_syscall() == 1 &&
2f610f
-			    syscall(_nr_seccomp, SECCOMP_GET_ACTION_AVAIL, 0,
2f610f
-				    &action) == 0)
2f610f
-				_support_seccomp_kill_process = 1;
2f610f
+			    syscall(state.nr_seccomp,
2f610f
+				    SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0)
2f610f
+				state.sup_kill_process = 1;
2f610f
 			else
2f610f
-				_support_seccomp_kill_process = 0;
2f610f
+				state.sup_kill_process = 0;
2f610f
 		}
2f610f
 
2f610f
-		return _support_seccomp_kill_process;
2f610f
+		return state.sup_kill_process;
2f610f
 	} else if (action == SCMP_ACT_KILL_THREAD) {
2f610f
 		return 1;
2f610f
 	} else if (action == SCMP_ACT_TRAP) {
2f610f
@@ -152,30 +197,30 @@ int sys_chk_seccomp_action(uint32_t action)
2f610f
 	} else if (action == SCMP_ACT_TRACE(action & 0x0000ffff)) {
2f610f
 		return 1;
2f610f
 	} else if (action == SCMP_ACT_LOG) {
2f610f
-		if (_support_seccomp_action_log < 0) {
2f610f
+		if (state.sup_action_log < 0) {
2f610f
 			if (sys_chk_seccomp_syscall() == 1 &&
2f610f
-			    syscall(_nr_seccomp, SECCOMP_GET_ACTION_AVAIL, 0,
2f610f
-				    &action) == 0)
2f610f
-				_support_seccomp_action_log = 1;
2f610f
+			    syscall(state.nr_seccomp,
2f610f
+				    SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0)
2f610f
+				state.sup_action_log = 1;
2f610f
 			else
2f610f
-				_support_seccomp_action_log = 0;
2f610f
+				state.sup_action_log = 0;
2f610f
 		}
2f610f
 
2f610f
-		return _support_seccomp_action_log;
2f610f
+		return state.sup_action_log;
2f610f
 	} else if (action == SCMP_ACT_ALLOW) {
2f610f
 		return 1;
2f610f
 	} else if (action == SCMP_ACT_NOTIFY) {
2f610f
-		if (_support_seccomp_user_notif < 0) {
2f610f
+		if (state.sup_user_notif < 0) {
2f610f
 			struct seccomp_notif_sizes sizes;
2f610f
 			if (sys_chk_seccomp_syscall() == 1 &&
2f610f
-			    syscall(_nr_seccomp, SECCOMP_GET_NOTIF_SIZES, 0,
2f610f
-				    &sizes) == 0)
2f610f
-				_support_seccomp_user_notif = 1;
2f610f
+			    syscall(state.nr_seccomp,
2f610f
+				    SECCOMP_GET_NOTIF_SIZES, 0, &sizes) == 0)
2f610f
+				state.sup_user_notif = 1;
2f610f
 			else
2f610f
-				_support_seccomp_user_notif = 0;
2f610f
+				state.sup_user_notif = 0;
2f610f
 		}
2f610f
 
2f610f
-		return _support_seccomp_user_notif;
2f610f
+		return state.sup_user_notif;
2f610f
 	}
2f610f
 
2f610f
 	return 0;
2f610f
@@ -193,13 +238,13 @@ void sys_set_seccomp_action(uint32_t action, bool enable)
2f610f
 {
2f610f
 	switch (action) {
2f610f
 	case SCMP_ACT_LOG:
2f610f
-		_support_seccomp_action_log = (enable ? 1 : 0);
2f610f
+		state.sup_action_log = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SCMP_ACT_KILL_PROCESS:
2f610f
-		_support_seccomp_kill_process = (enable ? 1 : 0);
2f610f
+		state.sup_kill_process = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SCMP_ACT_NOTIFY:
2f610f
-		_support_seccomp_user_notif = (enable ? 1 : 0);
2f610f
+		state.sup_user_notif = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	}
2f610f
 }
2f610f
@@ -212,13 +257,14 @@ void sys_set_seccomp_action(uint32_t action, bool enable)
2f610f
  * Return one if the flag is supported, zero otherwise.
2f610f
  *
2f610f
  */
2f610f
-static int _sys_chk_seccomp_flag_kernel(int flag)
2f610f
+static int _sys_chk_flag_kernel(int flag)
2f610f
 {
2f610f
 	/* this is an invalid seccomp(2) call because the last argument
2f610f
 	 * is NULL, but depending on the errno value of EFAULT we can
2f610f
 	 * guess if the filter flag is supported or not */
2f610f
 	if (sys_chk_seccomp_syscall() == 1 &&
2f610f
-	    syscall(_nr_seccomp, SECCOMP_SET_MODE_FILTER, flag, NULL) == -1 &&
2f610f
+	    syscall(state.nr_seccomp,
2f610f
+		    SECCOMP_SET_MODE_FILTER, flag, NULL) == -1 &&
2f610f
 	    errno == EFAULT)
2f610f
 		return 1;
2f610f
 
2f610f
@@ -238,29 +284,25 @@ int sys_chk_seccomp_flag(int flag)
2f610f
 {
2f610f
 	switch (flag) {
2f610f
 	case SECCOMP_FILTER_FLAG_TSYNC:
2f610f
-		if (_support_seccomp_flag_tsync < 0)
2f610f
-			_support_seccomp_flag_tsync = _sys_chk_seccomp_flag_kernel(flag);
2f610f
-
2f610f
-		return _support_seccomp_flag_tsync;
2f610f
+		if (state.sup_flag_tsync < 0)
2f610f
+			state.sup_flag_tsync = _sys_chk_flag_kernel(flag);
2f610f
+		return state.sup_flag_tsync;
2f610f
 	case SECCOMP_FILTER_FLAG_LOG:
2f610f
-		if (_support_seccomp_flag_log < 0)
2f610f
-			_support_seccomp_flag_log = _sys_chk_seccomp_flag_kernel(flag);
2f610f
-
2f610f
-		return _support_seccomp_flag_log;
2f610f
+		if (state.sup_flag_log < 0)
2f610f
+			state.sup_flag_log = _sys_chk_flag_kernel(flag);
2f610f
+		return state.sup_flag_log;
2f610f
 	case SECCOMP_FILTER_FLAG_SPEC_ALLOW:
2f610f
-		if (_support_seccomp_flag_spec_allow < 0)
2f610f
-			_support_seccomp_flag_spec_allow = _sys_chk_seccomp_flag_kernel(flag);
2f610f
-
2f610f
-		return _support_seccomp_flag_spec_allow;
2f610f
+		if (state.sup_flag_spec_allow < 0)
2f610f
+			state.sup_flag_spec_allow = _sys_chk_flag_kernel(flag);
2f610f
+		return state.sup_flag_spec_allow;
2f610f
 	case SECCOMP_FILTER_FLAG_NEW_LISTENER:
2f610f
-		if (_support_seccomp_flag_new_listener < 0)
2f610f
-			_support_seccomp_flag_new_listener = _sys_chk_seccomp_flag_kernel(flag);
2f610f
-
2f610f
-		return _support_seccomp_flag_new_listener;
2f610f
+		if (state.sup_flag_new_listener < 0)
2f610f
+			state.sup_flag_new_listener = _sys_chk_flag_kernel(flag);
2f610f
+		return state.sup_flag_new_listener;
2f610f
 	case SECCOMP_FILTER_FLAG_TSYNC_ESRCH:
2f610f
-		if (_support_seccomp_flag_tsync_esrch < 0)
2f610f
-			_support_seccomp_flag_tsync_esrch = _sys_chk_seccomp_flag_kernel(flag);
2f610f
-		return _support_seccomp_flag_tsync_esrch;
2f610f
+		if (state.sup_flag_tsync_esrch < 0)
2f610f
+			state.sup_flag_tsync_esrch = _sys_chk_flag_kernel(flag);
2f610f
+		return state.sup_flag_tsync_esrch;
2f610f
 	}
2f610f
 
2f610f
 	return -EOPNOTSUPP;
2f610f
@@ -279,19 +321,19 @@ void sys_set_seccomp_flag(int flag, bool enable)
2f610f
 {
2f610f
 	switch (flag) {
2f610f
 	case SECCOMP_FILTER_FLAG_TSYNC:
2f610f
-		_support_seccomp_flag_tsync = (enable ? 1 : 0);
2f610f
+		state.sup_flag_tsync = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SECCOMP_FILTER_FLAG_LOG:
2f610f
-		_support_seccomp_flag_log = (enable ? 1 : 0);
2f610f
+		state.sup_flag_log = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SECCOMP_FILTER_FLAG_SPEC_ALLOW:
2f610f
-		_support_seccomp_flag_spec_allow = (enable ? 1 : 0);
2f610f
+		state.sup_flag_spec_allow = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SECCOMP_FILTER_FLAG_NEW_LISTENER:
2f610f
-		_support_seccomp_flag_new_listener = (enable ? 1 : 0);
2f610f
+		state.sup_flag_new_listener = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	case SECCOMP_FILTER_FLAG_TSYNC_ESRCH:
2f610f
-		_support_seccomp_flag_tsync_esrch = (enable ? 1 : 0);
2f610f
+		state.sup_flag_tsync_esrch = (enable ? 1 : 0);
2f610f
 		break;
2f610f
 	}
2f610f
 }
2f610f
@@ -324,7 +366,7 @@ int sys_filter_load(struct db_filter_col *col, bool rawrc)
2f610f
 			goto filter_load_out;
2f610f
 	}
2f610f
 
2f610f
-	tsync_notify = (_support_seccomp_flag_tsync_esrch > 0);
2f610f
+	tsync_notify = state.sup_flag_tsync_esrch > 0 && state.notify_fd == -1;
2f610f
 
2f610f
 	/* load the filter into the kernel */
2f610f
 	if (sys_chk_seccomp_syscall() == 1) {
2f610f
@@ -333,28 +375,29 @@ int sys_filter_load(struct db_filter_col *col, bool rawrc)
2f610f
 			if (col->attr.tsync_enable)
2f610f
 				flgs |= SECCOMP_FILTER_FLAG_TSYNC | \
2f610f
 					SECCOMP_FILTER_FLAG_TSYNC_ESRCH;
2f610f
-			if (_support_seccomp_user_notif > 0)
2f610f
+			if (state.sup_user_notif > 0)
2f610f
 				flgs |= SECCOMP_FILTER_FLAG_NEW_LISTENER;
2f610f
 		} else if (col->attr.tsync_enable)
2f610f
 			flgs |= SECCOMP_FILTER_FLAG_TSYNC;
2f610f
-		else if (_support_seccomp_user_notif > 0)
2f610f
+		else if (state.sup_user_notif > 0 && state.notify_fd == -1)
2f610f
 			flgs |= SECCOMP_FILTER_FLAG_NEW_LISTENER;
2f610f
 		if (col->attr.log_enable)
2f610f
 			flgs |= SECCOMP_FILTER_FLAG_LOG;
2f610f
 		if (col->attr.spec_allow)
2f610f
 			flgs |= SECCOMP_FILTER_FLAG_SPEC_ALLOW;
2f610f
-		rc = syscall(_nr_seccomp, SECCOMP_SET_MODE_FILTER, flgs, prgm);
2f610f
+		rc = syscall(state.nr_seccomp,
2f610f
+			     SECCOMP_SET_MODE_FILTER, flgs, prgm);
2f610f
 		if (tsync_notify && rc > 0) {
2f610f
 			/* return 0 on NEW_LISTENER success, but save the fd */
2f610f
-			col->notify_fd = rc;
2f610f
+			state.notify_fd = rc;
2f610f
 			rc = 0;
2f610f
 		} else if (rc > 0 && col->attr.tsync_enable) {
2f610f
 			/* always return -ESRCH if we fail to sync threads */
2f610f
 			errno = ESRCH;
2f610f
 			rc = -errno;
2f610f
-		} else if (rc > 0 && _support_seccomp_user_notif > 0) {
2f610f
+		} else if (rc > 0 && state.sup_user_notif > 0) {
2f610f
 			/* return 0 on NEW_LISTENER success, but save the fd */
2f610f
-			col->notify_fd = rc;
2f610f
+			state.notify_fd = rc;
2f610f
 			rc = 0;
2f610f
 		}
2f610f
 	} else
2f610f
@@ -370,6 +413,19 @@ filter_load_out:
2f610f
 	return rc;
2f610f
 }
2f610f
 
2f610f
+/**
2f610f
+ * Return the userspace notification fd
2f610f
+ *
2f610f
+ * This function returns the userspace notification fd from
2f610f
+ * SECCOMP_FILTER_FLAG_NEW_LISTENER.  If the notification fd has not yet been
2f610f
+ * set, or an error has occurred, -1 is returned.
2f610f
+ *
2f610f
+ */
2f610f
+int sys_notify_fd(void)
2f610f
+{
2f610f
+	return state.notify_fd;
2f610f
+}
2f610f
+
2f610f
 /**
2f610f
  * Allocate a pair of notification request/response structures
2f610f
  * @param req the request location
2f610f
@@ -386,7 +442,7 @@ int sys_notify_alloc(struct seccomp_notif **req,
2f610f
 	int rc;
2f610f
 	static struct seccomp_notif_sizes sizes = { 0, 0, 0 };
2f610f
 
2f610f
-	if (_support_seccomp_syscall <= 0)
2f610f
+	if (state.sup_syscall <= 0)
2f610f
 		return -EOPNOTSUPP;
2f610f
 
2f610f
 	if (sizes.seccomp_notif == 0 && sizes.seccomp_notif_resp == 0) {
2f610f
@@ -427,7 +483,7 @@ int sys_notify_alloc(struct seccomp_notif **req,
2f610f
  */
2f610f
 int sys_notify_receive(int fd, struct seccomp_notif *req)
2f610f
 {
2f610f
-	if (_support_seccomp_user_notif <= 0)
2f610f
+	if (state.sup_user_notif <= 0)
2f610f
 		return -EOPNOTSUPP;
2f610f
 
2f610f
 	if (ioctl(fd, SECCOMP_IOCTL_NOTIF_RECV, req) < 0)
2f610f
@@ -448,7 +504,7 @@ int sys_notify_receive(int fd, struct seccomp_notif *req)
2f610f
  */
2f610f
 int sys_notify_respond(int fd, struct seccomp_notif_resp *resp)
2f610f
 {
2f610f
-	if (_support_seccomp_user_notif <= 0)
2f610f
+	if (state.sup_user_notif <= 0)
2f610f
 		return -EOPNOTSUPP;
2f610f
 
2f610f
 	if (ioctl(fd, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0)
2f610f
@@ -467,7 +523,7 @@ int sys_notify_respond(int fd, struct seccomp_notif_resp *resp)
2f610f
  */
2f610f
 int sys_notify_id_valid(int fd, uint64_t id)
2f610f
 {
2f610f
-	if (_support_seccomp_user_notif <= 0)
2f610f
+	if (state.sup_user_notif <= 0)
2f610f
 		return -EOPNOTSUPP;
2f610f
 
2f610f
 	if (ioctl(fd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) < 0)
2f610f
diff --git a/src/system.h b/src/system.h
2f610f
index 133f9b1..096f3ca 100644
2f610f
--- a/src/system.h
2f610f
+++ b/src/system.h
2f610f
@@ -182,6 +182,8 @@ struct seccomp_notif_resp {
2f610f
 #define SECCOMP_IOCTL_NOTIF_ID_VALID    SECCOMP_IOR(2, __u64)
2f610f
 #endif /* SECCOMP_RET_USER_NOTIF */
2f610f
 
2f610f
+void sys_reset_state(void);
2f610f
+
2f610f
 int sys_chk_seccomp_syscall(void);
2f610f
 void sys_set_seccomp_syscall(bool enable);
2f610f
 
2f610f
@@ -193,6 +195,7 @@ void sys_set_seccomp_flag(int flag, bool enable);
2f610f
 
2f610f
 int sys_filter_load(struct db_filter_col *col, bool rawrc);
2f610f
 
2f610f
+int sys_notify_fd(void);
2f610f
 int sys_notify_alloc(struct seccomp_notif **req,
2f610f
 		     struct seccomp_notif_resp **resp);
2f610f
 int sys_notify_receive(int fd, struct seccomp_notif *req);
2f610f
diff --git a/tests/11-basic-basic_errors.c b/tests/11-basic-basic_errors.c
2f610f
index d3b2256..da059df 100644
2f610f
--- a/tests/11-basic-basic_errors.c
2f610f
+++ b/tests/11-basic-basic_errors.c
2f610f
@@ -41,12 +41,9 @@ int main(int argc, char *argv[])
2f610f
 	seccomp_release(ctx);
2f610f
 	ctx = NULL;
2f610f
 
2f610f
-	/* seccomp_reset error */
2f610f
-	rc = seccomp_reset(ctx, SCMP_ACT_KILL + 1);
2f610f
-	if (rc != -EINVAL)
2f610f
-		return -1;
2f610f
-	rc = seccomp_reset(ctx, SCMP_ACT_KILL);
2f610f
-	if (rc != -EINVAL)
2f610f
+	/* ensure that seccomp_reset(NULL, ...) is accepted */
2f610f
+	rc = seccomp_reset(NULL, SCMP_ACT_ALLOW);
2f610f
+	if (rc != 0)
2f610f
 		return -1;
2f610f
 
2f610f
 	/* seccomp_load error */
2f610f
diff --git a/tests/51-live-user_notification.c b/tests/51-live-user_notification.c
2f610f
index de31d2f..a00627b 100644
2f610f
--- a/tests/51-live-user_notification.c
2f610f
+++ b/tests/51-live-user_notification.c
2f610f
@@ -98,6 +98,27 @@ int main(int argc, char *argv[])
2f610f
 		goto out;
2f610f
 	}
2f610f
 
2f610f
+	rc = seccomp_reset(ctx, SCMP_ACT_ALLOW);
2f610f
+	if (rc < 0)
2f610f
+		goto out;
2f610f
+
2f610f
+	rc = seccomp_rule_add(ctx, SCMP_ACT_NOTIFY, SCMP_SYS(getppid), 0, NULL);
2f610f
+	if (rc)
2f610f
+		goto out;
2f610f
+
2f610f
+	rc  = seccomp_load(ctx);
2f610f
+	if (rc < 0)
2f610f
+		goto out;
2f610f
+
2f610f
+	rc = seccomp_notify_fd(ctx);
2f610f
+	if (rc < 0)
2f610f
+		goto out;
2f610f
+	if (rc != fd) {
2f610f
+		rc = -EFAULT;
2f610f
+		goto out;
2f610f
+	} else
2f610f
+		rc = 0;
2f610f
+
2f610f
 out:
2f610f
 	if (fd >= 0)
2f610f
 		close(fd);
2f610f
diff --git a/tests/51-live-user_notification.py b/tests/51-live-user_notification.py
2f610f
index 0d81f5e..3449c44 100755
2f610f
--- a/tests/51-live-user_notification.py
2f610f
+++ b/tests/51-live-user_notification.py
2f610f
@@ -52,6 +52,10 @@ def test():
2f610f
             raise RuntimeError("Child process error")
2f610f
         if os.WEXITSTATUS(rc) != 0:
2f610f
             raise RuntimeError("Child process error")
2f610f
+        f.reset(ALLOW)
2f610f
+        f.add_rule(NOTIFY, "getppid")
2f610f
+        f.load()
2f610f
+        # no easy way to check the notification fd here
2f610f
         quit(160)
2f610f
 
2f610f
 test()
2f610f
-- 
2f610f
2.26.2
2f610f