Skip to contentSkip to conversation

signal: Adjust error codes according to restore_user_sigmask()

PATCH Superseded Deepa Dinamani 10 participants 123 messages May 22, 2019 – Jun 13, 2019

Subsystems (24)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 signal: Adjust error codes according to restore_user_sigmask()

A regression caused by 854a6ed56839 ("signal: Add restore_user_sigmask()") caused users of epoll_pwait, io_pgetevents, and ppoll to notice a latent problem in signal handling during these syscalls.

That patch (854a6ed56839) moved the signal_pending() check closer to restoring of the user sigmask. But, it failed to update the error code accordingly. From the userspace perspective, the patch increased the time window for the signal discovery and subsequent delivery to the userspace, but did not always adjust the errno afterwards. The behavior before 854a6ed56839a was that the signals were dropped after the error code was decided. This resulted in lost signals but the userspace did not notice it as the syscalls had finished executing the core functionality and the error codes returned notified success.

For all the syscalls that receive a sigmask from the userland, the user sigmask is to be in effect through the syscall execution. At the end of syscall, sigmask of the current process is restored to what it was before the switch over to user sigmask. But, for this to be true in practice, the sigmask should be restored only at the the point we change the saved_sigmask. Anything before that loses signals. And, anything after is just pointless as the signal is already lost by restoring the sigmask.

Detailed issue discussion permalink:
https://lore.kernel.org/linux-fsdevel/20190427093319.sgicqik2oqkez3wk@dcvr/

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")
Cc <[email protected]> # 5.0.x
Cc <[email protected]> # 5.1.x

---
Changes since v1:
* updated the commit text for more context of the pre-existing condition
* added stable tags as requested

 fs/aio.c               | 24 ++++++++++++------------
 fs/eventpoll.c         | 14 ++++++++++----
 fs/io_uring.c          |  7 +++++--
 fs/select.c            | 37 +++++++++++++++++++++----------------
 include/linux/signal.h |  2 +-
 kernel/signal.c        | 13 ++++++++++---
 6 files changed, 59 insertions(+), 38 deletions(-)
fs/aio.c +12 -12
index 3490d1fa0e16..ebd2b1980161 100644
@@ -2095,7 +2095,7 @@ SYSCALL_DEFINE6(io_pgetevents,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
-	int ret;
+	int ret, signal_detected;
 
 	if (timeout && unlikely(get_timespec64(&ts, timeout)))
 		return -EFAULT;
@@ -2108,8 +2108,8 @@ SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+	signal_detected = restore_user_sigmask(ksig.sigmask, &sigsaved);
+	if (signal_detected && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2128,7 +2128,7 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
-	int ret;
+	int ret, signal_detected;
 
 	if (timeout && unlikely(get_old_timespec32(&ts, timeout)))
 		return -EFAULT;
@@ -2142,8 +2142,8 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+	signal_detected = restore_user_sigmask(ksig.sigmask, &sigsaved);
+	if (signal_detected && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2193,7 +2193,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
-	int ret;
+	int ret, signal_detected;
 
 	if (timeout && get_old_timespec32(&t, timeout))
 		return -EFAULT;
@@ -2206,8 +2206,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+	signal_detected = restore_user_sigmask(ksig.sigmask, &sigsaved);
+	if (signal_detected && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2226,7 +2226,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
-	int ret;
+	int ret, signal_detected;
 
 	if (timeout && get_timespec64(&t, timeout))
 		return -EFAULT;
@@ -2239,8 +2239,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+	signal_detected = restore_user_sigmask(ksig.sigmask, &sigsaved);
+	if (signal_detected && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
fs/eventpoll.c +10 -4
index 4a0e98d87fcc..fe5a0724b417 100644
@@ -2317,7 +2317,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		int, maxevents, int, timeout, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	int error;
+	int error, signal_detected;
 	sigset_t ksigmask, sigsaved;
 
 	/*
@@ -2330,7 +2330,10 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
+
+	if (signal_detected && !error)
+		error = -EINTR;
 
 	return error;
 }
@@ -2342,7 +2345,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 			const compat_sigset_t __user *, sigmask,
 			compat_size_t, sigsetsize)
 {
-	long err;
+	long err, signal_detected;
 	sigset_t ksigmask, sigsaved;
 
 	/*
@@ -2355,7 +2358,10 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
+
+	if (signal_detected && !err)
+		err = -EINTR;
 
 	return err;
 }
fs/io_uring.c +5 -2
index e11d77181398..b785c8d7efc4 100644
@@ -2205,8 +2205,11 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 	if (ret == -ERESTARTSYS)
 		ret = -EINTR;
 
-	if (sig)
-		restore_user_sigmask(sig, &sigsaved);
+	if (sig) {
+		signal_detected = restore_user_sigmask(sig, &sigsaved);
+		if (signal_detected && !ret)
+			ret  = -EINTR;
+	}
 
 	return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }
fs/select.c +21 -16
index 6cbc9ff56ba0..da9cfea35159 100644
@@ -732,7 +732,7 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		switch (type) {
@@ -760,7 +760,9 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 	ret = core_sys_select(n, inp, outp, exp, to);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
+	if (signal_detected && !ret)
+		ret = -EINTR;
 
 	return ret;
 }
@@ -1089,7 +1091,7 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		if (get_timespec64(&ts, tsp))
@@ -1106,10 +1108,10 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
 
 	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
+	if (ret == -EINTR || (signal_detected && !ret))
 		ret = -ERESTARTNOHAND;
 
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
@@ -1125,7 +1127,7 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		if (get_old_timespec32(&ts, tsp))
@@ -1142,10 +1144,10 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
 
 	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
+	if (ret == -EINTR || (signal_detected && !ret))
 		ret = -ERESTARTNOHAND;
 
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
@@ -1324,7 +1326,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		switch (type) {
@@ -1352,7 +1354,10 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
+
+	if (signal_detected && !ret)
+		ret = -EINTR;
 
 	return ret;
 }
@@ -1408,7 +1413,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		if (get_old_timespec32(&ts, tsp))
@@ -1425,10 +1430,10 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
 
 	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
+	if (ret == -EINTR || (signal_detected && !ret))
 		ret = -ERESTARTNOHAND;
 
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
@@ -1444,7 +1449,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 {
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
-	int ret;
+	int ret, signal_detected;
 
 	if (tsp) {
 		if (get_timespec64(&ts, tsp))
@@ -1461,10 +1466,10 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	signal_detected = restore_user_sigmask(sigmask, &sigsaved);
 
 	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
+	if (ret == -EINTR || (signal_detected && !ret))
 		ret = -ERESTARTNOHAND;
 
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
include/linux/signal.h +1 -1
index 9702016734b1..1d36e8629edf 100644
@@ -275,7 +275,7 @@ extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struc
 extern int sigprocmask(int, sigset_t *, sigset_t *);
 extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
 	sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
+extern int restore_user_sigmask(const void __user *usigmask,
 				 sigset_t *sigsaved);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
kernel/signal.c +10 -3
index 1c86b78a7597..7cc33d23ee4b 100644
@@ -2916,15 +2916,21 @@ EXPORT_SYMBOL(set_compat_user_sigmask);
  * usigmask: sigmask passed in from userland.
  * sigsaved: saved sigmask when the syscall started and changed the sigmask to
  *           usigmask.
+ * returns 1 in case a pending signal is detected.
+ *
+ * Users of the api need to adjust their return values based on whether the
+ * signal was detected here. If a signal is detected, it is delivered to the
+ * userspace. So without an error like -ETINR, userspace might fail to
+ * adjust the flow of execution.
  *
  * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
  * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
  */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
+int restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
 {
 
 	if (!usigmask)
-		return;
+		return 0;
 	/*
 	 * When signals are pending, do not restore them here.
 	 * Restoring sigmask here can lead to delivering signals that the above
@@ -2933,7 +2939,7 @@ void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
 	if (signal_pending(current)) {
 		current->saved_sigmask = *sigsaved;
 		set_restore_sigmask();
-		return;
+		return 1;
 	}
 
 	/*
@@ -2941,6 +2947,7 @@ void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
 	 * saved_sigmask when signals are not pending.
 	 */
 	set_current_blocked(sigsaved);
+	return 0;
 }
 EXPORT_SYMBOL(restore_user_sigmask);
 
Signature · 1 line
2.17.1
maintainer
#1 lore raw
Quoted 5 lines · Deepa Dinamani

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean.

restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending).

Oleg.

fs/eventpoll.c +3 -3
index 4a0e98d..85f56e4 100644
@@ -2318,19 +2318,19 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		size_t, sigsetsize)
 {
 	int error;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	error = set_user_sigmask(sigmask, sigsetsize);
 	if (error)
 		return error;
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	if (error != -EINTR)
+		restore_saved_sigmask();
 
 	return error;
 }
include/linux/sched/signal.h +0 -2
index e412c09..1e82ae0 100644
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
 	set_thread_flag(TIF_RESTORE_SIGMASK);
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *tsk)
@@ -447,7 +446,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
 	current->restore_sigmask = true;
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *tsk)
 {
include/linux/signal.h +1 -2
index 9702016..887cea6 100644
@@ -273,8 +273,7 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
 			       struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-	sigset_t *oldset, size_t sigsetsize);
+extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
 extern void restore_user_sigmask(const void __user *usigmask,
 				 sigset_t *sigsaved);
 extern void set_current_blocked(sigset_t *);
kernel/signal.c +8 -39

66 lines not on this page · raw message

#2 lore raw

-Deepa

Quoted 9 lines · Oleg Nesterov
Quoted 7 lines · Deepa Dinamani

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Quoted 7 lines

Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean. restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending).

restore_user_sigmask() was added because of all the variants of these syscalls we added because of y2038 as noted in commit message:

signal: Add restore_user_sigmask()

Refactor the logic to restore the sigmask before the syscall returns into an api.
This is useful for versions of syscalls that pass in the sigmask and expect the current->sigmask to be changed during the execution and restored after the execution of the syscall.

With the advent of new y2038 syscalls in the subsequent patches, we add two more new versions of the syscalls (for pselect, ppoll and io_pgetevents) in addition to the existing native and compat versions. Adding such an api reduces the logic that would need to be replicated otherwise.

Quoted 14 lines
fs/eventpoll.c +0 -1
index 4a0e98d..85f56e4 100644
@@ -2318,19 +2318,19 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
       size_t, sigsetsize)
 {
   int error;
-    sigset_t ksigmask, sigsaved;
 
   /*
    * If the caller wants a certain signal mask to be set during the wait,
    * we apply it here.

fs/eventpoll.c SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, · line 2332

Quoted 10 lines
fs/eventpoll.c +2 -2
    */
-    error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+    error = set_user_sigmask(sigmask, sigsetsize);
   if (error)
       return error;
 
   error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-    restore_user_sigmask(sigmask, &sigsaved);
+    if (error != -EINTR)

As you address all the other syscalls this condition becomes more and more complicated.

include/linux/sched/signal.h void task_join_group_stop(struct task_struct *task); · line 419

Quoted 13 lines
fs/eventpoll.c +1 -0
+        restore_saved_sigmask();
 
   return error;
 }
include/linux/sched/signal.h +0 -1
index e412c09..1e82ae0 100644
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
   set_thread_flag(TIF_RESTORE_SIGMASK);
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));

So you always want do_signal() to be called?
You will have to check each architecture's implementation of do_signal() to check if that has any side effects.

Although this is not what the patch is solving. What we want is to adjust return codes on all these syscalls to user and not drop signals. Please check v2/v3 of the patch. I've updated the commit text to provide more context into what is actually being fixed here.

If we really want to simplify, we should rewrite all the internal logic of all the ppoll, epoll_pwait, io_pgetevent syscall internal handling where we set the error code.
As new versions of syscalls were added, the internal logic got reworked rather hapazardly. But, as the current issue points out, these are delicate changes.

-Deepa

Quoted 98 lines
include/linux/sched/signal.h +0 -1
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *tsk)
@@ -447,7 +446,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
   current->restore_sigmask = true;
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *tsk)
 {
include/linux/signal.h +1 -2
index 9702016..887cea6 100644
@@ -273,8 +273,7 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
                  struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-    sigset_t *oldset, size_t sigsetsize);
+extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
 extern void restore_user_sigmask(const void __user *usigmask,
                sigset_t *sigsaved);
 extern void set_current_blocked(sigset_t *);
kernel/signal.c +8 -39
index 227ba17..76f4f9a 100644
@@ -2801,19 +2801,21 @@ EXPORT_SYMBOL(sigprocmask);
 * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
 * epoll_pwait where a new sigmask is passed from userland for the syscalls.
 */
-int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-             sigset_t *oldset, size_t sigsetsize)
+int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 {
-    if (!usigmask)
+    sigset_t *kmask;
+
+    if (!umask)
       return 0;
 
   if (sigsetsize != sizeof(sigset_t))
       return -EINVAL;
-    if (copy_from_user(set, usigmask, sizeof(sigset_t)))
+    if (copy_from_user(kmask, umask, sizeof(sigset_t)))
       return -EFAULT;
 
-    *oldset = current->blocked;
-    set_current_blocked(set);
+    set_restore_sigmask();
+    current->saved_sigmask = current->blocked;
+    set_current_blocked(kmask);
 
   return 0;
 }
@@ -2840,39 +2842,6 @@ int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
 EXPORT_SYMBOL(set_compat_user_sigmask);
 #endif
 
-/*
- * restore_user_sigmask:
- * usigmask: sigmask passed in from userland.
- * sigsaved: saved sigmask when the syscall started and changed the sigmask to
- *           usigmask.
- *
- * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
- * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
- */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
-{
-
-    if (!usigmask)
-        return;
-    /*
-     * When signals are pending, do not restore them here.
-     * Restoring sigmask here can lead to delivering signals that the above
-     * syscalls are intended to block because of the sigmask passed in.
-     */
-    if (signal_pending(current)) {
-        current->saved_sigmask = *sigsaved;
-        set_restore_sigmask();
-        return;
-    }
-
-    /*
-     * This is needed because the fast syscall return path does not restore
-     * saved_sigmask when signals are not pending.
-     */
-    set_current_blocked(sigsaved);
-}
-EXPORT_SYMBOL(restore_user_sigmask);
-
 /**
 *  sys_rt_sigprocmask - change the list of currently blocked signals
 *  @how: whether to add, remove, or set signals
maintainer
#3 lore raw
Quoted 31 lines · Deepa Dinamani

-Deepa

Quoted 9 lines · Oleg Nesterov
Quoted 7 lines · Deepa Dinamani

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Quoted 7 lines

Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean. restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending).

restore_user_sigmask() was added because of all the variants of these syscalls we added because of y2038 as noted in commit message:

signal: Add restore_user_sigmask()

Refactor the logic to restore the sigmask before the syscall returns into an api.
This is useful for versions of syscalls that pass in the sigmask and expect the current->sigmask to be changed during the execution and restored after the execution of the syscall.

Quoted 5 lines

With the advent of new y2038 syscalls in the subsequent patches, we add two more new versions of the syscalls (for pselect, ppoll and io_pgetevents) in addition to the existing native and compat versions. Adding such an api reduces the logic that would need to be replicated otherwise.

Again, I need to re-check, will continue tomorrow. But so far I am not sure this helper can actually help.

Quoted 17 lines
Quoted 17 lines
fs/eventpoll.c +1 -2
@@ -2318,19 +2318,19 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
       size_t, sigsetsize)
 {
   int error;
-    sigset_t ksigmask, sigsaved;
 
   /*
    * If the caller wants a certain signal mask to be set during the wait,
    * we apply it here.
    */
-    error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+    error = set_user_sigmask(sigmask, sigsetsize);
   if (error)
       return error;
 
Quoted 7 lines

fs/eventpoll.c SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, · line 2332

Quoted 4 lines
fs/eventpoll.c +1 -1
   error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-    restore_user_sigmask(sigmask, &sigsaved);
+    if (error != -EINTR)

As you address all the other syscalls this condition becomes more and more complicated.

May be.

Quoted 9 lines

include/linux/sched/signal.h void task_join_group_stop(struct task_struct *task); · line 419

Quoted 7 lines
include/linux/sched/signal.h +0 -1
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
   set_thread_flag(TIF_RESTORE_SIGMASK);
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));

So you always want do_signal() to be called?

Why do you think so? No. This is just to avoid the warning, because with the patch I sent set_restore_sigmask() is called "in advance".

Quoted 2 lines

You will have to check each architecture's implementation of do_signal() to check if that has any side effects.

I don't think so.

Quoted 1 line

Although this is not what the patch is solving.

Sure. But you know, after I tried to read the changelog, I am not sure I understand what exactly you are trying to fix. Could you please explain this part

The behavior
before 854a6ed56839a was that the signals were dropped after the error code was decided. This resulted in lost signals but the userspace did not notice it

? I fail to understand it, sorry. It looks as if the code was already buggy before that commit and it could miss a signal or something like this, but I do not see how.

Oleg.

#4 lore raw
Quoted 79 lines · Oleg Nesterov
Quoted 31 lines · Deepa Dinamani

-Deepa

Quoted 9 lines · Oleg Nesterov
Quoted 7 lines · Deepa Dinamani

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Quoted 7 lines

Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean. restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending).

restore_user_sigmask() was added because of all the variants of these syscalls we added because of y2038 as noted in commit message:

signal: Add restore_user_sigmask()

Refactor the logic to restore the sigmask before the syscall returns into an api.
This is useful for versions of syscalls that pass in the sigmask and expect the current->sigmask to be changed during the execution and restored after the execution of the syscall.

Quoted 5 lines

With the advent of new y2038 syscalls in the subsequent patches, we add two more new versions of the syscalls (for pselect, ppoll and io_pgetevents) in addition to the existing native and compat versions. Adding such an api reduces the logic that would need to be replicated otherwise.

Again, I need to re-check, will continue tomorrow. But so far I am not sure this helper can actually help.

Quoted 17 lines
Quoted 17 lines
fs/eventpoll.c +1 -2
@@ -2318,19 +2318,19 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
       size_t, sigsetsize)
 {
   int error;
-    sigset_t ksigmask, sigsaved;
 
   /*
    * If the caller wants a certain signal mask to be set during the wait,
    * we apply it here.
    */
-    error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+    error = set_user_sigmask(sigmask, sigsetsize);
   if (error)
       return error;
 
Quoted 7 lines

fs/eventpoll.c SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, · line 2332

Quoted 4 lines
fs/eventpoll.c +1 -1
   error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-    restore_user_sigmask(sigmask, &sigsaved);
+    if (error != -EINTR)

As you address all the other syscalls this condition becomes more and more complicated.

May be.

Quoted 9 lines

include/linux/sched/signal.h void task_join_group_stop(struct task_struct *task); · line 419

Quoted 7 lines
include/linux/sched/signal.h +0 -1
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
   set_thread_flag(TIF_RESTORE_SIGMASK);
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));

So you always want do_signal() to be called?

Quoted 7 lines

Why do you think so? No. This is just to avoid the warning, because with the patch I sent set_restore_sigmask() is called "in advance".

Quoted 2 lines

You will have to check each architecture's implementation of do_signal() to check if that has any side effects.

I don't think so.

Why not?

Quoted 6 lines
Quoted 1 line

Although this is not what the patch is solving.

Sure. But you know, after I tried to read the changelog, I am not sure I understand what exactly you are trying to fix. Could you please explain this part

Quoted 7 lines

The behavior
before 854a6ed56839a was that the signals were dropped after the error code was decided. This resulted in lost signals but the userspace did not notice it

? I fail to understand it, sorry. It looks as if the code was already buggy before that commit and it could miss a signal or something like this, but I do not see how.

Did you read the explanation pointed to in the commit text? :

https://lore.kernel.org/linux-fsdevel/20190427093319.sgicqik2oqkez3wk@dcvr/

Let me know what part you don't understand and I can explain more.

It would be better to understand the isssue before we start discussing the fix.

-Deepa

#5 lore raw

From: Deepa Dinamani

Quoted 106 lines

Sent: 22 May 2019 17:34

Quoted 39 lines · Oleg Nesterov
Quoted 31 lines · Deepa Dinamani

-Deepa

Quoted 9 lines · Oleg Nesterov
Quoted 7 lines · Deepa Dinamani

Note that this patch returns interrupted errors (EINTR, ERESTARTNOHAND, etc) only when there is no other error. If there is a signal and an error like EINVAL, the syscalls return -EINVAL rather than the interrupted error codes.

Quoted 7 lines

Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean. restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending).

restore_user_sigmask() was added because of all the variants of these syscalls we added because of y2038 as noted in commit message:

signal: Add restore_user_sigmask()

Refactor the logic to restore the sigmask before the syscall returns into an api.
This is useful for versions of syscalls that pass in the sigmask and expect the current->sigmask to be changed during the execution and restored after the execution of the syscall.

Quoted 5 lines

With the advent of new y2038 syscalls in the subsequent patches, we add two more new versions of the syscalls (for pselect, ppoll and io_pgetevents) in addition to the existing native and compat versions. Adding such an api reduces the logic that would need to be replicated otherwise.

Quoted 6 lines

Again, I need to re-check, will continue tomorrow. But so far I am not sure this helper can actually help.

Quoted 3 lines

fs/eventpoll.c SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *,

Quoted 3 lines
fs/eventpoll.c +0 -0
@@ -2318,19 +2318,19 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *,

events,

Quoted 34 lines
Quoted 14 lines
Quoted 14 lines
fs/eventpoll.c +1 -2
       size_t, sigsetsize)
 {
   int error;
-    sigset_t ksigmask, sigsaved;
 
   /*
    * If the caller wants a certain signal mask to be set during the wait,
    * we apply it here.
    */
-    error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+    error = set_user_sigmask(sigmask, sigsetsize);
   if (error)
       return error;
 
Quoted 7 lines

fs/eventpoll.c SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, · line 2332

Quoted 4 lines
fs/eventpoll.c +1 -1
   error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-    restore_user_sigmask(sigmask, &sigsaved);
+    if (error != -EINTR)

As you address all the other syscalls this condition becomes more and more complicated.

May be.

Quoted 9 lines

include/linux/sched/signal.h void task_join_group_stop(struct task_struct *task); · line 419

Quoted 7 lines
include/linux/sched/signal.h +0 -1
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
   set_thread_flag(TIF_RESTORE_SIGMASK);
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));

So you always want do_signal() to be called?

Quoted 7 lines

Why do you think so? No. This is just to avoid the warning, because with the patch I sent set_restore_sigmask() is called "in advance".

Quoted 2 lines

You will have to check each architecture's implementation of do_signal() to check if that has any side effects.

I don't think so.

Why not?

Quoted 6 lines
Quoted 1 line

Although this is not what the patch is solving.

Sure. But you know, after I tried to read the changelog, I am not sure I understand what exactly you are trying to fix. Could you please explain this part

Quoted 7 lines

The behavior
before 854a6ed56839a was that the signals were dropped after the error code was decided. This resulted in lost signals but the userspace did not notice it

? I fail to understand it, sorry. It looks as if the code was already buggy before that commit and it could miss a signal or something like this, but I do not see how.

Quoted 7 lines

Did you read the explanation pointed to in the commit text? :

https://lore.kernel.org/linux-fsdevel/20190427093319.sgicqik2oqkez3wk@dcvr/

Let me know what part you don't understand and I can explain more.

It would be better to understand the isssue before we start discussing the fix.

I'm confused...
I thought:

EINTR should only be returned if a blocking sleep (eg in do_epoll_wait() itself) was interrupted by a signal that was enabled at the time of the sleep.

The handlers for all unblocked signals should be run on return to user. This is after the mask has been restored and regardless of the error code.

So epoll() can return 'success' or 'timeout' (etc) and the handler for SIG_URG should still be called.
This is exactly equivalent to the interrupt that generates the signal happening just after the 'return to user' of the system call.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#6 lore raw
Quoted 2 lines · David Laight

I'm confused...

Me too. To clarify, the current code is obviously buggy, pselect/whatever shouldn't return 0 (or anything else) if it was interrupted and we are going to deliver the signal.

But it seems that Deepa has other concerns which I do not understand at all.

In any case, the signal_pending() check _inside_ restore_user_sigmask() can't be right, with or without this patch. If nothing else, a signal can come right after the check.

Quoted 2 lines

So epoll() can return 'success' or 'timeout' (etc) and the handler for SIG_URG should still be called.

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

or I am totally confused...

Oleg.

#7 lore raw

From: Oleg Nesterov

Quoted 7 lines · David Laight
Quoted 2 lines

I'm confused...

Me too. To clarify, the current code is obviously buggy, pselect/whatever shouldn't return 0 (or anything else) if it was interrupted and we are going to deliver the signal.

If it was interrupted the return value has to be EINTR. Whether any signal handlers are called is a separate matter.

Quoted 9 lines

But it seems that Deepa has other concerns which I do not understand at all.

In any case, the signal_pending() check _inside_ restore_user_sigmask() can't be right, with or without this patch. If nothing else, a signal can come right after the check.

Quoted 2 lines

So epoll() can return 'success' or 'timeout' (etc) and the handler for SIG_URG should still be called.

Quoted 6 lines

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

Ugg...
Posix probably allows the signal handler be called at the point the event happens rather than being deferred until the system call completes. Queueing up the signal handler to be run at a later time (syscall exit) certainly makes sense.
Definitely safest to call the signal handler even if success/timeout is returned.
pselect() exists to stop the entry race, not the exit one.

Quoted 1 line

or I am totally confused...

The pselect(2) man page says that the signal handler for a signal that is enabled for the duration should run.
Clearly it is also valid to call the signal handlers for any signals that are allowed on entry/exit (they could happen just after the return). Also remember that pselect() can also be used to disable signals.

So ISTM that signal handlers allowed by either signal mask should be called during syscall exit.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#8 lore raw
Quoted 11 lines · David Laight

From: Oleg Nesterov

Quoted 7 lines · David Laight
Quoted 2 lines

I'm confused...

Me too. To clarify, the current code is obviously buggy, pselect/whatever shouldn't return 0 (or anything else) if it was interrupted and we are going to deliver the signal.

If it was interrupted the return value has to be EINTR.

Yes, and this is what we need to fix.

Quoted 1 line

Whether any signal handlers are called is a separate matter.

Not really... because in this case we know that the signal will be delivered,

Quoted 7 lines
Quoted 6 lines

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

Quoted 7 lines

Ugg...
Posix probably allows the signal handler be called at the point the event happens rather than being deferred until the system call completes. Queueing up the signal handler to be run at a later time (syscall exit) certainly makes sense.
Definitely safest to call the signal handler even if success/timeout is returned.

Why?

Quoted 1 line

pselect() exists to stop the entry race, not the exit one.

pselect() has to block SIG_URG again before it returns to user-mode, right?

Suppose pselect() finds a ready fd, and this races with SIG_URG.

Why do you think the handler should run?

What if SIG_URG comes right after pselect() blocks SIG_URG again? I mean, how this differs the case when it comes before, but a ready fd was already found?

Oleg.

#9 lore raw

From: Oleg Nesterov

Quoted 5 lines

Sent: 23 May 2019 17:36

Quoted 3 lines · David Laight

From: Oleg Nesterov

Quoted 1 line · David Laight

...

Quoted 15 lines
Quoted 7 lines
Quoted 6 lines

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

Quoted 7 lines

Ugg...
Posix probably allows the signal handler be called at the point the event happens rather than being deferred until the system call completes. Queueing up the signal handler to be run at a later time (syscall exit) certainly makes sense.
Definitely safest to call the signal handler even if success/timeout is returned.

Quoted 5 lines

Why?

Quoted 1 line

pselect() exists to stop the entry race, not the exit one.

pselect() has to block SIG_URG again before it returns to user-mode, right?

Yep.
So the signal handler can't be called for a signal that happens after pselect() returns.

Quoted 1 line

Suppose pselect() finds a ready fd, and this races with SIG_URG.

You mean if SIG_URG is raised after a ready fd is found (or even timeout)? So the return value isn't EINTR.
(If an fd is readable on entry, the SIG_URG could have happened much earlier.)

Quoted 1 line

Why do you think the handler should run?

Think of the application code loop.
Consider what happens if the signal is SIG_INT - to request the program stop.
After every pselect() call the application looks to see if the handler has been called.
If one of the fds is always readable pselect() will never return EINTR but you want the SIG_INT handler run so that the loop gets terminated. If you only call the signal handler when EINTR is returned the process will never stop.
So you need to call the handler even when pselect() succeeds/time out.

Quoted 3 lines

What if SIG_URG comes right after pselect() blocks SIG_URG again? I mean, how this differs the case when it comes before, but a ready fd was already found?

I suspect you need to defer the re-instatement of the original mask to the code that calls the signal handlers (which probably should be called with the programs signal mask).
So that particular window doesn't exist.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#10 lore raw

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a
b. State after 854a6ed56839a
c. Proposed fix as per the patchset in question.

Oleg, I will discuss these first and then we can discuss the additional changes you suggested.

Some background on why we have these syscalls that take sigmask as an argument. This is just for the sake of completeness of the argument.

These are particularly meant for a scenario(d) such as below:

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

The problem here is that if there is a signal that is not blocked by 1 and such a signal is delivered between 1 and 2(since they are not atomic), the syscall in 2 might block forever as it never found out about the signal.

As per [a] and let's consider the case of epoll_pwait only first for simplicity.

As I said before, ep_poll() is what checks for signal_pending() and is responsible for setting errno to -EINTR when there is a signal.

So if a signal is received after ep_poll() and ep_poll() returns success, it is never noticed by the syscall during execution. So the question is does the userspace have to know about this signal or not. From scenario [d] above, I would say it should, even if all the fd's completed successfully.
This does not happen in [a]. So this is what I said was already broken.

What [b] does is to move the signal check closer to the restoration of the signal. This way it is good. So, if there is a signal after ep_poll() returns success, it is noticed and the signal is delivered when the syscall exits. But, the syscall error status itself is 0.

So now [c] is adjusting the return values based on whether extra signals were detected after ep_poll(). This part was needed even for [a].

Let me know if this clarifies things a bit.

-Deepa

#11 lore raw
Quoted 43 lines · Deepa Dinamani

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a
b. State after 854a6ed56839a
c. Proposed fix as per the patchset in question.

Oleg, I will discuss these first and then we can discuss the additional changes you suggested.

Some background on why we have these syscalls that take sigmask as an argument. This is just for the sake of completeness of the argument.

These are particularly meant for a scenario(d) such as below:

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

The problem here is that if there is a signal that is not blocked by 1 and such a signal is delivered between 1 and 2(since they are not atomic), the syscall in 2 might block forever as it never found out about the signal.

As per [a] and let's consider the case of epoll_pwait only first for simplicity.

As I said before, ep_poll() is what checks for signal_pending() and is responsible for setting errno to -EINTR when there is a signal.

So if a signal is received after ep_poll() and ep_poll() returns success, it is never noticed by the syscall during execution. So the question is does the userspace have to know about this signal or not. From scenario [d] above, I would say it should, even if all the fd's completed successfully.
This does not happen in [a]. So this is what I said was already broken.

What [b] does is to move the signal check closer to the restoration of the signal. This way it is good. So, if there is a signal after ep_poll() returns success, it is noticed and the signal is delivered when the syscall exits. But, the syscall error status itself is 0.

Quoted 5 lines

So now [c] is adjusting the return values based on whether extra signals were detected after ep_poll(). This part was needed even for [a].

Let me know if this clarifies things a bit.

Just adding a little more clarification, there is an additional change between [a] and [b].
As per [a] we would just restore the signal instead of changing the saved_sigmask and the signal could get delivered right then. [b] changes this to happen at syscall exit:

