My understanding is that, in general, the behavior is undefined if you call a non-async signal safe function from a signal handler, but I've heard that linux allows you to call any system call safely. Is this true? Also, the only portable behavior for a SIGSEGV handler is to abort or exit, but I understand linux will actually resume execution if you return, true?
3 Answers
According to section 2 signal
manual:
See signal(7) for a list of the async-signal-safe functions that can be safely called from inside a signal handler.
And section 7 signals
manual lists the following functions and/or system calls along with a pretty clear description:
Async-signal-safe functions
A signal handler function must be very careful, since processing elsewhere may
be interrupted at some arbitrary point in the execution of the program. POSIX
has the concept of "safe function". If a signal interrupts the execution of
an unsafe function, and handler calls an unsafe function, then the behavior of
the program is undefined.
POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2) requires an
implementation to guarantee that the following functions can be safely called
inside a signal handler:
_Exit()
_exit()
abort()
accept()
access()
aio_error()
aio_return()
aio_suspend()
alarm()
bind()
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime()
close()
connect()
creat()
dup()
dup2()
execle()
execve()
fchmod()
fchown()
fcntl()
fdatasync()
fork()
fpathconf()
fstat()
fsync()
ftruncate()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()
getsockopt()
getuid()
kill()
link()
listen()
lseek()
lstat()
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll()
posix_trace_event()
pselect()
raise()
read()
readlink()
recv()
recvfrom()
recvmsg()
rename()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()
sigismember()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
sigset()
sigsuspend()
sleep()
sockatmark()
socket()
socketpair()
stat()
symlink()
sysconf()
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
utime()
wait()
waitpid()
write()
POSIX.1-2008 removes fpathconf(), pathconf(), and sysconf() from the above
list, and adds the following functions:
execl()
execv()
faccessat()
fchmodat()
fchownat()
fexecve()
fstatat()
futimens()
linkat()
mkdirat()
mkfifoat()
mknod()
mknodat()
openat()
readlinkat()
renameat()
symlinkat()
unlinkat()
utimensat()
utimes()
I believe this information to be more reliable than something that we hear sometimes somewhere . So Linux does allow only some system calls but not all of them. So the answer to your question is simply — no.
-
1That would seem to be posix rather than linux specific though. I know linux is (supposed to be) posix compliant, so I took that to mean at a minimum those functions are safe, I'd like to understand if there's any above and beyond (in particular mprotect) that are as well. – gct Jul 26 '12 at 18:03
-
1@gct: This is Linux—specific manual page. It is the most accurate thing you can get after looking at source code. If you feel like going through the source code and doing a better analysis... go for it :) – Jul 26 '12 at 18:31
-
1@gct: BTW, there is a better way of handling signals w/o this restriction — you have to use `epoll` with `signalfd`. Then you can do whatever you want in the handler, see http://www.kernel.org/doc/man-pages/online/pages/man2/signalfd.2.html – Jul 26 '12 at 18:49
-
@Vlad: epoll defeats the whole purpose, as you can't do anything else while waiting for the signal (and since its those "other things" that cause the signal, it will never happen)... – Chris Dodd Jul 27 '12 at 18:37
-
@ChrisDodd: Eh.. well, yeah if you don't build your application around async model — then yes. I prefer not to have any single blocking call and everything is epoll-based. So... – Jul 27 '12 at 20:36
-
4The problem is this list is copied verbatim from the POSIX standard, and is not Linux specific - even though it appears in Linux man pages. Presumably Linux has other async-safe methods - especially methods like `gettid()` (analog of POSIX `getpid`) which don't exist in POSIX - but it is not clear where to get that information. Most man pages don't specify whether a method is async safe or not. – BeeOnRope Feb 13 '14 at 02:10
-
@BeeOnRope I'd understand it the other way around: whether other methods are **currently** async-safe is irrelevant unless it appears in the list, because that absence means there is no guarantee and they could become async-unsafe with any new release. – spectras Dec 01 '18 at 17:17
-
@spectras - I'm not sure how that's the "other way around". I'm just saying there are POSIX methods, and Linux-specific methods. For POSIX methods, one would assume that any method appearing in this list is async-safe, and any not appearing is unsafe. For Linux-specific methods, however, the situation is less clear: there is no corresponding list for Linux methods, so you can take the very conservative approach and assume that _no_ Linux-specific methods are async-safe, or you can try to find some indication of safety outside of this list and use that. – BeeOnRope Dec 01 '18 at 17:26
-
Can someone please explain how `write` and especially `read` can be async-signal-safe if they can block? – ScumCoder Feb 17 '23 at 16:09
Yes and NO
Yes:
You can call any real/raw syscall inside a signal handler. The kernel has the responsibility to ensure it is safety(in the view of the kernel).
1) The kernel don't know the context of the userspace, or saying the kernel forget it intentionally after it saves the state to userspace when delivered signal. (NOTE: execution resuming is done by the user via a syscall with the help from the saved states, not really by the kernel, the kernel has already forgotten)
2) some thread lib is implemented via singles, so threads are already in "signal handler", but these threads can call any syscall.
NO:
But user space functions have their own purpose and side-effect. Some are not re-entrance safe, those functions can't be called from signal handler. man 7 signal
will help you find out which are re-entrance safe.
Take example, you can call sys_futex()
anywhere including signal handler, but if you use sys_futex()
to implement a mutex, the sys_futex()
inside signal handler may blocked for ever when the signal interrupts the critical section of the mutex.
Also, the only portable behavior for a SIGSEGV handler is to abort or exit, but I understand linux will actually resume execution if you return, true?
Yes, if you can't find out the reason. Some user may use SIGSEGV for their own map-when-demanded purpose(example, in JIT, you can translate the code in SIGSEGV signal handler and mmap the translated code to the memory and then return), they can call mmap() or mprotect() ...etc.

- 1,420
- 1
- 13
- 23
-
2"You can call any real/raw syscall inside a signal handler" Do you have any resource (e.g. kernel docs, comments) to support that claim? – Ciro Santilli OurBigBook.com Sep 20 '17 at 07:03
I would believe that any real system call can be called from a signal handler. A true syscall has a number in <asm/unistd.h>
(or <asm/unistd_64.h>
).
some posix functions from section 2 of man pages are implemented thru a "multiplexing" syscall, so they are not "true syscalls" in my sense
A system call is an atomic operation from the point of view of the application; it is almost like a single machine instruction (from inside the application). See this answer.
If your question is: can a SIGSEGV
handler change the faulty address mapping thru mprotect
or mmap
? then I believe the answer is yes (at least on x86-64 & x86-32 architectures), as said here in a question you quoted, but I did not try. I've read that doing that is quite inefficient (SIGSEGV
handling is not very fast, and mprotect
or mmap
is also a bit slow). In particular, mimicking this way Hurd/Mach external pagers might be inefficient.

- 1
- 1

- 223,805
- 18
- 296
- 547
-
System calls are sort of atomic, but they can return control to user code either where they were called from, or into a signal handler. The normal behaviour is for the signal handler to do something, then return, then the interrupted system call return EINTR. (and the glibc can call it again, I think is what happens.) If you make a system call from inside a signal handler, the OS I guess queues further signals until you're done. Or optionally does something weird if you call a system call that's not on the guaranteed-safe list. – Peter Cordes Dec 03 '14 at 14:54
-
`sigaction` allows you to specify exactly what happens with other signals if they occur within a signal handler -- whether they are blocked or or not. There's more info on the `sigreturn` and `restart_syscall` linux man pages. – Chris Dodd May 03 '22 at 02:21