|
11 | 11 | */ |
12 | 12 |
|
13 | 13 | import cpp |
| 14 | +import semmle.code.cpp.dataflow.new.DataFlow |
| 15 | + |
14 | 16 |
|
15 | 17 | /* List from https://man7.org/linux/man-pages/man3/stdio.3.html */ |
16 | 18 | class StdioFunction extends Function { |
@@ -46,22 +48,106 @@ predicate isAsyncUnsafe(Function signalHandler) { |
46 | 48 | ) |
47 | 49 | } |
48 | 50 |
|
49 | | -predicate isSignalHandlerField(FieldAccess fa) { |
50 | | - fa.getTarget().getName() in ["__sa_handler", "__sa_sigaction", "sa_handler", "sa_sigaction"] |
| 51 | +/** |
| 52 | + * Flows from any function ptr |
| 53 | + */ |
| 54 | +module HandlerToSignalConfiguration implements DataFlow::ConfigSig { |
| 55 | + predicate isSource(DataFlow::Node source) { |
| 56 | + source.asExpr() = any(Function f || f.getAnAccess()) |
| 57 | + } |
| 58 | + |
| 59 | + predicate isSink(DataFlow::Node sink) { |
| 60 | + sink = sink |
| 61 | + } |
51 | 62 | } |
| 63 | +module HandlerToSignal = DataFlow::Global<HandlerToSignalConfiguration>; |
52 | 64 |
|
53 | | -from FunctionCall fc, Function signalHandler, FieldAccess fa |
54 | | -where |
55 | | - ( |
56 | | - fc.getTarget().getName().matches("%_signal") or |
57 | | - fc.getTarget().getName() = "signal" |
58 | | - ) and |
59 | | - signalHandler.getName() = fc.getArgument(1).toString() and |
60 | | - isAsyncUnsafe(signalHandler) |
| 65 | +/** |
| 66 | + * signal(SIGX, signalHandler) |
| 67 | + */ |
| 68 | +predicate isSignal(FunctionCall signalCall, Function signalHandler) { |
| 69 | + signalCall.getTarget().getName() = "signal" |
| 70 | + and exists(DataFlow::Node source, DataFlow::Node sink | |
| 71 | + HandlerToSignal::flow(source, sink) |
| 72 | + and source.asExpr() = signalHandler.getAnAccess() |
| 73 | + and sink.asExpr() = signalCall.getArgument(1) |
| 74 | + ) |
| 75 | +} |
| 76 | + |
| 77 | +/** |
| 78 | + * struct sigaction sigactVar = ... |
| 79 | + * sigaction(SIGX, &sigactVar, ...) |
| 80 | + */ |
| 81 | +predicate isSigaction(FunctionCall sigactionCall, Function signalHandler, Variable sigactVar) { |
| 82 | + exists(Struct sigactStruct, Field handlerField, DataFlow::Node source, DataFlow::Node sink | |
| 83 | + sigactionCall.getTarget().getName() = "sigaction" |
| 84 | + and sigactionCall.getArgument(1).getAChild*() = sigactVar.getAnAccess() |
| 85 | + |
| 86 | + // struct sigaction with the handler func |
| 87 | + and sigactStruct.getName() = "sigaction" |
| 88 | + and handlerField.getName() = ["sa_handler", "sa_sigaction", "__sa_handler", "__sa_sigaction", "__sigaction_u"] |
| 89 | + and ( |
| 90 | + handlerField = sigactStruct.getAField() |
| 91 | + or |
| 92 | + exists(Union u | u.getAField() = handlerField and u = sigactStruct.getAField().getType()) |
| 93 | + ) |
| 94 | + |
| 95 | + and ( |
| 96 | + // sigactVar.sa_handler = &signalHandler |
| 97 | + exists(Assignment a, ValueFieldAccess vfa | |
| 98 | + vfa.getTarget() = handlerField |
| 99 | + and vfa.getQualifier+() = sigactVar.getAnAccess() |
| 100 | + and a.getLValue() = vfa.getAChild*() |
| 101 | + |
| 102 | + and source.asExpr() = signalHandler.getAnAccess() |
| 103 | + and sink.asExpr() = a.getRValue() |
| 104 | + and HandlerToSignal::flow(source, sink) |
| 105 | + ) |
| 106 | + or |
| 107 | + // struct sigaction sigactVar = {.sa_sigaction = signalHandler}; |
| 108 | + exists(ClassAggregateLiteral initLit | |
| 109 | + sigactVar.getInitializer().getExpr() = initLit |
| 110 | + and source.asExpr() = signalHandler.getAnAccess() |
| 111 | + and sink.asExpr() = initLit.getAFieldExpr(handlerField).getAChild*() |
| 112 | + and HandlerToSignal::flow(source, sink) |
| 113 | + ) |
| 114 | + ) |
| 115 | + ) |
| 116 | +} |
| 117 | + |
| 118 | +/** |
| 119 | + * Determine if new signals are blocked |
| 120 | + * TODO: should only find writes and only for correct (or all) signals |
| 121 | + * TODO: should detect other block mechanisms |
| 122 | + */ |
| 123 | +predicate isSignalDeliveryBlocked(Variable sigactVar) { |
| 124 | + exists(ValueFieldAccess dfa | |
| 125 | + dfa.getQualifier+() = sigactVar.getAnAccess() and dfa.getTarget().getName() = "sa_mask" |
| 126 | + ) |
61 | 127 | or |
62 | | - fc.getTarget().getName() = "sigaction" and |
63 | | - isSignalHandlerField(fa) and |
64 | | - signalHandler = fa.getTarget().getAnAssignedValue().(AddressOfExpr).getAddressable() and |
| 128 | + exists(Field mask | |
| 129 | + mask.getName() = "sa_mask" |
| 130 | + and exists(sigactVar.getInitializer().getExpr().(ClassAggregateLiteral).getAFieldExpr(mask)) |
| 131 | + ) |
| 132 | +} |
| 133 | + |
| 134 | +string deliveryNotBlockedMsg() { |
| 135 | + result = "Moreover, delivery of new signals may be not blocked. " |
| 136 | +} |
| 137 | + |
| 138 | +from FunctionCall fc, Function signalHandler, string msg |
| 139 | +where |
65 | 140 | isAsyncUnsafe(signalHandler) |
66 | | -select signalHandler, |
67 | | - "is a non-trivial signal handler that may be using functions that are not async-safe." |
| 141 | + and ( |
| 142 | + (isSignal(fc, signalHandler) and msg = deliveryNotBlockedMsg()) |
| 143 | + or |
| 144 | + exists(Variable sigactVar | |
| 145 | + isSigaction(fc, signalHandler, sigactVar) |
| 146 | + and if isSignalDeliveryBlocked(sigactVar) then |
| 147 | + msg = "" |
| 148 | + else |
| 149 | + msg = deliveryNotBlockedMsg() |
| 150 | + ) |
| 151 | + ) |
| 152 | +select signalHandler, "$@ is a non-trivial signal handler that uses not async-safe functions. " + msg + |
| 153 | + "Handler is registered by $@.", signalHandler, signalHandler.toString(), fc, fc.toString() |
0 commit comments