void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved) {

<snip>

/*
* When signals are pending, do not restore them here.
* Restoring sigmask here can lead to delivering signals
that the above

           * syscalls are intended to block because of the sigmask passed in.
           */
           if (signal_pending(current)) {
           current->saved_sigmask = *sigsaved;
           set_restore_sigmask();
           return;
}

-Deepa

#12 lore raw
Quoted 5 lines

Just adding a little more clarification, there is an additional change between [a] and [b].
As per [a] we would just restore the signal instead of changing the saved_sigmask and the signal could get delivered right then. [b] changes this to happen at syscall exit:

Rewording above, as there seems to be a few misrepresentations:

Just adding a little more clarification, there is an additional change between [a] and [b].
As per [a] we would just restore the signal mask instead of changing the saved_sigmask and the even the blocked signals could get delivered right then. [b] changes the restoration to happen at syscall exit:

Quoted 9 lines

void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved) {

<snip>

/*
* When signals are pending, do not restore them here.
* Restoring sigmask here can lead to delivering signals
that the above

Quoted 7 lines
           * syscalls are intended to block because of the sigmask passed in.
           */
           if (signal_pending(current)) {
           current->saved_sigmask = *sigsaved;
           set_restore_sigmask();
           return;
}

-Deepa

#13 lore raw
In reply to Deepa Dinamani

From: Deepa Dinamani

Quoted 17 lines

Sent: 23 May 2019 19:07
Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a
b. State after 854a6ed56839a
c. Proposed fix as per the patchset in question.

Oleg, I will discuss these first and then we can discuss the additional changes you suggested.

Some background on why we have these syscalls that take sigmask as an argument. This is just for the sake of completeness of the argument.

These are particularly meant for a scenario(d) such as below:

Quoted 8 lines

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

The problem here is that if there is a signal that is not blocked by 1 and such a signal is delivered between 1 and 2(since they are not atomic), the syscall in 2 might block forever as it never found out about the signal.

I think we all agree about the underlying problem these system calls solve.

Quoted 1 line

As per [a] and let's consider the case of epoll_pwait only first for simplicity.

For simplicity you ought to consider sigwaitinfo() :-)

Quoted 2 lines

As I said before, ep_poll() is what checks for signal_pending() and is responsible for setting errno to -EINTR when there is a signal.

Ah, there in lies the problem (well one of them).
ep_poll() should only return -EINTR if its sleep (waiting for an fd to be ready) is interrupted.
The signal handler(s) should still be called though.
If the timeout is 0 then any signal handler should be called, but the return value is still 0 (if no fd are 'ready').

Quoted 5 lines

So if a signal is received after ep_poll() and ep_poll() returns success, it is never noticed by the syscall during execution. So the question is does the userspace have to know about this signal or not. From scenario [d] above, I would say it should, even if all the fd's completed successfully.

What is scenario [d]? You've code versions a/b/c but no scenarios.

Quoted 6 lines

This does not happen in [a]. So this is what I said was already broken.

What [b] does is to move the signal check closer to the restoration of the signal. This way it is good. So, if there is a signal after ep_poll() returns success, it is noticed and the signal is delivered when the syscall exits. But, the syscall error status itself is 0.

By 0 you mean >= 0 ??

Quoted 3 lines

So now [c] is adjusting the return values based on whether extra signals were detected after ep_poll(). This part was needed even for [a].

IMHO The return value should never be changed.
Much like write() can return a partial length if a signal happens.

ISTM that a user signal handler should be scheduled to be run if the signal is pending and not masked.
On return to user all scheduled signal handlers are called (regardless as to whether the signal is masked at that time). This might mean getting the 'return to user' code to restore the original signal mask saved for epoll_pwait() and pselect() etc.

If, for some perverted reason (compatibility with broken apps), you need epoll_pwait() to return EINTR instead of 0 the you probably need a special 'kernel internal' errno value that is always converted by the syscall exit code to EINTR/0 (and a second one that is EINTR/EAGAIN etc).

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer NAK
#14 lore raw
In reply to Deepa Dinamani
Quoted 6 lines · Deepa Dinamani

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a

I think everything was correct,

Quoted 1 line

b. State after 854a6ed56839a

obviously buggy,

Quoted 1 line

c. Proposed fix as per the patchset in question.

Nack, sorry. I'll try to finish my patch on Monday. It will restore the state before 854a6ed56839a and (imo) cleanup/simplify this code.

At leat this is what I think right now. May be I will have to change my mind after this discussion. But in any case I can't believe I will ever agree with your fix ;)

Quoted 10 lines

These are particularly meant for a scenario(d) such as below:

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

The problem here is that if there is a signal that is not blocked by 1 and such a signal is delivered between 1 and 2(since they are not atomic), the syscall in 2 might block forever as it never found out about the signal.

and that is why we have pselect/etc to make this sequence "atomic".

Quoted 4 lines

As per [a] and let's consider the case of epoll_pwait only first for simplicity.

As I said before, ep_poll() is what checks for signal_pending() and is responsible for setting errno to -EINTR when there is a signal.

To clarify, if do_epoll_wait() return -EINTR then signal_pending() is true, right?

Quoted 2 lines

So if a signal is received after ep_poll() and ep_poll() returns success, it is never noticed by the syscall during execution.

What you are saying looks very confusing to me, I will assume that you meant something like

- a signal SIG_XXX was blocked before sys_epoll_pwait() was called

- sys_epoll_pwait(sigmask) unblocks SIG_XXX according to sigmask

- sys_epoll_pwait() calls do_epoll_wait() which returns success

- SIG_XXX comes after that and it is "never noticed"

Yes. Everything is correct. And see my reply to David, SIG_XXX can even come _before_ sys_epoll_pwait() was called.

Quoted 2 lines

So the question is does the userspace have to know about this signal or not.

If userspace needs to know about SIG_XXX it should not block it, that is all.

Quoted 2 lines

What [b] does is to move the signal check closer to the restoration of the signal.

FOR NO REASON, afaics (to simplify, lets forget the problem with the wrong return value you are trying to fix).

And even if there were ANY reason to do this, note that (with or without this fix) the signal_pending() check inside restore_user_sigmask() can NOT help, simply because SIG_XXX can come right after this check.

Oleg.

#15 lore raw
Quoted 10 lines · Oleg Nesterov
Quoted 6 lines · Deepa Dinamani

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a

I think everything was correct,

There were 2 things that were wrong:

1. If an unblocked signal was received, after the ep_poll(), then the return status did not indicate that. This is expected behavior according to man page. If this is indeed what is expected then the man page should note that signal will be delivered in this case and return code will still be 0.

"EINTR
The call was interrupted by a signal handler before either any of the requested events occurred or the timeout expired; see signal(7)."

2. The restoring of the sigmask is done right in the syscall part and not while exiting the syscall and if you get a blocked signal here, you will deliver this to userspace.

Quoted 3 lines
Quoted 1 line

b. State after 854a6ed56839a

obviously buggy,

Ok, then can you point out what specifically was wrong with 854a6ed56839a? And, not how it could be more simple?

Quoted 9 lines
Quoted 6 lines

c. Proposed fix as per the patchset in question.

As per [a] and let's consider the case of epoll_pwait only first for simplicity.

As I said before, ep_poll() is what checks for signal_pending() and is responsible for setting errno to -EINTR when there is a signal.

To clarify, if do_epoll_wait() return -EINTR then signal_pending() is true, right?

Yes, the case I'm talking about is when do_epoll_wait() returns 0 and then you get a signal.

Quoted 8 lines
Quoted 2 lines

So if a signal is received after ep_poll() and ep_poll() returns success, it is never noticed by the syscall during execution.

What you are saying looks very confusing to me, I will assume that you meant something like

- a signal SIG_XXX was blocked before sys_epoll_pwait() was called

Quoted 8 lines

- sys_epoll_pwait(sigmask) unblocks SIG_XXX according to sigmask

- sys_epoll_pwait() calls do_epoll_wait() which returns success

- SIG_XXX comes after that and it is "never noticed"

Yes. Everything is correct. And see my reply to David, SIG_XXX can even come _before_ sys_epoll_pwait() was called.

No, I'm talking about a signal that was not blocked.

Quoted 4 lines
Quoted 2 lines

So the question is does the userspace have to know about this signal or not.

If userspace needs to know about SIG_XXX it should not block it, that is all.

What should be the return value if a signal is detected after a fd completed?

Quoted 5 lines
Quoted 2 lines

What [b] does is to move the signal check closer to the restoration of the signal.

FOR NO REASON, afaics (to simplify, lets forget the problem with the wrong return value you are trying to fix).

As I already pointed out, the restoring of the sigmask is done during the syscall and not while exiting the syscall and if you get a blocked signal here, you will deliver this to userspace.

Quoted 3 lines

And even if there were ANY reason to do this, note that (with or without this fix) the signal_pending() check inside restore_user_sigmask() can NOT help, simply because SIG_XXX can come right after this check.

This I pointed out already that we should probably make this sequence atomic.

-Deepa

maintainer
#16 lore raw
Quoted 11 lines · Deepa Dinamani
Quoted 9 lines · Oleg Nesterov
Quoted 6 lines · Deepa Dinamani

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a

Quoted 6 lines
Quoted 1 line

I think everything was correct,

There were 2 things that were wrong:

1. If an unblocked signal was received, after the ep_poll(), then the return status did not indicate that.

Yes,

Quoted 8 lines

This is expected behavior
according to man page. If this is indeed what is expected then the man page should note that signal will be delivered in this case and return code will still be 0.

"EINTR
The call was interrupted by a signal handler before either any of the requested events occurred or the timeout expired; see signal(7)."

and what do you think the man page could say?

This is obviously possible for any syscall, and we can't avoid this. A signal can come right after syscall insn completes. The signal handler will be called but this won't change $rax, user-space can see return code == 0 or anything else.

And this doesn't differ from the case when the signal comes before syscall returns.

Quoted 3 lines

2. The restoring of the sigmask is done right in the syscall part and not while exiting the syscall and if you get a blocked signal here, you will deliver this to userspace.

So I assume that this time you are talking about epoll_pwait() and not epoll_wait()...

And I simply can't understand you. But yes, if the original mask doesn't include the pending signal it will be delivered while the syscall can return success/timout or -EFAULT or anything.

This is correct, see above.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

b. State after 854a6ed56839a

obviously buggy,

Ok, then can you point out what specifically was wrong with 854a6ed56839a?

Cough. If nothing else the lost -EINTR?

Quoted 1 line

And, not how it could be more simple?

Well, I already sent the patch and after that I even showed you the code with the patch applied. See https://lore.kernel.org/lkml/[email protected]/

Quoted 7 lines
Quoted 7 lines

What you are saying looks very confusing to me, I will assume that you meant something like

- a signal SIG_XXX was blocked before sys_epoll_pwait() was called

- sys_epoll_pwait(sigmask) unblocks SIG_XXX according to sigmask

Quoted 8 lines
Quoted 6 lines

- sys_epoll_pwait() calls do_epoll_wait() which returns success

- SIG_XXX comes after that and it is "never noticed"

Yes. Everything is correct. And see my reply to David, SIG_XXX can even come _before_ sys_epoll_pwait() was called.

No, I'm talking about a signal that was not blocked.

OK, see above.

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

So the question is does the userspace have to know about this signal or not.

If userspace needs to know about SIG_XXX it should not block it, that is all.

What should be the return value if a signal is detected after a fd completed?

Did you mean "if a signal is detected after a ready fd was already found" ?

In this case the return value should report success. But I have already lost, this all looks irrelevant wrt to fix we need.

Quoted 10 lines
Quoted 5 lines
Quoted 2 lines

What [b] does is to move the signal check closer to the restoration of the signal.

FOR NO REASON, afaics (to simplify, lets forget the problem with the wrong return value you are trying to fix).

As I already pointed out, the restoring of the sigmask is done during the syscall and not while exiting the syscall and if you get a blocked signal here, you will deliver this to userspace.

Quoted 5 lines
Quoted 3 lines

And even if there were ANY reason to do this, note that (with or without this fix) the signal_pending() check inside restore_user_sigmask() can NOT help, simply because SIG_XXX can come right after this check.

This I pointed out already that we should probably make this sequence atomic.

See above.

Oleg.

#17 lore raw
Quoted 27 lines · Oleg Nesterov
Quoted 11 lines · Deepa Dinamani
Quoted 9 lines · Oleg Nesterov
Quoted 6 lines · Deepa Dinamani

Ok, since there has been quite a bit of argument here, I will backtrack a little bit and maybe it will help us understand what's happening here.
There are many scenarios being discussed on this thread:
a. State of code before 854a6ed56839a

Quoted 6 lines
Quoted 1 line

I think everything was correct,

There were 2 things that were wrong:

1. If an unblocked signal was received, after the ep_poll(), then the return status did not indicate that.

Yes,

Quoted 5 lines

This is expected behavior
according to man page. If this is indeed what is expected then the man page should note that signal will be delivered in this case and return code will still be 0.

Quoted 5 lines
Quoted 3 lines

"EINTR
The call was interrupted by a signal handler before either any of the requested events occurred or the timeout expired; see signal(7)."

and what do you think the man page could say?

Maybe clarify that a signal handler can be invoked even if the syscall return indicates a success.

Maybe a crude userspace application could do something like this:

sig_handler()
{
set global abort = 1
}

poll_the_fds()
{
ret = epoll_pwait()
if (ret)
return ret
if (abort) # but this abort should be ignored
if ret was 0.
return try_again

}

Quoted 5 lines

This is obviously possible for any syscall, and we can't avoid this. A signal can come right after syscall insn completes. The signal handler will be called but this won't change $rax, user-space can see return code == 0 or anything else.

And this doesn't differ from the case when the signal comes before syscall returns.

But, these syscalls are depending on there signals. I would assume for the purpose of these syscalls that the execution is done when we updated the saved_sigmask. We can pick a different point per syscall like ep_poll() also, but then we need to probably make it clear for each such syscall.

Quoted 5 lines
Quoted 3 lines

2. The restoring of the sigmask is done right in the syscall part and not while exiting the syscall and if you get a blocked signal here, you will deliver this to userspace.

So I assume that this time you are talking about epoll_pwait() and not epoll_wait()...

Yes.

Quoted 5 lines

And I simply can't understand you. But yes, if the original mask doesn't include the pending signal it will be delivered while the syscall can return success/timout or -EFAULT or anything.

This is correct, see above.

Look at the code before 854a6ed56839a:

/*
* If we changed the signal mask, we need to restore the original one.
* In case we've got a signal while waiting, we do not restore the
* signal mask yet, and we allow do_signal() to deliver the signal on
* the way back to userspace, before the signal mask is restored. */
if (sigmask) {
####### This err has not been changed since ep_poll() ####### So if there is a signal before this point, but
err = 0, then we goto else.

               if (err == -EINTR) {
                       memcpy(&current->saved_sigmask, &sigsaved,
                              sizeof(sigsaved));
                       set_restore_sigmask();
               } else
                     ############ This is a problem if there is signal
pending that is sigmask should block.
                     ########### This is the whole reason we have
current->saved_sigmask?
                       set_current_blocked(&sigsaved);
       }
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Quoted 1 line

b. State after 854a6ed56839a

obviously buggy,

Ok, then can you point out what specifically was wrong with 854a6ed56839a?

Cough. If nothing else the lost -EINTR?

This was my theory. My basis behind the theory was [1](the issue with return value not being updated) above. And, you are saying this is ok.

854a6ed56839a also has timing differences compared to the original code. So unless we are sure what was uncovered because of 854a6ed56839a, we might just be masking a pre-existing problem by reverting it. So I think we should code review 854a6ed56839a and figure out what is wrong programatically before just reverting it.

Quoted 1 line
Quoted 1 line

And, not how it could be more simple?

Oh, I was not asking here. I was saying let's please discuss what's wrong before simplifying the code.

-Deepa

maintainer
#18 lore raw

Deepa,

it seems that we both are saying the same things again and again, and we simply can't understand each other.

I'll try to write another email to restart this discussion. Tomorrow, somehow I can't wake up today.

And let me repeat, of course I can be wrong. IOW, it is not that I am trying to blame you for all this confusion.

Quoted 8 lines · Deepa Dinamani
Quoted 5 lines
Quoted 3 lines

Ok, then can you point out what specifically was wrong with 854a6ed56839a?

Cough. If nothing else the lost -EINTR?

This was my theory. My basis behind the theory was [1](the issue with return value not being updated) above. And, you are saying this is ok.

I agree that "the lost -EINTR" above was not clear. I'll try to clarify what I think is not OK.

Oleg.

#19 lore raw
Quoted 5 lines · Oleg Nesterov

Deepa,

it seems that we both are saying the same things again and again, and we simply can't understand each other.

Oleg, I'm sorry for the confusion. Maybe I should point out what I agree with also.

I agree that signal handller being called and return value not being altered is an issue with other syscalls also. I was just wondering if some userspace code assumption would be assuming this. This is not a kernel bug.

But, I do not think we have an understanding of what was wrong in 854a6ed56839a anymore since you pointed out that my assumption was not correct that the signal handler being called without errno being set is wrong.

One open question: this part of epoll_pwait was already broken before 854a6ed56839a. Do you agree?

if (err == -EINTR) {
                   memcpy(&current->saved_sigmask, &sigsaved,
                          sizeof(sigsaved));
                    set_restore_sigmask();
  } else
                   set_current_blocked(&sigsaved);

What to do next?
We could just see if your optimization patch resolves Eric's issue. Or, I could revert the signal_pending() check and provide a fix something like below(not a complete patch) since mainline has this regression. Eric had tested something like this works also. And, I can continue to look at what was wrong with 854a6ed56839a in the first place. Let me know what you prefer:

-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved) +int restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved, int sig_pending)
{

if (!usigmask)
return;

/*
* When signals are pending, do not restore them here.
* Restoring sigmask here can lead to delivering signals that the above
* syscalls are intended to block because of the sigmask passed in. */

+       if (sig_pending) {
                current->saved_sigmask = *sigsaved;
                set_restore_sigmask();
               return;
           }

@@ -2330,7 +2330,8 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,

        error = do_epoll_wait(epfd, events, maxevents, timeout);

- restore_user_sigmask(sigmask, &sigsaved);
+ signal_detected = restore_user_sigmask(sigmask, &sigsaved,
error == -EINTR);

-Deepa

maintainer
#20 lore raw
Quoted 10 lines · Deepa Dinamani

I agree that signal handller being called and return value not being altered is an issue with other syscalls also. I was just wondering if some userspace code assumption would be assuming this. This is not a kernel bug.

But, I do not think we have an understanding of what was wrong in 854a6ed56839a anymore since you pointed out that my assumption was not correct that the signal handler being called without errno being set is wrong.

Deepa, sorry, I simply can't parse the above... most probably because of my bad English.

Quoted 9 lines

One open question: this part of epoll_pwait was already broken before 854a6ed56839a. Do you agree?

if (err == -EINTR) {
                   memcpy(&current->saved_sigmask, &sigsaved,
                          sizeof(sigsaved));
                    set_restore_sigmask();
  } else
                   set_current_blocked(&sigsaved);

I do not understand why do you think this part was broken :/

Quoted 2 lines

Or, I could revert the signal_pending() check and provide a fix something like below(not a complete patch)

...

Quoted 20 lines
(quoted diff) +1 -1
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
+int restore_user_sigmask(const void __user *usigmask, sigset_t

*sigsaved, int sig_pending)
{

if (!usigmask)
return;

/*
* When signals are pending, do not restore them here.
* Restoring sigmask here can lead to delivering signals that the above
* syscalls are intended to block because of the sigmask passed in. */

(quoted diff) +1 -0
+       if (sig_pending) {
                current->saved_sigmask = *sigsaved;
                set_restore_sigmask();
               return;
           }

Review SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct · line 2335

Quoted 7 lines
(quoted diff) +1 -1
@@ -2330,7 +2330,8 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct
 epoll_event __user *, events,
 
        error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-       restore_user_sigmask(sigmask, &sigsaved);
+       signal_detected = restore_user_sigmask(sigmask, &sigsaved,
 error == -EINTR);

I fail to understand this pseudo-code, sorry. In particular, do not understand why restore_user_sigmask() needs to return a boolean.

The only thing I _seem to_ understand is the "sig_pending" flag passed by the caller which replaces the signal_pending() check. Yes, this is what I think we should do, and this is what I tried to propose from the very beginning in my 1st email in this thread.

Oleg.

#21 lore raw
Quoted 8 lines · Oleg Nesterov
Quoted 6 lines · Deepa Dinamani

I agree that signal handller being called and return value not being altered is an issue with other syscalls also. I was just wondering if some userspace code assumption would be assuming this. This is not a kernel bug.

Quoted 7 lines
Quoted 4 lines

But, I do not think we have an understanding of what was wrong in 854a6ed56839a anymore since you pointed out that my assumption was not correct that the signal handler being called without errno being set is wrong.

Deepa, sorry, I simply can't parse the above... most probably because of my bad English.

Ok, All I meant was that I had thought a signal handler being invoked without the error value reflecting it was wrong. That is what I had thought was wrong with 854a6ed56839a. Now, that we agree that signal handler can be invoked without the errno returning success, I thought I did not know what is wrong with 854a6ed56839a anymore.

But, you now pointed out that the signals we care about should not be delivered after an event has been ready. This points out to what was wrong with 854a6ed56839a. Thanks.

Quoted 11 lines
Quoted 9 lines

One open question: this part of epoll_pwait was already broken before 854a6ed56839a. Do you agree?

if (err == -EINTR) {
                   memcpy(&current->saved_sigmask, &sigsaved,
                          sizeof(sigsaved));
                    set_restore_sigmask();
  } else
                   set_current_blocked(&sigsaved);

I do not understand why do you think this part was broken :/

Ok, because of your other statement that the signals the application cares about do not want to know about signals they care about after an event is ready this is also not a problem.

Quoted 27 lines
Quoted 2 lines

Or, I could revert the signal_pending() check and provide a fix something like below(not a complete patch)

...

Quoted 22 lines
(quoted diff) +1 -1
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
+int restore_user_sigmask(const void __user *usigmask, sigset_t

*sigsaved, int sig_pending)
{

if (!usigmask)
return;

/*
* When signals are pending, do not restore them here.
* Restoring sigmask here can lead to delivering signals that the above
* syscalls are intended to block because of the sigmask passed in. */

(quoted diff) +1 -0
+       if (sig_pending) {
                current->saved_sigmask = *sigsaved;
                set_restore_sigmask();
               return;
           }
@@ -2330,7 +2330,8 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct
 epoll_event __user *, events,
 
Quoted 8 lines

Review SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct · line 2335

Quoted 5 lines
(quoted diff) +1 -1
        error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-       restore_user_sigmask(sigmask, &sigsaved);
+       signal_detected = restore_user_sigmask(sigmask, &sigsaved,
 error == -EINTR);

I fail to understand this pseudo-code, sorry. In particular, do not understand why restore_user_sigmask() needs to return a boolean.

That was a remnant from the other patch. Return type needs to be void.

Quoted 2 lines

The only thing I _seem to_ understand is the "sig_pending" flag passed by the caller which replaces the signal_pending() check.

Correct. This is what is the main change I was proposing.

Quoted 3 lines

Yes, this is what I think we
should do, and this is what I tried to propose from the very beginning in my 1st email in this thread.

This was not clear to me in your first response that you did not want the signal_pending() check in restore_user_sigmask(). :
https://lore.kernel.org/lkml/[email protected]/

"Ugh. I need to re-check, but at first glance I really dislike this change.

I think we can fix the problem _and_ simplify the code. Something like below. The patch is obviously incomplete, it changes only only one caller of set_user_sigmask(), epoll_pwait() to explain what I mean.

restore_user_sigmask() should simply die. Although perhaps another helper makes sense to add WARN_ON(test_tsk_restore_sigmask() && !signal_pending)."

-Deepa

#22 lore raw
In reply to Deepa Dinamani

From: Deepa Dinamani

Quoted 1 line

Sent: 24 May 2019 18:02

...

Quoted 16 lines

Maybe a crude userspace application could do something like this:

sig_handler()
{
set global abort = 1
}

poll_the_fds()
{
ret = epoll_pwait()
if (ret)
return ret
if (abort)
// but this abort should be ignored if ret was 0. return try_again

Quoted 1 line

}

As an application writer I'd want to see 'abort == 1' even if epoll_pwait() returned that an fd was 'ready'.

So the code above should probably be:

    wait_again:
        ret = epoll_pwait();
        if (abort)
            process_abort();
        if (ret <= 0) {
            if (ret == 0)
                process_timeout();
            if (ret == 0 || errno == EINTR)
                goto wait_again;
            // Something went horribly wrong in epoll_pwait()
            return ret;
        }
        // process the 'ready' fds

It would be non-unreasonable for the application to have all signals blocked except during the epoll_pwait(). So the application needs the signal handler for SIG_INT (etc) to be called even if one of the fd is always 'ready'.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#23 lore raw
In reply to Deepa Dinamani

From: Deepa Dinamani

Quoted 1 line

Sent: 24 May 2019 18:02

...

Quoted 12 lines

Look at the code before 854a6ed56839a:

/*
* If we changed the signal mask, we need to restore the original one.
* In case we've got a signal while waiting, we do not restore the
* signal mask yet, and we allow do_signal() to deliver the signal on
* the way back to userspace, before the signal mask is restored. */
if (sigmask) {
####### This err has not been changed since ep_poll() ####### So if there is a signal before this point, but
err = 0, then we goto else.

Quoted 11 lines
               if (err == -EINTR) {
                       memcpy(&current->saved_sigmask, &sigsaved,
                              sizeof(sigsaved));
                       set_restore_sigmask();
               } else
                     ############ This is a problem if there is signal
pending that is sigmask should block.
                     ########### This is the whole reason we have
current->saved_sigmask?
                       set_current_blocked(&sigsaved);
       }

What happens if all that crap is just deleted (I presume from the bottom of ep_wait()) ?

I'm guessing that on the way back to userspace signal handlers for signals enabled in the process's current mask (the one specified to epoll_pwait) get called.
Then the signal mask is loaded from current->saved_sigmask and and enabled signal handlers are called again.
No special code there that depends on the syscall result, errno of the syscall number.

That seems exactly correct!

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#24 lore raw
Quoted 31 lines · David Laight

From: Deepa Dinamani

Quoted 1 line

Sent: 24 May 2019 18:02

...

Quoted 12 lines

Look at the code before 854a6ed56839a:

/*
* If we changed the signal mask, we need to restore the original one.
* In case we've got a signal while waiting, we do not restore the
* signal mask yet, and we allow do_signal() to deliver the signal on
* the way back to userspace, before the signal mask is restored. */
if (sigmask) {
####### This err has not been changed since ep_poll() ####### So if there is a signal before this point, but
err = 0, then we goto else.

Quoted 11 lines
             if (err == -EINTR) {
                     memcpy(&current->saved_sigmask, &sigsaved,
                            sizeof(sigsaved));
                     set_restore_sigmask();
             } else
                   ############ This is a problem if there is signal
pending that is sigmask should block.
                   ########### This is the whole reason we have
current->saved_sigmask?
                     set_current_blocked(&sigsaved);
     }
Quoted 2 lines

What happens if all that crap is just deleted (I presume from the bottom of ep_wait()) ?

Hmm, you have to update the saved_sigmask or the sigmask.

Quoted 5 lines

I'm guessing that on the way back to userspace signal handlers for signals enabled in the process's current mask (the one specified to epoll_pwait) get called.
Then the signal mask is loaded from current->saved_sigmask and and enabled signal handlers are called again.

Who is saving this saved_sigmask that is being restored on the way back?

Quoted 2 lines

No special code there that depends on the syscall result, errno of the syscall number.

I didn’t say this has anything to do with errno.

-Deepa

#25 lore raw

From: Deepa Dinamani

Quoted 30 lines

Sent: 28 May 2019 12:38

Quoted 29 lines · David Laight

From: Deepa Dinamani

Quoted 1 line

Sent: 24 May 2019 18:02

...

Quoted 23 lines

Look at the code before 854a6ed56839a:

/*
* If we changed the signal mask, we need to restore the original one.
* In case we've got a signal while waiting, we do not restore the
* signal mask yet, and we allow do_signal() to deliver the signal on
* the way back to userspace, before the signal mask is restored. */
if (sigmask) {
####### This err has not been changed since ep_poll() ####### So if there is a signal before this point, but
err = 0, then we goto else.

             if (err == -EINTR) {
                     memcpy(&current->saved_sigmask, &sigsaved,
                            sizeof(sigsaved));
                     set_restore_sigmask();
             } else
                   ############ This is a problem if there is signal
pending that is sigmask should block.
                   ########### This is the whole reason we have
current->saved_sigmask?
                     set_current_blocked(&sigsaved);
     }
Quoted 4 lines
Quoted 2 lines

What happens if all that crap is just deleted (I presume from the bottom of ep_wait()) ?

Hmm, you have to update the saved_sigmask or the sigmask.

Doesn't the top of ep_poll() do all that?
Something like copying the current sigmask to the saved_sigmask and the user-supplied sigmask to the current sigmask. Otherwise the sleeps won't be interruptible.

It is worth noting that both pselect() and epoll_pwait() differ from sigwait() (and friends) which were (IIRC) the first system calls to try to remove the timing windows associated with waiting for signals.
sigwait() returns the signal number and removes it from the pending mask - so the signal handler won't be called for the returned signal. (Unless it wasn't actually masked!)
So the sigwait() code does have to restore the signal mask itself.

Quoted 7 lines
Quoted 5 lines

I'm guessing that on the way back to userspace signal handlers for signals enabled in the process's current mask (the one specified to epoll_pwait) get called.
Then the signal mask is loaded from current->saved_sigmask and and enabled signal handlers are called again.

Who is saving this saved_sigmask that is being restored on the way back?

It has to be saved when the user-supplied one in installed.

Quoted 4 lines
Quoted 2 lines

No special code there that depends on the syscall result, errno of the syscall number.

I didn’t say this has anything to do with errno.

The code snippet above checks it ...

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#26 lore raw
In reply to Deepa Dinamani
Quoted 4 lines · Deepa Dinamani

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

and even this part of your email is very confusing. because in this case we can never miss a signal. I'd say

1. block the signals you don't care about
2. unblock the signals which should interrupt the syscall below
3. syscall()
4. block the signals unblocked in 2.

Oleg.

#27 lore raw

I think you are misunderstanding what I said. You are taking things out of context. I was saying here what I did was inspired by why the syscall was designed to begin with. The syscall below refers to epoll_wait and not epoll_pwait.

-Deepa

Quoted 17 lines · Oleg Nesterov
Quoted 4 lines · Deepa Dinamani

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

and even this part of your email is very confusing. because in this case we can never miss a signal. I'd say

1. block the signals you don't care about
2. unblock the signals which should interrupt the syscall below
3. syscall()
4. block the signals unblocked in 2.

Oleg.

maintainer
#28 lore raw
Quoted 2 lines · Deepa Dinamani

I think you are misunderstanding what I said.

probably. Everything was very confusing to me from the very beginning. And yes, I can hardly understand your emails, sorry. This one too :/

Quoted 3 lines

You are taking things
out of context. I was saying here what I did was inspired by why the syscall was designed to begin with.

which syscall?

Quoted 2 lines

The syscall below refers to
epoll_wait and not epoll_pwait.

So you tried to explain why epoll_pwait() was designed? Or what?

Either way, everything I said below still looks right to me. This probably means that I still can't understand you.

But this is irrelevant. My main point is that the kernel was correct before 854a6ed568 ("signal: Add restore_user_sigmask()"), the (incomplete) patch I sent tries to a) restore the correct behaviour and b) simplify/cleanup the code.

Quoted 18 lines · Oleg Nesterov
Quoted 17 lines · Deepa Dinamani
Quoted 4 lines

1. block the signals you don't care about.
2. syscall()
3. unblock the signals blocked in 1.

and even this part of your email is very confusing. because in this case we can never miss a signal. I'd say

1. block the signals you don't care about
2. unblock the signals which should interrupt the syscall below
3. syscall()
4. block the signals unblocked in 2.

Oleg.

maintainer
#29 lore raw
In reply to David Laight

It seems that we all are just trying to confuse each other. I got lost.

Quoted 25 lines · David Laight

From: Oleg Nesterov

Quoted 5 lines

Sent: 23 May 2019 17:36

Quoted 3 lines · David Laight

From: Oleg Nesterov

Quoted 1 line · David Laight

...

Quoted 17 lines
Quoted 7 lines
Quoted 6 lines

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

Quoted 7 lines

