|
| 1 | +/** |
| 2 | + * @name Iterator to expired container |
| 3 | + * @description Using an iterator owned by a container whose lifetimes has expired may lead to unexpected behavior. |
| 4 | + * @kind problem |
| 5 | + * @precision high |
| 6 | + * @id cpp/iterator-to-expired-container |
| 7 | + * @problem.severity warning |
| 8 | + * @tags reliability |
| 9 | + * security |
| 10 | + * external/cwe/cwe-416 |
| 11 | + * external/cwe/cwe-664 |
| 12 | + */ |
| 13 | + |
| 14 | +// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released |
| 15 | +import cpp |
| 16 | +import semmle.code.cpp.ir.IR |
| 17 | +import semmle.code.cpp.dataflow.new.DataFlow |
| 18 | +import semmle.code.cpp.models.implementations.StdContainer |
| 19 | +import semmle.code.cpp.models.implementations.StdMap |
| 20 | +import semmle.code.cpp.models.implementations.Iterator |
| 21 | + |
| 22 | +/** |
| 23 | + * A configuration to track flow from a temporary variable to the qualifier of |
| 24 | + * a destructor call |
| 25 | + */ |
| 26 | +module TempToDestructorConfig implements DataFlow::ConfigSig { |
| 27 | + predicate isSource(DataFlow::Node source) { |
| 28 | + source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable |
| 29 | + } |
| 30 | + |
| 31 | + predicate isSink(DataFlow::Node sink) { |
| 32 | + sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>; |
| 37 | + |
| 38 | +/** |
| 39 | + * Gets a `DataFlow::Node` that represents a temporary that will be destroyed |
| 40 | + * by a call to a destructor, or a `DataFlow::Node` that will transitively be |
| 41 | + * destroyed by a call to a destructor. |
| 42 | + * |
| 43 | + * For the latter case, consider something like: |
| 44 | + * ``` |
| 45 | + * std::vector<std::vector<int>> get_2d_vector(); |
| 46 | + * auto& v = get_2d_vector()[0]; |
| 47 | + * ``` |
| 48 | + * Given the above, this predicate returns the node corresponding |
| 49 | + * to `get_2d_vector()[0]` since the temporary `get_2d_vector()` gets |
| 50 | + * destroyed by a call to `std::vector<std::vector<int>>::~vector`, |
| 51 | + * and thus the result of `get_2d_vector()[0]` is also an invalid reference. |
| 52 | + */ |
| 53 | +DataFlow::Node getADestroyedNode() { |
| 54 | + exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() | |
| 55 | + result = destroyedTemp.getNode() |
| 56 | + or |
| 57 | + exists(CallInstruction call | |
| 58 | + result.asInstruction() = call and |
| 59 | + DataFlow::localFlow(destroyedTemp.getNode(), |
| 60 | + DataFlow::operandNode(call.getThisArgumentOperand())) |
| 61 | + | |
| 62 | + call.getStaticCallTarget() instanceof StdSequenceContainerAt or |
| 63 | + call.getStaticCallTarget() instanceof StdMapAt |
| 64 | + ) |
| 65 | + ) |
| 66 | +} |
| 67 | + |
| 68 | +predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) { |
| 69 | + exists(CallInstruction call | |
| 70 | + call = sink.asOperand().(ThisArgumentOperand).getCall() and |
| 71 | + fc = call.getUnconvertedResultExpression() and |
| 72 | + call.getStaticCallTarget() instanceof BeginOrEndFunction |
| 73 | + ) |
| 74 | +} |
| 75 | + |
| 76 | +/** |
| 77 | + * Flow from any destroyed object to the qualifier of a `begin` call |
| 78 | + */ |
| 79 | +module DestroyedToBeginConfig implements DataFlow::ConfigSig { |
| 80 | + predicate isSource(DataFlow::Node source) { source = getADestroyedNode() } |
| 81 | + |
| 82 | + predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) } |
| 83 | +} |
| 84 | + |
| 85 | +module DestroyedToBeginFlow = DataFlow::Global<DestroyedToBeginConfig>; |
| 86 | + |
| 87 | +from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd |
| 88 | +where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd) |
| 89 | +select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString() |
0 commit comments