kernel/thread: restore signal mask just before returning to userland

* otherwise the signal to be handled might be blocked. fixes #15193
* also remove automatic syscall restart on _kern_select, to match Linux and
BSDs behavior: this fixes parallel build with newer gnu make, which happens
to use pselect.
* also remove automatic syscall restart on _kern_poll.

from https://man7.org/linux/man-pages/man7/signal.7.html
"The following interfaces are never restarted after being
       interrupted by a signal handler, regardless of the use of
       SA_RESTART; they always fail with the error EINTR when
       interrupted by a signal handler: ...
	select(2), and pselect(2)."
from https://notes.shichao.io/unp/ch6/
"Berkeley-derived kernels never automatically restart select."

Change-Id: I7f86d221eae1ad93d8a308a75581d2c30a369c9e
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3627
Reviewed-by: Adrien Destugues <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Jérôme Duval
2021-01-15 17:41:33 +00:00
parent 4d0b11bd22
commit 837f4f48db
3 changed files with 31 additions and 14 deletions
+11 -14
View File
@@ -475,15 +475,21 @@ common_select(int numFDs, fd_set *readSet, fd_set *writeSet, fd_set *errorSet,
// set new signal mask
sigset_t oldSigMask;
if (sigMask != NULL)
if (sigMask != NULL) {
sigprocmask(SIG_SETMASK, sigMask, &oldSigMask);
if (!kernel) {
Thread *thread = thread_get_current_thread();
thread->old_sig_block_mask = oldSigMask;
thread->flags |= THREAD_FLAGS_OLD_SIGMASK;
}
}
// wait for something to happen
status = acquire_sem_etc(sync->sem, 1,
B_CAN_INTERRUPT | (timeout >= 0 ? B_ABSOLUTE_TIMEOUT : 0), timeout);
// restore the old signal mask
if (sigMask != NULL)
if (sigMask != NULL && kernel)
sigprocmask(SIG_SETMASK, &oldSigMask, NULL);
PRINT(("common_select(): acquire_sem_etc() returned: %lx\n", status));
@@ -927,8 +933,6 @@ _user_select(int numFDs, fd_set *userReadSet, fd_set *userWriteSet,
sigset_t sigMask;
int result;
syscall_restart_handle_timeout_pre(timeout);
if (numFDs < 0 || !check_max_fds(numFDs))
return B_BAD_VALUE;
@@ -994,8 +998,7 @@ _user_select(int numFDs, fd_set *userReadSet, fd_set *userWriteSet,
|| (errorSet != NULL
&& user_memcpy(userErrorSet, errorSet, bytes) < B_OK))) {
result = B_BAD_ADDRESS;
} else
syscall_restart_handle_timeout_post(result, timeout);
}
err:
free(readSet);
@@ -1013,16 +1016,12 @@ _user_poll(struct pollfd *userfds, int numFDs, bigtime_t timeout)
size_t bytes;
int result;
syscall_restart_handle_timeout_pre(timeout);
if (numFDs < 0)
return B_BAD_VALUE;
if (numFDs == 0) {
// special case: no FDs
result = common_poll(NULL, 0, timeout, false);
return result < 0
? syscall_restart_handle_timeout_post(result, timeout) : result;
return common_poll(NULL, 0, timeout, false);
}
if (!check_max_fds(numFDs))
@@ -1047,9 +1046,7 @@ _user_poll(struct pollfd *userfds, int numFDs, bigtime_t timeout)
if (numFDs > 0 && user_memcpy(userfds, fds, bytes) != 0) {
if (result >= 0)
result = B_BAD_ADDRESS;
} else
syscall_restart_handle_timeout_post(result, timeout);
}
err:
free(fds);