Ugg...
Posix probably allows the signal handler be called at the point the event happens rather than being deferred until the system call completes. Queueing up the signal handler to be run at a later time (syscall exit) certainly makes sense.
Definitely safest to call the signal handler even if success/timeout is returned.

Why?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

pselect() exists to stop the entry race, not the exit one.

pselect() has to block SIG_URG again before it returns to user-mode, right?

Yep.
So the signal handler can't be called for a signal that happens after pselect() returns.

Yes. And "after pselect() returns" actually means "after pselect() restores the old sigmask while it returns to user mode".

Quoted 4 lines
Quoted 1 line

Suppose pselect() finds a ready fd, and this races with SIG_URG.

You mean if SIG_URG is raised after a ready fd is found (or even timeout)? So the return value isn't EINTR.

Yes.

Quoted 1 line

(If an fd is readable on entry, the SIG_URG could have happened much earlier.)

Why not? See the pseudo code above. It was blocked before pselect() was called. So SIG_URG can be already pending when pselect() is called but since an fd is already ready on entry pselect() restores the old sigmask (and thus blocks SIG_URG again) and returns success. The handler is not called.

However, if there is no a ready fd, pselect won't block. It will notice SIG_URG, deliver this signal, and return -EINTR.

Quoted 5 lines
Quoted 1 line

Why do you think the handler should run?

Think of the application code loop.
Consider what happens if the signal is SIG_INT - to request the program stop.

SIG_INT or SIG_URG ? Again, please look at the pseudo code above. SIG_INT is blocked and never unblocked.

Quoted 7 lines

After every pselect() call the application looks to see if the handler has been called.
If one of the fds is always readable pselect() will never return EINTR but you want the SIG_INT handler run so that the loop gets terminated. If you only call the signal handler when EINTR is returned the process will never stop.
So you need to call the handler even when pselect() succeeds/time out.

Then do not block SIG_INT ?

	block-all-signals-except-SIG_INT;
	ret = pselect(..., sigmask{SIG_URG, SIG_INT});
Quoted 7 lines
Quoted 3 lines

What if SIG_URG comes right after pselect() blocks SIG_URG again? I mean, how this differs the case when it comes before, but a ready fd was already found?

I suspect you need to defer the re-instatement of the original mask to the code that calls the signal handlers (which probably should be called with the programs signal mask).

This is what the kernel does when the signal is delivered, the original mask is restored after the signal handler runs.

Quoted 1 line

So that particular window doesn't exist.

Which window???

Oleg.

#30 lore raw

From: Oleg Nesterov

Quoted 2 lines

Sent: 24 May 2019 14:29
It seems that we all are just trying to confuse each other. I got lost.

I'm always lost :-)

Quoted 46 lines · David Laight
Quoted 25 lines

From: Oleg Nesterov

Quoted 5 lines

Sent: 23 May 2019 17:36

Quoted 3 lines · David Laight

From: Oleg Nesterov

Quoted 1 line · David Laight

...

Quoted 17 lines
Quoted 7 lines
Quoted 6 lines

Not sure I understand... OK, suppose that you do

	block-all-signals;
	ret = pselect(..., sigmask(SIG_URG));

if it returns success/timeout then the handler for SIG_URG should not be called?

Quoted 7 lines

Ugg...
Posix probably allows the signal handler be called at the point the event happens rather than being deferred until the system call completes. Queueing up the signal handler to be run at a later time (syscall exit) certainly makes sense.
Definitely safest to call the signal handler even if success/timeout is returned.

Why?

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

pselect() exists to stop the entry race, not the exit one.

pselect() has to block SIG_URG again before it returns to user-mode, right?

Yep.
So the signal handler can't be called for a signal that happens after pselect() returns.

Yes. And "after pselect() returns" actually means "after pselect() restores the old sigmask while it returns to user mode".

Quoted 4 lines
Quoted 1 line

Suppose pselect() finds a ready fd, and this races with SIG_URG.

You mean if SIG_URG is raised after a ready fd is found (or even timeout)? So the return value isn't EINTR.

Yes.

Quoted 1 line

(If an fd is readable on entry, the SIG_URG could have happened much earlier.)

Quoted 7 lines

Why not? See the pseudo code above. It was blocked before pselect() was called. So SIG_URG can be already pending when pselect() is called but since an fd is already ready on entry pselect() restores the old sigmask (and thus blocks SIG_URG again) and returns success. The handler is not called.

However, if there is no a ready fd, pselect won't block. It will notice SIG_URG, deliver this signal, and return -EINTR.

To my mind changing the signal mask should be enough to get a masked signal handler called - even if the mask is reset before the syscall exits. There shouldn't be any need for an interruptible wait to be interrupted.

I suspect that if you send a signal to a process that is looping in userspace (on a different) the signal handler is called on the next exit to userspace regardless as to whether the kernel blocks.

epoll and pselect shouldn't be any different.
Having the signal unmasked at any time should be enough to get it called.

...

Quoted 10 lines
Quoted 7 lines
Quoted 3 lines

What if SIG_URG comes right after pselect() blocks SIG_URG again? I mean, how this differs the case when it comes before, but a ready fd was already found?

I suspect you need to defer the re-instatement of the original mask to the code that calls the signal handlers (which probably should be called with the programs signal mask).

This is what the kernel does when the signal is delivered, the original mask is restored after the signal handler runs.

I'd have thought that the original signal mask (all blocked in the examples) should be restored before the signal handler is called.
After all the signal handler is allowed to modify the processes signal mask. I've had horrid thoughts about SIG_SUSPEND :-)

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#31 lore raw

From: David Laight

Quoted 1 line

Sent: 24 May 2019 16:00

...

Quoted 1 line

I've had horrid thoughts about SIG_SUSPEND :-)

Not to mention exiting signal handlers with longjmp().

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#32 lore raw
Quoted 7 lines · David Laight

From: David Laight

Quoted 1 line

Sent: 24 May 2019 16:00

...

Quoted 1 line

I've had horrid thoughts about SIG_SUSPEND :-)

Not to mention exiting signal handlers with longjmp().

that is why we have siglongjmp().

Oleg.

maintainer
#33 lore raw
In reply to David Laight
Quoted 6 lines · David Laight

From: Oleg Nesterov

Quoted 2 lines

Sent: 24 May 2019 14:29
It seems that we all are just trying to confuse each other. I got lost.

I'm always lost :-)

same here ;)

Quoted 2 lines

To my mind changing the signal mask should be enough to get a masked signal handler called - even if the mask is reset before the syscall exits.

well, the kernel doesn't do this, and on purpose.

Quoted 1 line

There shouldn't be any need for an interruptible wait to be interrupted.

can't parse ;)

Quoted 5 lines

I suspect that if you send a signal to a process that is looping in userspace (on a different) the signal handler is called on the next exit to userspace regardless as to whether the kernel blocks.

epoll and pselect shouldn't be any different.

They differ exactly because they manipulate the blocked mask,

Quoted 1 line

Having the signal unmasked at any time should be enough to get it called.

No. The sigmask passed to pselect() tells the kernel which signals should interrupt the syscall if it blocks. The fact that pselect() actually unblocks a signal is just the internal implementation detail.

Quoted 9 lines
Quoted 6 lines
Quoted 3 lines

I suspect you need to defer the re-instatement of the original mask to the code that calls the signal handlers (which probably should be called with the programs signal mask).

This is what the kernel does when the signal is delivered, the original mask is restored after the signal handler runs.

I'd have thought that the original signal mask (all blocked in the examples) should be restored before the signal handler is called.

No. And this means that if you have 2 pending signals, they both will be delivered. Unless of course sigaction->sa_mask includes the 2nd one.

Quoted 1 line

After all the signal handler is allowed to modify the processes signal mask.

only untill the handler returns.

Quoted 1 line

I've had horrid thoughts about SIG_SUSPEND :-)

google knows nothing about SIG_SUSPEND, neither me ;)

Oleg.

#34 lore raw

From: Oleg Nesterov

Quoted 16 lines

Sent: 24 May 2019 16:44

Quoted 2 lines

To my mind changing the signal mask should be enough to get a masked signal handler called - even if the mask is reset before the syscall exits.

well, the kernel doesn't do this, and on purpose.

Quoted 1 line

There shouldn't be any need for an interruptible wait to be interrupted.

can't parse ;)

Quoted 5 lines

I suspect that if you send a signal to a process that is looping in userspace (on a different) the signal handler is called on the next exit to userspace regardless as to whether the kernel blocks.

epoll and pselect shouldn't be any different.

Quoted 7 lines

They differ exactly because they manipulate the blocked mask,

Quoted 1 line

Having the signal unmasked at any time should be enough to get it called.

No. The sigmask passed to pselect() tells the kernel which signals should interrupt the syscall if it blocks. The fact that pselect() actually unblocks a signal is just the internal implementation detail.

If you take that line of reasoning the signal handler shouldn't be called at all.

For pselect() (which ought to work the same way as epoll_pwait()) the man page states that the current signal mask is replaced by the specified one for the duration of the call - so you'd expect signal handlers to run even if pselect() returns >= 0.

Consider a program that disables all signals at the top of main() then has a processing loop with epoll_pwait() (or pselect()) at the top) that enables a variety of signals.

It would be reasonable to expect that a signal handler would run even if one of the fds was always 'ready'.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#35 lore raw
In reply to Deepa Dinamani
Quoted 14 lines · Deepa Dinamani
Quoted 14 lines
Quoted 10 lines

include/linux/sched/signal.h void task_join_group_stop(struct task_struct *task); · line 419

Quoted 8 lines
include/linux/sched/signal.h +0 -1
@@ -416,7 +416,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
   set_thread_flag(TIF_RESTORE_SIGMASK);
-    WARN_ON(!test_thread_flag(TIF_SIGPENDING));

So you always want do_signal() to be called?

Why do you think so? No. This is just to avoid the warning, because with the patch I sent set_restore_sigmask() is called "in advance".

Quoted 6 lines
Quoted 4 lines
Quoted 2 lines

You will have to check each architecture's implementation of do_signal() to check if that has any side effects.

I don't think so.

Why not?

Why yes?

it seems that we have some communication problems. OK, please look at the code I proposed, I only added a couple of TODO comments

	static inline void set_restore_sigmask(void)
	{
		// WARN_ON(!TIF_SIGPENDING) was removed by this patch
		current->restore_sigmask = true;
	}

	int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
	{
		sigset_t *kmask;

		if (!umask)
			return 0;

		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (copy_from_user(kmask, umask, sizeof(sigset_t)))
			return -EFAULT;

		set_restore_sigmask();
		current->saved_sigmask = current->blocked;
		set_current_blocked(kmask);

		return 0;
	}

	SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
			int, maxevents, int, timeout, const sigset_t __user *, sigmask,
			size_t, sigsetsize)
	{
		int error;

		/*
		 * If the caller wants a certain signal mask to be set during the wait,
		 * we apply it here.
		 */
		error = set_user_sigmask(sigmask, sigsetsize);
		if (error)
			return error;

		error = do_epoll_wait(epfd, events, maxevents, timeout);

		// TODO. Add another helper to restore WARN_ON(!TIF_SIGPENDING)
		// in case restore_saved_sigmask() is NOT called.

		if (error != -EINTR)
			restore_saved_sigmask();

		return error;
	}

Note that it looks much simpler. Now, could you please explain

- why do you think this code is not correct ?

- why do you think we need to audit do_signal() ???

Quoted 11 lines
Quoted 11 lines
Quoted 1 line

Although this is not what the patch is solving.

Sure. But you know, after I tried to read the changelog, I am not sure I understand what exactly you are trying to fix. Could you please explain this part

The behavior
before 854a6ed56839a was that the signals were dropped after the error code was decided. This resulted in lost signals but the userspace did not notice it

Quoted 6 lines
Quoted 2 lines

? I fail to understand it, sorry. It looks as if the code was already buggy before that commit and it could miss a signal or something like this, but I do not see how.

Did you read the explanation pointed to in the commit text? :

https://lore.kernel.org/linux-fsdevel/20190427093319.sgicqik2oqkez3wk@dcvr/

this link points to the lengthy and confusing discussion... after a quick glance I didn't find an answer to my question, so let me repeat it again: why do you think the kernel was buggy even before 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal:
Add restore_user_sigmask()") ?

Just in case...
https://lore.kernel.org/linux-fsdevel/CABeXuvq7gCV2qPOo+Q8jvNyRaTvhkRLRbnL_oJ-AuK7Sp=P3QQ@mail.gmail.com/
doesn't look right to me... let me quite some parts of your email:

- /*
- * If we changed the signal mask, we need to restore the original one.
- * In case we've got a signal while waiting, we do not restore the
- * signal mask yet, and we allow do_signal() to deliver the signal on
- * the way back to userspace, before the signal mask is restored.
- */
- if (sigmask) {
- if (error == -EINTR) {
- memcpy(&current->saved_sigmask, &sigsaved,
- sizeof(sigsaved));
- set_restore_sigmask();
- } else

**** Execution reaches this else statement and the sigmask is restored directly, ignoring the newly generated signal.

I see nothing wrong. This is what we want.

The signal is never
handled.

Well, "never" is not right. It won't be handled now, because it is blocked, but for example think of another pselect/whatever call with the same sigmask.

Quoted 1 line

It would be better to understand the isssue before we start discussing the fix.

Agreed. And that is why I am asking for your explanations, quite possibly I missed something, but so far I fail to understand you.

Oleg.

#36 lore raw
In reply to Deepa Dinamani

+Cc: linux-mm, since this broke mmots tree and has been applied there

This patch is missing a definition for signal_detected in io_cqring_wait, which breaks the build.

io_uring.c +0 -0
index b785c8d7efc4..b34311675d2d 100644
@@ -2182,7 +2182,7 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
{
       struct io_cq_ring *ring = ctx->cq_ring;
       sigset_t ksigmask, sigsaved;

- int ret;
+ int ret, signal_detected;

if (io_cqring_events(ring) >= min_events)
return 0;

#37 lore raw
Quoted 5 lines · Chris Down

+Cc: linux-mm, since this broke mmots tree and has been applied there

This patch is missing a definition for signal_detected in io_cqring_wait, which breaks the build.

This patch does not break the build.
The patch the breaks the build was the v2 of this patch since there was an accidental deletion.
That's what the v3 fixed. I think v3 got picked up today morning into the mm tree

-Deepa

maintainer
#38 lore raw

pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

In reply to Deepa Dinamani

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

I think that the following code is correct

	int interrupted = 0;

	void sigint_handler(int sig)
	{
		interrupted = 1;
	}

	int main(void)
	{
		sigset_t sigint, empty;

		sigemptyset(&sigint);
		sigaddset(&sigint, SIGINT);
		sigprocmask(SIG_BLOCK, &sigint, NULL);

		signal(SIGINT, sigint_handler);

		sigemptyset(&empty);	// so pselect() unblocks SIGINT

		ret = pselect(..., &empty);

		if (ret >= 0)		// sucess or timeout
			assert(!interrupted);

		if (interrupted)
			assert(ret == -EINTR);
	}

IOW, if pselect(sigmask) temporary unblocks SIGINT according to sigmask, this signal should not be delivered if a ready fd was found or timeout. The signal handle should only run if ret == -EINTR.

(pselect() can be interrupted by any other signal which has a handler. In this
case the handler can be called even if ret >= 0. This is correct, I fail to understand why some people think this is wrong, and in any case we simply can't avoid this).

This was true until 854a6ed56839a ("signal: Add restore_user_sigmask()"), now this is broken by the signal_pending() check in restore_user_sigmask().

This patch https://lore.kernel.org/lkml/[email protected]/
turns 0 into -EINTR if signal_pending(), but I think we should simply restore the old behaviour and simplify the code.

See the compile-tested patch at the end. Of course, the new _xxx() helpers should be renamed somehow. fs/aio.c doesn't look right with or without this patch, but iiuc this is what it did before 854a6ed56839a.

Let me show the code with the patch applied. I am using epoll_pwait() as an example because it looks very simple.

	static inline void set_restore_sigmask(void)
	{
// WARN_ON(!TIF_SIGPENDING) was removed by this patch
		current->restore_sigmask = true;
	}

	int set_xxx(const sigset_t __user *umask, size_t sigsetsize)
	{
		sigset_t *kmask;

		if (!umask)
			return 0;
		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (copy_from_user(kmask, umask, sizeof(sigset_t)))
			return -EFAULT;

// we can safely modify ->saved_sigmask/restore_sigmask, they has no meaning // until the syscall returns.

		set_restore_sigmask();
		current->saved_sigmask = current->blocked;
		set_current_blocked(kmask);

		return 0;
	}


	void update_xxx(bool interrupted)
	{
// the main reason for this helper is WARN_ON(!TIF_SIGPENDING) which was "moved"
// from set_restore_sigmask() above.
		if (interrupted)
			WARN_ON(!test_thread_flag(TIF_SIGPENDING));
		else
			restore_saved_sigmask();
	}

	SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
			int, maxevents, int, timeout, const sigset_t __user *, sigmask,
			size_t, sigsetsize)
	{
		int error;

		error = set_xxx(sigmask, sigsetsize);
		if (error)
			return error;

		error = do_epoll_wait(epfd, events, maxevents, timeout);
		update_xxx(error == -EINTR);

		return error;
	}

Oleg.
---

 fs/aio.c                     | 40 ++++++++++++++---------
 fs/eventpoll.c               | 12 +++----
 fs/io_uring.c                | 12 +++----
 fs/select.c                  | 40 +++++++----------------
 include/linux/compat.h       |  4 +--
 include/linux/sched/signal.h |  2 --
 include/linux/signal.h       |  6 ++--
 kernel/signal.c              | 77 ++++++++++++++++----------------------------
 8 files changed, 74 insertions(+), 119 deletions(-)
fs/aio.c +24 -16
index 3490d1f..8315bd2 100644
@@ -2093,8 +2093,8 @@ SYSCALL_DEFINE6(io_pgetevents,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_timespec64(&ts, timeout)))
@@ -2103,13 +2103,15 @@ SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	update_xxx(interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2126,8 +2128,8 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_old_timespec32(&ts, timeout)))
@@ -2137,13 +2139,15 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return -EFAULT;
 
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	update_xxx(interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2191,8 +2195,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_old_timespec32(&t, timeout))
@@ -2201,13 +2205,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	update_xxx(interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2224,8 +2230,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_timespec64(&t, timeout))
@@ -2234,13 +2240,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	update_xxx(interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
fs/eventpoll.c +4 -8
index 4a0e98d..0b1a337 100644
@@ -2318,19 +2318,17 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		size_t, sigsetsize)
 {
 	int error;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	error = set_xxx(sigmask, sigsetsize);
 	if (error)
 		return error;
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved);
+	update_xxx(error == -EINTR);
 
 	return error;
 }
@@ -2343,19 +2341,17 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 			compat_size_t, sigsetsize)
 {
 	long err;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	err = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	err = set_compat_xxx(sigmask, sigsetsize);
 	if (err)
 		return err;
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved);
+	update_xxx(err == -EINTR);
 
 	return err;
 }
fs/io_uring.c +4 -8
index 310f8d1..b5b99e2 100644
@@ -2180,7 +2180,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 			  const sigset_t __user *sig, size_t sigsz)
 {
 	struct io_cq_ring *ring = ctx->cq_ring;
-	sigset_t ksigmask, sigsaved;
 	int ret;
 
 	if (io_cqring_events(ring) >= min_events)
@@ -2189,24 +2188,21 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 	if (sig) {
 #ifdef CONFIG_COMPAT
 		if (in_compat_syscall())
-			ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig,
-						      &ksigmask, &sigsaved, sigsz);
+			ret = set_compat_xxx((const compat_sigset_t __user *)sig,
+						      sigsz);
 		else
 #endif
-			ret = set_user_sigmask(sig, &ksigmask,
-					       &sigsaved, sigsz);
+			ret = set_xxx(sig, sigsz);
 
 		if (ret)
 			return ret;
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
+	update_xxx(ret == -ERESTARTSYS);
 	if (ret == -ERESTARTSYS)
 		ret = -EINTR;
 
-	if (sig)
-		restore_user_sigmask(sig, &sigsaved);
-
 	return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }
 
fs/select.c +12 -28
index 6cbc9ff..7eab132 100644
@@ -730,7 +730,6 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		       const sigset_t __user *sigmask, size_t sigsetsize,
 		       enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -753,15 +752,14 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
+	update_xxx(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1087,7 +1085,6 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1100,18 +1097,16 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	update_xxx(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
@@ -1123,7 +1118,6 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct old_timespec32 __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1136,18 +1130,16 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	update_xxx(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1322,7 +1314,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 	void __user *tsp, compat_sigset_t __user *sigmask,
 	compat_size_t sigsetsize, enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1345,15 +1336,14 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
+	update_xxx(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1406,7 +1396,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct old_timespec32 __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1419,18 +1408,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	update_xxx(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1442,7 +1429,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct __kernel_timespec __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1455,18 +1441,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_xxx(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	update_xxx(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
include/linux/compat.h +1 -3
index ebddcb6..b20b001 100644
@@ -138,9 +138,7 @@ typedef struct {
 	compat_sigset_word	sig[_COMPAT_NSIG_WORDS];
 } compat_sigset_t;
 
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-			    sigset_t *set, sigset_t *oldset,
-			    size_t sigsetsize);
+int set_compat_xxx(const compat_sigset_t __user *umask, size_t sigsetsize);
 
 struct compat_sigaction {
 #ifndef __ARCH_HAS_IRIX_SIGACTION
include/linux/sched/signal.h +0 -2
index 38a0f07..8b5b537 100644
@@ -417,7 +417,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
 	set_thread_flag(TIF_RESTORE_SIGMASK);
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
@@ -448,7 +447,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
 	current->restore_sigmask = true;
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
 {
include/linux/signal.h +2 -4
index 9702016..65f84ac 100644
@@ -273,10 +273,8 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
 			       struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-	sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
-				 sigset_t *sigsaved);
+extern int set_xxx(const sigset_t __user *umask, size_t sigsetsize);
+extern void update_xxx(bool interupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +27 -50

106 lines not on this page · raw message

#39 lore raw

RE: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

From: Oleg Nesterov

Quoted 23 lines

Sent: 29 May 2019 17:12
Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

I think that the following code is correct

	int interrupted = 0;

	void sigint_handler(int sig)
	{
		interrupted = 1;
	}

	int main(void)
	{
		sigset_t sigint, empty;

		sigemptyset(&sigint);
Quoted 8 lines
		sigaddset(&sigint, SIGINT);
		sigprocmask(SIG_BLOCK, &sigint, NULL);

		signal(SIGINT, sigint_handler);

		sigemptyset(&empty);	// so pselect() unblocks SIGINT

		ret = pselect(..., &empty);
                                ^^^^^ sigint
Quoted 11 lines
		if (ret >= 0)		// sucess or timeout
			assert(!interrupted);

		if (interrupted)
			assert(ret == -EINTR);
	}

IOW, if pselect(sigmask) temporary unblocks SIGINT according to sigmask, this signal should not be delivered if a ready fd was found or timeout. The signal handle should only run if ret == -EINTR.

Personally I think that is wrong.
Given code like the above that has:

		while (!interrupted) {
			pselect(..., &sigint);
			// process available data
		}

You want the signal handler to be executed even if one of the fds always has available data.
Otherwise you can't interrupt a process that is always busy.

One option is to return -EINTR if a signal is pending when the mask is updated - before even looking at anything else.
Signals that happen later on (eg after a timeout) need not be reported (until the next time around the loop).

Quoted 4 lines

(pselect() can be interrupted by any other signal which has a handler. In this
case the handler can be called even if ret >= 0. This is correct, I fail to understand why some people think this is wrong, and in any case we simply can't avoid this).

You mean any signal that isn't blocked when pselect() is called....

Quoted 15 lines

This was true until 854a6ed56839a ("signal: Add restore_user_sigmask()"), now this is broken by the signal_pending() check in restore_user_sigmask().

This patch https://lore.kernel.org/lkml/[email protected]/
turns 0 into -EINTR if signal_pending(), but I think we should simply restore the old behaviour and simplify the code.

See the compile-tested patch at the end. Of course, the new _xxx() helpers should be renamed somehow. fs/aio.c doesn't look right with or without this patch, but iiuc this is what it did before 854a6ed56839a.

Let me show the code with the patch applied. I am using epoll_pwait() as an example because it looks very simple.

Quoted 9 lines
	static inline void set_restore_sigmask(void)
	{
// WARN_ON(!TIF_SIGPENDING) was removed by this patch
		current->restore_sigmask = true;
	}

	int set_xxx(const sigset_t __user *umask, size_t sigsetsize)
	{
		sigset_t *kmask;

^ no '*' here, add & before uses.

Quoted 19 lines
		if (!umask)
			return 0;
		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (copy_from_user(kmask, umask, sizeof(sigset_t)))
			return -EFAULT;

// we can safely modify ->saved_sigmask/restore_sigmask, they has no meaning // until the syscall returns.

		set_restore_sigmask();
		current->saved_sigmask = current->blocked;
		set_current_blocked(kmask);

		return 0;
	}


	void update_xxx(bool interrupted)
Quoted 8 lines
	{
// the main reason for this helper is WARN_ON(!TIF_SIGPENDING) which was "moved"
// from set_restore_sigmask() above.
		if (interrupted)
			WARN_ON(!test_thread_flag(TIF_SIGPENDING));
		else
			restore_saved_sigmask();
	}

I looked at the code earlier, but failed to find the code that actually delivers the signals.
It may be 'racy' with update_xxx() regardless of whether that is looking for -EINTR or just a pending signal.

I assume that TIF_SIGPENGING is used to (not) short-circuit the system call return path so that signals get delivered.
So that it is important that update_xxx() calls restore_saved_sigmask() if there is no signal pending.
(Although a signal can happen after the test - which can/will be ignored until the signal is enabled again.)

restore_saved_sigmask() must itself be able to set TIF_SIGPENDING (the inner sigmask could be more restrictive!).

If restore_saved_sigmask() isn't called here, the syscall return path must do it after calling all the handlers and after clearing TIF_SIGPENDING, and then call unmasked handlers again.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#40 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

Quoted 47 lines · David Laight

From: Oleg Nesterov

Quoted 23 lines

Sent: 29 May 2019 17:12
Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

I think that the following code is correct

	int interrupted = 0;

	void sigint_handler(int sig)
	{
		interrupted = 1;
	}

	int main(void)
	{
		sigset_t sigint, empty;

		sigemptyset(&sigint);
Quoted 8 lines
		sigaddset(&sigint, SIGINT);
		sigprocmask(SIG_BLOCK, &sigint, NULL);

		signal(SIGINT, sigint_handler);

		sigemptyset(&empty);	// so pselect() unblocks SIGINT

		ret = pselect(..., &empty);
                                ^^^^^ sigint
Quoted 11 lines
		if (ret >= 0)		// sucess or timeout
			assert(!interrupted);

		if (interrupted)
			assert(ret == -EINTR);
	}

IOW, if pselect(sigmask) temporary unblocks SIGINT according to sigmask, this signal should not be delivered if a ready fd was found or timeout. The signal handle should only run if ret == -EINTR.

Personally I think that is wrong.
Given code like the above that has:

Quoted 8 lines
		while (!interrupted) {
			pselect(..., &sigint);
			// process available data
		}

You want the signal handler to be executed even if one of the fds always has available data.
Otherwise you can't interrupt a process that is always busy.

Agreed... I believe cmogstored has always had a bug in the way it uses epoll_pwait because it failed to check interrupts if:

a) an FD is ready + interrupt
b) epoll_pwait returns 0 on interrupt

The bug remains in userspace for a), which I will fix by adding an interrupt check when an FD is ready. The window is very small for a) and difficult to trigger, and also in a rare code path.

The b) case is the kernel bug introduced in 854a6ed56839a40f ("signal: Add restore_user_sigmask()").

I don't think there's any disagreement that b) is a kernel bug.

So the confusion is for a), and POSIX is not clear w.r.t. how pselect/poll works when there's both FD readiness and an interrupt.

Thus I'm inclined to believe *select/*poll/epoll_*wait should follow POSIX read() semantics:

If a read() is interrupted by a signal before it reads any data, it shall return −1 with errno set to [EINTR].

If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read.

Quoted 5 lines

One option is to return -EINTR if a signal is pending when the mask is updated - before even looking at anything else.

Signals that happen later on (eg after a timeout) need not be reported (until the next time around the loop).

I'm not sure that's necessary and it would cause delays in signal handling.

#41 lore raw

RE: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

From: Eric Wong

Quoted 1 line

Sent: 29 May 2019 19:50

...

Quoted 10 lines
Quoted 10 lines

Personally I think that is wrong.
Given code like the above that has:

		while (!interrupted) {
			pselect(..., &sigint);
			// process available data
		}

You want the signal handler to be executed even if one of the fds always has available data.
Otherwise you can't interrupt a process that is always busy.

FWIW in the past I've seen a process that loops select-accept-fork-exec get its priority reduced to the point where it never blocked in select. The client side retries made it go badly wrong! If it had limited when SIG_INT was detected it would have been a little more difficult to kill.

Quoted 5 lines

Agreed... I believe cmogstored has always had a bug in the way it uses epoll_pwait because it failed to check interrupts if:

a) an FD is ready + interrupt
b) epoll_pwait returns 0 on interrupt

But the kernel code seems to only call the signal handler (for signals that are enabled during pselect() (etc)) if the underlying wait is interrupted.

Quoted 9 lines

The bug remains in userspace for a), which I will fix by adding an interrupt check when an FD is ready. The window is very small for a) and difficult to trigger, and also in a rare code path.

The b) case is the kernel bug introduced in 854a6ed56839a40f ("signal: Add restore_user_sigmask()").

I don't think there's any disagreement that b) is a kernel bug.

If the signal is raised after the wait has timed out but before the signal mask is restored.

Quoted 4 lines

So the confusion is for a), and POSIX is not clear w.r.t. how pselect/poll works when there's both FD readiness and an interrupt.

Quoted 8 lines

Thus I'm inclined to believe *select/*poll/epoll_*wait should follow POSIX read() semantics:

If a read() is interrupted by a signal before it reads any data, it shall return −1 with errno set to [EINTR].

If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read.

Except that above you want different semantics :-)
For read() any signal handler is always called.
And the return value of a non-blocking read that returns no data is not affected by any pending signals.

For pselect() that would mean that if the wait timed out (result 0) and then a signal was raised (before the mask got changed back) then the return value would be zero and the signal handler would be called. So your (b) above is not a bug.
Even select() can return 'timeout' and have a signal handler called.

There are really two separate issues:
1) Signals that are pending when the new mask is applied.
2) Signals that are raised after the wait terminates (success or timeout). If the signal handlers for (2) are not called then they become (1) next time around the application loop.

Maybe the 'restore sigmask' function should be passed an indication of whether it is allowed to let signal handler be called and return whether they would be called (ie whether it restored the signal mask or left it for the syscall exit code to do after calling the signal handlers).

That would allow epoll() to convert timeout+pending signal to EINTR, or to allow all handlers be called regardless of the return value.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#42 lore raw

Re: pselect/etc semantics

In reply to Eric Wong
Quoted 11 lines · Eric Wong

Agreed... I believe cmogstored has always had a bug in the way it uses epoll_pwait because it failed to check interrupts if:

a) an FD is ready + interrupt
b) epoll_pwait returns 0 on interrupt

The bug remains in userspace for a), which I will fix by adding an interrupt check when an FD is ready. The window is very small for a) and difficult to trigger, and also in a rare code path.

Quoted 4 lines

The b) case is the kernel bug introduced in 854a6ed56839a40f ("signal: Add restore_user_sigmask()").

I don't think there's any disagreement that b) is a kernel bug.

See my reply to Oleg. I think (b) is a regression that needs to be fixed. I do not think that (b) is a kernel bug. Both versions of the of what sigmask means posix and naive will allow (b).

Because fundamentally the sigmask is restored after the rest of the system call happens.

Eric

#43 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

In reply to Oleg Nesterov

Resending due to inadvertent conversion of prior message to html.

