|
11 | 11 |
|
12 | 12 | import cpp |
13 | 13 | import codingstandards.c.cert |
| 14 | +import codingstandards.c.Signal |
| 15 | +import semmle.code.cpp.dataflow.DataFlow |
14 | 16 |
|
15 | | -from |
| 17 | +/** |
| 18 | + * Does not an access an external variable except |
| 19 | + * to assign a value to a volatile static variable of sig_atomic_t type |
| 20 | + */ |
| 21 | +class AsyncSafeVariableAccess extends VariableAccess { |
| 22 | + AsyncSafeVariableAccess() { |
| 23 | + this.getTarget() instanceof StackVariable |
| 24 | + or |
| 25 | + this.getTarget().getType().hasName("volatile sig_atomic_t") and // TODO search without "volatile" |
| 26 | + this.isModified() and |
| 27 | + this.getTarget().isVolatile() |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +abstract class AsyncSafeFunction extends Function { } |
| 32 | + |
| 33 | +/** |
| 34 | + * C standard library ayncronous-safe functions |
| 35 | + */ |
| 36 | +class CAsyncSafeFunction extends AsyncSafeFunction { |
| 37 | + //tion, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler |
| 38 | + CAsyncSafeFunction() { this.hasGlobalName(["abort", "_Exit", "quick_exit", "signal"]) } |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * POSIX defined ayncronous-safe functions |
| 43 | + */ |
| 44 | +class PosixAsyncSafeFunction extends AsyncSafeFunction { |
| 45 | + PosixAsyncSafeFunction() { |
| 46 | + this.hasGlobalName([ |
| 47 | + "_Exit", "_exit", "abort", "accept", "access", "aio_error", "aio_return", "aio_suspend", |
| 48 | + "alarm", "bind", "cfgetispeed", "cfgetospeed", "cfsetispeed", "cfsetospeed", "chdir", |
| 49 | + "chmod", "chown", "clock_gettime", "close", "connect", "creat", "dup", "dup2", "execl", |
| 50 | + "execle", "execv", "execve", "faccessat", "fchdir", "fchmod", "fchmodat", "fchown", |
| 51 | + "fchownat", "fcntl", "fdatasync", "fexecve", "fork", "fstat", "fstatat", "fsync", |
| 52 | + "ftruncate", "futimens", "getegid", "geteuid", "getgid", "getgroups", "getpeername", |
| 53 | + "getpgrp", "getpid", "getppid", "getsockname", "getsockopt", "getuid", "kill", "link", |
| 54 | + "linkat", "listen", "lseek", "lstat", "mkdir", "mkdirat", "mkfifo", "mkfifoat", "mknod", |
| 55 | + "mknodat", "open", "openat", "pause", "pipe", "poll", "posix_trace_event", "pselect", |
| 56 | + "pthread_kill", "pthread_self", "pthread_sigmask", "raise", "read", "readlink", |
| 57 | + "readlinkat", "recv", "recvfrom", "recvmsg", "rename", "renameat", "rmdir", "select", |
| 58 | + "sem_post", "send", "sendmsg", "sendto", "setgid", "setpgid", "setsid", "setsockopt", |
| 59 | + "setuid", "shutdown", "sigaction", "sigaddset", "sigdelset", "sigemptyset", "sigfillset", |
| 60 | + "sigismember", "signal", "sigpause", "sigpending", "sigprocmask", "sigqueue", "sigset", |
| 61 | + "sigsuspend", "sleep", "sockatmark", "socket", "socketpair", "stat", "symlink", "symlinkat", |
| 62 | + "tcdrain", "tcflow", "tcflush", "tcgetattr", "tcgetpgrp", "tcsendbreak", "tcsetattr", |
| 63 | + "tcsetpgrp", "time", "timer_getoverrun", "timer_gettime", "timer_settime", "times", "umask", |
| 64 | + "uname", "unlink", "unlinkat", "utime", "utimensat", "utimes", "wait", "waitpid", "write" |
| 65 | + ]) |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +/** |
| 70 | + * Application defined ayncronous-safe functions |
| 71 | + */ |
| 72 | +class ApplicationAsyncSafeFunction extends AsyncSafeFunction { |
| 73 | + pragma[nomagic] |
| 74 | + ApplicationAsyncSafeFunction() { |
| 75 | + // Application-defined |
| 76 | + this.hasDefinition() and |
| 77 | + exists(this.getFile().getRelativePath()) and |
| 78 | + // Only references async-safe variables |
| 79 | + not exists(VariableAccess va | |
| 80 | + this = va.getEnclosingFunction() and not va instanceof AsyncSafeVariableAccess |
| 81 | + ) and |
| 82 | + // Only calls async-safe functions |
| 83 | + not exists(Function f | this.calls(f) and not f instanceof AsyncSafeFunction) |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +/** |
| 88 | + * Call to function `raise` withing a signal handler with mismatching signals |
| 89 | + * ``` |
| 90 | + * void int_handler(int signum) { |
| 91 | + * raise(SIGTERM); |
| 92 | + * } |
| 93 | + * int main(void) { |
| 94 | + * signal(SIGINT, int_handler); |
| 95 | + * } |
| 96 | + * ``` |
| 97 | + */ |
| 98 | +class AsyncUnsafeRaiseCall extends FunctionCall { |
| 99 | + AsyncUnsafeRaiseCall() { |
| 100 | + this.getTarget().hasGlobalName("raise") and |
| 101 | + exists(SignalHandler handler | handler = this.getEnclosingFunction() | |
| 102 | + not handler.getRegistration().getArgument(0).getValue() = this.getArgument(0).getValue() and |
| 103 | + not DataFlow::localFlow(DataFlow::parameterNode(handler.getParameter(0)), |
| 104 | + DataFlow::exprNode(this.getArgument(0))) |
| 105 | + ) |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +from FunctionCall fc, SignalHandler handler |
16 | 110 | where |
17 | | - not isExcluded(x, SignalHandlersPackage::callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery()) and |
18 | | -select |
| 111 | + not isExcluded(fc, SignalHandlersPackage::callOnlyAsyncSafeFunctionsWithinSignalHandlersQuery()) and |
| 112 | + handler = fc.getEnclosingFunction() and |
| 113 | + ( |
| 114 | + not fc.getTarget() instanceof AsyncSafeFunction |
| 115 | + or |
| 116 | + fc instanceof AsyncUnsafeRaiseCall |
| 117 | + ) |
| 118 | +select fc, "Asyncronous-unsafe function calls within a $@ can lead to undefined behavior.", |
| 119 | + handler.getRegistration(), "signal handler" |
0 commit comments