fs/aio.c SYSCALL_DEFINE6(io_pgetevents,

Quoted 144 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

I think that the following code is correct

        int interrupted = 0;

        void sigint_handler(int sig)
        {
                interrupted = 1;
        }

        int main(void)
        {
                sigset_t sigint, empty;

                sigemptyset(&sigint);
                sigaddset(&sigint, SIGINT);
                sigprocmask(SIG_BLOCK, &sigint, NULL);

                signal(SIGINT, sigint_handler);

                sigemptyset(&empty);    // so pselect() unblocks SIGINT

                ret = pselect(..., &empty);

                if (ret >= 0)           // sucess or timeout
                        assert(!interrupted);

                if (interrupted)
                        assert(ret == -EINTR);
        }

IOW, if pselect(sigmask) temporary unblocks SIGINT according to sigmask, this signal should not be delivered if a ready fd was found or timeout. The signal handle should only run if ret == -EINTR.

(pselect() can be interrupted by any other signal which has a handler. In this
case the handler can be called even if ret >= 0. This is correct, I fail to understand why some people think this is wrong, and in any case we simply can't avoid this).

This was true until 854a6ed56839a ("signal: Add restore_user_sigmask()"), now this is broken by the signal_pending() check in restore_user_sigmask().

This patch https://lore.kernel.org/lkml/[email protected]/
turns 0 into -EINTR if signal_pending(), but I think we should simply restore the old behaviour and simplify the code.

See the compile-tested patch at the end. Of course, the new _xxx() helpers should be renamed somehow. fs/aio.c doesn't look right with or without this patch, but iiuc this is what it did before 854a6ed56839a.

Let me show the code with the patch applied. I am using epoll_pwait() as an example because it looks very simple.

static inline void set_restore_sigmask(void) {
// WARN_ON(!TIF_SIGPENDING) was removed by this patch

                current->restore_sigmask = true;
        }

        int set_xxx(const sigset_t __user *umask, size_t sigsetsize)
        {
                sigset_t *kmask;

                if (!umask)
                        return 0;
                if (sigsetsize != sizeof(sigset_t))
                        return -EINVAL;
                if (copy_from_user(kmask, umask, sizeof(sigset_t)))
                        return -EFAULT;

// we can safely modify ->saved_sigmask/restore_sigmask, they has no meaning // until the syscall returns.

                set_restore_sigmask();
                current->saved_sigmask = current->blocked;
                set_current_blocked(kmask);

                return 0;
        }


        void update_xxx(bool interrupted)
        {
// the main reason for this helper is WARN_ON(!TIF_SIGPENDING) which was "moved"
// from set_restore_sigmask() above.
                if (interrupted)
                        WARN_ON(!test_thread_flag(TIF_SIGPENDING));
                else
                        restore_saved_sigmask();
        }

        SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
                        int, maxevents, int, timeout, const sigset_t __user *, sigmask,
                        size_t, sigsetsize)
        {
                int error;

                error = set_xxx(sigmask, sigsetsize);
                if (error)
                        return error;

                error = do_epoll_wait(epfd, events, maxevents, timeout);
                update_xxx(error == -EINTR);

                return error;
        }

Oleg.
---

 fs/aio.c                     | 40 ++++++++++++++---------
 fs/eventpoll.c               | 12 +++----
 fs/io_uring.c                | 12 +++----
 fs/select.c                  | 40 +++++++----------------
 include/linux/compat.h       |  4 +--
 include/linux/sched/signal.h |  2 --
 include/linux/signal.h       |  6 ++--
 kernel/signal.c              | 77 ++++++++++++++++----------------------------
 8 files changed, 74 insertions(+), 119 deletions(-)
fs/aio.c +1 -1
index 3490d1f..8315bd2 100644
@@ -2093,8 +2093,8 @@ SYSCALL_DEFINE6(io_pgetevents,
                const struct __aio_sigset __user *, usig)
 {
        struct __aio_sigset     ksig = { NULL, };
-       sigset_t                ksigmask, sigsaved;
        struct timespec64       ts;
+       bool interrupted;
        int ret;
 
        if (timeout && unlikely(get_timespec64(&ts, timeout)))
@@ -2103,13 +2103,15 @@ SYSCALL_DEFINE6(io_pgetevents,

I already admitted I was wrong about this. And the behavior you point out is consistent with man page. I'm not sure why we are not understanding that I'm agreeing with you:
https://lore.kernel.org/lkml/CABeXuvouBzZuNarmNcd9JgZgvonL1N_p21gat=O_x0-1hMx=6A@mail.gmail.com/

This was true until 854a6ed56839a("signal: Add restore_user_sigmask()"),

now this is broken by the signal_pending() check in restore_user_sigmask().

This patch is wrong because I did not know about the above behavior before. I also admitted to this. And proposed another way to revert the patch.

This patch
https://lore.kernel.org/lkml/[email protected]/
turns 0 into -EINTR if signal_pending(), but I think we should simply restore the old behaviour and simplify the code.

I agree that the patch that turns 0 to -EINTR should not be applied. It was my misunderstanding.

The part I'm not sure is that I cannot find anything that is wrong with 854a6ed56839a("signal: Add restore_user_sigmask()") that was not already before. But, that is ok, we can do the revert and I will investigate with the test application if I can find something.

-Deepa

Quoted 530 lines
fs/aio.c +23 -15
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
        return ret;
@@ -2126,8 +2128,8 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
                const struct __aio_sigset __user *, usig)
 {
        struct __aio_sigset     ksig = { NULL, };
-       sigset_t                ksigmask, sigsaved;
        struct timespec64       ts;
+       bool interrupted;
        int ret;
 
        if (timeout && unlikely(get_old_timespec32(&ts, timeout)))
@@ -2137,13 +2139,15 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
                return -EFAULT;
 
 
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
        return ret;
@@ -2191,8 +2195,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
                const struct __compat_aio_sigset __user *, usig)
 {
        struct __compat_aio_sigset ksig = { NULL, };
-       sigset_t ksigmask, sigsaved;
        struct timespec64 t;
+       bool interrupted;
        int ret;
 
        if (timeout && get_old_timespec32(&t, timeout))
@@ -2201,13 +2205,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
        return ret;
@@ -2224,8 +2230,8 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
                const struct __compat_aio_sigset __user *, usig)
 {
        struct __compat_aio_sigset ksig = { NULL, };
-       sigset_t ksigmask, sigsaved;
        struct timespec64 t;
+       bool interrupted;
        int ret;
 
        if (timeout && get_timespec64(&t, timeout))
@@ -2234,13 +2240,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
 
        return ret;
fs/eventpoll.c +4 -8
index 4a0e98d..0b1a337 100644
@@ -2318,19 +2318,17 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
                size_t, sigsetsize)
 {
        int error;
-       sigset_t ksigmask, sigsaved;
 
        /*
         * If the caller wants a certain signal mask to be set during the wait,
         * we apply it here.
         */
-       error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       error = set_xxx(sigmask, sigsetsize);
        if (error)
                return error;
 
        error = do_epoll_wait(epfd, events, maxevents, timeout);
-
-       restore_user_sigmask(sigmask, &sigsaved);
+       update_xxx(error == -EINTR);
 
        return error;
 }
@@ -2343,19 +2341,17 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
                        compat_size_t, sigsetsize)
 {
        long err;
-       sigset_t ksigmask, sigsaved;
 
        /*
         * If the caller wants a certain signal mask to be set during the wait,
         * we apply it here.
         */
-       err = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       err = set_compat_xxx(sigmask, sigsetsize);
        if (err)
                return err;
 
        err = do_epoll_wait(epfd, events, maxevents, timeout);
-
-       restore_user_sigmask(sigmask, &sigsaved);
+       update_xxx(err == -EINTR);
 
        return err;
 }
fs/io_uring.c +4 -8
index 310f8d1..b5b99e2 100644
@@ -2180,7 +2180,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
                          const sigset_t __user *sig, size_t sigsz)
 {
        struct io_cq_ring *ring = ctx->cq_ring;
-       sigset_t ksigmask, sigsaved;
        int ret;
 
        if (io_cqring_events(ring) >= min_events)
@@ -2189,24 +2188,21 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
        if (sig) {
 #ifdef CONFIG_COMPAT
                if (in_compat_syscall())
-                       ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig,
-                                                     &ksigmask, &sigsaved, sigsz);
+                       ret = set_compat_xxx((const compat_sigset_t __user *)sig,
+                                                     sigsz);
                else
 #endif
-                       ret = set_user_sigmask(sig, &ksigmask,
-                                              &sigsaved, sigsz);
+                       ret = set_xxx(sig, sigsz);
 
                if (ret)
                        return ret;
        }
 
        ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
+       update_xxx(ret == -ERESTARTSYS);
        if (ret == -ERESTARTSYS)
                ret = -EINTR;
 
-       if (sig)
-               restore_user_sigmask(sig, &sigsaved);
-
        return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }
 
fs/select.c +12 -28
index 6cbc9ff..7eab132 100644
@@ -730,7 +730,6 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
                       const sigset_t __user *sigmask, size_t sigsetsize,
                       enum poll_time_type type)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -753,15 +752,14 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = core_sys_select(n, inp, outp, exp, to);
+       update_xxx(ret == -ERESTARTNOHAND);
        ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
        return ret;
 }
 
@@ -1087,7 +1085,6 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
                struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask,
                size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1100,18 +1097,16 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
+       update_xxx(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
        return ret;
@@ -1123,7 +1118,6 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
                struct old_timespec32 __user *, tsp, const sigset_t __user *, sigmask,
                size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1136,18 +1130,16 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
                        return -EINVAL;
        }
 
-       ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
+       update_xxx(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
        return ret;
@@ -1322,7 +1314,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
        void __user *tsp, compat_sigset_t __user *sigmask,
        compat_size_t sigsetsize, enum poll_time_type type)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1345,15 +1336,14 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = compat_core_sys_select(n, inp, outp, exp, to);
+       update_xxx(ret == -ERESTARTNOHAND);
        ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
        return ret;
 }
 
@@ -1406,7 +1396,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
        unsigned int,  nfds, struct old_timespec32 __user *, tsp,
        const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1419,18 +1408,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
+       update_xxx(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
        return ret;
@@ -1442,7 +1429,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
        unsigned int,  nfds, struct __kernel_timespec __user *, tsp,
        const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-       sigset_t ksigmask, sigsaved;
        struct timespec64 ts, end_time, *to = NULL;
        int ret;
 
@@ -1455,18 +1441,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
                        return -EINVAL;
        }
 
-       ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+       ret = set_compat_xxx(sigmask, sigsetsize);
        if (ret)
                return ret;
 
        ret = do_sys_poll(ufds, nfds, to);
 
-       restore_user_sigmask(sigmask, &sigsaved);
-
+       update_xxx(ret == -EINTR);
        /* We can restart this syscall, usually */
        if (ret == -EINTR)
                ret = -ERESTARTNOHAND;
-
        ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
        return ret;
include/linux/compat.h +1 -3
index ebddcb6..b20b001 100644
@@ -138,9 +138,7 @@ typedef struct {
        compat_sigset_word      sig[_COMPAT_NSIG_WORDS];
 } compat_sigset_t;
 
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-                           sigset_t *set, sigset_t *oldset,
-                           size_t sigsetsize);
+int set_compat_xxx(const compat_sigset_t __user *umask, size_t sigsetsize);
 
 struct compat_sigaction {
 #ifndef __ARCH_HAS_IRIX_SIGACTION
include/linux/sched/signal.h +0 -2
index 38a0f07..8b5b537 100644
@@ -417,7 +417,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
        set_thread_flag(TIF_RESTORE_SIGMASK);
-       WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
@@ -448,7 +447,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
        current->restore_sigmask = true;
-       WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
 {
include/linux/signal.h +2 -4
index 9702016..65f84ac 100644
@@ -273,10 +273,8 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
                               struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-       sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
-                                sigset_t *sigsaved);
+extern int set_xxx(const sigset_t __user *umask, size_t sigsetsize);
+extern void update_xxx(bool interupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +27 -50
index d7b9d14..0a1ec68 100644
@@ -2861,79 +2861,56 @@ EXPORT_SYMBOL(sigprocmask);
  *
  * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
  * epoll_pwait where a new sigmask is passed from userland for the syscalls.
+ *
+ * Note that it does set_restore_sigmask() in advance, so it must be always
+ * paired with update_xxx() before return from syscall.
  */
-int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-                    sigset_t *oldset, size_t sigsetsize)
+int set_xxx(const sigset_t __user *umask, size_t sigsetsize)
 {
-       if (!usigmask)
-               return 0;
+       sigset_t *kmask;
 
+       if (!umask)
+               return 0;
        if (sigsetsize != sizeof(sigset_t))
                return -EINVAL;
-       if (copy_from_user(set, usigmask, sizeof(sigset_t)))
+       if (copy_from_user(kmask, umask, sizeof(sigset_t)))
                return -EFAULT;
 
-       *oldset = current->blocked;
-       set_current_blocked(set);
+       set_restore_sigmask();
+       current->saved_sigmask = current->blocked;
+       set_current_blocked(kmask);
 
        return 0;
 }
-EXPORT_SYMBOL(set_user_sigmask);
+
+void update_xxx(bool interrupted)
+{
+       if (interrupted)
+               WARN_ON(!test_thread_flag(TIF_SIGPENDING));
+       else
+               restore_saved_sigmask();
+}
 
 #ifdef CONFIG_COMPAT
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-                           sigset_t *set, sigset_t *oldset,
-                           size_t sigsetsize)
+int set_compat_xxx(const compat_sigset_t __user *umask, size_t sigsetsize)
 {
-       if (!usigmask)
-               return 0;
+       sigset_t *kmask;
 
+       if (!umask)
+               return 0;
        if (sigsetsize != sizeof(compat_sigset_t))
                return -EINVAL;
-       if (get_compat_sigset(set, usigmask))
+       if (get_compat_sigset(kmask, umask))
                return -EFAULT;
 
-       *oldset = current->blocked;
-       set_current_blocked(set);
+       set_restore_sigmask();
+       current->saved_sigmask = current->blocked;
+       set_current_blocked(kmask);
 
        return 0;
 }
-EXPORT_SYMBOL(set_compat_user_sigmask);
 #endif
 
-/*
- * restore_user_sigmask:
- * usigmask: sigmask passed in from userland.
- * sigsaved: saved sigmask when the syscall started and changed the sigmask to
- *           usigmask.
- *
- * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
- * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
- */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
-{
-
-       if (!usigmask)
-               return;
-       /*
-        * When signals are pending, do not restore them here.
-        * Restoring sigmask here can lead to delivering signals that the above
-        * syscalls are intended to block because of the sigmask passed in.
-        */
-       if (signal_pending(current)) {
-               current->saved_sigmask = *sigsaved;
-               set_restore_sigmask();
-               return;
-       }
-
-       /*
-        * This is needed because the fast syscall return path does not restore
-        * saved_sigmask when signals are not pending.
-        */
-       set_current_blocked(sigsaved);
-}
-EXPORT_SYMBOL(restore_user_sigmask);
-
 /**
  *  sys_rt_sigprocmask - change the list of currently blocked signals
  *  @how: whether to add, remove, or set signals
#44 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

In reply to Oleg Nesterov
Quoted 39 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

I think that the following code is correct

        int interrupted = 0;

        void sigint_handler(int sig)
        {
                interrupted = 1;
        }

        int main(void)
        {
                sigset_t sigint, empty;

                sigemptyset(&sigint);
                sigaddset(&sigint, SIGINT);
                sigprocmask(SIG_BLOCK, &sigint, NULL);

                signal(SIGINT, sigint_handler);

                sigemptyset(&empty);    // so pselect() unblocks SIGINT

                ret = pselect(..., &empty);

                if (ret >= 0)           // sucess or timeout
                        assert(!interrupted);

                if (interrupted)
                        assert(ret == -EINTR);
        }
Quoted 3 lines

IOW, if pselect(sigmask) temporary unblocks SIGINT according to sigmask, this signal should not be delivered if a ready fd was found or timeout. The signal handle should only run if ret == -EINTR.

I do not think we discussed this part earlier. But, if this is true then this is what is wrong as part of 854a6ed56839a. I missed that before.

Quoted 4 lines

(pselect() can be interrupted by any other signal which has a handler. In this
case the handler can be called even if ret >= 0. This is correct, I fail to understand why some people think this is wrong, and in any case we simply can't avoid this).

This patch is wrong because I did not know that it was ok to deliver a signal and not set the errno before. I also admitted to this. And proposed another way to revert the patch.:
https://lore.kernel.org/lkml/CABeXuvouBzZuNarmNcd9JgZgvonL1N_p21gat=O_x0-1hMx=6A@mail.gmail.com/

-Deepa

maintainer
#45 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

In reply to Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

Thanks for the elaborate explanation in this patch, it all starts making sense to me now. I also looked at your patch in detail and thought I had found a few mistakes at first but those all turned out to be mistakes in my reading.

Quoted 3 lines

See the compile-tested patch at the end. Of course, the new _xxx() helpers should be renamed somehow. fs/aio.c doesn't look right with or without this patch, but iiuc this is what it did before 854a6ed56839a.

I think this is a nice simplification, but it would help not to mix up the minimal regression fix with the rewrite of those functions. For the stable kernels, I think we want just the addition of the 'bool interrupted' argument to restore_user_sigmask() to close the race that was introduced 854a6ed56839a. Following up on that for future kernels, your patch improves the readability, but we can probably take it even further.

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2113

Quoted 11 lines
fs/aio.c +4 -3
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);

Maybe name this

           restore_saved_sigmask_if(!interrupted);

and make restore_saved_sigmask_if() an inline function next to restore_saved_sigmask()?

fs/aio.c COMPAT_SYSCALL_DEFINE6(io_pgetevents, · line 2210

Quoted 8 lines
fs/aio.c +1 -1
@@ -2201,13 +2205,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;

With some of the recent discussions about compat syscall handling, I now think that we want to just fold set_compat_user_sigmask() into set_user_sigmask() (whatever they get called in the end) with an in_compat_syscall() conditional inside it, and completely get rid of the COMPAT_SYSCALL_DEFINEx() definitions for those system calls for which this is the only difference.

Unfortunately we still need the time32/time64 distinction, but removing syscall handlers is a significant cleanup here already, and we can move most of the function body of sys_io_pgetevents() into do_io_getevents() in the process. Same for some of the other calls.

Not sure about the order of the cleanups, but probably something like this would work:

1. fix the race (to be backported)
2. unify set_compat_user_sigmask/set_user_sigmask
3. remove unneeded compat handlers
4. replace restore_user_sigmask with restore_saved_sigmask_if()
5. also unify compat_get_fd_set()/get_fd_set() and kill off compat select() variants.

Arnd

#46 lore raw

Re: pselect/etc semantics

Quoted 68 lines · Arnd Bergmann
Quoted 8 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

Thanks for the elaborate explanation in this patch, it all starts making sense to me now. I also looked at your patch in detail and thought I had found a few mistakes at first but those all turned out to be mistakes in my reading.

Quoted 3 lines

See the compile-tested patch at the end. Of course, the new _xxx() helpers should be renamed somehow. fs/aio.c doesn't look right with or without this patch, but iiuc this is what it did before 854a6ed56839a.

I think this is a nice simplification, but it would help not to mix up the minimal regression fix with the rewrite of those functions. For the stable kernels, I think we want just the addition of the 'bool interrupted' argument to restore_user_sigmask() to close the race that was introduced 854a6ed56839a. Following up on that for future kernels, your patch improves the readability, but we can probably take it even further.

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2113

Quoted 11 lines
fs/aio.c +4 -3
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);

Maybe name this

           restore_saved_sigmask_if(!interrupted);

and make restore_saved_sigmask_if() an inline function next to restore_saved_sigmask()?

fs/aio.c COMPAT_SYSCALL_DEFINE6(io_pgetevents, · line 2210

Quoted 8 lines
fs/aio.c +1 -1
@@ -2201,13 +2205,15 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
        if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
                return -EFAULT;
 
-       ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_compat_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;

With some of the recent discussions about compat syscall handling, I now think that we want to just fold set_compat_user_sigmask() into set_user_sigmask() (whatever they get called in the end) with an in_compat_syscall() conditional inside it, and completely get rid of the COMPAT_SYSCALL_DEFINEx() definitions for those system calls for which this is the only difference.

Unfortunately we still need the time32/time64 distinction, but removing syscall handlers is a significant cleanup here already, and we can move most of the function body of sys_io_pgetevents() into do_io_getevents() in the process. Same for some of the other calls.

Not sure about the order of the cleanups, but probably something like this would work:

Quoted 6 lines

1. fix the race (to be backported)
2. unify set_compat_user_sigmask/set_user_sigmask
3. remove unneeded compat handlers
4. replace restore_user_sigmask with restore_saved_sigmask_if()
5. also unify compat_get_fd_set()/get_fd_set() and kill off compat select() variants.

Are new system calls added preventing a revert of the patch in question for stable kernels?

Eric

maintainer
#47 lore raw

Re: pselect/etc semantics

Quoted 14 lines · Eric W. Biederman
Quoted 6 lines · Arnd Bergmann

On Wed, May 29, 2019 at 6:12 PM Oleg Nesterov <[email protected]> wrote:

Not sure about the order of the cleanups, but probably something like this would work:

Quoted 6 lines

1. fix the race (to be backported)
2. unify set_compat_user_sigmask/set_user_sigmask
3. remove unneeded compat handlers
4. replace restore_user_sigmask with restore_saved_sigmask_if()
5. also unify compat_get_fd_set()/get_fd_set() and kill off compat select() variants.

Quoted 2 lines

Are new system calls added preventing a revert of the patch in question for stable kernels?

Yes, a straight revert would not work, as it was done as a cleanup in order to simplify the following conversion. I suppose one could undo the cleanup in both the time32 and time64 versions of each syscall, but I would consider that a more risky change than just fixing the bug that was reported.

Arnd

maintainer
#48 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

In reply to Arnd Bergmann
Quoted 3 lines · Arnd Bergmann

I think this is a nice simplification, but it would help not to mix up the minimal regression fix with the rewrite of those functions.

Yes, yes, agreed.

Plus every file touched by this patch asks for more cleanups. Say, do_poll() should return -ERESTARTNOHAND, not -EINTR, after that we can remove the ugly EINTR->ERESTARTNOHAND in its callers. And more.

Quoted 3 lines

For the stable
kernels, I think we want just the addition of the 'bool interrupted' argument to restore_user_sigmask()

or simply revert this patch. I will check if this is possible today... At first glance 854a6ed56839a40f6 fixed another bug by accident, do_pselect() did "ret == -ERESTARTNOHAND" after "ret = poll_select_copy_remaining()" which can turn ERESTARTNOHAND into EINTR, but this is simple. I'll check tomorrow.

Quoted 12 lines

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2113

Quoted 11 lines
fs/aio.c +4 -3
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
Quoted 3 lines

Maybe name this

           restore_saved_sigmask_if(!interrupted);

Yes, I thought about restore_if(), but to me

		restore_saved_sigmask_if(ret != -EINTR);

doesn't look readable... May be

		restore_saved_sigmask_unless(ret == -EINTR);

? but actually I agree with any naming.

Quoted 2 lines

and make restore_saved_sigmask_if() an inline function next to restore_saved_sigmask()?

agreed,

Quoted 3 lines

With some of the recent discussions about compat syscall handling, I now think that we want to just fold set_compat_user_sigmask() into set_user_sigmask()

agreed, and I thought about this too. But again, I'd prefer to do this and other cleanups later, on top of this patch.

Oleg.

maintainer
#49 lore raw

Re: pselect/etc semantics (Was: [PATCH v2] signal: Adjust error codes according to restore_user_sigmask())

Quoted 5 lines · Oleg Nesterov

On 05/30, Arnd Bergmann wrote:
Plus every file touched by this patch asks for more cleanups. Say, do_poll() should return -ERESTARTNOHAND, not -EINTR, after that we can remove the ugly EINTR->ERESTARTNOHAND in its callers. And more.

Quoted 8 lines
Quoted 3 lines

For the stable
kernels, I think we want just the addition of the 'bool interrupted' argument to restore_user_sigmask()

or simply revert this patch. I will check if this is possible today... At first glance 854a6ed56839a40f6 fixed another bug by accident, do_pselect() did "ret == -ERESTARTNOHAND" after "ret = poll_select_copy_remaining()" which can turn ERESTARTNOHAND into EINTR, but this is simple. I'll check tomorrow.

Right, there were several differences between the system calls that Deepa's original change got rid of. I don't know if any ones besides the do_pselect() return code can be observed in practice.

Quoted 18 lines
Quoted 12 lines

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2113

Quoted 11 lines
fs/aio.c +4 -3
-       ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+       ret = set_xxx(ksig.sigmask, ksig.sigsetsize);
        if (ret)
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       update_xxx(interrupted);
Quoted 3 lines

Maybe name this

           restore_saved_sigmask_if(!interrupted);

Yes, I thought about restore_if(), but to me

Quoted 7 lines
                restore_saved_sigmask_if(ret != -EINTR);

doesn't look readable... May be

                restore_saved_sigmask_unless(ret == -EINTR);

? but actually I agree with any naming.

Yes, restore_saved_sigmask_unless() probably better.

Quoted 6 lines
Quoted 3 lines

With some of the recent discussions about compat syscall handling, I now think that we want to just fold set_compat_user_sigmask() into set_user_sigmask()

agreed, and I thought about this too. But again, I'd prefer to do this and other cleanups later, on top of this patch.

Ok, fair enough. I don't care much about the order as long as the regression fix comes first.

Arnd

#50 lore raw

Re: pselect/etc semantics

In reply to Oleg Nesterov
Quoted 7 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

If I have read this thread correctly the core issue is that ther is a program that used to work and that fails now. The question is why.

There are two ways the semantics for a sigmask changing system call can be defined. The naive way and by reading posix. I will pick on pselect.

The naive way:
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)

{
        sigset_t oldmask;
	int ret;

        if (sigmask)
		sigprocmask(SIG_SETMASK, sigmask, &oldmask);

	ret = select(nfds, readfds, writefds, exceptfds, timeout);

	if (sigmask)
		sigprocmask(SIG_SETMASK, &oldmask, NULL);

        return ret;
}

The standard for pselect behavior at
https://pubs.opengroup.org/onlinepubs/009695399/functions/pselect.html
says:

Quoted 4 lines

If sigmask is not a null pointer, then the pselect() function shall replace the signal mask of the caller by the set of signals pointed to by sigmask before examining the descriptors, and shall restore the signal mask of the calling thread before returning.

...

Quoted 5 lines

On failure, the objects pointed to by the readfds, writefds, and errorfds arguments shall not be modified. If the timeout interval expires without the specified condition being true for any of the specified file descriptors, the objects pointed to by the readfds, writefds, and errorfds arguments shall have all bits set to 0.

So the standard specified behavior is if the return value is -EINTR you know you were interrupted by a signal, for any other return value you don't know.

An error value just indicates that the file descriptor sets have not been updated.

That is what the standard and reasonable people will expect from the system call. Looking at set_user_sigmask and restore_user_sigmask I believe we are don't violate those semantics today.

Which means I believe we have a semantically valid change in behavior that is causing a regression.

From my inspection of the code the change in behavior is from these two pieces of code:

From the v4.18 epoll_pwait.

	/*
	 * If we changed the signal mask, we need to restore the original one.
	 * In case we've got a signal while waiting, we do not restore the
	 * signal mask yet, and we allow do_signal() to deliver the signal on
	 * the way back to userspace, before the signal mask is restored.
	 */
	if (sigmask) {
		if (error == -EINTR) {
			memcpy(&current->saved_sigmask, &sigsaved,
			       sizeof(sigsaved));
			set_restore_sigmask();
		} else
			set_current_blocked(&sigsaved);
	}

/*
* restore_user_sigmask:
* usigmask: sigmask passed in from userland.
* sigsaved: saved sigmask when the syscall started and changed the sigmask to
* usigmask.
*
* This is useful for syscalls such as ppoll, pselect, io_pgetevents and
* epoll_pwait where a new sigmask is passed in from userland for the syscalls. */
void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved) {

	if (!usigmask)
		return;
	/*
	 * When signals are pending, do not restore them here.
	 * Restoring sigmask here can lead to delivering signals that the above
	 * syscalls are intended to block because of the sigmask passed in.
	 */
	if (signal_pending(current)) {
		current->saved_sigmask = *sigsaved;
		set_restore_sigmask();
		return;
	}

	/*
	 * This is needed because the fast syscall return path does not restore
	 * saved_sigmask when signals are not pending.
	 */
	set_current_blocked(sigsaved);
}

Which has been reported results in a return value of 0, and a signal delivered, where previously that did not happen.

They only way I can see that happening is that set_current_blocked in __set_task_blocked clears pending signals (that will be blocked) and calls retarget_shared_pending.

Frankly the only reason this appears to be worth touching is that we have a userspace regression. Otherwise I would call the current behavior more correct and desirable than ignoring the signal longer.

If I am reading sitaution properly I suggest we go back to resoring the sigmask by hand in epoll_pwait, and put in a big fat comment about a silly userspace program depending on that behavior.

That will be easier to backport and it will just fix the regression and not overfix the problem for reasonable applications.

Oleg your cleanup also seems reasonable. But I would like to make certain we understand and fix the regression first. We seem to be talking very small windows for both cases.

Eric

#51 lore raw

RE: pselect/etc semantics

From: Eric W. Biederman

Quoted 37 lines

Sent: 30 May 2019 14:01

Quoted 7 lines · Oleg Nesterov

Al, Linus, Eric, please help.

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

If I have read this thread correctly the core issue is that ther is a program that used to work and that fails now. The question is why.

There are two ways the semantics for a sigmask changing system call can be defined. The naive way and by reading posix. I will pick on pselect.

The naive way:
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)

{
    sigset_t oldmask;
	int ret;

    if (sigmask)
		sigprocmask(SIG_SETMASK, sigmask, &oldmask);

	ret = select(nfds, readfds, writefds, exceptfds, timeout);

	if (sigmask)
		sigprocmask(SIG_SETMASK, &oldmask, NULL);

    return ret;
}
Quoted 7 lines

The standard for pselect behavior at
https://pubs.opengroup.org/onlinepubs/009695399/functions/pselect.html
says:

Quoted 4 lines

If sigmask is not a null pointer, then the pselect() function shall replace the signal mask of the caller by the set of signals pointed to by sigmask before examining the descriptors, and shall restore the signal mask of the calling thread before returning.

Right, but that bit says nothing about when signals are 'delivered'. Section 2.4 of http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
isn't very enlightening either - well, pretty impenetrable really.
But since the ToG specs don't require a user-kernel boundary I believe that the signal handler is expected to be called as soon as it is unmasked.

Now, for async signals, the kernel can always pretend that they happened slightly later than they actually did.
So signal delivery is delayed until 'return to user'.
In some cases system calls are restarted to make the whole thing transparent.

For pselect() etc I think this means:

1) Signal handlers can only be called if EINTR is returned.
2) If a signal is deliverable after the mask is changed then the signal hander must be called.
This means EINTR must be returned.
ie this must be detected before checking anything else.
3) A signal that is raised after the wait completes can be 'deemed' to have happened after the mask is restored and left masked until the applications next pselect() call.
4) As an optimisation a signal that arrives after the timer expires, but before the mask is restored can be 'deemed' to have happened before the timeout expired and EINTR returned.

Now not all system calls that use a temporary mask may want to work that way.
In particular they may want to return 'success' and have the signal handlers called.

So maybe the set_xxx() function that installs to user's mask should return:

   -ve:   errno value
     0:   no signal pending
     1:   signal pending.

And the update_xxx() function that restores the saved mask should take a parameter to indicate whether signal handlers should be called maybe 'bool call_handlers' and return 0/1 depending on whether signals were pending.

pselect() then contains:

	rval = set_xxx();
	if (rval) {
		if (rval < 0)
			return rval;
		update_xxx(true);
		return -EINTR:
	}

	rval = wait....();
#if 0  // don't report late signals
	update_xxx(rval == -EINTR);
#else  // report signals after timeout
	if (update_xxx(!rval || rval == -EINTR))
		rval == -EINTR;

#endif

	return rval;
}

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#52 lore raw

Re: pselect/etc semantics

Quoted 5 lines · David Laight

4) As an optimisation a signal that arrives after the timer expires, but before the mask is restored can be 'deemed' to have happened before the timeout expired and EINTR returned.

This is what pselect/ppoll already does.

Oleg.

#53 lore raw

Re: pselect/etc semantics

In reply to Eric W. Biederman
Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

AKA pselect is fine. While epoll_pwait can be affected.

Has anyone contacted Omar Kilani to see if that is his issue?
https://lore.kernel.org/lkml/CA+8F9hicnF=kvjXPZFQy=Pa2HJUS3JS+G9VswFHNQQynPMHGVQ@mail.gmail.com/

So far the only regression report I am seeing is from Eric Wong. AKA https://lore.kernel.org/lkml/20190501021405.hfvd7ps623liu25i@dcvr/
Are there any others? How did we get to be talking about more than just epoll_pwait?

Eric

#54 lore raw

Re: pselect/etc semantics

Quoted 12 lines · Eric W. Biederman
Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

AKA pselect is fine. While epoll_pwait can be affected.

This was my understanding as well.

Quoted 2 lines

Has anyone contacted Omar Kilani to see if that is his issue?
https://lore.kernel.org/lkml/CA+8F9hicnF=kvjXPZFQy=Pa2HJUS3JS+G9VswFHNQQynPMHGVQ@mail.gmail.com/

Omar was cc-ed when this regression was reported. I did cc him on fix and asked if he could try it. We have not heard from him.

Quoted 4 lines

So far the only regression report I am seeing is from Eric Wong. AKA https://lore.kernel.org/lkml/20190501021405.hfvd7ps623liu25i@dcvr/
Are there any others? How did we get to be talking about more than just epoll_pwait?

This is the only report that I know of. I’m not sure why people started talking about pselect. I was also confused why instead of reviewing the patch and discussing the fix, we ended up talking about how to simplify the code. We have deviated much from what should have been a code review.

-Deepa

#55 lore raw

Re: pselect/etc semantics

Quoted 8 lines · Deepa Dinamani
Quoted 8 lines · Eric W. Biederman
Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

Quoted 7 lines
Quoted 5 lines

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

AKA pselect is fine. While epoll_pwait can be affected.

This was my understanding as well.

I think I was misremembered here. I had noted this before:
https://lore.kernel.org/linux-fsdevel/CABeXuvq7gCV2qPOo+Q8jvNyRaTvhkRLRbnL_oJ-AuK7Sp=P3QQ@mail.gmail.com/

"sys_io_pgetevents() does not seem to have this problem as we are still checking signal_pending() here.
sys_pselect6() seems to have a similar problem. The changes to sys_pselect6() also impact sys_select() as the changes are in the common code path."

This was the code replaced for io_pgetevents by 854a6ed56839a40f6b is as below. No matter what events completed, there was signal_pending() check after the return from do_io_getevents().

fs/aio.c +3 -12
@@ -2110,18 +2110,9 @@ SYSCALL_DEFINE6(io_pgetevents,
                return ret;
 
        ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-       if (signal_pending(current)) {
-               if (ksig.sigmask) {
-                       current->saved_sigmask = sigsaved;
-                       set_restore_sigmask();
-               }
-
-               if (!ret)
-                       ret = -ERESTARTNOHAND;
-       } else {
-               if (ksig.sigmask)
-                       sigprocmask(SIG_SETMASK, &sigsaved, NULL);
-       }
+       restore_user_sigmask(ksig.sigmask, &sigsaved);
+       if (signal_pending(current) && !ret)
+               ret = -ERESTARTNOHAND;
 

Can I ask a simple question for my understanding?

man page for epoll_pwait says

EINTR
The call was interrupted by a signal handler before either any of the requested events occurred or the timeout expired; see signal(7).

But it is not clear to me if we can figure out(without race) the chronological order if one of the requested events are completed or a signal came first.
Is this a correct exectation?

Also like pointed out above, this behavior is not consistent for all such syscalls(io_pgetevents). Was this also by design?

-Deepa

maintainer
#56 lore raw

Re: pselect/etc semantics

In reply to Eric W. Biederman
Quoted 9 lines · Eric W. Biederman
Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

Hmm. handle_signal:

		case -ERESTARTNOHAND:
			regs->ax = -EINTR;
			break;

but I am not sure I understand which problem do you mean..

Oleg.

#57 lore raw

Re: pselect/etc semantics

Quoted 11 lines · Oleg Nesterov
Quoted 9 lines · Eric W. Biederman
Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

Quoted 7 lines

Hmm. handle_signal:

		case -ERESTARTNOHAND:
			regs->ax = -EINTR;
			break;

but I am not sure I understand which problem do you mean..

Yes. My mistake. I looked at the transparent restart case for when a signal is not pending and failed to look at what happens when a signal is delivered.

So yes. Everything changed does appear to have a behavioral difference where they can now succeed and not return -EINTR.

Eric

#58 lore raw

RE: pselect/etc semantics

In reply to Eric W. Biederman

From: Eric W. Biederman

Quoted 9 lines

Sent: 30 May 2019 16:38

Quoted 2 lines · [email protected] (Eric W. Biederman) writes:

Which means I believe we have a semantically valid change in behavior that is causing a regression.

I haven't made a survey of all of the functions yet but fucntions return -ENORESTARTNOHAND will never return -EINTR and are immune from this problem.

Eh?
ERESTARTNOHAND just makes the system call restart if there is no signal handler, EINTR should still be returned if there is a handler.

All the functions that have a temporary signal mask are likely to be expected to work the same way and thus have the same bugs.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html#
isn't overly helpful.
But I think it should return EINTR even if there is no handler unless SA_RESTART is set:

[EINTR]
The function was interrupted while blocked waiting for any of the selected descriptors to become ready and before the timeout interval expired.

If SA_RESTART has been set for the interrupting signal, it is implementation-defined whether the function restarts or returns with [EINTR].

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#59 lore raw

Re: pselect/etc semantics

In reply to Eric W. Biederman
Quoted 5 lines · Eric W. Biederman
Quoted 2 lines · Oleg Nesterov

Al, Linus, Eric, please help.

Quoted 8 lines
Quoted 5 lines

The previous discussion was very confusing, we simply can not understand each other.

To me everything looks very simple and clear, but perhaps I missed something obvious? Please correct me.

If I have read this thread correctly the core issue is that ther is a program that used to work and that fails now. The question is why.

I didn't even try to investigate, I wasn't cc'ed initially and I then I had enough confusion when I started to look at the patch.

However, 854a6ed56839a40f6 obviously introduced the user-visible change so I am not surprised it breaks something. And yes, personally I think this change is not right.

Quoted 2 lines

Which means I believe we have a semantically valid change in behavior that is causing a regression.

See below,

Quoted 23 lines

void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved) {

	if (!usigmask)
		return;
	/*
	 * When signals are pending, do not restore them here.
	 * Restoring sigmask here can lead to delivering signals that the above
	 * syscalls are intended to block because of the sigmask passed in.
	 */
	if (signal_pending(current)) {
		current->saved_sigmask = *sigsaved;
		set_restore_sigmask();
		return;
	}

	/*
	 * This is needed because the fast syscall return path does not restore
	 * saved_sigmask when signals are not pending.
	 */
	set_current_blocked(sigsaved);
}
Quoted 1 line

Which has been reported results in a return value of 0,

0 or success

Quoted 2 lines

and a signal
delivered, where previously that did not happen.

Yes.

And to me this doesn't look right. OK, OK, probably this is because I never read the docs, only the source code in fs/select.c. But to me pselect() should either return success/timeout or deliver a signal. Not both. Even if the signal was already pending before pselect() was called.

To clarify, "a signal" means a signal which was blocked before pselect(sigmask) and temporary unblocked in this syscall.

And even if this doesn't violate posix, I see no reason to change the historic behaviour. And this regression probably means we can't ;)

Oleg.

#60 lore raw

Re: pselect/etc semantics

In reply to Eric W. Biederman
Quoted 10 lines · Eric W. Biederman

Frankly the only reason this appears to be worth touching is that we have a userspace regression. Otherwise I would call the current behavior more correct and desirable than ignoring the signal longer.

If I am reading sitaution properly I suggest we go back to resoring the sigmask by hand in epoll_pwait, and put in a big fat comment about a silly userspace program depending on that behavior.

That will be easier to backport and it will just fix the regression and not overfix the problem for reasonable applications.

Fwiw, the cmogstored (userspace program which regressed) only hit this regression in an obscure code path for tuning; that code path probably sees no use outside of the test suite.

Add to that, cmogstored is an unofficial and optional component to the obscure, largely-forgotten MogileFS.

Finally, the main users of cmogstored are on FreeBSD. They hit the kqueue+self-pipe code path instead of epoll_pwait.

I only used epoll_pwait on Linux since I figured I could save a few bytes of memory by skipping eventfd/self-pipe...

Anyways, I updated cmogstored a few weeks back to call note_run' (the signal dispatcher) if epoll_pwait (wrapped by mog_idleq_wait_intr') returns 0 instead of -1 (EINTR) to workaround this kernel change:

https://bogomips.org/cmogstored-public/[email protected]/

I could easily make a change to call note_run' unconditionally when mog_idleq_wait_intr' returns, too.

But that said, there could be other users hitting the same problem I did. So maybe cmogstored's primary use on Linux these days is finding regressions :>

maintainer
#61 lore raw

PATCH 1/1 signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

The commit 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()") introduced the visible change which breaks
user-space: a signal temporary unblocked by set_user_sigmask() can be delivered even if the caller returns success or timeout.

Change restore_user_sigmask() to accept the additional "interrupted" argument which should be used instead of signal_pending() check, and update the callers.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")
---
 fs/aio.c               | 28 ++++++++++++++++++++--------
 fs/eventpoll.c         |  4 ++--
 fs/io_uring.c          |  7 ++++---
 fs/select.c            | 18 ++++++------------
 include/linux/signal.h |  2 +-
 kernel/signal.c        |  5 +++--
 6 files changed, 36 insertions(+), 28 deletions(-)
fs/aio.c +20 -8
index 3490d1f..c1e581d 100644
@@ -2095,6 +2095,7 @@ SYSCALL_DEFINE6(io_pgetevents,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_timespec64(&ts, timeout)))
@@ -2108,8 +2109,10 @@ SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2128,6 +2131,7 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_old_timespec32(&ts, timeout)))
@@ -2142,8 +2146,10 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2193,6 +2199,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_old_timespec32(&t, timeout))
@@ -2206,8 +2213,10 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2226,6 +2235,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_timespec64(&t, timeout))
@@ -2239,8 +2249,10 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
fs/eventpoll.c +2 -2
index c6f5131..4c74c76 100644
@@ -2325,7 +2325,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	restore_user_sigmask(sigmask, &sigsaved, error == -EINTR);
 
 	return error;
 }
@@ -2350,7 +2350,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	restore_user_sigmask(sigmask, &sigsaved, err == -EINTR);
 
 	return err;
 }
fs/io_uring.c +4 -3
index 0fbb486..1147c5d 100644
@@ -2201,11 +2201,12 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-	if (ret == -ERESTARTSYS)
-		ret = -EINTR;
 
 	if (sig)
-		restore_user_sigmask(sig, &sigsaved);
+		restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
+
+	if (ret == -ERESTARTSYS)
+		ret = -EINTR;
 
 	return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }
fs/select.c +6 -12
index 6cbc9ff..a4d8f6e 100644
@@ -758,10 +758,9 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
+	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1106,8 +1105,7 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1142,8 +1140,7 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1350,10 +1347,9 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
+	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1425,8 +1421,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1461,8 +1456,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
include/linux/signal.h +1 -1
index 9702016..78c2bb3 100644
@@ -276,7 +276,7 @@ extern int sigprocmask(int, sigset_t *, sigset_t *);
 extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
 	sigset_t *oldset, size_t sigsetsize);
 extern void restore_user_sigmask(const void __user *usigmask,
-				 sigset_t *sigsaved);
+				 sigset_t *sigsaved, bool interrupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +3 -2
index 328a01e..aa6a6f1 100644
@@ -2912,7 +2912,8 @@ EXPORT_SYMBOL(set_compat_user_sigmask);
  * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
  * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
  */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
+void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved,
+				bool interrupted)
 {
 
 	if (!usigmask)
@@ -2922,7 +2923,7 @@ void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
 	 * Restoring sigmask here can lead to delivering signals that the above
 	 * syscalls are intended to block because of the sigmask passed in.
 	 */
-	if (signal_pending(current)) {
+	if (interrupted) {
 		current->saved_sigmask = *sigsaved;
 		set_restore_sigmask();
 		return;
Signature · 1 line
2.5.0
Acked-by on v1 1/1
#62 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

Quoted 7 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

The commit 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()") introduced the visible change which breaks
user-space: a signal temporary unblocked by set_user_sigmask() can be delivered even if the caller returns success or timeout.

Quoted 8 lines

Change restore_user_sigmask() to accept the additional "interrupted" argument which should be used instead of signal_pending() check, and update the callers.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")

I have read through the patch and it looks good.

For clarity. I don't think this is required by posix, or fundamentally to remove the races in select. It is what linux has always done and we have applications who care so I agree this fix is needed.

Further in any case where the semantic change that this patch rolls back (aka where allowing a signal to be delivered and the select like call to complete) would be advantage we can do as well if not better by using signalfd.

Michael is there any chance we can get this guarantee of the linux implementation of pselect and friends clearly documented. The guarantee that if the system call completes successfully we are guaranteed that no signal that is unblocked by using sigmask will be delivered?

Eric

Quoted 243 lines
---
 fs/aio.c               | 28 ++++++++++++++++++++--------
 fs/eventpoll.c         |  4 ++--
 fs/io_uring.c          |  7 ++++---
 fs/select.c            | 18 ++++++------------
 include/linux/signal.h |  2 +-
 kernel/signal.c        |  5 +++--
 6 files changed, 36 insertions(+), 28 deletions(-)
fs/aio.c +20 -8
index 3490d1f..c1e581d 100644
@@ -2095,6 +2095,7 @@ SYSCALL_DEFINE6(io_pgetevents,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_timespec64(&ts, timeout)))
@@ -2108,8 +2109,10 @@ SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2128,6 +2131,7 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 	struct __aio_sigset	ksig = { NULL, };
 	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
+	bool interrupted;
 	int ret;
 
 	if (timeout && unlikely(get_old_timespec32(&ts, timeout)))
@@ -2142,8 +2146,10 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2193,6 +2199,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_old_timespec32(&t, timeout))
@@ -2206,8 +2213,10 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
@@ -2226,6 +2235,7 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	struct __compat_aio_sigset ksig = { NULL, };
 	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
+	bool interrupted;
 	int ret;
 
 	if (timeout && get_timespec64(&t, timeout))
@@ -2239,8 +2249,10 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
-	restore_user_sigmask(ksig.sigmask, &sigsaved);
-	if (signal_pending(current) && !ret)
+
+	interrupted = signal_pending(current);
+	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
 	return ret;
fs/eventpoll.c +2 -2
index c6f5131..4c74c76 100644
@@ -2325,7 +2325,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	restore_user_sigmask(sigmask, &sigsaved, error == -EINTR);
 
 	return error;
 }
@@ -2350,7 +2350,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
 
-	restore_user_sigmask(sigmask, &sigsaved);
+	restore_user_sigmask(sigmask, &sigsaved, err == -EINTR);
 
 	return err;
 }
fs/io_uring.c +4 -3
index 0fbb486..1147c5d 100644
@@ -2201,11 +2201,12 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-	if (ret == -ERESTARTSYS)
-		ret = -EINTR;
 
 	if (sig)
-		restore_user_sigmask(sig, &sigsaved);
+		restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
+
+	if (ret == -ERESTARTSYS)
+		ret = -EINTR;
 
 	return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }
fs/select.c +6 -12
index 6cbc9ff..a4d8f6e 100644
@@ -758,10 +758,9 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
+	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1106,8 +1105,7 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1142,8 +1140,7 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1350,10 +1347,9 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
+	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
 	return ret;
 }
 
@@ -1425,8 +1421,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
@@ -1461,8 +1456,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved);
-
+	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
include/linux/signal.h +1 -1
index 9702016..78c2bb3 100644
@@ -276,7 +276,7 @@ extern int sigprocmask(int, sigset_t *, sigset_t *);
 extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
 	sigset_t *oldset, size_t sigsetsize);
 extern void restore_user_sigmask(const void __user *usigmask,
-				 sigset_t *sigsaved);
+				 sigset_t *sigsaved, bool interrupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +3 -2
index 328a01e..aa6a6f1 100644
@@ -2912,7 +2912,8 @@ EXPORT_SYMBOL(set_compat_user_sigmask);
  * This is useful for syscalls such as ppoll, pselect, io_pgetevents and
  * epoll_pwait where a new sigmask is passed in from userland for the syscalls.
  */
-void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
+void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved,
+				bool interrupted)
 {
 
 	if (!usigmask)
@@ -2922,7 +2923,7 @@ void restore_user_sigmask(const void __user *usigmask, sigset_t *sigsaved)
 	 * Restoring sigmask here can lead to delivering signals that the above
 	 * syscalls are intended to block because of the sigmask passed in.
 	 */
-	if (signal_pending(current)) {
+	if (interrupted) {
 		current->saved_sigmask = *sigsaved;
 		set_restore_sigmask();
 		return;
#63 lore raw

RE: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

From: Eric W. Biederman

Quoted 1 line

Sent: 04 June 2019 16:32

...

Quoted 4 lines

Michael is there any chance we can get this guarantee of the linux implementation of pselect and friends clearly documented. The guarantee that if the system call completes successfully we are guaranteed that no signal that is unblocked by using sigmask will be delivered?

The behaviour certainly needs documenting - the ToG docs are unclear. I think you need stronger statement that the one above.

Maybe "signals will only be delivered (ie the handler called) if the system call has to wait and the wait is interrupted by a signal. (Even then pselect might find ready fd and return success.)

There is also the 'issue' of ERESTARTNOHAND.
Some of the system calls will return EINTR if the wait is interrupted by a signal that doesn't have a handler, others get restarted. I'm not at all sure about why there is a difference.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer Acked-by on v1 1/1
#64 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

The commit 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()") introduced the visible change which breaks
user-space: a signal temporary unblocked by set_user_sigmask() can be delivered even if the caller returns success or timeout.

Quoted 8 lines

Change restore_user_sigmask() to accept the additional "interrupted" argument which should be used instead of signal_pending() check, and update the callers.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")

I hope Eric can test this with the original reproducer, or maybe someone could create a test case that can be added into LTP.

Arnd

author Acked-by
#65 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

Quoted 9 lines · Oleg Nesterov
Quoted 8 lines

This is the minimal fix for stable, I'll send cleanups later.

The commit 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()") introduced the visible change which breaks
user-space: a signal temporary unblocked by set_user_sigmask() can be delivered even if the caller returns success or timeout.

Quoted 9 lines
Quoted 8 lines

Change restore_user_sigmask() to accept the additional "interrupted" argument which should be used instead of signal_pending() check, and update the callers.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")

The original fix posted:
https://lore.kernel.org/patchwork/patch/1077355/ would also have been a correct fix for this problem. But, given the cleanups that are in the pipeline, this is a better fix.

-Deepa

Tested-by on v1 1/1
#66 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Oleg Nesterov
Quoted 7 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

The commit 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()") introduced the visible change which breaks
user-space: a signal temporary unblocked by set_user_sigmask() can be delivered even if the caller returns success or timeout.

Quoted 8 lines

Change restore_user_sigmask() to accept the additional "interrupted" argument which should be used instead of signal_pending() check, and update the callers.

Fixes 854a6ed56839a40f6b5d02a2962f48841482eec4 ("signal: Add restore_user_sigmask()")

Thanks, for epoll_pwait on top of Linux v5.1.7 and cmogstored v1.7.0:

(cmogstored v1.7.1 already works around this when it sees a 0 return value (but not >0, yet...))

Quoted 10 lines
fs/io_uring.c +0 -2
index 0fbb486..1147c5d 100644
@@ -2201,11 +2201,12 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-	if (ret == -ERESTARTSYS)
-		ret = -EINTR;

fs/io_uring.c static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, · line 2212

Quoted 10 lines
fs/io_uring.c +4 -1
 
 	if (sig)
-		restore_user_sigmask(sig, &sigsaved);
+		restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
+
+	if (ret == -ERESTARTSYS)
+		ret = -EINTR;
 
 	return READ_ONCE(ring->r.head) == READ_ONCE(ring->r.tail) ? ret : 0;
 }

That io_uring bit didn't apply cleanly to stable,
since stable is missing fdb288a679cdf6a71f3c1ae6f348ba4dae742681 ("io_uring: use wait_event_interruptible for cq_wait conditional wait") and related commits.

In any case, I'm not using io_uring anywhere, yet (and probably won't, since I'll still need threads to deal with open/unlink/rename on slow JBOD HDDs).

#67 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Oleg Nesterov
Quoted 2 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

Ugh. I htink this is correct, but I wish we had a better and more intuitive interface.

In particular, since restore_user_sigmask() basically wants to check for "signal_pending()" anyway (to decide if the mask should be restored by signal handling or by that function), I really get the feeling that a lot of these patterns like

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 7 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;

are wrong to begin with, and we really should aim for an interface which says "tell me whether you completed the system call, and I'll give you an error return if not".

How about we make restore_user_sigmask() take two return codes: the 'ret' we already have, and the return we would get if there is a signal pending and w're currently returning zero.

IOW, I think the above could become

        ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret, -ERESTARTHAND);

instead if we just made the right interface decision.

Hmm?

Linus

#68 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

Quoted 6 lines · Linus Torvalds
Quoted 2 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

Ugh. I htink this is correct, but I wish we had a better and more intuitive interface.

I had the same thoughts, but am not a regular kernel hacker, so I didn't say anything earlier.

Quoted 21 lines

In particular, since restore_user_sigmask() basically wants to check for "signal_pending()" anyway (to decide if the mask should be restored by signal handling or by that function), I really get the feeling that a lot of these patterns like

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 7 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;

are wrong to begin with, and we really should aim for an interface which says "tell me whether you completed the system call, and I'll give you an error return if not".

How about we make restore_user_sigmask() take two return codes: the 'ret' we already have, and the return we would get if there is a signal pending and w're currently returning zero.

Quoted 5 lines

IOW, I think the above could become

        ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret, -ERESTARTHAND);

instead if we just made the right interface decision.

But that falls down if ret were ever expected to match several similar error codes (not sure if it happens)

When I was considering fixing this on my own a few weeks ago, I was looking for an inline that could quickly tell if `ret' was any of the EINTR-like error codes; but couldn't find one...

It'd probably end up being switch/case statement so I'm not sure if it'd be too big and slow or not...

The caller would just do:

	ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret);

And restore_user_sigmask would call some "was_interrupted(ret)" inline which could return true if `ret' matched any of the too-many-to-keep-track-of EINTR-like codes. But I figured there's probably a good reason it did not exist, already *shrug*

/me goes back to the wonderful world of userspace...

#69 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Linus Torvalds
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

Ugh. I htink this is correct, but I wish we had a better and more intuitive interface.

In particular, since restore_user_sigmask() basically wants to check for "signal_pending()" anyway (to decide if the mask should be restored by signal handling or by that function), I really get the feeling that a lot of these patterns like

Linus that checking for signal_pending() in restore_user_sigmask is the bug that caused the regression.

Quoted 11 lines

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 7 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;

are wrong to begin with, and we really should aim for an interface which says "tell me whether you completed the system call, and I'll give you an error return if not".

The pattern you are pointing out is specific to io_pgetevents and it's variations. It does look buggy to me but not for the reason you point out, but instead because it does not appear to let a pending signal cause io_pgetevents to return early.

I suspect we should fix that and have do_io_getevents return -EINTR or -ERESTARTNOHAND like everyone else.

The concept of interrupted (aka return -EINTR to userspace) is truly fundamental to the current semantics. We effectively put a normally blocked signal that was triggered back if we won't be returning -EINTR to userspace.

Quoted 11 lines

How about we make restore_user_sigmask() take two return codes: the 'ret' we already have, and the return we would get if there is a signal pending and w're currently returning zero.

IOW, I think the above could become

        ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret, -ERESTARTHAND);

instead if we just made the right interface decision.

Hmm?

At best I think that is a cleanup that will complicate creating a simple straight forward regression fix.

Unless I am misreading things that is optimizing the interface for dealing with broken code.

So can we please get this fix in and then look at cleaning up and simplifying this code.

Eric

p.s. A rather compelling cleanup is to:

- Leave the signal mask alone.
- Register with signalfd_wqh for wake ups.
- Have a helper

int signal_pending_sigmask(sigset_t *blocked)
{
struct task_struct *tsk = current;
int ret = 0;

   	spin_lock_irq(&tsk->sighand->siglock);
        if (next_signal(&tsk->pending, blocked) ||
            next_signal(&tsk->signal->pending, blocked)) {
        	ret = -ERESTARTHAND;
                if (!sigequalsets(&tsk->blocked, blocked)) {
                	tsk->saved_sigmask = tsk->blocked;
                	__set_task_blocked(tsk, blocked);
                        set_restore_sigmask();
		}
        }
        spin_unlock_irq(&tsk->sighand->siglock);
        return ret;

}

- Use that helper instead of signal_pending() in the various sleep functions.
- Possibly get the signal mask from tsk instead of passing it into all of the helpers.

Eric

maintainer
#70 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

Quoted 13 lines · Eric W. Biederman
Quoted 9 lines

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 8 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;
Quoted 3 lines

are wrong to begin with, and we really should aim for an interface which says "tell me whether you completed the system call, and I'll give you an error return if not".

Quoted 7 lines

The pattern you are pointing out is specific to io_pgetevents and it's variations. It does look buggy to me but not for the reason you point out, but instead because it does not appear to let a pending signal cause io_pgetevents to return early.

I suspect we should fix that and have do_io_getevents return -EINTR or -ERESTARTNOHAND like everyone else.

Exactly. It should not even check signal_pending(). It can rely on wait_event_interruptible_hrtimeout().

Quoted 2 lines

So can we please get this fix in and then look at cleaning up and simplifying this code.

Yes ;)

Oleg.

maintainer
#71 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Linus Torvalds
Quoted 7 lines · Linus Torvalds
Quoted 2 lines · Oleg Nesterov

This is the minimal fix for stable, I'll send cleanups later.

Ugh. I htink this is correct, but I wish we had a better and more intuitive interface.

Yes,

Quoted 2 lines

In particular, since restore_user_sigmask() basically wants to check for "signal_pending()" anyway

No, the caller should check signal_pending() anyway and this is enough.

Quoted 9 lines

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 7 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;

are wrong to begin with,

This is fs/aio.c and I have already mentioned that this code doesn't look right anyway.

Quoted 5 lines

IOW, I think the above could become

        ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret, -ERESTARTHAND);

instead if we just made the right interface decision.

I think this particular code should simply do

		ret = do_io_getevents(...);

		if (ret == -ERESTARTSYS)
			ret = -EINTR;

		restore_user_sigmask(ret == -EINTR);

However I agree that another helper(s) which takes/returns the error code makes sense and I was going to do this. Lets do this step by step, I think we should kill sigmask/sigsaved first.

Oleg.

#72 lore raw

RE: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

In reply to Linus Torvalds

From: Linus Torvalds

Quoted 25 lines

Sent: 04 June 2019 22:27
Ugh. I htink this is correct, but I wish we had a better and more intuitive interface.

In particular, since restore_user_sigmask() basically wants to check for "signal_pending()" anyway (to decide if the mask should be restored by signal handling or by that function), I really get the feeling that a lot of these patterns like

fs/aio.c SYSCALL_DEFINE6(io_pgetevents, · line 2116

Quoted 7 lines
fs/aio.c +4 -2
-       restore_user_sigmask(ksig.sigmask, &sigsaved);
-       if (signal_pending(current) && !ret)
+
+       interrupted = signal_pending(current);
+       restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+       if (interrupted && !ret)
                ret = -ERESTARTNOHAND;

are wrong to begin with, and we really should aim for an interface which says "tell me whether you completed the system call, and I'll give you an error return if not".

How about we make restore_user_sigmask() take two return codes: the 'ret' we already have, and the return we would get if there is a signal pending and w're currently returning zero.

Quoted 5 lines

IOW, I think the above could become

        ret = restore_user_sigmask(ksig.sigmask, &sigsaved, ret, -ERESTARTHAND);

instead if we just made the right interface decision.

I think we should tell restore_user_sigmask() whether it should cause any signal handles to be run (using the current mask) and it should tell us whether it would run any (ie if it deferred restoring the mask to syscall exit).

So the above would (probably) be:

	if (restore_user_sigmask(ksig.sigmask, &sigsaved, true) && !ret)
		ret = -ERESTARTNOHAND;

epoll() would have:

	if (restore_user_sigmask(xxx.sigmask, &sigsaved, !ret || ret == -EINTR))
		ret = -EINTR;

I also think it could be simplified if code that loaded the 'user sigmask' saved the old one in 'current->saved_sigmask' (and saved that it had done it). You'd not need 'sigsaved' nor pass the user sigmask address into the restore function.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#73 lore raw

Re: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

Quoted 4 lines · David Laight

epoll() would have:

	if (restore_user_sigmask(xxx.sigmask, &sigsaved, !ret || ret == -EINTR))
		ret = -EINTR;

I don't think so but lets discuss this later.

Quoted 4 lines

I also think it could be simplified if code that loaded the 'user sigmask' saved the old one in 'current->saved_sigmask' (and saved that it had done it). You'd not need 'sigsaved' nor pass the user sigmask address into the restore function.

Heh. apparently you do not read my emails ;)

This is what I proposed in my very 1st email, and I even showed the patch and the code with the patch applied twice. Let me do this again.

Let me show the code with the patch applied. I am using epoll_pwait() as an example because it looks very simple.

	static inline void set_restore_sigmask(void)
	{
// WARN_ON(!TIF_SIGPENDING) was removed by this patch
		current->restore_sigmask = true;
	}

	int set_xxx(const sigset_t __user *umask, size_t sigsetsize)
	{
		sigset_t *kmask;

		if (!umask)
			return 0;
		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (copy_from_user(kmask, umask, sizeof(sigset_t)))
			return -EFAULT;

// we can safely modify ->saved_sigmask/restore_sigmask, they has no meaning // until the syscall returns.

		set_restore_sigmask();
		current->saved_sigmask = current->blocked;
		set_current_blocked(kmask);

		return 0;
	}


	void update_xxx(bool interrupted)
	{
// the main reason for this helper is WARN_ON(!TIF_SIGPENDING) which was "moved"
// from set_restore_sigmask() above.
		if (interrupted)
			WARN_ON(!test_thread_flag(TIF_SIGPENDING));
		else
			restore_saved_sigmask();
	}

	SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
			int, maxevents, int, timeout, const sigset_t __user *, sigmask,
			size_t, sigsetsize)
	{
		int error;

		error = set_xxx(sigmask, sigsetsize);
		if (error)
			return error;

		error = do_epoll_wait(epfd, events, maxevents, timeout);
		update_xxx(error == -EINTR);

		return error;
	}

Oleg.

#74 lore raw

RE: [PATCH] signal: remove the wrong signal_pending() check in restore_user_sigmask()

From: Oleg Nesterov [mailto:[email protected]]

Quoted 8 lines

Sent: 05 June 2019 10:25

Quoted 4 lines · David Laight

epoll() would have:

	if (restore_user_sigmask(xxx.sigmask, &sigsaved, !ret || ret == -EINTR))
		ret = -EINTR;

I don't think so but lets discuss this later.

I certainly think there should be some comments at least about when/whether signal handlers get called and that being separate from the return value.

The system call restart stuff does seem strange.
ISTR that was originally added for SIG_SUSPEND (^Z) so that those signals wouldn't be seen by the appication.
But that makes it a property of the signal, not the system call.

Quoted 9 lines
Quoted 4 lines

I also think it could be simplified if code that loaded the 'user sigmask' saved the old one in 'current->saved_sigmask' (and saved that it had done it). You'd not need 'sigsaved' nor pass the user sigmask address into the restore function.

Heh. apparently you do not read my emails ;)

This is what I proposed in my very 1st email, and I even showed the patch and the code with the patch applied twice. Let me do this again.

I did read that one, I've even quoted it in the past :-) It's just not been mentioned recently.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#75 lore raw

cover signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Oleg Nesterov

On top of

signal-remove-the-wrong-signal_pending-check-in-restore_user_sigmask.patch

Let me repeat, every file touched by this patch needs more cleanups, fs/aio.c looks wrong with or without the recent changes. Lets discuss this later.

To simplify the review, please see the code with this patch applied. I am using epoll_pwait() as an example because it looks very simple.

Note that this patch moves WARN_ON(!TIF_SIGPENDING) from set_restore_sigmask() to restore_unless().

	int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
	{
		sigset_t *kmask;

		if (!umask)
			return 0;
		if (sigsetsize != sizeof(sigset_t))
			return -EINVAL;
		if (copy_from_user(kmask, umask, sizeof(sigset_t)))
			return -EFAULT;

		set_restore_sigmask();
		current->saved_sigmask = current->blocked;
		set_current_blocked(kmask);

		return 0;
	}

	static inline void restore_saved_sigmask_unless(bool interrupted)
	{
		if (interrupted)
			WARN_ON(!test_thread_flag(TIF_SIGPENDING));
		else
			restore_saved_sigmask();
	}

	SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
			int, maxevents, int, timeout, const sigset_t __user *, sigmask,
			size_t, sigsetsize)
	{
		int error;

		/*
		 * If the caller wants a certain signal mask to be set during the wait,
		 * we apply it here.
		 */
		error = set_user_sigmask(sigmask, sigsetsize);
		if (error)
			return error;

		error = do_epoll_wait(epfd, events, maxevents, timeout);
		restore_saved_sigmask_unless(error == -EINTR);

		return error;
	}

Oleg.

maintainer
#76 lore raw

PATCH 1/1 signal: simplify set_user_sigmask/restore_user_sigmask

task->saved_sigmask and ->restore_sigmask are only used in the ret-from- syscall paths. This means that set_user_sigmask() can save ->blocked in ->saved_sigmask and do set_restore_sigmask() to indicate that ->blocked was modified.

This way the callers do not need 2 sigset_t's passed to set/restore and restore_user_sigmask() renamed to restore_saved_sigmask_unless() turns into the trivial helper which just calls restore_saved_sigmask().

---
 fs/aio.c                     | 20 +++++--------
 fs/eventpoll.c               | 12 +++-----
 fs/io_uring.c                | 11 ++-----
 fs/select.c                  | 34 ++++++++--------------
 include/linux/compat.h       |  3 +-
 include/linux/sched/signal.h | 12 ++++++--
 include/linux/signal.h       |  4 ---
 kernel/signal.c              | 69 ++++++++++++--------------------------------
 8 files changed, 57 insertions(+), 108 deletions(-)
fs/aio.c +8 -12
index c1e581d..8200f97 100644
@@ -2093,7 +2093,6 @@ SYSCALL_DEFINE6(io_pgetevents,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
 	bool interrupted;
 	int ret;
@@ -2104,14 +2103,14 @@ SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2129,7 +2128,6 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
 	bool interrupted;
 	int ret;
@@ -2141,14 +2139,14 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return -EFAULT;
 
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2197,7 +2195,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
 	bool interrupted;
 	int ret;
@@ -2208,14 +2205,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2233,7 +2230,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
 	bool interrupted;
 	int ret;
@@ -2244,14 +2240,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
fs/eventpoll.c +4 -8
index 4c74c76..0f9c073 100644
@@ -2313,19 +2313,17 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		size_t, sigsetsize)
 {
 	int error;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	error = set_user_sigmask(sigmask, sigsetsize);
 	if (error)
 		return error;
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved, error == -EINTR);
+	restore_saved_sigmask_unless(error == -EINTR);
 
 	return error;
 }
@@ -2338,19 +2336,17 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 			compat_size_t, sigsetsize)
 {
 	long err;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	err = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	err = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (err)
 		return err;
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved, err == -EINTR);
+	restore_saved_sigmask_unless(err == -EINTR);
 
 	return err;
 }
fs/io_uring.c +3 -8
index 1147c5d..ad7b9bc 100644
@@ -2180,7 +2180,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 			  const sigset_t __user *sig, size_t sigsz)
 {
 	struct io_cq_ring *ring = ctx->cq_ring;
-	sigset_t ksigmask, sigsaved;
 	int ret;
 
 	if (io_cqring_events(ring) >= min_events)
@@ -2190,21 +2189,17 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 #ifdef CONFIG_COMPAT
 		if (in_compat_syscall())
 			ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig,
-						      &ksigmask, &sigsaved, sigsz);
+						      sigsz);
 		else
 #endif
-			ret = set_user_sigmask(sig, &ksigmask,
-					       &sigsaved, sigsz);
+			ret = set_user_sigmask(sig, sigsz);
 
 		if (ret)
 			return ret;
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-
-	if (sig)
-		restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
-
+	restore_saved_sigmask_unless(ret == -ERESTARTSYS);
 	if (ret == -ERESTARTSYS)
 		ret = -EINTR;
 
fs/select.c +12 -22
index a4d8f6e..1fc1b24 100644
@@ -730,7 +730,6 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		       const sigset_t __user *sigmask, size_t sigsetsize,
 		       enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -753,12 +752,12 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
-	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
 	return ret;
@@ -1086,7 +1085,6 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1099,17 +1097,16 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
@@ -1121,7 +1118,6 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct old_timespec32 __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1134,17 +1130,16 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1319,7 +1314,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 	void __user *tsp, compat_sigset_t __user *sigmask,
 	compat_size_t sigsetsize, enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1342,12 +1336,12 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
-	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
 	return ret;
@@ -1402,7 +1396,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct old_timespec32 __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1415,17 +1408,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1437,7 +1429,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct __kernel_timespec __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1450,17 +1441,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
include/linux/compat.h +1 -2
index ebddcb6..16dafd9 100644
@@ -138,8 +138,7 @@ typedef struct {
 	compat_sigset_word	sig[_COMPAT_NSIG_WORDS];
 } compat_sigset_t;
 
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-			    sigset_t *set, sigset_t *oldset,
+int set_compat_user_sigmask(const compat_sigset_t __user *umask,
 			    size_t sigsetsize);
 
 struct compat_sigaction {
include/linux/sched/signal.h +10 -2
index 38a0f07..4d9c71d 100644
@@ -417,7 +417,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
 	set_thread_flag(TIF_RESTORE_SIGMASK);
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
@@ -448,7 +447,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
 	current->restore_sigmask = true;
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
 {
@@ -481,6 +479,16 @@ static inline void restore_saved_sigmask(void)
 		__set_current_blocked(&current->saved_sigmask);
 }
 
+extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
+
+static inline void restore_saved_sigmask_unless(bool interrupted)
+{
+	if (interrupted)
+		WARN_ON(!test_thread_flag(TIF_SIGPENDING));
+	else
+		restore_saved_sigmask();
+}
+
 static inline sigset_t *sigmask_to_save(void)
 {
 	sigset_t *res = &current->blocked;
include/linux/signal.h +0 -4
index 78c2bb3..b5d99482 100644
@@ -273,10 +273,6 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
 			       struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-	sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
-				 sigset_t *sigsaved, bool interrupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +19 -50

99 lines not on this page · raw message

Signature · 1 line
2.5.0
#77 lore raw

Re: [PATCH -mm 1/1] signal: simplify set_user_sigmask/restore_user_sigmask

I love your patch! Perhaps something to improve:

Hi Oleg,

I love your patch! Perhaps something to improve:

[auto build test WARNING on mmotm/master]

url:    https://github.com/0day-ci/linux/commits/Oleg-Nesterov/signal-simplify-set_user_sigmask-restore_user_sigmask/20190606-062512
base:   git://git.cmpxchg.org/linux-mmotm.git master
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 7.4.0

reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=m68k

If you fix the issue, kindly add following tag

Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings

All warnings (new ones prefixed by >>):

In file included from arch/m68k/include/asm/uaccess.h:5:0,
from include/linux/uaccess.h:11,
from include/asm-generic/termios.h:6,
from ./arch/m68k/include/generated/uapi/asm/termios.h:1, from include/uapi/linux/termios.h:6,
from include/linux/tty.h:7,
from kernel/signal.c:26:
kernel/signal.c: In function 'set_user_sigmask':

>> arch/m68k/include/asm/uaccess_mm.h:191:2: warning: 'kmask' may be used uninitialized in this function [-Wmaybe-uninitialized]
     asm volatile ("\n"      \
     ^~~
   kernel/signal.c:2960:12: note: 'kmask' was declared here
     sigset_t *kmask;
               ^~~~~

vim +/kmask +191 arch/m68k/include/asm/uaccess_mm.h

7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  189  
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  190  #define ____constant_copy_from_user_asm(res, to, from, tmp, n1, n2, n3, s1, s2, s3)\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25 @191  	asm volatile ("\n"						\
e08d703c arch/m68k/include/asm/uaccess_mm.h Greg Ungerer 2011-10-14  192  		"1:	"MOVES"."#s1"	(%2)+,%3\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  193  		"	move."#s1"	%3,(%1)+\n"			\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  194  		"	.ifnc	\""#s2"\",\"\"\n"			\
e08d703c arch/m68k/include/asm/uaccess_mm.h Greg Ungerer 2011-10-14  195  		"2:	"MOVES"."#s2"	(%2)+,%3\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  196  		"	move."#s2"	%3,(%1)+\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  197  		"	.ifnc	\""#s3"\",\"\"\n"			\
e08d703c arch/m68k/include/asm/uaccess_mm.h Greg Ungerer 2011-10-14  198  		"3:	"MOVES"."#s3"	(%2)+,%3\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  199  		"	move."#s3"	%3,(%1)+\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  200  		"	.endif\n"					\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  201  		"	.endif\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  202  		"4:\n"							\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  203  		"	.section __ex_table,\"a\"\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  204  		"	.align	4\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  205  		"	.long	1b,10f\n"				\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  206  		"	.ifnc	\""#s2"\",\"\"\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  207  		"	.long	2b,20f\n"				\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  208  		"	.ifnc	\""#s3"\",\"\"\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  209  		"	.long	3b,30f\n"				\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  210  		"	.endif\n"					\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  211  		"	.endif\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  212  		"	.previous\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  213  		"\n"							\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  214  		"	.section .fixup,\"ax\"\n"			\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  215  		"	.even\n"					\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  216  		"10:	addq.l #"#n1",%0\n"				\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  217  		"	.ifnc	\""#s2"\",\"\"\n"			\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  218  		"20:	addq.l #"#n2",%0\n"				\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  219  		"	.ifnc	\""#s3"\",\"\"\n"			\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  220  		"30:	addq.l #"#n3",%0\n"				\
7cefa5a0 arch/m68k/include/asm/uaccess_mm.h Al Viro      2017-03-20  221  		"	.endif\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  222  		"	.endif\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  223  		"	jra	4b\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  224  		"	.previous\n"					\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  225  		: "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp)	\
53617825 include/asm-m68k/uaccess.h         Roman Zippel 2006-06-25  226  		: : "memory")

53617825 include/asm-m68k/uaccess.h Roman Zippel 2006-06-25 227

:::::: The code at line 191 was first introduced by commit
:::::: 53617825ccf3ff8a71e6efcf3dcf58885ed6f3e5 [PATCH] m68k: fix uaccess.h for gcc-3.x

:::::: TO: Roman Zippel <[email protected]>
:::::: CC: Linus Torvalds <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

#78 lore raw

Re: [PATCH -mm 1/1] signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Oleg Nesterov
I love your patch! Perhaps something to improve:

Hi Oleg,

I love your patch! Perhaps something to improve:

[auto build test WARNING on mmotm/master]

url:    https://github.com/0day-ci/linux/commits/Oleg-Nesterov/signal-simplify-set_user_sigmask-restore_user_sigmask/20190606-062512
base:   git://git.cmpxchg.org/linux-mmotm.git master
config: sparc64-allyesconfig (attached as .config)
compiler: sparc64-linux-gcc (GCC) 7.4.0

reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=sparc64

If you fix the issue, kindly add following tag

Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings

All warnings (new ones prefixed by >>):

In file included from include/linux/poll.h:12:0,
from include/linux/ring_buffer.h:7,
from include/linux/trace_events.h:6,
from include/trace/syscall.h:7,
from include/linux/syscalls.h:86,
from kernel/signal.c:30:
kernel/signal.c: In function 'set_user_sigmask':
include/linux/uaccess.h:113:7: warning: 'kmask' may be used uninitialized in this function [-Wmaybe-uninitialized]

      res = raw_copy_from_user(to, from, n);
      ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/signal.c:2960:12: note: 'kmask' was declared here
     sigset_t *kmask;
               ^~~~~
   kernel/signal.c: In function 'set_compat_user_sigmask':
>> kernel/signal.c:2986:6: warning: 'kmask' may be used uninitialized in this function [-Wmaybe-uninitialized]

if (get_compat_sigset(kmask, umask))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

vim +/kmask +2986 kernel/signal.c

2975
2976 #ifdef CONFIG_COMPAT
2977 int set_compat_user_sigmask(const compat_sigset_t __user *umask,
2978 size_t sigsetsize)
2979 {
2980 sigset_t *kmask;
2981
2982 if (!umask)
2983 return 0;
2984 if (sigsetsize != sizeof(compat_sigset_t))
2985 return -EINVAL;

> 2986		if (get_compat_sigset(kmask, umask))

2987 return -EFAULT;
2988
2989 set_restore_sigmask();
2990 current->saved_sigmask = current->blocked;
2991 set_current_blocked(kmask);
2992
2993 return 0;
2994 }
2995 #endif
2996

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

maintainer
#79 lore raw

Re: [PATCH -mm 1/1] signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Oleg Nesterov

kernel/signal.c EXPORT_SYMBOL(sigprocmask); · line 2872

Quoted 6 lines · Oleg Nesterov
kernel/signal.c +2 -2
+int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 {
-	if (!usigmask)
-		return 0;
+	sigset_t *kmask;

Typo, this obviously should be

	sigset_t kmask;

I'll send v2.

Dear Kbuild Test Robot, thank you very much,

Oleg.

#80 lore raw

Re: [PATCH -mm 1/1] signal: simplify set_user_sigmask/restore_user_sigmask

Quoted 9 lines · Oleg Nesterov

kernel/signal.c EXPORT_SYMBOL(sigprocmask); · line 2872

Quoted 6 lines · Oleg Nesterov
kernel/signal.c +2 -2
+int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 {
-     if (!usigmask)
-             return 0;
+     sigset_t *kmask;
Quoted 9 lines

Typo, this obviously should be

        sigset_t kmask;

I'll send v2.

Dear Kbuild Test Robot, thank you very much,

Machines have emotions, too.

- sed@ -

#81 lore raw

Re: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Oleg Nesterov
Quoted 3 lines · Oleg Nesterov

To simplify the review, please see the code with this patch applied. I am using epoll_pwait() as an example because it looks very simple.

I like it.

However.

I think I'd like it even more if we just said "we don't need restore_saved_sigmask AT ALL".

Which would be fairly easy to do with something like the attached...

(Yes, this only does x86, which is a problem, but I'm bringing this up as a RFC..)

Is it worth another TIF flag? This sure would simplify things, and it really fits the concept too: this really is a do_signal() issue, and fundamentally goes together with TIF_SIGPENDING.

Linus

 arch/x86/entry/common.c            | 2 +-
 arch/x86/include/asm/thread_info.h | 2 ++
 kernel/signal.c                    | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)
arch/x86/entry/common.c +1 -1
index a986b3c8294c..eb538ecd6603 100644
@@ -160,7 +160,7 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
 			klp_update_patch_state(current);
 
 		/* deal with pending signal delivery */
-		if (cached_flags & _TIF_SIGPENDING)
+		if (cached_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
 			do_signal(regs);
 
 		if (cached_flags & _TIF_NOTIFY_RESUME) {
arch/x86/include/asm/thread_info.h +2 -0
index f9453536f9bb..d77a9f841455 100644
@@ -92,6 +92,7 @@ struct thread_info {
 #define TIF_NOCPUID		15	/* CPUID is not accessible in userland */
 #define TIF_NOTSC		16	/* TSC is not accessible in userland */
 #define TIF_IA32		17	/* IA32 compatibility process */
+#define TIF_RESTORE_SIGMASK	18	/* Restore saved sigmask on return to user space */
 #define TIF_NOHZ		19	/* in adaptive nohz mode */
 #define TIF_MEMDIE		20	/* is terminating due to OOM killer */
 #define TIF_POLLING_NRFLAG	21	/* idle is polling for TIF_NEED_RESCHED */
@@ -122,6 +123,7 @@ struct thread_info {
 #define _TIF_NOCPUID		(1 << TIF_NOCPUID)
 #define _TIF_NOTSC		(1 << TIF_NOTSC)
 #define _TIF_IA32		(1 << TIF_IA32)
+#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 #define _TIF_NOHZ		(1 << TIF_NOHZ)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_IO_BITMAP		(1 << TIF_IO_BITMAP)
kernel/signal.c +1 -0
index 328a01e1a2f0..a3346da1a4f5 100644
@@ -2877,6 +2877,7 @@ int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
 
 	*oldset = current->blocked;
 	set_current_blocked(set);
+	set_thread_flag(TIF_RESTORE_SIGMASK);
 
 	return 0;
 }
#82 lore raw

RE: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

From: Linus Torvalds

Quoted 6 lines

Sent: 05 June 2019 18:25

Quoted 3 lines · Oleg Nesterov

To simplify the review, please see the code with this patch applied. I am using epoll_pwait() as an example because it looks very simple.

Quoted 8 lines

I like it.

However.

I think I'd like it even more if we just said "we don't need restore_saved_sigmask AT ALL".

Which would be fairly easy to do with something like the attached...

That would always call the signal handlers even when EINTR wasn't being returned (which I think ought to happen ...).
The real purpose of restore_saved_sigmask() is to stop signal handlers that are enabled by the temporary mask being called.

If a signal handler is called, I presume that the trampoline calls back into the kernel to get further handlers called and to finally restore the original signal mask?

What happens if a signal handler calls something that would normally write to current->saved_sigmask?

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#83 lore raw

Re: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

Quoted 4 lines · David Laight

If a signal handler is called, I presume that the trampoline calls back into the kernel to get further handlers called and to finally restore the original signal mask?

See sigmask_to_save(), this is what the kernel records in uc.uc_sigmask before the signal handler runs, after that current->saved_sigmask has no meaning.

When signal handler returns it does sys_rt_sigreturn() which restores the original mask saved in uc_sigmask.

Quoted 2 lines

What happens if a signal handler calls something that would normally write to current->saved_sigmask?

See above.

Oleg.

#84 lore raw

RE: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

From: Oleg Nesterov

Quoted 10 lines

Sent: 06 June 2019 12:05

Quoted 4 lines · David Laight

If a signal handler is called, I presume that the trampoline calls back into the kernel to get further handlers called and to finally restore the original signal mask?

See sigmask_to_save(), this is what the kernel records in uc.uc_sigmask before the signal handler runs, after that current->saved_sigmask has no meaning.

Some of this code is hard to grep through :-)

Quoted 2 lines

When signal handler returns it does sys_rt_sigreturn() which restores the original mask saved in uc_sigmask.

Does that mean that if 2 signals interrupt epoll_wait() only one of the signal handlers is run?

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#85 lore raw

Re: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

Quoted 2 lines · David Laight

Some of this code is hard to grep through :-)

I'd suggest to simply read the kernel code once and memorise it, after that you will not need to use grep.

Quoted 5 lines
Quoted 2 lines

When signal handler returns it does sys_rt_sigreturn() which restores the original mask saved in uc_sigmask.

Does that mean that if 2 signals interrupt epoll_wait() only one of the signal handlers is run?

I'll assume that both signals were blocked before syscall and temporary unblocked by pselect.

Quite contrary, they both will be delivered exactly because original mask won't be restored until the 1st handler returns.

Unless, of course, the sigaction->sa_mask of the 1st signal blocks another one.

Didn't I say you do not read my emails? I have already explained this to you in this thread ;)

Oleg.

#86 lore raw

RE: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

From: Oleg Nesterov

Quoted 7 lines

Sent: 06 June 2019 13:41

Quoted 2 lines · David Laight

Some of this code is hard to grep through :-)

I'd suggest to simply read the kernel code once and memorise it, after that you will not need to use grep.

Unfortunately all the available buffer space is full of the SYSV and NetBSD kernels, there isn't any room for the Linux one :-)

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

maintainer
#87 lore raw

Re: [PATCH -mm 0/1] signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Linus Torvalds
Quoted 6 lines · Linus Torvalds
Quoted 3 lines · Oleg Nesterov

To simplify the review, please see the code with this patch applied. I am using epoll_pwait() as an example because it looks very simple.

Quoted 6 lines

I like it.

However.

I think I'd like it even more if we just said "we don't need restore_saved_sigmask AT ALL".

^^^^^^^^^^^^^^^^^^^^^

Did you mean restore_saved_sigmask_unless() introduced by this patch?

If yes:

Quoted 1 line

Which would be fairly easy to do with something like the attached...

I don't think so,

Quoted 9 lines
arch/x86/entry/common.c +1 -1
@@ -160,7 +160,7 @@ static void exit_to_usermode_loop(struct pt_regs *regs, u32 cached_flags)
 			klp_update_patch_state(current);
 
 		/* deal with pending signal delivery */
-		if (cached_flags & _TIF_SIGPENDING)
+		if (cached_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
 			do_signal(regs);

...

kernel/signal.c int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set, · line 2880

Quoted 7 lines
kernel/signal.c +1 -0
@@ -2877,6 +2877,7 @@ int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
 
 	*oldset = current->blocked;
 	set_current_blocked(set);
+	set_thread_flag(TIF_RESTORE_SIGMASK);

This will re-introduce the problem fixed by the previous patch.

Yes, do_signal() does restore_saved_sigmask() at the end, but only if get_signal() returns false.

This means that restore_saved_sigmask()->set_current_blocked(saved_mask) should restore ->blocked (and may be clear TIF_SIGPENDING) before ret-from-syscall.

Or I misunderstood?

Oleg.

maintainer
#88 lore raw

PATCH v2 1/1 signal: simplify set_user_sigmask/restore_user_sigmask

In reply to Oleg Nesterov

task->saved_sigmask and ->restore_sigmask are only used in the ret-from- syscall paths. This means that set_user_sigmask() can save ->blocked in ->saved_sigmask and do set_restore_sigmask() to indicate that ->blocked was modified.

This way the callers do not need 2 sigset_t's passed to set/restore and restore_user_sigmask() renamed to restore_saved_sigmask_unless() turns into the trivial helper which just calls restore_saved_sigmask().

---
 fs/aio.c                     | 20 +++++--------
 fs/eventpoll.c               | 12 +++-----
 fs/io_uring.c                | 11 ++-----
 fs/select.c                  | 34 ++++++++--------------
 include/linux/compat.h       |  3 +-
 include/linux/sched/signal.h | 12 ++++++--
 include/linux/signal.h       |  4 ---
 kernel/signal.c              | 69 ++++++++++++--------------------------------
 8 files changed, 57 insertions(+), 108 deletions(-)
fs/aio.c +8 -12
index c1e581d..8200f97 100644
@@ -2093,7 +2093,6 @@ SYSCALL_DEFINE6(io_pgetevents,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
 	bool interrupted;
 	int ret;
@@ -2104,14 +2103,14 @@ SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2129,7 +2128,6 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		const struct __aio_sigset __user *, usig)
 {
 	struct __aio_sigset	ksig = { NULL, };
-	sigset_t		ksigmask, sigsaved;
 	struct timespec64	ts;
 	bool interrupted;
 	int ret;
@@ -2141,14 +2139,14 @@ SYSCALL_DEFINE6(io_pgetevents_time32,
 		return -EFAULT;
 
 
-	ret = set_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &ts : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2197,7 +2195,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
 	bool interrupted;
 	int ret;
@@ -2208,14 +2205,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
@@ -2233,7 +2230,6 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 		const struct __compat_aio_sigset __user *, usig)
 {
 	struct __compat_aio_sigset ksig = { NULL, };
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 t;
 	bool interrupted;
 	int ret;
@@ -2244,14 +2240,14 @@ COMPAT_SYSCALL_DEFINE6(io_pgetevents_time64,
 	if (usig && copy_from_user(&ksig, usig, sizeof(ksig)))
 		return -EFAULT;
 
-	ret = set_compat_user_sigmask(ksig.sigmask, &ksigmask, &sigsaved, ksig.sigsetsize);
+	ret = set_compat_user_sigmask(ksig.sigmask, ksig.sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_io_getevents(ctx_id, min_nr, nr, events, timeout ? &t : NULL);
 
 	interrupted = signal_pending(current);
-	restore_user_sigmask(ksig.sigmask, &sigsaved, interrupted);
+	restore_saved_sigmask_unless(interrupted);
 	if (interrupted && !ret)
 		ret = -ERESTARTNOHAND;
 
fs/eventpoll.c +4 -8
index 4c74c76..0f9c073 100644
@@ -2313,19 +2313,17 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events,
 		size_t, sigsetsize)
 {
 	int error;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	error = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	error = set_user_sigmask(sigmask, sigsetsize);
 	if (error)
 		return error;
 
 	error = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved, error == -EINTR);
+	restore_saved_sigmask_unless(error == -EINTR);
 
 	return error;
 }
@@ -2338,19 +2336,17 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd,
 			compat_size_t, sigsetsize)
 {
 	long err;
-	sigset_t ksigmask, sigsaved;
 
 	/*
 	 * If the caller wants a certain signal mask to be set during the wait,
 	 * we apply it here.
 	 */
-	err = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	err = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (err)
 		return err;
 
 	err = do_epoll_wait(epfd, events, maxevents, timeout);
-
-	restore_user_sigmask(sigmask, &sigsaved, err == -EINTR);
+	restore_saved_sigmask_unless(err == -EINTR);
 
 	return err;
 }
fs/io_uring.c +3 -8
index 1147c5d..ad7b9bc 100644
@@ -2180,7 +2180,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 			  const sigset_t __user *sig, size_t sigsz)
 {
 	struct io_cq_ring *ring = ctx->cq_ring;
-	sigset_t ksigmask, sigsaved;
 	int ret;
 
 	if (io_cqring_events(ring) >= min_events)
@@ -2190,21 +2189,17 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
 #ifdef CONFIG_COMPAT
 		if (in_compat_syscall())
 			ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig,
-						      &ksigmask, &sigsaved, sigsz);
+						      sigsz);
 		else
 #endif
-			ret = set_user_sigmask(sig, &ksigmask,
-					       &sigsaved, sigsz);
+			ret = set_user_sigmask(sig, sigsz);
 
 		if (ret)
 			return ret;
 	}
 
 	ret = wait_event_interruptible(ctx->wait, io_cqring_events(ring) >= min_events);
-
-	if (sig)
-		restore_user_sigmask(sig, &sigsaved, ret == -ERESTARTSYS);
-
+	restore_saved_sigmask_unless(ret == -ERESTARTSYS);
 	if (ret == -ERESTARTSYS)
 		ret = -EINTR;
 
fs/select.c +12 -22
index a4d8f6e..1fc1b24 100644
@@ -730,7 +730,6 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		       const sigset_t __user *sigmask, size_t sigsetsize,
 		       enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -753,12 +752,12 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
-	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
 	return ret;
@@ -1086,7 +1085,6 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1099,17 +1097,16 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
@@ -1121,7 +1118,6 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 		struct old_timespec32 __user *, tsp, const sigset_t __user *, sigmask,
 		size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1134,17 +1130,16 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 			return -EINVAL;
 	}
 
-	ret = set_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1319,7 +1314,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 	void __user *tsp, compat_sigset_t __user *sigmask,
 	compat_size_t sigsetsize, enum poll_time_type type)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1342,12 +1336,12 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
-	restore_user_sigmask(sigmask, &sigsaved, ret == -ERESTARTNOHAND);
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
 
 	return ret;
@@ -1402,7 +1396,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct old_timespec32 __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1415,17 +1408,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1437,7 +1429,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 	unsigned int,  nfds, struct __kernel_timespec __user *, tsp,
 	const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize)
 {
-	sigset_t ksigmask, sigsaved;
 	struct timespec64 ts, end_time, *to = NULL;
 	int ret;
 
@@ -1450,17 +1441,16 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 			return -EINVAL;
 	}
 
-	ret = set_compat_user_sigmask(sigmask, &ksigmask, &sigsaved, sigsetsize);
+	ret = set_compat_user_sigmask(sigmask, sigsetsize);
 	if (ret)
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	restore_user_sigmask(sigmask, &sigsaved, ret == -EINTR);
+	restore_saved_sigmask_unless(ret == -EINTR);
 	/* We can restart this syscall, usually */
 	if (ret == -EINTR)
 		ret = -ERESTARTNOHAND;
-
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
include/linux/compat.h +1 -2
index ebddcb6..16dafd9 100644
@@ -138,8 +138,7 @@ typedef struct {
 	compat_sigset_word	sig[_COMPAT_NSIG_WORDS];
 } compat_sigset_t;
 
-int set_compat_user_sigmask(const compat_sigset_t __user *usigmask,
-			    sigset_t *set, sigset_t *oldset,
+int set_compat_user_sigmask(const compat_sigset_t __user *umask,
 			    size_t sigsetsize);
 
 struct compat_sigaction {
include/linux/sched/signal.h +10 -2
index 38a0f07..4d9c71d 100644
@@ -417,7 +417,6 @@ void task_join_group_stop(struct task_struct *task);
 static inline void set_restore_sigmask(void)
 {
 	set_thread_flag(TIF_RESTORE_SIGMASK);
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
@@ -448,7 +447,6 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void set_restore_sigmask(void)
 {
 	current->restore_sigmask = true;
-	WARN_ON(!test_thread_flag(TIF_SIGPENDING));
 }
 static inline void clear_tsk_restore_sigmask(struct task_struct *task)
 {
@@ -481,6 +479,16 @@ static inline void restore_saved_sigmask(void)
 		__set_current_blocked(&current->saved_sigmask);
 }
 
+extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
+
+static inline void restore_saved_sigmask_unless(bool interrupted)
+{
+	if (interrupted)
+		WARN_ON(!test_thread_flag(TIF_SIGPENDING));
+	else
+		restore_saved_sigmask();
+}
+
 static inline sigset_t *sigmask_to_save(void)
 {
 	sigset_t *res = &current->blocked;
include/linux/signal.h +0 -4
index 78c2bb3..b5d99482 100644
@@ -273,10 +273,6 @@ extern int group_send_sig_info(int sig, struct kernel_siginfo *info,
 			       struct task_struct *p, enum pid_type type);
 extern int __group_send_sig_info(int, struct kernel_siginfo *, struct task_struct *);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
-extern int set_user_sigmask(const sigset_t __user *usigmask, sigset_t *set,
-	sigset_t *oldset, size_t sigsetsize);
-extern void restore_user_sigmask(const void __user *usigmask,
-				 sigset_t *sigsaved, bool interrupted);
 extern void set_current_blocked(sigset_t *);
 extern void __set_current_blocked(const sigset_t *);
 extern int show_unhandled_signals;
kernel/signal.c +19 -50

99 lines not on this page · raw message

Signature · 1 line
2.5.0
maintainer
#89 lore raw

cover select: simplify the usage of restore_saved_sigmask_unless()

In reply to Oleg Nesterov

Lets start from fs/select.c

I have no idead how to test these changes, please review.

On top of "[PATCH -mm V2 1/1] signal: simplify set_user_sigmask/restore_user_sigmask"

it seems that we can do more unrelated cleanups in this code, say, poll_select_prepare(enum poll_time_type pt_type).

Oleg.

 fs/select.c | 68 +++++++++++++++----------------------------------------------
 1 file changed, 16 insertions(+), 52 deletions(-)
maintainer
#90 lore raw

PATCH 1/2 select: change do_poll() to return -ERESTARTNOHAND rather than -EINTR

do_poll() returns -EINTR if interrupted and after that all its callers have to translate it into -ERESTARTNOHAND. Change do_poll() to return -ERESTARTNOHAND and update (simplify) the callers.

Note that this also unifies all users of restore_saved_sigmask_unless(), see the next patch.

---
 fs/select.c | 30 +++++++-----------------------
 1 file changed, 7 insertions(+), 23 deletions(-)
fs/select.c +7 -23
index 1fc1b24..57712c3 100644
@@ -925,7 +925,7 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait,
 		if (!count) {
 			count = wait->error;
 			if (signal_pending(current))
-				count = -EINTR;
+				count = -ERESTARTNOHAND;
 		}
 		if (count || timed_out)
 			break;
@@ -1040,7 +1040,7 @@ static long do_restart_poll(struct restart_block *restart_block)
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	if (ret == -EINTR) {
+	if (ret == -ERESTARTNOHAND) {
 		restart_block->fn = do_restart_poll;
 		ret = -ERESTART_RESTARTBLOCK;
 	}
@@ -1061,7 +1061,7 @@ SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds,
 
 	ret = do_sys_poll(ufds, nfds, to);
 
-	if (ret == -EINTR) {
+	if (ret == -ERESTARTNOHAND) {
 		struct restart_block *restart_block;
 
 		restart_block = &current->restart_block;
@@ -1102,11 +1102,7 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-
-	restore_saved_sigmask_unless(ret == -EINTR);
-	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
-		ret = -ERESTARTNOHAND;
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
@@ -1135,11 +1131,7 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-
-	restore_saved_sigmask_unless(ret == -EINTR);
-	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
-		ret = -ERESTARTNOHAND;
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1413,11 +1405,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-
-	restore_saved_sigmask_unless(ret == -EINTR);
-	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
-		ret = -ERESTARTNOHAND;
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 
 	return ret;
@@ -1446,11 +1434,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-
-	restore_saved_sigmask_unless(ret == -EINTR);
-	/* We can restart this syscall, usually */
-	if (ret == -EINTR)
-		ret = -ERESTARTNOHAND;
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
 	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
 
 	return ret;
Signature · 1 line
2.5.0
#91 lore raw

Re: [PATCH 1/2] select: change do_poll() to return -ERESTARTNOHAND rather than -EINTR

Quoted 4 lines · Oleg Nesterov

do_poll() returns -EINTR if interrupted and after that all its callers have to translate it into -ERESTARTNOHAND. Change do_poll() to return -ERESTARTNOHAND and update (simplify) the callers.

Ack.

The *right* return value will actually be then chosen by poll_select_copy_remaining(), which will turn ERESTARTNOHAND to EINTR when it can't update the timeout.

Except for the cases that use restart_block and do that instead and don't have the whole timeout restart issue as a result.

Linus

maintainer
#92 lore raw

PATCH 2/2 select: shift restore_saved_sigmask_unless() into poll_select_copy_remaining()

In reply to Oleg Nesterov

Now that restore_saved_sigmask_unless() is always called with the same argument right before poll_select_copy_remaining() we can move it into poll_select_copy_remaining() and make it the only caller of restore() in fs/select.c.

The patch also renames poll_select_copy_remaining(), poll_select_finish() looks better after this change.

kern_select() doesn't use set_user_sigmask(), so in this case poll_select_finish() does restore_saved_sigmask_unless() "for no reason". But this won't hurt, and WARN_ON(!TIF_SIGPENDING) is still valid.

---
 fs/select.c | 46 +++++++++++++---------------------------------
 1 file changed, 13 insertions(+), 33 deletions(-)
fs/select.c +13 -33
index 57712c3..51ceec2 100644
@@ -294,12 +294,14 @@ enum poll_time_type {
 	PT_OLD_TIMESPEC = 3,
 };
 
-static int poll_select_copy_remaining(struct timespec64 *end_time,
-				      void __user *p,
-				      enum poll_time_type pt_type, int ret)
+static int poll_select_finish(struct timespec64 *end_time,
+			      void __user *p,
+			      enum poll_time_type pt_type, int ret)
 {
 	struct timespec64 rts;
 
+	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
+
 	if (!p)
 		return ret;
 
@@ -714,9 +716,7 @@ static int kern_select(int n, fd_set __user *inp, fd_set __user *outp,
 	}
 
 	ret = core_sys_select(n, inp, outp, exp, to);
-	ret = poll_select_copy_remaining(&end_time, tvp, PT_TIMEVAL, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tvp, PT_TIMEVAL, ret);
 }
 
 SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp,
@@ -757,10 +757,7 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp,
 		return ret;
 
 	ret = core_sys_select(n, inp, outp, exp, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, type, ret);
 }
 
 /*
@@ -1102,10 +1099,7 @@ SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, PT_TIMESPEC, ret);
 }
 
 #if defined(CONFIG_COMPAT_32BIT_TIME) && !defined(CONFIG_64BIT)
@@ -1131,10 +1125,7 @@ SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds, unsigned int, nfds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 }
 #endif
 
@@ -1271,9 +1262,7 @@ static int do_compat_select(int n, compat_ulong_t __user *inp,
 	}
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
-	ret = poll_select_copy_remaining(&end_time, tvp, PT_OLD_TIMEVAL, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tvp, PT_OLD_TIMEVAL, ret);
 }
 
 COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp,
@@ -1333,10 +1322,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp,
 		return ret;
 
 	ret = compat_core_sys_select(n, inp, outp, exp, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, type, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, type, ret);
 }
 
 COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp,
@@ -1405,10 +1391,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time32, struct pollfd __user *, ufds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, PT_OLD_TIMESPEC, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, PT_OLD_TIMESPEC, ret);
 }
 #endif
 
@@ -1434,10 +1417,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds,
 		return ret;
 
 	ret = do_sys_poll(ufds, nfds, to);
-	restore_saved_sigmask_unless(ret == -ERESTARTNOHAND);
-	ret = poll_select_copy_remaining(&end_time, tsp, PT_TIMESPEC, ret);
-
-	return ret;
+	return poll_select_finish(&end_time, tsp, PT_TIMESPEC, ret);
 }
 
 #endif
Signature · 1 line
2.5.0
#93 lore raw

cover : Removing saved_sigmask

In reply to Oleg Nesterov

While reviewing Oleg's patches I realized a bunch of the logic around saved_sigmask was redundant. So I dug just to see what I could see.

I turns out that real_blocked and saved_sigmask were different implementations of the same idea for slightly different purposes. Which means we only need either real_blocked or saved_sigmask.

I chose real_blocked as it has just a little bit of code associated with it to disable optimizations on the signal sending path that do not apply to blocked signals.

I did a little bit of cleanup of the users. Modified the core to keep real_blocked in sync with blocked except while a clever system call that like pselect or sigtimedwait is running.

After the dust cleared this allowed restore_sigmask and all of the logic to keep it valid to be removed entirely.

I have only done the most cursory of testing at this point. Does anyone have any thoughts in cleaning up the code in this direction?

Eric W. Biederman (5):
signal: Teach sigsuspend to use set_user_sigmask signal/kvm: Stop using sigprocmask in kvm_sigset_(activate|deactivate)
signal: Always keep real_blocked in sync with blocked
signal: Remove saved_sigmask
signal: Remove the unnecessary restore_sigmask flag

 arch/arc/include/asm/thread_info.h       |  1 -
 arch/arm/include/asm/thread_info.h       |  1 -
 arch/arm64/include/asm/thread_info.h     |  1 -
 arch/c6x/include/asm/thread_info.h       |  1 -
 arch/csky/include/asm/thread_info.h      |  2 -
 arch/h8300/include/asm/thread_info.h     |  1 -
 arch/hexagon/include/asm/thread_info.h   |  1 -
 arch/m68k/include/asm/thread_info.h      |  1 -
 arch/mips/include/asm/thread_info.h      |  1 -
 arch/nds32/include/asm/thread_info.h     |  2 -
 arch/nios2/include/asm/thread_info.h     |  2 -
 arch/riscv/include/asm/thread_info.h     |  1 -
 arch/s390/include/asm/thread_info.h      |  1 -
 arch/sparc/include/asm/thread_info_32.h  |  1 -
 arch/um/include/asm/thread_info.h        |  1 -
 arch/unicore32/include/asm/thread_info.h |  1 -
 arch/xtensa/include/asm/thread_info.h    |  1 -
 include/linux/sched.h                    |  5 --
 include/linux/sched/signal.h             | 84 +-------------------------------
 kernel/ptrace.c                          | 15 ++----
 kernel/signal.c                          | 56 +++++++++------------
 virt/kvm/kvm_main.c                      | 11 +----
 22 files changed, 31 insertions(+), 160 deletions(-)

Eric

#94 lore raw

RFC PATCH 1/5 signal: Teach sigsuspend to use set_user_sigmask

The sigsuspend system call overrides the signal mask just like all of the other users of set_user_sigmask, so convert it to use the same helpers.

---
 kernel/signal.c | 43 +++++++++++++++++++------------------------
 1 file changed, 19 insertions(+), 24 deletions(-)
kernel/signal.c +19 -24
index 6f72cff043f0..bfa36320a4f7 100644
@@ -2932,6 +2932,15 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
 }
 EXPORT_SYMBOL(sigprocmask);
 
+static int set_sigmask(sigset_t *kmask)
+{
+	set_restore_sigmask();
+	current->saved_sigmask = current->blocked;
+	set_current_blocked(kmask);
+
+	return 0;
+}
+
 /*
  * The api helps set app-provided sigmasks.
  *
@@ -2952,11 +2961,7 @@ int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize)
 	if (copy_from_user(&kmask, umask, sizeof(sigset_t)))
 		return -EFAULT;
 
-	set_restore_sigmask();
-	current->saved_sigmask = current->blocked;
-	set_current_blocked(&kmask);
-
-	return 0;
+	return set_sigmask(&kmask);
 }
 
 #ifdef CONFIG_COMPAT
@@ -2972,11 +2977,7 @@ int set_compat_user_sigmask(const compat_sigset_t __user *umask,
 	if (get_compat_sigset(&kmask, umask))
 		return -EFAULT;
 
-	set_restore_sigmask();
-	current->saved_sigmask = current->blocked;
-	set_current_blocked(&kmask);
-
-	return 0;
+	return set_sigmask(&kmask);
 }
 #endif
 
@@ -4409,14 +4410,10 @@ SYSCALL_DEFINE0(pause)
 
 static int sigsuspend(sigset_t *set)
 {
-	current->saved_sigmask = current->blocked;
-	set_current_blocked(set);
-
 	while (!signal_pending(current)) {
 		__set_current_state(TASK_INTERRUPTIBLE);
 		schedule();
 	}
-	set_restore_sigmask();
 	return -ERESTARTNOHAND;
 }
 
@@ -4430,12 +4427,10 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)
 {
 	sigset_t newset;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (copy_from_user(&newset, unewset, sizeof(newset)))
+	set_user_sigmask(unewset, sigsetsize);
+	if (!unewset)
 		return -EFAULT;
+
 	return sigsuspend(&newset);
 }
  
@@ -4444,12 +4439,10 @@ COMPAT_SYSCALL_DEFINE2(rt_sigsuspend, compat_sigset_t __user *, unewset, compat_
 {
 	sigset_t newset;
 
-	/* XXX: Don't preclude handling different sized sigset_t's.  */
-	if (sigsetsize != sizeof(sigset_t))
-		return -EINVAL;
-
-	if (get_compat_sigset(&newset, unewset))
+	set_compat_user_sigmask(unewset, sigsetsize);
+	if (!unewset)
 		return -EFAULT;
+
 	return sigsuspend(&newset);
 }
 #endif
@@ -4459,6 +4452,7 @@ SYSCALL_DEFINE1(sigsuspend, old_sigset_t, mask)
 {
 	sigset_t blocked;
 	siginitset(&blocked, mask);
+	set_sigmask(&blocked);
 	return sigsuspend(&blocked);
 }
 #endif
@@ -4467,6 +4461,7 @@ SYSCALL_DEFINE3(sigsuspend, int, unused1, int, unused2, old_sigset_t, mask)
 {
 	sigset_t blocked;
 	siginitset(&blocked, mask);
+	set_sigmask(&blocked);
 	return sigsuspend(&blocked);
 }
 #endif
Signature · 7 lines
2.21.0.dirty

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to [email protected].  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>
#95 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 4 lines · Eric W. Biederman

The sigsuspend system call overrides the signal mask just like all of the other users of set_user_sigmask, so convert it to use the same helpers.

Me likey.

Whole series looks good to me, but that's just from looking at the patches. Maybe testing shows problems..

Linus

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#96 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to Eric W. Biederman

kernel/signal.c int sigprocmask(int how, sigset_t *set, sigset_t *oldset) · line 2942

Quoted 9 lines · Eric W. Biederman
kernel/signal.c +8 -0
+static int set_sigmask(sigset_t *kmask)
+{
+	set_restore_sigmask();
+	current->saved_sigmask = current->blocked;
+	set_current_blocked(kmask);
+
+	return 0;
+}

I was going to do the same change except my version returns void ;)

So ACK.

As for 2-5, sorry I can't read them today, will do tomorrow.

But at first glance... yes, we can remove TIF_RESTORE_SIGMASK.

As for "remove saved_sigmask" I have some concerns... At least this means a user-visible change iiuc. Say, pselect unblocks a fatal signal. Say, SIGINT without a handler. Suppose SIGINT comes after set_sigmask().

Before this change the process will be killed.

After this change it will be killed or not. It won't be killed if do_select() finds an already ready fd without blocking, or it finds a ready fd right after SIGINT interrupts poll_schedule_timeout().

And _to me_ the new behaviour makes more sense. But when it comes to user-visible changes you can never know if it breaks something or not.

Oleg.

#97 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 24 lines · Oleg Nesterov

kernel/signal.c int sigprocmask(int how, sigset_t *set, sigset_t *oldset) · line 2942

Quoted 9 lines · Eric W. Biederman
kernel/signal.c +8 -0
+static int set_sigmask(sigset_t *kmask)
+{
+	set_restore_sigmask();
+	current->saved_sigmask = current->blocked;
+	set_current_blocked(kmask);
+
+	return 0;
+}

I was going to do the same change except my version returns void ;)

So ACK.

As for 2-5, sorry I can't read them today, will do tomorrow.

But at first glance... yes, we can remove TIF_RESTORE_SIGMASK.

As for "remove saved_sigmask" I have some concerns... At least this means a user-visible change iiuc. Say, pselect unblocks a fatal signal. Say, SIGINT without a handler. Suppose SIGINT comes after set_sigmask().

Quoted 5 lines

Before this change the process will be killed.

After this change it will be killed or not. It won't be killed if do_select() finds an already ready fd without blocking, or it finds a ready fd right after SIGINT interrupts poll_schedule_timeout().

Yes. Because having the signal set in real_blocked disables the immediate kill optimization, and the signal has to be delivered before we decide to kill the process. Which matters because as you say if nothing checks signal_pending() when the signals are unblocked we might not attempt to deliver the signal.

So it is a matter of timing.

If we have both a signal and a file descriptor become ready at the same time I would call that a race. Either could wake up the process and depending on the exact time we could return either one.

So it is possible that today if the signal came just after the file descriptor ,the code might have made it to restore_saved_sigmask_unless, before __send_signal runs.

I see the concern. I think in a matter like this we try it. Make the patches clean so people can bisect the problem. Then if someone runs into this problem we revert the offending patches.

If it looks like bisection won't cleanly reveal the potential problem please let me know.

Personally I don't think anyone sane would intentionally depend on this and I don't think there is a sufficiently reliable way to depend on this by accident that people would actually be depending on it.

Quoted 2 lines

And _to me_ the new behaviour makes more sense. But when it comes to user-visible changes you can never know if it breaks something or not.

True.

The set of applications is larger than any developer can reasonably test.

Eric

#98 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: Eric W. Biederman

Quoted 1 line

Sent: 10 June 2019 22:21

...

Quoted 24 lines
Quoted 10 lines

As for "remove saved_sigmask" I have some concerns... At least this means a user-visible change iiuc. Say, pselect unblocks a fatal signal. Say, SIGINT without a handler. Suppose SIGINT comes after set_sigmask().

Before this change the process will be killed.

After this change it will be killed or not. It won't be killed if do_select() finds an already ready fd without blocking, or it finds a ready fd right after SIGINT interrupts poll_schedule_timeout().

Yes. Because having the signal set in real_blocked disables the immediate kill optimization, and the signal has to be delivered before we decide to kill the process. Which matters because as you say if nothing checks signal_pending() when the signals are unblocked we might not attempt to deliver the signal.

So it is a matter of timing.

If we have both a signal and a file descriptor become ready at the same time I would call that a race. Either could wake up the process and depending on the exact time we could return either one.

Quoted 7 lines

So it is possible that today if the signal came just after the file descriptor ,the code might have made it to restore_saved_sigmask_unless, before __send_signal runs.

I see the concern. I think in a matter like this we try it. Make the patches clean so people can bisect the problem. Then if someone runs into this problem we revert the offending patches.

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

A quick test program:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/select.h>
#include <signal.h>

int main(int argc, char **argv)

{
        fd_set readfds;
        sigset_t sig_int;
        struct timespec delay = {1, 0};

        sigfillset(&sig_int);
        sigdelset(&sig_int, SIGINT);

        sighold(SIGINT);

        for (;;) {
                FD_ZERO(&readfds);
                FD_SET(0, &readfds);
                pselect(1, &readfds, NULL, NULL, &delay, &sig_int);

                poll(0,0,1000);
        }
}

Run under strace to see what is happening and send SIGINT from a different terminal. The program sleeps for a second in each of the pselect() and poll() calls. Send a SIGINT and in terminates after pselect() returns ERESTARTNOHAND.

Run again, this time press enter - making fd 0 readable. pselect() returns 1, but the program still exits. (Tested on a 5.1.0-rc5 kernel.)

If a signal handler were defined it should be called instead.

FWIW is ERESTARTNOHAND actually sane here?
If I've used setitimer() to get SIGALARM generated every second I'd expect select() to return EINTR every second even if I don't have a SIGALARM handler?

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#99 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: David Laight

Quoted 1 line

Sent: 11 June 2019 10:52

...

Quoted 38 lines

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

A quick test program:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/select.h>
#include <signal.h>

int main(int argc, char **argv)

{
        fd_set readfds;
        sigset_t sig_int;
        struct timespec delay = {1, 0};

        sigfillset(&sig_int);
        sigdelset(&sig_int, SIGINT);

        sighold(SIGINT);

        for (;;) {
                FD_ZERO(&readfds);
                FD_SET(0, &readfds);
                pselect(1, &readfds, NULL, NULL, &delay, &sig_int);

                poll(0,0,1000);
        }
}

Run under strace to see what is happening and send SIGINT from a different terminal. The program sleeps for a second in each of the pselect() and poll() calls. Send a SIGINT and in terminates after pselect() returns ERESTARTNOHAND.

Quoted 5 lines

Run again, this time press enter - making fd 0 readable. pselect() returns 1, but the program still exits. (Tested on a 5.1.0-rc5 kernel.)

If a signal handler were defined it should be called instead.

If I add a signal handler for SIGINT it is called when pselect() returns regardless of the return value.

If I setup SIGUSR1/2 the same way as SIGINT and get the SIGINT handler to sighold() and then raise both of them, the USR1/2 handlers are both called on the next pselect() call. (Without the extra sighold() the handlers are called when kill() returns.)

I'd expect the epoll functions to work the same way.

sigtimedwait is different though - it returns the number of the pending signal (and doesn't call the handler).
So if two signals are pending neither handler should be called. The second signal would be returned on the following sigtimedwait call.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#100 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 41 lines · David Laight

From: David Laight

Quoted 1 line

Sent: 11 June 2019 10:52

...

Quoted 38 lines

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

A quick test program:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/select.h>
#include <signal.h>

int main(int argc, char **argv)

{
        fd_set readfds;
        sigset_t sig_int;
        struct timespec delay = {1, 0};

        sigfillset(&sig_int);
        sigdelset(&sig_int, SIGINT);

        sighold(SIGINT);

        for (;;) {
                FD_ZERO(&readfds);
                FD_SET(0, &readfds);
                pselect(1, &readfds, NULL, NULL, &delay, &sig_int);

                poll(0,0,1000);
        }
}

Run under strace to see what is happening and send SIGINT from a different terminal. The program sleeps for a second in each of the pselect() and poll() calls. Send a SIGINT and in terminates after pselect() returns ERESTARTNOHAND.

Quoted 8 lines
Quoted 5 lines

Run again, this time press enter - making fd 0 readable. pselect() returns 1, but the program still exits. (Tested on a 5.1.0-rc5 kernel.)

If a signal handler were defined it should be called instead.

If I add a signal handler for SIGINT it is called when pselect() returns regardless of the return value.

That is odd. Is this with Oleg's fix applied?

Eric

#101 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: Eric W. Biederman

Quoted 48 lines

Sent: 12 June 2019 13:56

Quoted 45 lines · David Laight

From: David Laight

Quoted 1 line

Sent: 11 June 2019 10:52

...

Quoted 42 lines

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

A quick test program:

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <sys/select.h>
#include <signal.h>

int main(int argc, char **argv)

{
        fd_set readfds;
        sigset_t sig_int;
        struct timespec delay = {1, 0};

        sigfillset(&sig_int);
        sigdelset(&sig_int, SIGINT);

        sighold(SIGINT);

        for (;;) {
                FD_ZERO(&readfds);
                FD_SET(0, &readfds);
                pselect(1, &readfds, NULL, NULL, &delay, &sig_int);

                poll(0,0,1000);
        }
}

Run under strace to see what is happening and send SIGINT from a different terminal. The program sleeps for a second in each of the pselect() and poll() calls. Send a SIGINT and in terminates after pselect() returns ERESTARTNOHAND.

Run again, this time press enter - making fd 0 readable. pselect() returns 1, but the program still exits. (Tested on a 5.1.0-rc5 kernel.)

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

If a signal handler were defined it should be called instead.

If I add a signal handler for SIGINT it is called when pselect() returns regardless of the return value.

That is odd. Is this with Oleg's fix applied?

No it is a 5.1.0-rc5 kernel with no related local patches.
So it is the 'historic' behaviour of pselect().
But not the original one! Under 2.6.22-5-31 the signal handler isn't caller when pselect() returns 1.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#102 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 8 lines · David Laight
Quoted 5 lines
Quoted 3 lines

If I add a signal handler for SIGINT it is called when pselect() returns regardless of the return value.

That is odd. Is this with Oleg's fix applied?

No it is a 5.1.0-rc5 kernel with no related local patches. So it is the 'historic' behaviour of pselect().

No, this is not historic behaviour,

Quoted 2 lines

But not the original one! Under 2.6.22-5-31 the signal handler isn't caller when pselect() returns 1.

This is historic behaviour.

And it was broken by 854a6ed56839a4 ("signal: Add restore_user_sigmask()").

And this is what we already discussed many, many times in this thread ;)

Oleg.

#103 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: Oleg Nesterov

Quoted 13 lines

Sent: 12 June 2019 14:35

Quoted 8 lines · David Laight
Quoted 5 lines
Quoted 3 lines

If I add a signal handler for SIGINT it is called when pselect() returns regardless of the return value.

That is odd. Is this with Oleg's fix applied?

No it is a 5.1.0-rc5 kernel with no related local patches. So it is the 'historic' behaviour of pselect().

No, this is not historic behaviour,

Quoted 8 lines
Quoted 2 lines

But not the original one! Under 2.6.22-5-31 the signal handler isn't caller when pselect() returns 1.

This is historic behaviour.

And it was broken by 854a6ed56839a4 ("signal: Add restore_user_sigmask()").

And this is what we already discussed many, many times in this thread ;)

My brain hurts :-)

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#104 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to David Laight

From: David Laight

Quoted 1 line

Sent: 11 June 2019 10:52

...

Quoted 4 lines

FWIW is ERESTARTNOHAND actually sane here?
If I've used setitimer() to get SIGALARM generated every second I'd expect select() to return EINTR every second even if I don't have a SIGALARM handler?

Actually no - after sigset(SIGALRM, SIG_IGN) I'd expect absolutely nothing to happen when kill(pid, SIGALRM) is called.

However if I run:

	struct itimerval itimerval = {{1, 0}, {1, 0}};
	setitimer(ITIMER_REAL, &itimerval, NULL);
	sigset(SIGALRM, SIG_IGN);

	poll(0, 0, big_timeout);

I see (with strace) poll() repeatedly returning ERESTART_RESTARTBLOCK and being restarted.
Replacing poll() with pselect() returns ERESTARTNOHAND. (In both cases the timeout must be updated since the application does see the timeout.)

I'm sure other unix kernels completely ignore signals set to SIG_IGN. So this restart dance just isn't needed.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

#105 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 19 lines · David Laight

From: David Laight

Quoted 1 line

Sent: 11 June 2019 10:52

...

Quoted 4 lines

FWIW is ERESTARTNOHAND actually sane here?
If I've used setitimer() to get SIGALARM generated every second I'd expect select() to return EINTR every second even if I don't have a SIGALARM handler?

Actually no - after sigset(SIGALRM, SIG_IGN) I'd expect absolutely nothing to happen when kill(pid, SIGALRM) is called.

However if I run:

	struct itimerval itimerval = {{1, 0}, {1, 0}};
	setitimer(ITIMER_REAL, &itimerval, NULL);
	sigset(SIGALRM, SIG_IGN);

	poll(0, 0, big_timeout);
Quoted 8 lines

I see (with strace) poll() repeatedly returning ERESTART_RESTARTBLOCK and being restarted.
Replacing poll() with pselect() returns ERESTARTNOHAND. (In both cases the timeout must be updated since the application does see the timeout.)

I'm sure other unix kernels completely ignore signals set to SIG_IGN. So this restart dance just isn't needed.

We also ignore such signals except when the signal is blocked when it is received.

We don't currently but it would be perfectly legitimate for set_current_blocked to dequeue and drop signals that have become unblocked whose handler is SIG_IGN.

The dance would still be needed for the rare case where TIF_SIGPENDING gets set for a non-signal.

Dropping the signal while it is blocked if it's handler is SIG_IGN looks like a bad idea.

Eric

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#106 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to David Laight
Quoted 5 lines · David Laight

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

This was never true.

Before Eric's patches SIGINT can kill a process or not, depending on timing. In particular, if SIGINT was already pending before pselect() and it finds an already ready fd, the program won't terminate.

After the Eric's patches SIGINT will only kill the program if pselect() does not find a ready fd.

And this is much more consistent. Now we can simply say that the signal will be delivered only if pselect() fails and returns -EINTR. If it doesn't have a handler the process will be killed, otherwise the handler will be called.

Oleg.

#107 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: Oleg Nesterov

Quoted 8 lines

Sent: 12 June 2019 14:46

Quoted 5 lines · David Laight

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

Quoted 5 lines

This was never true.

Before Eric's patches SIGINT can kill a process or not, depending on timing. In particular, if SIGINT was already pending before pselect() and it finds an already ready fd, the program won't terminate.

Which matches what I see on a very old Linux system.

Quoted 6 lines

After the Eric's patches SIGINT will only kill the program if pselect() does not find a ready fd.

And this is much more consistent. Now we can simply say that the signal will be delivered only if pselect() fails and returns -EINTR. If it doesn't have a handler the process will be killed, otherwise the handler will be called.

But is it what the standards mandate?
Can anyone check how Solaris and any of the BSDs behave?
I don't have access to any solaris systems (I doubt I'll get the disk to spin on the one in my garage).
I can check NetBSD when I get home.

The ToG page for pselect() http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html says:
"If sigmask is not a null pointer, then the pselect() function shall replace
the signal mask of the caller by the set of signals pointed to by sigmask
before examining the descriptors, and shall restore the signal mask of the
calling thread before returning."
Note that it says 'before examining the descriptors' not 'before blocking'.
Under the general description about signals it also says that the signal handler
will be called (or other action happen) when a pending signal is unblocked.
So unblocking SIGINT (set to SIG_DFL) prior to examining the descriptors
should be enough to cause the process to exit.
The fact that signal handlers are not called until 'return to user'
is really an implementation choice - but (IMHO) it should appear as if they
were called at the time they became unmasked.

If nothing else the man pages need a note about the standards and portability.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#108 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 8 lines · David Laight

From: Oleg Nesterov

Quoted 7 lines

Sent: 12 June 2019 14:46

Quoted 5 lines · David Laight

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

I think this gets into a quality of implementation.

I suspect that set_user_sigmask should do:
if (signal_pending())
return -ERESTARNOSIGHAND; /* -EINTR that restarts if nothing was pending */

Which should be safe as nothing has blocked yet to consume any of the timeouts, and it should ensure that none of the routines miss a signal.

Quoted 35 lines
Quoted 5 lines

This was never true.

Before Eric's patches SIGINT can kill a process or not, depending on timing. In particular, if SIGINT was already pending before pselect() and it finds an already ready fd, the program won't terminate.

Which matches what I see on a very old Linux system.

Quoted 6 lines

After the Eric's patches SIGINT will only kill the program if pselect() does not find a ready fd.

And this is much more consistent. Now we can simply say that the signal will be delivered only if pselect() fails and returns -EINTR. If it doesn't have a handler the process will be killed, otherwise the handler will be called.

But is it what the standards mandate?
Can anyone check how Solaris and any of the BSDs behave?
I don't have access to any solaris systems (I doubt I'll get the disk to spin on the one in my garage).
I can check NetBSD when I get home.

The ToG page for pselect() http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html says:
"If sigmask is not a null pointer, then the pselect() function shall replace
the signal mask of the caller by the set of signals pointed to by sigmask
before examining the descriptors, and shall restore the signal mask of the
calling thread before returning."
Note that it says 'before examining the descriptors' not 'before blocking'.
Under the general description about signals it also says that the signal handler
will be called (or other action happen) when a pending signal is unblocked.
So unblocking SIGINT (set to SIG_DFL) prior to examining the descriptors
should be enough to cause the process to exit.
The fact that signal handlers are not called until 'return to user'
is really an implementation choice - but (IMHO) it should appear as if they
were called at the time they became unmasked.

Quoted 1 line

If nothing else the man pages need a note about the standards and portability.

I think the entire point is so that signals can be added to an event loop in a race free way. *cough* signalfd *cough*. I think if you are setting SIGINT to SIG_DFL you would leave SIGINT unblocked so it can happen anywhere.

I can see more utility in having a SIGINT handler and you block SIGINT except in your polling loop so you can do something deterministic when SIGINT comes in.

Which makes it independent of my patches, but still worth fixing.

Eric

maintainer
#109 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 11 lines · Eric W. Biederman
Quoted 8 lines · David Laight

From: Oleg Nesterov

Quoted 7 lines

Sent: 12 June 2019 14:46

Quoted 5 lines · David Laight

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

Quoted 8 lines

I think this gets into a quality of implementation.

I suspect that set_user_sigmask should do:
if (signal_pending())
return -ERESTARNOSIGHAND; /* -EINTR that restarts if nothing was pending */

Which should be safe as nothing has blocked yet to consume any of the timeouts, and it should ensure that none of the routines miss a signal.

Why? I don't think this makes any sense.

Perhaps we could do this _after_ set_current_blocked() for the case when the already pending SIGINT was unblocked but a) I am not sure this would be really better and b) I think it is too late to change this.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

#110 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to David Laight

From: David Laight

Quoted 25 lines

Sent: 12 June 2019 15:18
From: Oleg Nesterov

Quoted 8 lines

Sent: 12 June 2019 14:46

Quoted 5 lines · David Laight

If I have an application that has a loop with a pselect call that enables SIGINT (without a handler) and, for whatever reason, one of the fd is always 'ready' then I'd expect a SIGINT (from ^C) to terminate the program.

Quoted 5 lines

This was never true.

Before Eric's patches SIGINT can kill a process or not, depending on timing. In particular, if SIGINT was already pending before pselect() and it finds an already ready fd, the program won't terminate.

Which matches what I see on a very old Linux system.

Quoted 6 lines

After the Eric's patches SIGINT will only kill the program if pselect() does not find a ready fd.

And this is much more consistent. Now we can simply say that the signal will be delivered only if pselect() fails and returns -EINTR. If it doesn't have a handler the process will be killed, otherwise the handler will be called.

Quoted 5 lines

But is it what the standards mandate?
Can anyone check how Solaris and any of the BSDs behave?
I don't have access to any solaris systems (I doubt I'll get the disk to spin on the one in my garage).
I can check NetBSD when I get home.

I tested NetBSD last night.
pselect() always calls the signal handlers even when an fd is ready. I'm beginning to suspect that this is the 'standards conforming' behaviour. I don't remember when pselect() was added to the ToG specs, it didn't go through XNET while I was going to the meetings.

David

Quoted 16 lines

The ToG page for pselect() http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
says:
"If sigmask is not a null pointer, then the pselect() function shall replace the signal mask of the caller by the set of signals pointed to by sigmask before examining the descriptors, and shall restore the signal mask of the calling thread before returning."
Note that it says 'before examining the descriptors' not 'before blocking'.
Under the general description about signals it also says that the signal handler will be called (or other action happen) when a pending signal is unblocked.
So unblocking SIGINT (set to SIG_DFL) prior to examining the descriptors
should be enough to cause the process to exit.
The fact that signal handlers are not called until 'return to user'
is really an implementation choice - but (IMHO) it should appear as if they
were called at the time they became unmasked.

Quoted 3 lines

If nothing else the man pages need a note about the standards and portability.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#111 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 4 lines · David Laight

I tested NetBSD last night.
pselect() always calls the signal handlers even when an fd is ready. I'm beginning to suspect that this is the 'standards conforming' behaviour.

May be. May be not. I have no idea.

Quoted 8 lines
Quoted 8 lines

The ToG page for pselect() http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
says:
"If sigmask is not a null pointer, then the pselect() function shall replace the signal mask of the caller by the set of signals pointed to by sigmask before examining the descriptors, and shall restore the signal mask of the calling thread before returning."

Note that it says 'before examining the descriptors' not 'before blocking'.

And you interpret this as if a pending signal should be delivered in any case, even if pselect succeeds. Again, perhaps you are right, but to me this is simply undocumented.

However, linux never did this. Until the commit 854a6ed56839 ("signal: Add restore_user_sigmask()"). This commit caused regression. We had to revert it.

Quoted 1 line
Quoted 1 line

If nothing else the man pages need a note about the standards and portability.

Agreed.

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

#112 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

From: Oleg Nesterov

Quoted 16 lines

Sent: 13 June 2019 10:43

Quoted 4 lines · David Laight

I tested NetBSD last night.
pselect() always calls the signal handlers even when an fd is ready. I'm beginning to suspect that this is the 'standards conforming' behaviour.

May be. May be not. I have no idea.

Quoted 6 lines
Quoted 6 lines

The ToG page for pselect() http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html
says:
"If sigmask is not a null pointer, then the pselect() function shall replace the signal mask of the caller by the set of signals pointed to by sigmask before examining the descriptors, and shall restore the signal mask of the calling thread before returning."

Quoted 5 lines
Quoted 1 line
Quoted 1 line

Note that it says 'before examining the descriptors' not 'before blocking'.

And you interpret this as if a pending signal should be delivered in any case, even if pselect succeeds. Again, perhaps you are right, but to me this is simply undocumented.

This text (from http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html) is moderately clear:
... if all threads within the process block delivery of the signal, the signal shall remain pending on the process until a thread calls a sigwait() function selecting that signal, a thread unblocks delivery of the signal, or the action associated with the signal is set to ignore the signal.

So when pselect() 'replaces the signal mask' any pending signals should be delivered. And 'delivery' means 'call the signal handler'.
All Unix systems will defer calling the signal handler until the system call returns, but this is not mandated by Posix.

Quoted 2 lines

However, linux never did this. Until the commit 854a6ed56839 ("signal: Add restore_user_sigmask()"). This commit caused regression. We had to revert it.

That change wasn't expected to change the behaviour...

David

Quoted 5 lines
Quoted 1 line
Quoted 1 line

If nothing else the man pages need a note about the standards and portability.

Agreed.

Oleg.

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#113 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 5 lines · David Laight
Quoted 4 lines

And you interpret this as if a pending signal should be delivered in any case, even if pselect succeeds. Again, perhaps you are right, but to me this is simply undocumented.

Quoted 7 lines

This text (from http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html) is moderately clear:
... if all threads within the process block delivery of the signal, the signal shall remain pending on the process until a thread calls a sigwait() function selecting that signal, a thread unblocks delivery of the signal, or the action associated with the signal is set to ignore the signal.

So when pselect() 'replaces the signal mask' any pending signals should be delivered.

I fail to understand this logic.

Quoted 4 lines
Quoted 2 lines

However, linux never did this. Until the commit 854a6ed56839 ("signal: Add restore_user_sigmask()"). This commit caused regression. We had to revert it.

That change wasn't expected to change the behaviour...

Yes.

And the changed behaviour matched your understanding of standard. We had to change it back.

So what do you want from me? ;)

Oleg.

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

maintainer
#114 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to Eric W. Biederman
Quoted 4 lines · Eric W. Biederman

Personally I don't think anyone sane would intentionally depend on this and I don't think there is a sufficiently reliable way to depend on this by accident that people would actually be depending on it.

Agreed.

As I said I like these changes and I see nothing wrong. To me they fix the current behaviour, or at least make it more consistent.

But perhaps this should be documented in the changelog? To make it clear that this change was intentional.

Oleg.

#115 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 6 lines · Oleg Nesterov
Quoted 4 lines · Eric W. Biederman

Personally I don't think anyone sane would intentionally depend on this and I don't think there is a sufficiently reliable way to depend on this by accident that people would actually be depending on it.

Quoted 7 lines

Agreed.

As I said I like these changes and I see nothing wrong. To me they fix the current behaviour, or at least make it more consistent.

But perhaps this should be documented in the changelog? To make it clear that this change was intentional.

Good point. I had not documented it because I thought I was only disabling an optimization.

Eric

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

#116 lore raw

RE: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

In reply to Oleg Nesterov

From: Oleg Nesterov [mailto:[email protected]]

Quoted 7 lines

Sent: 11 June 2019 19:56

Quoted 4 lines · Eric W. Biederman

Personally I don't think anyone sane would intentionally depend on this and I don't think there is a sufficiently reliable way to depend on this by accident that people would actually be depending on it.

Quoted 7 lines

Agreed.

As I said I like these changes and I see nothing wrong. To me they fix the current behaviour, or at least make it more consistent.

But perhaps this should be documented in the changelog? To make it clear that this change was intentional.

What happens if you run the test program I posted yesterday after the changes?

It looks like pselect() and epoll_pwait() operated completely differently. pselect() would always calls the signal handlers. epoll_pwait() only calls them when EINTR is returned.
So changing epoll_pwait() and pselect() to work the same way is bound to break some applications.

David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)

#117 lore raw

Re: [RFC PATCH 1/5] signal: Teach sigsuspend to use set_user_sigmask

Quoted 16 lines · David Laight

From: Oleg Nesterov [mailto:[email protected]]

Quoted 7 lines

Sent: 11 June 2019 19:56

Quoted 4 lines · Eric W. Biederman

Personally I don't think anyone sane would intentionally depend on this and I don't think there is a sufficiently reliable way to depend on this by accident that people would actually be depending on it.

Quoted 7 lines

Agreed.

As I said I like these changes and I see nothing wrong. To me they fix the current behaviour, or at least make it more consistent.

But perhaps this should be documented in the changelog? To make it clear that this change was intentional.

Quoted 7 lines

What happens if you run the test program I posted yesterday after the changes?

It looks like pselect() and epoll_pwait() operated completely differently. pselect() would always calls the signal handlers. epoll_pwait() only calls them when EINTR is returned.
So changing epoll_pwait() and pselect() to work the same way is bound to break some applications.

That is not the change we are discussing. We are looking at making pselect and epoll_pwait act a little more like sigtimedwait.

In particular we are discussiong signals whose handler is SIG_DFL and whose default disposition is to kill the process, such as SIGINT.

When those signals are delivered and they are not blocked, we take an optimized path in complete_signal and start the process tear down.

That early start of process tear down does not happen if the signal is blocked or it happens to be in real_blocked (from sigtimedwait).

This matters is the small time window where the signal is received while the process has temporarily unblocked the signal, and the signal is not detected by the code and blocked and oridinarily would not be delivered until next time because of restore_sigmask_unless.

If the tear down has started early. Even if we would not have returned the process normally the signal can kill the process. AKA epoll_pwait can today result in it's caller being killed without -EINTR being returned.

My change fixes that behavior and disables the early process tear down for those signals, by having real_blocked match the set of signals that are normally blocked by the process. The result should be the signal will have to wait for the next call that temporarily unblocked the process.

The real benefit is that that the code becomes more comprehensible.

It is my patch that titled: "signal: Always keep real_blocked in sync with blocked" that causes that to happen.

Eric

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in the body to [email protected]. For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>

#118 lore raw

RFC PATCH 2/5 signal/kvm: Stop using sigprocmask in kvm_sigset_(activate|deactivate)

In reply to Eric W. Biederman

Instead of jumping through hoops call __set_current_blocked directly. As well as directly manipulating real_blocked.

This is in preparation for modifying the code to always keep real_blocked in sync with blocked except in the rare cases when the kernel needs to override it.

---
 virt/kvm/kvm_main.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
virt/kvm/kvm_main.c +3 -2
index f0d13d9d125d..8575a1010bfc 100644
@@ -2240,7 +2240,8 @@ void kvm_sigset_activate(struct kvm_vcpu *vcpu)
 	 * ->real_blocked don't care as long ->real_blocked is always a subset
 	 * of ->blocked.
 	 */
-	sigprocmask(SIG_SETMASK, &vcpu->sigset, &current->real_blocked);
+	current->real_blocked = current->blocked;
+	__set_current_blocked(&vcpu->sigset);
 }
 
 void kvm_sigset_deactivate(struct kvm_vcpu *vcpu)
@@ -2248,7 +2249,7 @@ void kvm_sigset_deactivate(struct kvm_vcpu *vcpu)
 	if (!vcpu->sigset_active)
 		return;
 
-	sigprocmask(SIG_SETMASK, &current->real_blocked, NULL);
+	__set_current_blocked(&current->real_blocked);
 	sigemptyset(&current->real_blocked);
 }
 
Signature · 7 lines
2.21.0.dirty

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to [email protected].  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>
#119 lore raw

RFC PATCH 3/5 signal: Always keep real_blocked in sync with blocked

In reply to Eric W. Biederman

Except where we temporarily override blocked always keep real_blocked in sync with blocked.

By always setting real_blocked when we set blocked this allows some slight efficiency and simplifications, by not having to save blocked.

This also preparse the code for the removal of saved_sigmask. That should result in a massive simplification.

---
 kernel/ptrace.c     |  1 +
 kernel/signal.c     | 14 ++++++++++----
 virt/kvm/kvm_main.c |  8 --------
 3 files changed, 11 insertions(+), 12 deletions(-)
kernel/ptrace.c +1 -0
index 6f357f4fc859..6507d700d70f 100644
@@ -968,6 +968,7 @@ int ptrace_request(struct task_struct *child, long request,
 		 */
 		spin_lock_irq(&child->sighand->siglock);
 		child->blocked = new_set;
+		child->real_blocked = new_set;
 		spin_unlock_irq(&child->sighand->siglock);
 
 		clear_tsk_restore_sigmask(child);
kernel/signal.c +10 -4
index bfa36320a4f7..fcd84f4a93c9 100644
@@ -2878,6 +2878,9 @@ void set_current_blocked(sigset_t *newset)
 {
 	sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
 	__set_current_blocked(newset);
+
+	/* Lockless, only current can change ->real_blocked, never from irq */
+	current->real_blocked = *newset;
 }
 
 void __set_current_blocked(const sigset_t *newset)
@@ -2928,15 +2931,20 @@ int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
 	}
 
 	__set_current_blocked(&newset);
+
+	/* Lockless, only current can change ->real_blocked, never from irq */
+	tsk->real_blocked = newset;
 	return 0;
 }
 EXPORT_SYMBOL(sigprocmask);
 
-static int set_sigmask(sigset_t *kmask)
+static int set_sigmask(sigset_t *newset)
 {
 	set_restore_sigmask();
 	current->saved_sigmask = current->blocked;
-	set_current_blocked(kmask);
+
+	sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
+	__set_current_blocked(newset);
 
 	return 0;
 }
@@ -3440,7 +3448,6 @@ static int do_sigtimedwait(const sigset_t *which, kernel_siginfo_t *info,
 		 * they arrive. Unblocking is always fine, we can avoid
 		 * set_current_blocked().
 		 */
-		tsk->real_blocked = tsk->blocked;
 		sigandsets(&tsk->blocked, &tsk->blocked, &mask);
 		recalc_sigpending();
 		spin_unlock_irq(&tsk->sighand->siglock);
@@ -3450,7 +3457,6 @@ static int do_sigtimedwait(const sigset_t *which, kernel_siginfo_t *info,
 							 HRTIMER_MODE_REL);
 		spin_lock_irq(&tsk->sighand->siglock);
 		__set_task_blocked(tsk, &tsk->real_blocked);
-		sigemptyset(&tsk->real_blocked);
 		sig = dequeue_signal(tsk, &mask, info);
 	}
 	spin_unlock_irq(&tsk->sighand->siglock);
virt/kvm/kvm_main.c +0 -8
index 8575a1010bfc..4bfed018574a 100644
@@ -2234,13 +2234,6 @@ void kvm_sigset_activate(struct kvm_vcpu *vcpu)
 	if (!vcpu->sigset_active)
 		return;
 
-	/*
-	 * This does a lockless modification of ->real_blocked, which is fine
-	 * because, only current can change ->real_blocked and all readers of
-	 * ->real_blocked don't care as long ->real_blocked is always a subset
-	 * of ->blocked.
-	 */
-	current->real_blocked = current->blocked;
 	__set_current_blocked(&vcpu->sigset);
 }
 
@@ -2250,7 +2243,6 @@ void kvm_sigset_deactivate(struct kvm_vcpu *vcpu)
 		return;
 
 	__set_current_blocked(&current->real_blocked);
-	sigemptyset(&current->real_blocked);
 }
 
 static void grow_halt_poll_ns(struct kvm_vcpu *vcpu)
Signature · 7 lines
2.21.0.dirty

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to [email protected].  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>
#120 lore raw

RFC PATCH 4/5 signal: Remove saved_sigmask

In reply to Eric W. Biederman

We have real_sigmask that serves the same purpose and is always kept uptodate. Remove a field from the task structure and a conditional from the signal delivery code by removing this field.

---
 include/linux/sched.h        |  2 --
 include/linux/sched/signal.h |  9 +++------
 kernel/ptrace.c              | 12 ++----------
 kernel/signal.c              |  2 --
 4 files changed, 5 insertions(+), 20 deletions(-)
include/linux/sched.h +0 -2
index 11837410690f..520efbd355be 100644
@@ -864,8 +864,6 @@ struct task_struct {
 	struct sighand_struct		*sighand;
 	sigset_t			blocked;
 	sigset_t			real_blocked;
-	/* Restored if set_restore_sigmask() was used: */
-	sigset_t			saved_sigmask;
 	struct sigpending		pending;
 	unsigned long			sas_ss_sp;
 	size_t				sas_ss_size;
include/linux/sched/signal.h +3 -6
index 97f6a6a35a81..78678de45278 100644
@@ -407,7 +407,7 @@ void task_join_group_stop(struct task_struct *task);
  */
 
 /**
- * set_restore_sigmask() - make sure saved_sigmask processing gets done
+ * set_restore_sigmask() - make sure real_sigmask processing gets done
  *
  * This sets TIF_RESTORE_SIGMASK and ensures that the arch signal code
  * will run before returning to user mode, to process the flag.  For
@@ -479,7 +479,7 @@ static inline bool test_and_clear_restore_sigmask(void)
 static inline void restore_saved_sigmask(void)
 {
 	if (test_and_clear_restore_sigmask())
-		__set_current_blocked(&current->saved_sigmask);
+		__set_current_blocked(&current->real_blocked);
 }
 
 extern int set_user_sigmask(const sigset_t __user *umask, size_t sigsetsize);
@@ -494,10 +494,7 @@ static inline void restore_saved_sigmask_unless(bool interrupted)
 
 static inline sigset_t *sigmask_to_save(void)
 {
-	sigset_t *res = &current->blocked;
-	if (unlikely(test_restore_sigmask()))
-		res = &current->saved_sigmask;
-	return res;
+	return &current->real_blocked;
 }
 
 static inline int kill_cad_pid(int sig, int priv)
kernel/ptrace.c +2 -10
index 6507d700d70f..5ed6126e1cc5 100644
@@ -925,26 +925,18 @@ int ptrace_request(struct task_struct *child, long request,
 			ret = ptrace_setsiginfo(child, &siginfo);
 		break;
 
-	case PTRACE_GETSIGMASK: {
-		sigset_t *mask;
-
+	case PTRACE_GETSIGMASK:
 		if (addr != sizeof(sigset_t)) {
 			ret = -EINVAL;
 			break;
 		}
 
-		if (test_tsk_restore_sigmask(child))
-			mask = &child->saved_sigmask;
-		else
-			mask = &child->blocked;
-
-		if (copy_to_user(datavp, mask, sizeof(sigset_t)))
+		if (copy_to_user(datavp, &child->real_blocked, sizeof(sigset_t)))
 			ret = -EFAULT;
 		else
 			ret = 0;
 
 		break;
-	}
 
 	case PTRACE_SETSIGMASK: {
 		sigset_t new_set;
kernel/signal.c +0 -2
index fcd84f4a93c9..c37d4f209699 100644
@@ -2941,8 +2941,6 @@ EXPORT_SYMBOL(sigprocmask);
 static int set_sigmask(sigset_t *newset)
 {
 	set_restore_sigmask();
-	current->saved_sigmask = current->blocked;
-
 	sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
 	__set_current_blocked(newset);
 
Signature · 7 lines
2.21.0.dirty

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to [email protected].  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>
#121 lore raw

RFC PATCH 5/5 signal: Remove the unnecessary restore_sigmask flag

In reply to Eric W. Biederman

__set_current_blocked already has a test to see if the new value and the old value of the signal mask differ. Both real_blocked and blocked should be on the same cache line, and are a single word compare on 64bit.

Historically real_blocked or saved_sigmask could be garbage so a test had to be used that always tested valid bits. Now that real_blocked is alwasy valid there is no need to test something different.

---
 arch/arc/include/asm/thread_info.h       |  1 -
 arch/arm/include/asm/thread_info.h       |  1 -
 arch/arm64/include/asm/thread_info.h     |  1 -
 arch/c6x/include/asm/thread_info.h       |  1 -
 arch/csky/include/asm/thread_info.h      |  2 -
 arch/h8300/include/asm/thread_info.h     |  1 -
 arch/hexagon/include/asm/thread_info.h   |  1 -
 arch/m68k/include/asm/thread_info.h      |  1 -
 arch/mips/include/asm/thread_info.h      |  1 -
 arch/nds32/include/asm/thread_info.h     |  2 -
 arch/nios2/include/asm/thread_info.h     |  2 -
 arch/riscv/include/asm/thread_info.h     |  1 -
 arch/s390/include/asm/thread_info.h      |  1 -
 arch/sparc/include/asm/thread_info_32.h  |  1 -
 arch/um/include/asm/thread_info.h        |  1 -
 arch/unicore32/include/asm/thread_info.h |  1 -
 arch/xtensa/include/asm/thread_info.h    |  1 -
 include/linux/sched.h                    |  3 -
 include/linux/sched/signal.h             | 79 +-----------------------
 kernel/ptrace.c                          |  2 -
 kernel/signal.c                          |  7 ---
 21 files changed, 1 insertion(+), 110 deletions(-)
arch/arc/include/asm/thread_info.h +0 -1
index c85947bac5e5..bde0f76a986b 100644
@@ -77,7 +77,6 @@ static inline __attribute_const__ struct thread_info *current_thread_info(void)
  * - pending work-to-be-done flags are in LSW
  * - other flags in MSW
  */
-#define TIF_RESTORE_SIGMASK	0	/* restore sig mask in do_signal() */
 #define TIF_NOTIFY_RESUME	1	/* resumption notification requested */
 #define TIF_SIGPENDING		2	/* signal pending */
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
arch/arm/include/asm/thread_info.h +0 -1
index 286eb61c632b..d1ee97031c21 100644
@@ -147,7 +147,6 @@ extern int vfp_restore_user_hwstate(struct user_vfp *,
 #define TIF_NOHZ		12	/* in adaptive nohz mode */
 #define TIF_USING_IWMMXT	17
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	20
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
arch/arm64/include/asm/thread_info.h +0 -1
index eb3ef73e07cf..bb8fdc9f78d2 100644
@@ -93,7 +93,6 @@ void arch_release_task_struct(struct task_struct *tsk);
 #define TIF_SECCOMP		11
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
 #define TIF_FREEZE		19
-#define TIF_RESTORE_SIGMASK	20
 #define TIF_SINGLESTEP		21
 #define TIF_32BIT		22	/* 32bit process */
 #define TIF_SVE			23	/* Scalable Vector Extension in use */
arch/c6x/include/asm/thread_info.h +0 -1
index 59a5697fe0f3..229d365ffa9d 100644
@@ -84,7 +84,6 @@ struct thread_info *current_thread_info(void)
 #define TIF_NOTIFY_RESUME	1	/* resumption notification requested */
 #define TIF_SIGPENDING		2	/* signal pending */
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
-#define TIF_RESTORE_SIGMASK	4	/* restore signal mask in do_signal() */
 
 #define TIF_MEMDIE		17	/* OOM killer killed process */
 
arch/csky/include/asm/thread_info.h +0 -2
index 0b546a55a8bf..f7261865e9dc 100644
@@ -59,7 +59,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SYSCALL_AUDIT	5	/* syscall auditing */
 #define TIF_POLLING_NRFLAG	16	/* poll_idle() is TIF_NEED_RESCHED */
 #define TIF_MEMDIE		18      /* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	20	/* restore signal mask in do_signal() */
 #define TIF_SECCOMP		21	/* secure computing */
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
@@ -70,7 +69,6 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_MEMDIE		(1 << TIF_MEMDIE)
-#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 
 #endif	/* _ASM_CSKY_THREAD_INFO_H */
arch/h8300/include/asm/thread_info.h +0 -1
index 0cdaa302d3d2..8d5d5063628a 100644
@@ -68,7 +68,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_NEED_RESCHED	2	/* rescheduling necessary */
 #define TIF_SINGLESTEP		3	/* singlestepping active */
 #define TIF_MEMDIE		4	/* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	5	/* restore signal mask in do_signal() */
 #define TIF_NOTIFY_RESUME	6	/* callback before returning to user */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
 #define TIF_SYSCALL_TRACEPOINT	8	/* for ftrace syscall instrumentation */
arch/hexagon/include/asm/thread_info.h +0 -1
index f41f9c6f0e31..2d1183690bc5 100644
@@ -107,7 +107,6 @@ register struct thread_info *__current_thread_info asm(QUOTED_THREADINFO_REG);
 #define TIF_SIGPENDING          2       /* signal pending */
 #define TIF_NEED_RESCHED        3       /* rescheduling necessary */
 #define TIF_SINGLESTEP          4       /* restore ss @ return to usr mode */
-#define TIF_RESTORE_SIGMASK     6       /* restore sig mask in do_signal() */
 /* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE              17      /* OOM killer killed process */
 
arch/m68k/include/asm/thread_info.h +0 -1
index 015f1ca38305..d620cb4f240e 100644
@@ -66,6 +66,5 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_DELAYED_TRACE	14	/* single step a syscall */
 #define TIF_SYSCALL_TRACE	15	/* syscall trace active */
 #define TIF_MEMDIE		16	/* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	18	/* restore signal mask in do_signal */
 
 #endif	/* _ASM_M68K_THREAD_INFO_H */
arch/mips/include/asm/thread_info.h +0 -1
index 4993db40482c..b2327b51c141 100644
@@ -97,7 +97,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SECCOMP		4	/* secure computing */
 #define TIF_NOTIFY_RESUME	5	/* callback before returning to user */
 #define TIF_UPROBE		6	/* breakpointed or singlestepping */
-#define TIF_RESTORE_SIGMASK	9	/* restore signal mask in do_signal() */
 #define TIF_USEDFPU		16	/* FPU was used by this task this quantum (SMP) */
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
 #define TIF_NOHZ		19	/* in adaptive nohz mode */
arch/nds32/include/asm/thread_info.h +0 -2
index c135111ec44e..79914ce56cd9 100644
@@ -52,7 +52,6 @@ struct thread_info {
 #define TIF_POLLING_NRFLAG	17
 #define TIF_MEMDIE		18
 #define TIF_FREEZE		19
-#define TIF_RESTORE_SIGMASK	20
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
@@ -61,7 +60,6 @@ struct thread_info {
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE		(1 << TIF_FREEZE)
-#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 
 /*
  * Change these and you break ASM code in entry-common.S
arch/nios2/include/asm/thread_info.h +0 -2
index 7349a4fa635b..b17309c3a5b3 100644
@@ -86,7 +86,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_MEMDIE		4	/* is terminating due to OOM killer */
 #define TIF_SECCOMP		5	/* secure computing */
 #define TIF_SYSCALL_AUDIT	6	/* syscall auditing active */
-#define TIF_RESTORE_SIGMASK	9	/* restore signal mask in do_signal() */
 
 #define TIF_POLLING_NRFLAG	16	/* true if poll_idle() is polling
 					   TIF_NEED_RESCHED */
@@ -97,7 +96,6 @@ static inline struct thread_info *current_thread_info(void)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT)
-#define _TIF_RESTORE_SIGMASK	(1 << TIF_RESTORE_SIGMASK)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 
 /* work to do on interrupt/exception return */
arch/riscv/include/asm/thread_info.h +0 -1
index 9c039870019b..6a1e393b4b5f 100644
@@ -79,7 +79,6 @@ struct thread_info {
 #define TIF_NOTIFY_RESUME	1	/* callback before returning to user */
 #define TIF_SIGPENDING		2	/* signal pending */
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
-#define TIF_RESTORE_SIGMASK	4	/* restore signal mask in do_signal() */
 #define TIF_MEMDIE		5	/* is terminating due to OOM killer */
 #define TIF_SYSCALL_TRACEPOINT  6       /* syscall tracepoint instrumentation */
 #define TIF_SYSCALL_AUDIT	7	/* syscall auditing */
arch/s390/include/asm/thread_info.h +0 -1
index ce4e17c9aad6..6a1d1636016b 100644
@@ -71,7 +71,6 @@ void arch_setup_new_exec(void);
 
 #define TIF_31BIT		16	/* 32bit process */
 #define TIF_MEMDIE		17	/* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	18	/* restore signal mask in do_signal() */
 #define TIF_SINGLE_STEP		19	/* This task is single stepped */
 #define TIF_BLOCK_STEP		20	/* This task is block stepped */
 #define TIF_UPROBE_SINGLESTEP	21	/* This task is uprobe single stepped */
arch/sparc/include/asm/thread_info_32.h +0 -1
index 548b366165dd..567f472c730b 100644
@@ -103,7 +103,6 @@ register struct thread_info *current_thread_info_reg asm("g6");
 #define TIF_NOTIFY_RESUME	1	/* callback before returning to user */
 #define TIF_SIGPENDING		2	/* signal pending */
 #define TIF_NEED_RESCHED	3	/* rescheduling necessary */
-#define TIF_RESTORE_SIGMASK	4	/* restore signal mask in do_signal() */
 #define TIF_USEDFPU		8	/* FPU was used by this task
 					 * this quantum (SMP) */
 #define TIF_POLLING_NRFLAG	9	/* true if poll_idle() is polling
arch/um/include/asm/thread_info.h +0 -1
index 4eecd960ee8c..565b7db73083 100644
@@ -60,7 +60,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_RESTART_BLOCK	4
 #define TIF_MEMDIE		5	/* is terminating due to OOM killer */
 #define TIF_SYSCALL_AUDIT	6
-#define TIF_RESTORE_SIGMASK	7
 #define TIF_NOTIFY_RESUME	8
 #define TIF_SECCOMP		9	/* secure computing */
 
arch/unicore32/include/asm/thread_info.h +0 -1
index 5fb728f3b49a..0f38f6072839 100644
@@ -119,7 +119,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */
 #define TIF_SYSCALL_TRACE	8
 #define TIF_MEMDIE		18
-#define TIF_RESTORE_SIGMASK	20
 
 #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)
arch/xtensa/include/asm/thread_info.h +0 -1
index f092cc3f4e66..27350241615c 100644
@@ -108,7 +108,6 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_SINGLESTEP		3	/* restore singlestep on return to user mode */
 #define TIF_SYSCALL_TRACEPOINT	4	/* syscall tracepoint instrumentation */
 #define TIF_MEMDIE		5	/* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK	6	/* restore signal mask in do_signal() */
 #define TIF_NOTIFY_RESUME	7	/* callback before returning to user */
 #define TIF_DB_DISABLED		8	/* debug trap disabled for syscall */
 
include/linux/sched.h +0 -3
index 520efbd355be..a5284f5ccb7c 100644
@@ -713,9 +713,6 @@ struct task_struct {
 	/* Bit to tell LSMs we're in execve(): */
 	unsigned			in_execve:1;
 	unsigned			in_iowait:1;
-#ifndef TIF_RESTORE_SIGMASK
-	unsigned			restore_sigmask:1;
-#endif
 #ifdef CONFIG_MEMCG
 	unsigned			in_user_fault:1;
 #endif
include/linux/sched/signal.h +1 -78

87 lines not on this page · raw message

kernel/ptrace.c +0 -2
index 5ed6126e1cc5..08e25bbb04f4 100644
@@ -963,8 +963,6 @@ int ptrace_request(struct task_struct *child, long request,
 		child->real_blocked = new_set;
 		spin_unlock_irq(&child->sighand->siglock);
 
-		clear_tsk_restore_sigmask(child);
-
 		ret = 0;
 		break;
 	}
kernel/signal.c +0 -7
index c37d4f209699..1358e6bf02d3 100644
@@ -2734,12 +2734,6 @@ static void signal_delivered(struct ksignal *ksig, int stepping)
 {
 	sigset_t blocked;
 
-	/* A signal was successfully delivered, and the
-	   saved sigmask was stored on the signal frame,
-	   and will be restored by sigreturn.  So we can
-	   simply clear the restore sigmask flag.  */
-	clear_restore_sigmask();
-
 	sigorsets(&blocked, &current->blocked, &ksig->ka.sa.sa_mask);
 	if (!(ksig->ka.sa.sa_flags & SA_NODEFER))
 		sigaddset(&blocked, ksig->sig);
@@ -2940,7 +2934,6 @@ EXPORT_SYMBOL(sigprocmask);
 
 static int set_sigmask(sigset_t *newset)
 {
-	set_restore_sigmask();
 	sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
 	__set_current_blocked(newset);
 
Signature · 7 lines
2.21.0.dirty

--
To unsubscribe, send a message with 'unsubscribe linux-aio' in
the body to [email protected].  For more info on Linux AIO,
see: http://www.kvack.org/aio/
Don't email: <a href=mailto:"[email protected]">[email protected]</a>
maintainer Reviewed-by on the v1 series
#122 lore raw

Re: [RFC PATCH 0/5]: Removing saved_sigmask

In reply to Eric W. Biederman
Quoted 7 lines · Eric W. Biederman

Eric W. Biederman (5):
signal: Teach sigsuspend to use set_user_sigmask signal/kvm: Stop using sigprocmask in kvm_sigset_(activate|deactivate)
signal: Always keep real_blocked in sync with blocked
signal: Remove saved_sigmask
signal: Remove the unnecessary restore_sigmask flag

I guess this should be routed via -mm tree? This depends on signal-simplify-set_user_sigmask-restore_user_sigmask.patch

Oleg.