diff --git a/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected b/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected index c21b79749d17..650bf3169412 100644 --- a/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected +++ b/rust/ql/integration-tests/query-suite/rust-security-and-quality.qls.expected @@ -16,6 +16,7 @@ ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql ql/rust/ql/src/queries/security/CWE-696/BadCtorInitialization.ql ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql +ql/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql ql/rust/ql/src/queries/security/CWE-825/AccessInvalidPointer.ql ql/rust/ql/src/queries/summary/LinesOfCode.ql ql/rust/ql/src/queries/summary/LinesOfUserCode.ql diff --git a/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected b/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected index b3683f02d927..b5df88f96eca 100644 --- a/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected +++ b/rust/ql/integration-tests/query-suite/rust-security-extended.qls.expected @@ -15,6 +15,7 @@ ql/rust/ql/src/queries/security/CWE-312/CleartextLogging.ql ql/rust/ql/src/queries/security/CWE-327/BrokenCryptoAlgorithm.ql ql/rust/ql/src/queries/security/CWE-328/WeakSensitiveDataHashing.ql ql/rust/ql/src/queries/security/CWE-770/UncontrolledAllocationSize.ql +ql/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql ql/rust/ql/src/queries/security/CWE-825/AccessInvalidPointer.ql ql/rust/ql/src/queries/summary/LinesOfCode.ql ql/rust/ql/src/queries/summary/LinesOfUserCode.ql diff --git a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll index b80da6d7084f..163af5da899c 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll @@ -59,6 +59,17 @@ module Impl { ) } + /** Gets the block that encloses this node, if any. */ + cached + BlockExpr getEnclosingBlock() { + exists(AstNode p | p = this.getParentNode() | + result = p + or + not p instanceof BlockExpr and + result = p.getEnclosingBlock() + ) + } + /** Holds if this node is inside a macro expansion. */ predicate isInMacroExpansion() { MacroCallImpl::isInMacroExpansion(_, this) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll index 790186bf2c9f..3c50487edd85 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariableImpl.qll @@ -127,6 +127,9 @@ module Impl { */ Name getName() { variableDecl(definingNode, result, text) } + /** Gets the block that encloses this variable, if any. */ + BlockExpr getEnclosingBlock() { result = definingNode.getEnclosingBlock() } + /** Gets the `self` parameter that declares this variable, if any. */ SelfParam getSelfParam() { result.getName() = this.getName() } diff --git a/rust/ql/lib/codeql/rust/security/AccessAfterLifetimeExtensions.qll b/rust/ql/lib/codeql/rust/security/AccessAfterLifetimeExtensions.qll new file mode 100644 index 000000000000..4b3177c9df95 --- /dev/null +++ b/rust/ql/lib/codeql/rust/security/AccessAfterLifetimeExtensions.qll @@ -0,0 +1,119 @@ +/** + * Provides classes and predicates for reasoning about accesses to a pointer + * after its lifetime has ended. + */ + +import rust +private import codeql.rust.dataflow.DataFlow +private import codeql.rust.security.AccessInvalidPointerExtensions +private import codeql.rust.internal.Type +private import codeql.rust.internal.TypeInference as TypeInference + +/** + * Provides default sources, sinks and barriers for detecting accesses to a + * pointer after its lifetime has ended, as well as extension points for + * adding your own. Note that a particular `(source, sink)` pair must be + * checked with `dereferenceAfterLifetime` to determine if it is a result. + */ +module AccessAfterLifetime { + /** + * A data flow source for accesses to a pointer after its lifetime has ended, + * that is, creation of a pointer or reference. + */ + abstract class Source extends DataFlow::Node { + /** + * Gets the value this pointer or reference points to. + */ + abstract Expr getTarget(); + } + + /** + * A data flow sink for accesses to a pointer after its lifetime has ended, + * that is, a dereference. We re-use the same sinks as for the accesses to + * invalid pointers query. + */ + class Sink = AccessInvalidPointer::Sink; + + /** + * A barrier for accesses to a pointer after its lifetime has ended. + */ + abstract class Barrier extends DataFlow::Node { } + + /** + * Holds if the pair `(source, sink)`, that represents a flow from a + * pointer or reference to a dereference, has its dereference outside the + * lifetime of the target variable `target`. + */ + bindingset[source, sink] + predicate dereferenceAfterLifetime(Source source, Sink sink, Variable target) { + exists(BlockExpr valueScope, BlockExpr accessScope | + valueScope(source.getTarget(), target, valueScope) and + accessScope = sink.asExpr().getExpr().getEnclosingBlock() and + not mayEncloseOnStack(valueScope, accessScope) + ) + } + + /** + * Holds if `var` has scope `scope`. + */ + private predicate variableScope(Variable var, BlockExpr scope) { + // local variable + scope = var.getEnclosingBlock() + or + // parameter + exists(Callable c | + var.getParameter().getEnclosingCallable() = c and + scope.getParentNode() = c + ) + } + + /** + * Holds if `value` accesses a variable `target` with scope `scope`. + */ + private predicate valueScope(Expr value, Variable target, BlockExpr scope) { + // variable access (to a non-reference) + target = value.(VariableAccess).getVariable() and + variableScope(target, scope) and + not TypeInference::inferType(value) instanceof RefType + or + // field access + valueScope(value.(FieldExpr).getContainer(), target, scope) + } + + /** + * Holds if block `a` contains block `b`, in the sense that a stack allocated variable in + * `a` may still be on the stack during execution of `b`. This is interprocedural, + * but is an overapproximation that doesn't accurately track call contexts + * (for example if `f` and `g` both call `b`, then then depending on the + * caller a variable in `f` or `g` may or may-not be on the stack during `b`). + */ + private predicate mayEncloseOnStack(BlockExpr a, BlockExpr b) { + // `b` is a child of `a` + a = b.getEnclosingBlock*() + or + // propagate through function calls + exists(CallExprBase ce | + mayEncloseOnStack(a, ce.getEnclosingBlock()) and + ce.getStaticTarget() = b.getEnclosingCallable() + ) + } + + /** + * A source that is a `RefExpr`. + */ + private class RefExprSource extends Source { + Expr targetValue; + + RefExprSource() { this.asExpr().getExpr().(RefExpr).getExpr() = targetValue } + + override Expr getTarget() { result = targetValue } + } + + /** + * A barrier for nodes inside closures, as we don't model lifetimes of + * variables through closures properly. + */ + private class ClosureBarrier extends Barrier { + ClosureBarrier() { this.asExpr().getExpr().getEnclosingCallable() instanceof ClosureExpr } + } +} diff --git a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.qhelp b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.qhelp new file mode 100644 index 000000000000..fe5dd64a270f --- /dev/null +++ b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.qhelp @@ -0,0 +1,50 @@ + + + + +

+Dereferencing a pointer after the lifetime of its target has ended causes undefined behavior. Memory +may be corrupted, causing the program to crash or behave incorrectly, in some cases exposing the program +to potential attacks. +

+ +
+ + +

+When dereferencing a pointer in unsafe code, take care that the pointer is still valid +at the time it is dereferenced. Code may need to be rearranged or changed to extend lifetimes. If +possible, rewrite the code using safe Rust types to avoid this kind of problem altogether. +

+ +
+ + +

+In the following example, val is local to get_pointer so its lifetime +ends when that function returns. However, a pointer to val is returned and dereferenced +after that lifetime has ended, causing undefined behavior: +

+ + + +

+One way to fix this is to change the return type of the function from a pointer to a Box, +which ensures that the value it points to remains on the heap for the lifetime of the Box +itself. Note that there is no longer a need for an unsafe block as the code no longer +handles pointers directly: +

+ + + +
+ + +
  • Rust Documentation: Behavior considered undefined >> Dangling pointers.
  • +
  • Rust Documentation: Module ptr - Safety.
  • +
  • Massachusetts Institute of Technology: Unsafe Rust - Dereferencing a Raw Pointer.
  • + +
    +
    diff --git a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql new file mode 100644 index 000000000000..b4f652668b71 --- /dev/null +++ b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetime.ql @@ -0,0 +1,52 @@ +/** + * @name Access of a pointer after its lifetime has ended + * @description Dereferencing a pointer after the lifetime of its target has ended + * causes undefined behavior and may result in memory corruption. + * @kind path-problem + * @problem.severity error + * @security-severity 9.8 + * @precision medium + * @id rust/access-after-lifetime-ended + * @tags reliability + * security + * external/cwe/cwe-825 + */ + +import rust +import codeql.rust.dataflow.DataFlow +import codeql.rust.dataflow.TaintTracking +import codeql.rust.security.AccessAfterLifetimeExtensions +import AccessAfterLifetimeFlow::PathGraph + +/** + * A data flow configuration for detecting accesses to a pointer after its + * lifetime has ended. + */ +module AccessAfterLifetimeConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node instanceof AccessAfterLifetime::Source } + + predicate isSink(DataFlow::Node node) { node instanceof AccessAfterLifetime::Sink } + + predicate isBarrier(DataFlow::Node barrier) { barrier instanceof AccessAfterLifetime::Barrier } +} + +module AccessAfterLifetimeFlow = TaintTracking::Global; + +from + AccessAfterLifetimeFlow::PathNode sourceNode, AccessAfterLifetimeFlow::PathNode sinkNode, + Variable target +where + // flow from a pointer or reference to the dereference + AccessAfterLifetimeFlow::flowPath(sourceNode, sinkNode) and + // check that the dereference is outside the lifetime of the target + AccessAfterLifetime::dereferenceAfterLifetime(sourceNode.getNode(), sinkNode.getNode(), target) and + // include only results inside `unsafe` blocks, as other results tend to be false positives + ( + sinkNode.getNode().asExpr().getExpr().getEnclosingBlock*().isUnsafe() or + sinkNode.getNode().asExpr().getExpr().getEnclosingCallable().(Function).isUnsafe() + ) and + // exclude cases with sources / sinks in macros, since these results are difficult to interpret + not sourceNode.getNode().asExpr().getExpr().isFromMacroExpansion() and + not sinkNode.getNode().asExpr().getExpr().isFromMacroExpansion() +select sinkNode.getNode(), sourceNode, sinkNode, + "Access of a pointer to $@ after its lifetime has ended.", target, target.toString() diff --git a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeBad.rs b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeBad.rs new file mode 100644 index 000000000000..c5f5cf607d10 --- /dev/null +++ b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeBad.rs @@ -0,0 +1,19 @@ + +fn get_pointer() -> *const i64 { + let val = 123; + + &val +} // lifetime of `val` ends here, the pointer becomes dangling + +fn example() { + let ptr = get_pointer(); + let val; + + // ... + + unsafe { + val = *ptr; // BAD: dereferences `ptr` after the lifetime of `val` has ended + } + + // ... +} diff --git a/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeGood.rs b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeGood.rs new file mode 100644 index 000000000000..944f6905b70b --- /dev/null +++ b/rust/ql/src/queries/security/CWE-825/AccessAfterLifetimeGood.rs @@ -0,0 +1,17 @@ + +fn get_box() -> Box { + let val = 123; + + Box::new(val) // copies `val` onto the heap, where it remains for the lifetime of the `Box`. +} + +fn example() { + let ptr = get_box(); + let val; + + // ... + + val = *ptr; // GOOD + + // ... +} diff --git a/rust/ql/test/extractor-tests/crate_graph/crates.expected b/rust/ql/test/extractor-tests/crate_graph/crates.expected index acc8aa3dec87..f934618db9c5 100644 --- a/rust/ql/test/extractor-tests/crate_graph/crates.expected +++ b/rust/ql/test/extractor-tests/crate_graph/crates.expected @@ -18,7 +18,7 @@ #-----| core -> Crate(core@0.0.0) #-----| compiler_builtins -> Crate(compiler_builtins@0.1.140) -#-----| Crate(cfg_if@1.0.0) +#-----| Crate(cfg_if@1.0.1) #-----| proc_macro -> Crate(proc_macro@0.0.0) #-----| alloc -> Crate(alloc@0.0.0) #-----| core -> Crate(core@0.0.0) @@ -89,7 +89,7 @@ main.rs: #-----| core -> Crate(core@0.0.0) #-----| std -> Crate(std@0.0.0) #-----| test -> Crate(test@0.0.0) -#-----| cfg_if -> Crate(cfg_if@1.0.0) +#-----| cfg_if -> Crate(cfg_if@1.0.1) #-----| digest -> Crate(digest@0.10.7) #-----| Crate(md5@0.7.0) diff --git a/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected new file mode 100644 index 000000000000..c4f00465654a --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected @@ -0,0 +1,422 @@ +#select +| lifetime.rs:69:13:69:14 | p1 | lifetime.rs:21:9:21:18 | &my_local1 | lifetime.rs:69:13:69:14 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:19:6:19:14 | my_local1 | my_local1 | +| lifetime.rs:70:13:70:14 | p2 | lifetime.rs:27:9:27:22 | &mut my_local2 | lifetime.rs:70:13:70:14 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:25:10:25:18 | my_local2 | my_local2 | +| lifetime.rs:71:13:71:14 | p3 | lifetime.rs:33:9:33:28 | &raw const my_local3 | lifetime.rs:71:13:71:14 | p3 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:31:6:31:14 | my_local3 | my_local3 | +| lifetime.rs:72:13:72:14 | p4 | lifetime.rs:39:9:39:26 | &raw mut my_local4 | lifetime.rs:72:13:72:14 | p4 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:37:10:37:18 | my_local4 | my_local4 | +| lifetime.rs:73:13:73:14 | p5 | lifetime.rs:43:9:43:15 | ¶m5 | lifetime.rs:73:13:73:14 | p5 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:42:23:42:28 | param5 | param5 | +| lifetime.rs:74:13:74:14 | p6 | lifetime.rs:50:9:50:18 | &... | lifetime.rs:74:13:74:14 | p6 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:47:6:47:8 | val | val | +| lifetime.rs:75:13:75:14 | p7 | lifetime.rs:63:8:63:27 | &raw const my_local7 | lifetime.rs:75:13:75:14 | p7 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:62:7:62:15 | my_local7 | my_local7 | +| lifetime.rs:76:4:76:5 | p2 | lifetime.rs:27:9:27:22 | &mut my_local2 | lifetime.rs:76:4:76:5 | p2 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:25:10:25:18 | my_local2 | my_local2 | +| lifetime.rs:77:4:77:5 | p4 | lifetime.rs:39:9:39:26 | &raw mut my_local4 | lifetime.rs:77:4:77:5 | p4 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:37:10:37:18 | my_local4 | my_local4 | +| lifetime.rs:172:13:172:15 | ptr | lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:172:13:172:15 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:186:6:186:14 | my_local1 | my_local1 | +| lifetime.rs:255:14:255:17 | prev | lifetime.rs:251:10:251:19 | &my_local2 | lifetime.rs:255:14:255:17 | prev | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:242:7:242:15 | my_local2 | my_local2 | +| lifetime.rs:310:31:310:32 | e1 | lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:310:31:310:32 | e1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:271:6:271:7 | e1 | e1 | +| lifetime.rs:317:13:317:18 | result | lifetime.rs:289:25:289:26 | &x | lifetime.rs:317:13:317:18 | result | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:289:17:289:17 | x | x | +| lifetime.rs:411:16:411:17 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:411:16:411:17 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair | +| lifetime.rs:416:16:416:17 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:416:16:416:17 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair | +| lifetime.rs:428:7:428:8 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:428:7:428:8 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair | +| lifetime.rs:433:7:433:8 | p1 | lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:433:7:433:8 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:382:11:382:17 | my_pair | my_pair | +| lifetime.rs:459:13:459:14 | p1 | lifetime.rs:442:17:442:23 | &my_val | lifetime.rs:459:13:459:14 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:441:6:441:11 | my_val | my_val | +| lifetime.rs:460:13:460:31 | get_ptr_from_ref(...) | lifetime.rs:442:17:442:23 | &my_val | lifetime.rs:460:13:460:31 | get_ptr_from_ref(...) | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:441:6:441:11 | my_val | my_val | +| lifetime.rs:659:15:659:18 | ref1 | lifetime.rs:654:31:654:35 | &str1 | lifetime.rs:659:15:659:18 | ref1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:653:8:653:11 | str1 | str1 | +| lifetime.rs:667:14:667:17 | ref1 | lifetime.rs:654:31:654:35 | &str1 | lifetime.rs:667:14:667:17 | ref1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:653:8:653:11 | str1 | str1 | +| lifetime.rs:667:14:667:17 | ref1 | lifetime.rs:655:11:655:25 | &raw const str2 | lifetime.rs:667:14:667:17 | ref1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:651:7:651:10 | str2 | str2 | +| lifetime.rs:789:12:789:13 | p1 | lifetime.rs:781:9:781:19 | &my_local10 | lifetime.rs:789:12:789:13 | p1 | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:779:6:779:15 | my_local10 | my_local10 | +| lifetime.rs:808:10:808:12 | ptr | lifetime.rs:798:9:798:12 | &val | lifetime.rs:808:10:808:12 | ptr | Access of a pointer to $@ after its lifetime has ended. | lifetime.rs:796:6:796:8 | val | val | +edges +| deallocation.rs:148:6:148:7 | p1 | deallocation.rs:151:14:151:15 | p1 | provenance | | +| deallocation.rs:148:6:148:7 | p1 | deallocation.rs:158:14:158:15 | p1 | provenance | | +| deallocation.rs:148:30:148:38 | &raw const my_buffer | deallocation.rs:148:6:148:7 | p1 | provenance | | +| deallocation.rs:228:28:228:43 | ...: ... | deallocation.rs:230:18:230:20 | ptr | provenance | | +| deallocation.rs:240:27:240:42 | ...: ... | deallocation.rs:248:18:248:20 | ptr | provenance | | +| deallocation.rs:257:7:257:10 | ptr1 | deallocation.rs:260:4:260:7 | ptr1 | provenance | | +| deallocation.rs:257:7:257:10 | ptr1 | deallocation.rs:260:4:260:7 | ptr1 | provenance | | +| deallocation.rs:257:14:257:33 | &raw mut ... | deallocation.rs:257:7:257:10 | ptr1 | provenance | | +| deallocation.rs:258:7:258:10 | ptr2 | deallocation.rs:261:4:261:7 | ptr2 | provenance | | +| deallocation.rs:258:7:258:10 | ptr2 | deallocation.rs:261:4:261:7 | ptr2 | provenance | | +| deallocation.rs:258:14:258:33 | &raw mut ... | deallocation.rs:258:7:258:10 | ptr2 | provenance | | +| deallocation.rs:260:4:260:7 | ptr1 | deallocation.rs:263:27:263:30 | ptr1 | provenance | | +| deallocation.rs:261:4:261:7 | ptr2 | deallocation.rs:265:26:265:29 | ptr2 | provenance | | +| deallocation.rs:263:27:263:30 | ptr1 | deallocation.rs:228:28:228:43 | ...: ... | provenance | | +| deallocation.rs:265:26:265:29 | ptr2 | deallocation.rs:240:27:240:42 | ...: ... | provenance | | +| deallocation.rs:276:6:276:9 | ptr1 | deallocation.rs:279:13:279:16 | ptr1 | provenance | | +| deallocation.rs:276:6:276:9 | ptr1 | deallocation.rs:287:13:287:16 | ptr1 | provenance | | +| deallocation.rs:276:13:276:28 | &raw mut ... | deallocation.rs:276:6:276:9 | ptr1 | provenance | | +| deallocation.rs:295:6:295:9 | ptr2 | deallocation.rs:298:13:298:16 | ptr2 | provenance | | +| deallocation.rs:295:6:295:9 | ptr2 | deallocation.rs:308:13:308:16 | ptr2 | provenance | | +| deallocation.rs:295:13:295:28 | &raw mut ... | deallocation.rs:295:6:295:9 | ptr2 | provenance | | +| lifetime.rs:21:2:21:18 | return ... | lifetime.rs:54:11:54:30 | get_local_dangling(...) | provenance | | +| lifetime.rs:21:9:21:18 | &my_local1 | lifetime.rs:21:2:21:18 | return ... | provenance | | +| lifetime.rs:27:2:27:22 | return ... | lifetime.rs:55:11:55:34 | get_local_dangling_mut(...) | provenance | | +| lifetime.rs:27:9:27:22 | &mut my_local2 | lifetime.rs:27:2:27:22 | return ... | provenance | | +| lifetime.rs:33:2:33:28 | return ... | lifetime.rs:56:11:56:40 | get_local_dangling_raw_const(...) | provenance | | +| lifetime.rs:33:9:33:28 | &raw const my_local3 | lifetime.rs:33:2:33:28 | return ... | provenance | | +| lifetime.rs:39:2:39:26 | return ... | lifetime.rs:57:11:57:38 | get_local_dangling_raw_mut(...) | provenance | | +| lifetime.rs:39:9:39:26 | &raw mut my_local4 | lifetime.rs:39:2:39:26 | return ... | provenance | | +| lifetime.rs:43:2:43:15 | return ... | lifetime.rs:58:11:58:31 | get_param_dangling(...) | provenance | | +| lifetime.rs:43:9:43:15 | ¶m5 | lifetime.rs:43:2:43:15 | return ... | provenance | | +| lifetime.rs:50:2:50:18 | return ... | lifetime.rs:59:11:59:36 | get_local_field_dangling(...) | provenance | | +| lifetime.rs:50:9:50:18 | &... | lifetime.rs:50:2:50:18 | return ... | provenance | | +| lifetime.rs:54:6:54:7 | p1 | lifetime.rs:69:13:69:14 | p1 | provenance | | +| lifetime.rs:54:11:54:30 | get_local_dangling(...) | lifetime.rs:54:6:54:7 | p1 | provenance | | +| lifetime.rs:55:6:55:7 | p2 | lifetime.rs:70:13:70:14 | p2 | provenance | | +| lifetime.rs:55:6:55:7 | p2 | lifetime.rs:76:4:76:5 | p2 | provenance | | +| lifetime.rs:55:11:55:34 | get_local_dangling_mut(...) | lifetime.rs:55:6:55:7 | p2 | provenance | | +| lifetime.rs:56:6:56:7 | p3 | lifetime.rs:71:13:71:14 | p3 | provenance | | +| lifetime.rs:56:11:56:40 | get_local_dangling_raw_const(...) | lifetime.rs:56:6:56:7 | p3 | provenance | | +| lifetime.rs:57:6:57:7 | p4 | lifetime.rs:72:13:72:14 | p4 | provenance | | +| lifetime.rs:57:6:57:7 | p4 | lifetime.rs:77:4:77:5 | p4 | provenance | | +| lifetime.rs:57:11:57:38 | get_local_dangling_raw_mut(...) | lifetime.rs:57:6:57:7 | p4 | provenance | | +| lifetime.rs:58:6:58:7 | p5 | lifetime.rs:73:13:73:14 | p5 | provenance | | +| lifetime.rs:58:11:58:31 | get_param_dangling(...) | lifetime.rs:58:6:58:7 | p5 | provenance | | +| lifetime.rs:59:6:59:7 | p6 | lifetime.rs:74:13:74:14 | p6 | provenance | | +| lifetime.rs:59:11:59:36 | get_local_field_dangling(...) | lifetime.rs:59:6:59:7 | p6 | provenance | | +| lifetime.rs:63:3:63:4 | p7 | lifetime.rs:75:13:75:14 | p7 | provenance | | +| lifetime.rs:63:8:63:27 | &raw const my_local7 | lifetime.rs:63:3:63:4 | p7 | provenance | | +| lifetime.rs:91:17:91:30 | ...: ... | lifetime.rs:101:14:101:15 | p1 | provenance | | +| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:102:14:102:15 | p2 | provenance | | +| lifetime.rs:91:33:91:44 | ...: ... | lifetime.rs:110:5:110:6 | p2 | provenance | | +| lifetime.rs:94:2:94:3 | p3 | lifetime.rs:103:14:103:15 | p3 | provenance | | +| lifetime.rs:94:7:94:16 | &my_local1 | lifetime.rs:94:2:94:3 | p3 | provenance | | +| lifetime.rs:119:15:119:24 | &my_local3 | lifetime.rs:91:17:91:30 | ...: ... | provenance | | +| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | lifetime.rs:91:33:91:44 | ...: ... | provenance | | +| lifetime.rs:127:2:127:24 | return ... | lifetime.rs:139:11:139:21 | get_const(...) | provenance | | +| lifetime.rs:127:9:127:24 | &MY_GLOBAL_CONST | lifetime.rs:127:2:127:24 | return ... | provenance | | +| lifetime.rs:134:3:134:30 | return ... | lifetime.rs:140:11:140:26 | get_static_mut(...) | provenance | | +| lifetime.rs:134:10:134:30 | &mut MY_GLOBAL_STATIC | lifetime.rs:134:3:134:30 | return ... | provenance | | +| lifetime.rs:139:6:139:7 | p1 | lifetime.rs:147:14:147:15 | p1 | provenance | | +| lifetime.rs:139:11:139:21 | get_const(...) | lifetime.rs:139:6:139:7 | p1 | provenance | | +| lifetime.rs:140:6:140:7 | p2 | lifetime.rs:148:14:148:15 | p2 | provenance | | +| lifetime.rs:140:6:140:7 | p2 | lifetime.rs:154:5:154:6 | p2 | provenance | | +| lifetime.rs:140:11:140:26 | get_static_mut(...) | lifetime.rs:140:6:140:7 | p2 | provenance | | +| lifetime.rs:161:17:161:31 | ...: ... | lifetime.rs:164:13:164:15 | ptr | provenance | | +| lifetime.rs:169:17:169:31 | ...: ... | lifetime.rs:172:13:172:15 | ptr | provenance | | +| lifetime.rs:177:17:177:31 | ...: ... | lifetime.rs:180:13:180:15 | ptr | provenance | | +| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:189:15:189:17 | ptr | provenance | | +| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:190:15:190:17 | ptr | provenance | | +| lifetime.rs:187:6:187:8 | ptr | lifetime.rs:192:2:192:11 | return ptr | provenance | | +| lifetime.rs:187:12:187:21 | &my_local1 | lifetime.rs:187:6:187:8 | ptr | provenance | | +| lifetime.rs:189:15:189:17 | ptr | lifetime.rs:161:17:161:31 | ...: ... | provenance | | +| lifetime.rs:190:15:190:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | | +| lifetime.rs:192:2:192:11 | return ptr | lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | provenance | | +| lifetime.rs:196:6:196:8 | ptr | lifetime.rs:200:15:200:17 | ptr | provenance | | +| lifetime.rs:196:6:196:8 | ptr | lifetime.rs:201:15:201:17 | ptr | provenance | | +| lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | lifetime.rs:196:6:196:8 | ptr | provenance | | +| lifetime.rs:200:15:200:17 | ptr | lifetime.rs:169:17:169:31 | ...: ... | provenance | | +| lifetime.rs:201:15:201:17 | ptr | lifetime.rs:177:17:177:31 | ...: ... | provenance | | +| lifetime.rs:206:19:206:36 | ...: ... | lifetime.rs:216:16:216:21 | ptr_up | provenance | | +| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:211:33:211:40 | ptr_ours | provenance | | +| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:217:18:217:25 | ptr_ours | provenance | | +| lifetime.rs:208:6:208:13 | ptr_ours | lifetime.rs:225:2:225:16 | return ptr_ours | provenance | | +| lifetime.rs:208:17:208:29 | &my_local_rec | lifetime.rs:208:6:208:13 | ptr_ours | provenance | | +| lifetime.rs:211:7:211:14 | ptr_down | lifetime.rs:218:18:218:25 | ptr_down | provenance | | +| lifetime.rs:211:18:211:52 | access_ptr_rec(...) | lifetime.rs:211:7:211:14 | ptr_down | provenance | | +| lifetime.rs:211:33:211:40 | ptr_ours | lifetime.rs:206:19:206:36 | ...: ... | provenance | | +| lifetime.rs:225:2:225:16 | return ptr_ours | lifetime.rs:211:18:211:52 | access_ptr_rec(...) | provenance | | +| lifetime.rs:230:6:230:14 | ptr_start | lifetime.rs:232:21:232:29 | ptr_start | provenance | | +| lifetime.rs:230:18:230:31 | &my_local_rec2 | lifetime.rs:230:6:230:14 | ptr_start | provenance | | +| lifetime.rs:232:21:232:29 | ptr_start | lifetime.rs:206:19:206:36 | ...: ... | provenance | | +| lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:247:15:247:18 | prev | provenance | | +| lifetime.rs:239:6:239:13 | mut prev | lifetime.rs:255:14:255:17 | prev | provenance | | +| lifetime.rs:239:34:239:43 | &my_local1 | lifetime.rs:239:6:239:13 | mut prev | provenance | | +| lifetime.rs:251:3:251:6 | prev | lifetime.rs:247:15:247:18 | prev | provenance | | +| lifetime.rs:251:3:251:6 | prev | lifetime.rs:255:14:255:17 | prev | provenance | | +| lifetime.rs:251:10:251:19 | &my_local2 | lifetime.rs:251:3:251:6 | prev | provenance | | +| lifetime.rs:270:47:275:1 | { ... } | lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | provenance | | +| lifetime.rs:272:6:272:11 | result | lifetime.rs:270:47:275:1 | { ... } | provenance | | +| lifetime.rs:272:30:272:32 | &e1 | lifetime.rs:272:6:272:11 | result | provenance | | +| lifetime.rs:284:46:300:1 | { ... } | lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | provenance | | +| lifetime.rs:288:2:288:7 | result | lifetime.rs:284:46:300:1 | { ... } | provenance | | +| lifetime.rs:288:2:288:7 | result | lifetime.rs:295:13:295:18 | result | provenance | | +| lifetime.rs:289:25:289:26 | &x | lifetime.rs:288:2:288:7 | result | provenance | | +| lifetime.rs:303:6:303:7 | e1 | lifetime.rs:310:31:310:32 | e1 | provenance | | +| lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | lifetime.rs:303:6:303:7 | e1 | provenance | | +| lifetime.rs:305:6:305:11 | result | lifetime.rs:317:13:317:18 | result | provenance | | +| lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | lifetime.rs:305:6:305:11 | result | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:388:15:388:16 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:391:15:391:16 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:399:6:399:7 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:401:6:401:7 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:411:16:411:17 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:416:16:416:17 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:428:7:428:8 | p1 | provenance | | +| lifetime.rs:383:3:383:4 | p1 | lifetime.rs:433:7:433:8 | p1 | provenance | | +| lifetime.rs:383:31:383:37 | &raw mut my_pair | lifetime.rs:383:3:383:4 | p1 | provenance | | +| lifetime.rs:384:3:384:4 | p2 | lifetime.rs:394:14:394:15 | p2 | provenance | | +| lifetime.rs:384:3:384:4 | p2 | lifetime.rs:421:15:421:16 | p2 | provenance | | +| lifetime.rs:384:27:384:35 | &raw const ... | lifetime.rs:384:3:384:4 | p2 | provenance | | +| lifetime.rs:385:3:385:4 | p3 | lifetime.rs:395:14:395:15 | p3 | provenance | | +| lifetime.rs:385:3:385:4 | p3 | lifetime.rs:400:5:400:6 | p3 | provenance | | +| lifetime.rs:385:3:385:4 | p3 | lifetime.rs:400:5:400:6 | p3 | provenance | | +| lifetime.rs:385:31:385:39 | &raw mut ... | lifetime.rs:385:3:385:4 | p3 | provenance | | +| lifetime.rs:400:5:400:6 | p3 | lifetime.rs:422:15:422:16 | p3 | provenance | | +| lifetime.rs:400:5:400:6 | p3 | lifetime.rs:429:6:429:7 | p3 | provenance | | +| lifetime.rs:442:6:442:7 | r1 | lifetime.rs:443:42:443:43 | r1 | provenance | | +| lifetime.rs:442:17:442:23 | &my_val | lifetime.rs:442:6:442:7 | r1 | provenance | | +| lifetime.rs:443:6:443:7 | p1 | lifetime.rs:446:13:446:14 | p1 | provenance | | +| lifetime.rs:443:6:443:7 | p1 | lifetime.rs:450:2:450:10 | return p1 | provenance | | +| lifetime.rs:443:23:443:44 | ...::from_ref(...) | lifetime.rs:443:6:443:7 | p1 | provenance | | +| lifetime.rs:443:42:443:43 | r1 | lifetime.rs:443:23:443:44 | ...::from_ref(...) | provenance | MaD:1 | +| lifetime.rs:450:2:450:10 | return p1 | lifetime.rs:454:11:454:29 | get_ptr_from_ref(...) | provenance | | +| lifetime.rs:450:2:450:10 | return p1 | lifetime.rs:460:13:460:31 | get_ptr_from_ref(...) | provenance | | +| lifetime.rs:454:6:454:7 | p1 | lifetime.rs:459:13:459:14 | p1 | provenance | | +| lifetime.rs:454:11:454:29 | get_ptr_from_ref(...) | lifetime.rs:454:6:454:7 | p1 | provenance | | +| lifetime.rs:568:7:568:8 | p2 | lifetime.rs:572:14:572:15 | p2 | provenance | | +| lifetime.rs:568:24:568:33 | &my_local2 | lifetime.rs:568:7:568:8 | p2 | provenance | | +| lifetime.rs:630:3:630:6 | str2 | lifetime.rs:633:15:633:18 | str2 | provenance | | +| lifetime.rs:630:3:630:6 | str2 | lifetime.rs:641:14:641:17 | str2 | provenance | | +| lifetime.rs:630:10:630:25 | &... | lifetime.rs:630:3:630:6 | str2 | provenance | | +| lifetime.rs:654:4:654:7 | str2 | lifetime.rs:655:22:655:25 | str2 | provenance | | +| lifetime.rs:654:31:654:35 | &str1 | lifetime.rs:654:4:654:7 | str2 | provenance | | +| lifetime.rs:655:4:655:7 | ref1 | lifetime.rs:659:15:659:18 | ref1 | provenance | | +| lifetime.rs:655:4:655:7 | ref1 | lifetime.rs:667:14:667:17 | ref1 | provenance | | +| lifetime.rs:655:4:655:7 | ref1 [&ref] | lifetime.rs:659:15:659:18 | ref1 | provenance | | +| lifetime.rs:655:4:655:7 | ref1 [&ref] | lifetime.rs:667:14:667:17 | ref1 | provenance | | +| lifetime.rs:655:11:655:25 | &raw const str2 | lifetime.rs:655:4:655:7 | ref1 | provenance | | +| lifetime.rs:655:11:655:25 | &raw const str2 [&ref] | lifetime.rs:655:4:655:7 | ref1 [&ref] | provenance | | +| lifetime.rs:655:22:655:25 | str2 | lifetime.rs:655:11:655:25 | &raw const str2 [&ref] | provenance | | +| lifetime.rs:680:7:680:8 | r1 | lifetime.rs:692:13:692:14 | r1 | provenance | | +| lifetime.rs:682:4:682:12 | &... | lifetime.rs:680:7:680:8 | r1 | provenance | | +| lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | lifetime.rs:684:8:684:9 | r2 | provenance | | +| lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | lifetime.rs:684:12:684:13 | r3 | provenance | | +| lifetime.rs:684:8:684:9 | r2 | lifetime.rs:693:13:693:14 | r2 | provenance | | +| lifetime.rs:684:12:684:13 | r3 | lifetime.rs:694:13:694:14 | r3 | provenance | | +| lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | provenance | | +| lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | provenance | | +| lifetime.rs:686:5:686:13 | &... | lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | provenance | | +| lifetime.rs:687:5:687:15 | &... | lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | provenance | | +| lifetime.rs:717:35:723:2 | { ... } | lifetime.rs:730:11:730:25 | e1.test_match() | provenance | | +| lifetime.rs:718:7:718:8 | r1 | lifetime.rs:717:35:723:2 | { ... } | provenance | | +| lifetime.rs:719:26:719:34 | &... | lifetime.rs:718:7:718:8 | r1 | provenance | | +| lifetime.rs:730:6:730:7 | r1 | lifetime.rs:734:12:734:13 | r1 | provenance | | +| lifetime.rs:730:11:730:25 | e1.test_match() | lifetime.rs:730:6:730:7 | r1 | provenance | | +| lifetime.rs:766:2:766:13 | &val | lifetime.rs:766:2:766:13 | ptr | provenance | | +| lifetime.rs:766:2:766:13 | ptr | lifetime.rs:767:2:767:13 | ptr | provenance | | +| lifetime.rs:769:6:769:8 | ptr | lifetime.rs:771:12:771:14 | ptr | provenance | | +| lifetime.rs:769:12:769:23 | &val | lifetime.rs:769:12:769:23 | ptr | provenance | | +| lifetime.rs:769:12:769:23 | ptr | lifetime.rs:769:6:769:8 | ptr | provenance | | +| lifetime.rs:781:2:781:19 | return ... | lifetime.rs:785:11:785:41 | get_local_for_unsafe_function(...) | provenance | | +| lifetime.rs:781:9:781:19 | &my_local10 | lifetime.rs:781:2:781:19 | return ... | provenance | | +| lifetime.rs:785:6:785:7 | p1 | lifetime.rs:789:12:789:13 | p1 | provenance | | +| lifetime.rs:785:11:785:41 | get_local_for_unsafe_function(...) | lifetime.rs:785:6:785:7 | p1 | provenance | | +| lifetime.rs:798:2:798:12 | return ... | lifetime.rs:802:12:802:24 | get_pointer(...) | provenance | | +| lifetime.rs:798:9:798:12 | &val | lifetime.rs:798:2:798:12 | return ... | provenance | | +| lifetime.rs:802:6:802:8 | ptr | lifetime.rs:808:10:808:12 | ptr | provenance | | +| lifetime.rs:802:12:802:24 | get_pointer(...) | lifetime.rs:802:6:802:8 | ptr | provenance | | +models +| 1 | Summary: lang:core; crate::ptr::from_ref; Argument[0]; ReturnValue; value | +nodes +| deallocation.rs:148:6:148:7 | p1 | semmle.label | p1 | +| deallocation.rs:148:30:148:38 | &raw const my_buffer | semmle.label | &raw const my_buffer | +| deallocation.rs:151:14:151:15 | p1 | semmle.label | p1 | +| deallocation.rs:158:14:158:15 | p1 | semmle.label | p1 | +| deallocation.rs:228:28:228:43 | ...: ... | semmle.label | ...: ... | +| deallocation.rs:230:18:230:20 | ptr | semmle.label | ptr | +| deallocation.rs:240:27:240:42 | ...: ... | semmle.label | ...: ... | +| deallocation.rs:248:18:248:20 | ptr | semmle.label | ptr | +| deallocation.rs:257:7:257:10 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:257:14:257:33 | &raw mut ... | semmle.label | &raw mut ... | +| deallocation.rs:258:7:258:10 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:258:14:258:33 | &raw mut ... | semmle.label | &raw mut ... | +| deallocation.rs:260:4:260:7 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:260:4:260:7 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:261:4:261:7 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:261:4:261:7 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:263:27:263:30 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:265:26:265:29 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:276:6:276:9 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:276:13:276:28 | &raw mut ... | semmle.label | &raw mut ... | +| deallocation.rs:279:13:279:16 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:287:13:287:16 | ptr1 | semmle.label | ptr1 | +| deallocation.rs:295:6:295:9 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:295:13:295:28 | &raw mut ... | semmle.label | &raw mut ... | +| deallocation.rs:298:13:298:16 | ptr2 | semmle.label | ptr2 | +| deallocation.rs:308:13:308:16 | ptr2 | semmle.label | ptr2 | +| lifetime.rs:21:2:21:18 | return ... | semmle.label | return ... | +| lifetime.rs:21:9:21:18 | &my_local1 | semmle.label | &my_local1 | +| lifetime.rs:27:2:27:22 | return ... | semmle.label | return ... | +| lifetime.rs:27:9:27:22 | &mut my_local2 | semmle.label | &mut my_local2 | +| lifetime.rs:33:2:33:28 | return ... | semmle.label | return ... | +| lifetime.rs:33:9:33:28 | &raw const my_local3 | semmle.label | &raw const my_local3 | +| lifetime.rs:39:2:39:26 | return ... | semmle.label | return ... | +| lifetime.rs:39:9:39:26 | &raw mut my_local4 | semmle.label | &raw mut my_local4 | +| lifetime.rs:43:2:43:15 | return ... | semmle.label | return ... | +| lifetime.rs:43:9:43:15 | ¶m5 | semmle.label | ¶m5 | +| lifetime.rs:50:2:50:18 | return ... | semmle.label | return ... | +| lifetime.rs:50:9:50:18 | &... | semmle.label | &... | +| lifetime.rs:54:6:54:7 | p1 | semmle.label | p1 | +| lifetime.rs:54:11:54:30 | get_local_dangling(...) | semmle.label | get_local_dangling(...) | +| lifetime.rs:55:6:55:7 | p2 | semmle.label | p2 | +| lifetime.rs:55:11:55:34 | get_local_dangling_mut(...) | semmle.label | get_local_dangling_mut(...) | +| lifetime.rs:56:6:56:7 | p3 | semmle.label | p3 | +| lifetime.rs:56:11:56:40 | get_local_dangling_raw_const(...) | semmle.label | get_local_dangling_raw_const(...) | +| lifetime.rs:57:6:57:7 | p4 | semmle.label | p4 | +| lifetime.rs:57:11:57:38 | get_local_dangling_raw_mut(...) | semmle.label | get_local_dangling_raw_mut(...) | +| lifetime.rs:58:6:58:7 | p5 | semmle.label | p5 | +| lifetime.rs:58:11:58:31 | get_param_dangling(...) | semmle.label | get_param_dangling(...) | +| lifetime.rs:59:6:59:7 | p6 | semmle.label | p6 | +| lifetime.rs:59:11:59:36 | get_local_field_dangling(...) | semmle.label | get_local_field_dangling(...) | +| lifetime.rs:63:3:63:4 | p7 | semmle.label | p7 | +| lifetime.rs:63:8:63:27 | &raw const my_local7 | semmle.label | &raw const my_local7 | +| lifetime.rs:69:13:69:14 | p1 | semmle.label | p1 | +| lifetime.rs:70:13:70:14 | p2 | semmle.label | p2 | +| lifetime.rs:71:13:71:14 | p3 | semmle.label | p3 | +| lifetime.rs:72:13:72:14 | p4 | semmle.label | p4 | +| lifetime.rs:73:13:73:14 | p5 | semmle.label | p5 | +| lifetime.rs:74:13:74:14 | p6 | semmle.label | p6 | +| lifetime.rs:75:13:75:14 | p7 | semmle.label | p7 | +| lifetime.rs:76:4:76:5 | p2 | semmle.label | p2 | +| lifetime.rs:77:4:77:5 | p4 | semmle.label | p4 | +| lifetime.rs:91:17:91:30 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:91:33:91:44 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:94:2:94:3 | p3 | semmle.label | p3 | +| lifetime.rs:94:7:94:16 | &my_local1 | semmle.label | &my_local1 | +| lifetime.rs:101:14:101:15 | p1 | semmle.label | p1 | +| lifetime.rs:102:14:102:15 | p2 | semmle.label | p2 | +| lifetime.rs:103:14:103:15 | p3 | semmle.label | p3 | +| lifetime.rs:110:5:110:6 | p2 | semmle.label | p2 | +| lifetime.rs:119:15:119:24 | &my_local3 | semmle.label | &my_local3 | +| lifetime.rs:119:27:119:44 | &mut my_local_mut4 | semmle.label | &mut my_local_mut4 | +| lifetime.rs:127:2:127:24 | return ... | semmle.label | return ... | +| lifetime.rs:127:9:127:24 | &MY_GLOBAL_CONST | semmle.label | &MY_GLOBAL_CONST | +| lifetime.rs:134:3:134:30 | return ... | semmle.label | return ... | +| lifetime.rs:134:10:134:30 | &mut MY_GLOBAL_STATIC | semmle.label | &mut MY_GLOBAL_STATIC | +| lifetime.rs:139:6:139:7 | p1 | semmle.label | p1 | +| lifetime.rs:139:11:139:21 | get_const(...) | semmle.label | get_const(...) | +| lifetime.rs:140:6:140:7 | p2 | semmle.label | p2 | +| lifetime.rs:140:11:140:26 | get_static_mut(...) | semmle.label | get_static_mut(...) | +| lifetime.rs:147:14:147:15 | p1 | semmle.label | p1 | +| lifetime.rs:148:14:148:15 | p2 | semmle.label | p2 | +| lifetime.rs:154:5:154:6 | p2 | semmle.label | p2 | +| lifetime.rs:161:17:161:31 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:164:13:164:15 | ptr | semmle.label | ptr | +| lifetime.rs:169:17:169:31 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:172:13:172:15 | ptr | semmle.label | ptr | +| lifetime.rs:177:17:177:31 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:180:13:180:15 | ptr | semmle.label | ptr | +| lifetime.rs:187:6:187:8 | ptr | semmle.label | ptr | +| lifetime.rs:187:12:187:21 | &my_local1 | semmle.label | &my_local1 | +| lifetime.rs:189:15:189:17 | ptr | semmle.label | ptr | +| lifetime.rs:190:15:190:17 | ptr | semmle.label | ptr | +| lifetime.rs:192:2:192:11 | return ptr | semmle.label | return ptr | +| lifetime.rs:196:6:196:8 | ptr | semmle.label | ptr | +| lifetime.rs:196:12:196:36 | access_and_get_dangling(...) | semmle.label | access_and_get_dangling(...) | +| lifetime.rs:200:15:200:17 | ptr | semmle.label | ptr | +| lifetime.rs:201:15:201:17 | ptr | semmle.label | ptr | +| lifetime.rs:206:19:206:36 | ...: ... | semmle.label | ...: ... | +| lifetime.rs:208:6:208:13 | ptr_ours | semmle.label | ptr_ours | +| lifetime.rs:208:17:208:29 | &my_local_rec | semmle.label | &my_local_rec | +| lifetime.rs:211:7:211:14 | ptr_down | semmle.label | ptr_down | +| lifetime.rs:211:18:211:52 | access_ptr_rec(...) | semmle.label | access_ptr_rec(...) | +| lifetime.rs:211:33:211:40 | ptr_ours | semmle.label | ptr_ours | +| lifetime.rs:216:16:216:21 | ptr_up | semmle.label | ptr_up | +| lifetime.rs:217:18:217:25 | ptr_ours | semmle.label | ptr_ours | +| lifetime.rs:218:18:218:25 | ptr_down | semmle.label | ptr_down | +| lifetime.rs:225:2:225:16 | return ptr_ours | semmle.label | return ptr_ours | +| lifetime.rs:230:6:230:14 | ptr_start | semmle.label | ptr_start | +| lifetime.rs:230:18:230:31 | &my_local_rec2 | semmle.label | &my_local_rec2 | +| lifetime.rs:232:21:232:29 | ptr_start | semmle.label | ptr_start | +| lifetime.rs:239:6:239:13 | mut prev | semmle.label | mut prev | +| lifetime.rs:239:34:239:43 | &my_local1 | semmle.label | &my_local1 | +| lifetime.rs:247:15:247:18 | prev | semmle.label | prev | +| lifetime.rs:251:3:251:6 | prev | semmle.label | prev | +| lifetime.rs:251:10:251:19 | &my_local2 | semmle.label | &my_local2 | +| lifetime.rs:255:14:255:17 | prev | semmle.label | prev | +| lifetime.rs:270:47:275:1 | { ... } | semmle.label | { ... } | +| lifetime.rs:272:6:272:11 | result | semmle.label | result | +| lifetime.rs:272:30:272:32 | &e1 | semmle.label | &e1 | +| lifetime.rs:284:46:300:1 | { ... } | semmle.label | { ... } | +| lifetime.rs:288:2:288:7 | result | semmle.label | result | +| lifetime.rs:289:25:289:26 | &x | semmle.label | &x | +| lifetime.rs:295:13:295:18 | result | semmle.label | result | +| lifetime.rs:303:6:303:7 | e1 | semmle.label | e1 | +| lifetime.rs:303:11:303:31 | get_pointer_to_enum(...) | semmle.label | get_pointer_to_enum(...) | +| lifetime.rs:305:6:305:11 | result | semmle.label | result | +| lifetime.rs:305:15:305:37 | get_pointer_from_enum(...) | semmle.label | get_pointer_from_enum(...) | +| lifetime.rs:310:31:310:32 | e1 | semmle.label | e1 | +| lifetime.rs:317:13:317:18 | result | semmle.label | result | +| lifetime.rs:383:3:383:4 | p1 | semmle.label | p1 | +| lifetime.rs:383:31:383:37 | &raw mut my_pair | semmle.label | &raw mut my_pair | +| lifetime.rs:384:3:384:4 | p2 | semmle.label | p2 | +| lifetime.rs:384:27:384:35 | &raw const ... | semmle.label | &raw const ... | +| lifetime.rs:385:3:385:4 | p3 | semmle.label | p3 | +| lifetime.rs:385:31:385:39 | &raw mut ... | semmle.label | &raw mut ... | +| lifetime.rs:388:15:388:16 | p1 | semmle.label | p1 | +| lifetime.rs:391:15:391:16 | p1 | semmle.label | p1 | +| lifetime.rs:394:14:394:15 | p2 | semmle.label | p2 | +| lifetime.rs:395:14:395:15 | p3 | semmle.label | p3 | +| lifetime.rs:399:6:399:7 | p1 | semmle.label | p1 | +| lifetime.rs:400:5:400:6 | p3 | semmle.label | p3 | +| lifetime.rs:400:5:400:6 | p3 | semmle.label | p3 | +| lifetime.rs:401:6:401:7 | p1 | semmle.label | p1 | +| lifetime.rs:411:16:411:17 | p1 | semmle.label | p1 | +| lifetime.rs:416:16:416:17 | p1 | semmle.label | p1 | +| lifetime.rs:421:15:421:16 | p2 | semmle.label | p2 | +| lifetime.rs:422:15:422:16 | p3 | semmle.label | p3 | +| lifetime.rs:428:7:428:8 | p1 | semmle.label | p1 | +| lifetime.rs:429:6:429:7 | p3 | semmle.label | p3 | +| lifetime.rs:433:7:433:8 | p1 | semmle.label | p1 | +| lifetime.rs:442:6:442:7 | r1 | semmle.label | r1 | +| lifetime.rs:442:17:442:23 | &my_val | semmle.label | &my_val | +| lifetime.rs:443:6:443:7 | p1 | semmle.label | p1 | +| lifetime.rs:443:23:443:44 | ...::from_ref(...) | semmle.label | ...::from_ref(...) | +| lifetime.rs:443:42:443:43 | r1 | semmle.label | r1 | +| lifetime.rs:446:13:446:14 | p1 | semmle.label | p1 | +| lifetime.rs:450:2:450:10 | return p1 | semmle.label | return p1 | +| lifetime.rs:454:6:454:7 | p1 | semmle.label | p1 | +| lifetime.rs:454:11:454:29 | get_ptr_from_ref(...) | semmle.label | get_ptr_from_ref(...) | +| lifetime.rs:459:13:459:14 | p1 | semmle.label | p1 | +| lifetime.rs:460:13:460:31 | get_ptr_from_ref(...) | semmle.label | get_ptr_from_ref(...) | +| lifetime.rs:568:7:568:8 | p2 | semmle.label | p2 | +| lifetime.rs:568:24:568:33 | &my_local2 | semmle.label | &my_local2 | +| lifetime.rs:572:14:572:15 | p2 | semmle.label | p2 | +| lifetime.rs:630:3:630:6 | str2 | semmle.label | str2 | +| lifetime.rs:630:10:630:25 | &... | semmle.label | &... | +| lifetime.rs:633:15:633:18 | str2 | semmle.label | str2 | +| lifetime.rs:641:14:641:17 | str2 | semmle.label | str2 | +| lifetime.rs:654:4:654:7 | str2 | semmle.label | str2 | +| lifetime.rs:654:31:654:35 | &str1 | semmle.label | &str1 | +| lifetime.rs:655:4:655:7 | ref1 | semmle.label | ref1 | +| lifetime.rs:655:4:655:7 | ref1 [&ref] | semmle.label | ref1 [&ref] | +| lifetime.rs:655:11:655:25 | &raw const str2 | semmle.label | &raw const str2 | +| lifetime.rs:655:11:655:25 | &raw const str2 [&ref] | semmle.label | &raw const str2 [&ref] | +| lifetime.rs:655:22:655:25 | str2 | semmle.label | str2 | +| lifetime.rs:659:15:659:18 | ref1 | semmle.label | ref1 | +| lifetime.rs:667:14:667:17 | ref1 | semmle.label | ref1 | +| lifetime.rs:680:7:680:8 | r1 | semmle.label | r1 | +| lifetime.rs:682:4:682:12 | &... | semmle.label | &... | +| lifetime.rs:684:7:684:14 | TuplePat [tuple.0] | semmle.label | TuplePat [tuple.0] | +| lifetime.rs:684:7:684:14 | TuplePat [tuple.1] | semmle.label | TuplePat [tuple.1] | +| lifetime.rs:684:8:684:9 | r2 | semmle.label | r2 | +| lifetime.rs:684:12:684:13 | r3 | semmle.label | r3 | +| lifetime.rs:686:4:687:16 | TupleExpr [tuple.0] | semmle.label | TupleExpr [tuple.0] | +| lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | semmle.label | TupleExpr [tuple.1] | +| lifetime.rs:686:5:686:13 | &... | semmle.label | &... | +| lifetime.rs:687:5:687:15 | &... | semmle.label | &... | +| lifetime.rs:692:13:692:14 | r1 | semmle.label | r1 | +| lifetime.rs:693:13:693:14 | r2 | semmle.label | r2 | +| lifetime.rs:694:13:694:14 | r3 | semmle.label | r3 | +| lifetime.rs:717:35:723:2 | { ... } | semmle.label | { ... } | +| lifetime.rs:718:7:718:8 | r1 | semmle.label | r1 | +| lifetime.rs:719:26:719:34 | &... | semmle.label | &... | +| lifetime.rs:730:6:730:7 | r1 | semmle.label | r1 | +| lifetime.rs:730:11:730:25 | e1.test_match() | semmle.label | e1.test_match() | +| lifetime.rs:734:12:734:13 | r1 | semmle.label | r1 | +| lifetime.rs:766:2:766:13 | &val | semmle.label | &val | +| lifetime.rs:766:2:766:13 | ptr | semmle.label | ptr | +| lifetime.rs:767:2:767:13 | ptr | semmle.label | ptr | +| lifetime.rs:769:6:769:8 | ptr | semmle.label | ptr | +| lifetime.rs:769:12:769:23 | &val | semmle.label | &val | +| lifetime.rs:769:12:769:23 | ptr | semmle.label | ptr | +| lifetime.rs:771:12:771:14 | ptr | semmle.label | ptr | +| lifetime.rs:781:2:781:19 | return ... | semmle.label | return ... | +| lifetime.rs:781:9:781:19 | &my_local10 | semmle.label | &my_local10 | +| lifetime.rs:785:6:785:7 | p1 | semmle.label | p1 | +| lifetime.rs:785:11:785:41 | get_local_for_unsafe_function(...) | semmle.label | get_local_for_unsafe_function(...) | +| lifetime.rs:789:12:789:13 | p1 | semmle.label | p1 | +| lifetime.rs:798:2:798:12 | return ... | semmle.label | return ... | +| lifetime.rs:798:9:798:12 | &val | semmle.label | &val | +| lifetime.rs:802:6:802:8 | ptr | semmle.label | ptr | +| lifetime.rs:802:12:802:24 | get_pointer(...) | semmle.label | get_pointer(...) | +| lifetime.rs:808:10:808:12 | ptr | semmle.label | ptr | +subpaths diff --git a/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.qlref b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.qlref new file mode 100644 index 000000000000..d9249badc000 --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.qlref @@ -0,0 +1,4 @@ +query: queries/security/CWE-825/AccessAfterLifetime.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/PathResolutionConsistency.expected index 804c13f6434b..b92e3920e3cd 100644 --- a/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/PathResolutionConsistency.expected +++ b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/PathResolutionConsistency.expected @@ -1,11 +1,11 @@ multiplePathResolutions | deallocation.rs:106:16:106:19 | libc | file://:0:0:0:0 | Crate(libc@0.2.171) | -| deallocation.rs:106:16:106:19 | libc | file://:0:0:0:0 | Crate(libc@0.2.172) | +| deallocation.rs:106:16:106:19 | libc | file://:0:0:0:0 | Crate(libc@0.2.174) | | deallocation.rs:106:16:106:27 | ...::malloc | file://:0:0:0:0 | fn malloc | | deallocation.rs:106:16:106:27 | ...::malloc | file://:0:0:0:0 | fn malloc | | deallocation.rs:112:3:112:6 | libc | file://:0:0:0:0 | Crate(libc@0.2.171) | -| deallocation.rs:112:3:112:6 | libc | file://:0:0:0:0 | Crate(libc@0.2.172) | +| deallocation.rs:112:3:112:6 | libc | file://:0:0:0:0 | Crate(libc@0.2.174) | | deallocation.rs:112:3:112:12 | ...::free | file://:0:0:0:0 | fn free | | deallocation.rs:112:3:112:12 | ...::free | file://:0:0:0:0 | fn free | | deallocation.rs:112:29:112:32 | libc | file://:0:0:0:0 | Crate(libc@0.2.171) | -| deallocation.rs:112:29:112:32 | libc | file://:0:0:0:0 | Crate(libc@0.2.172) | +| deallocation.rs:112:29:112:32 | libc | file://:0:0:0:0 | Crate(libc@0.2.174) | diff --git a/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/SsaConsistency.expected b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/SsaConsistency.expected new file mode 100644 index 000000000000..c29442521169 --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/SsaConsistency.expected @@ -0,0 +1,6 @@ +readWithoutDef +| lifetime.rs:511:6:511:14 | my_local2 | lifetime.rs:514:9:527:2 | enter \|...\| ... | 2 | +| lifetime.rs:564:6:564:14 | my_local2 | lifetime.rs:567:9:580:2 | enter { ... } | 2 | +readWithoutPriorRef +| lifetime.rs:511:6:511:14 | my_local2 | lifetime.rs:514:9:527:2 | enter \|...\| ... | 2 | +| lifetime.rs:564:6:564:14 | my_local2 | lifetime.rs:567:9:580:2 | enter { ... } | 2 | diff --git a/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/VariableCaptureConsistency.expected b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/VariableCaptureConsistency.expected new file mode 100644 index 000000000000..641265313115 --- /dev/null +++ b/rust/ql/test/query-tests/security/CWE-825/CONSISTENCY/VariableCaptureConsistency.expected @@ -0,0 +1,5 @@ +variableIsCaptured +| lifetime.rs:511:6:511:14 | my_local2 | CapturedVariable is not captured | +| lifetime.rs:564:6:564:14 | my_local2 | CapturedVariable is not captured | +consistencyOverview +| CapturedVariable is not captured | 2 | diff --git a/rust/ql/test/query-tests/security/CWE-825/deallocation.rs b/rust/ql/test/query-tests/security/CWE-825/deallocation.rs index 361f938e02c4..89ef0470e99d 100644 --- a/rust/ql/test/query-tests/security/CWE-825/deallocation.rs +++ b/rust/ql/test/query-tests/security/CWE-825/deallocation.rs @@ -17,7 +17,7 @@ pub fn test_alloc(mode: i32) { println!(" v3 = {v3}"); println!(" v4 = {v4}"); - std::alloc::dealloc(m1, layout); // $ Source=dealloc + std::alloc::dealloc(m1, layout); // $ Source[rust/access-invalid-pointer]=dealloc // (m1, m2 are now dangling) match mode { @@ -29,8 +29,8 @@ pub fn test_alloc(mode: i32) { println!(" v6 = {v6} (!)"); // corrupt in practice // test repeat reads (we don't want lots of very similar results for the same dealloc) - let v5b = *m1; - let v5c = *m1; + let _v5b = *m1; + let _v5c = *m1; }, 100 => { // more reads @@ -67,7 +67,7 @@ pub fn test_alloc_array(mode: i32) { println!(" v1 = {v1}"); println!(" v2 = {v2}"); - std::alloc::dealloc(m2 as *mut u8, layout); // $ Source=dealloc_array + std::alloc::dealloc(m2 as *mut u8, layout); // $ Source[rust/access-invalid-pointer]=dealloc_array // m1, m2 are now dangling match mode { @@ -109,7 +109,7 @@ pub fn test_libc() { let v1 = *my_ptr; // GOOD println!(" v1 = {v1}"); - libc::free(my_ptr as *mut libc::c_void); // $ Source=free + libc::free(my_ptr as *mut libc::c_void); // $ Source[rust/access-invalid-pointer]=free // (my_ptr is now dangling) let v2 = *my_ptr; // $ Alert[rust/access-invalid-pointer]=free @@ -120,9 +120,9 @@ pub fn test_libc() { // --- std::ptr --- pub fn test_ptr_invalid(mode: i32) { - let p1: *const i64 = std::ptr::dangling(); // $ Source=dangling - let p2: *mut i64 = std::ptr::dangling_mut(); // $ Source=dangling_mut - let p3: *const i64 = std::ptr::null(); // $ Source=null + let p1: *const i64 = std::ptr::dangling(); // $ Source[rust/access-invalid-pointer]=dangling + let p2: *mut i64 = std::ptr::dangling_mut(); // $ Source[rust/access-invalid-pointer]=dangling_mut + let p3: *const i64 = std::ptr::null(); // $ Source[rust/access-invalid-pointer]=null if mode == 120 { unsafe { @@ -173,7 +173,7 @@ pub fn test_ptr_drop(mode: i32) { println!(" v1 = {v1}"); println!(" v2 = {v2}"); - std::ptr::drop_in_place(p1); // $ Source=drop_in_place + std::ptr::drop_in_place(p1); // $ Source[rust/access-invalid-pointer]=drop_in_place // explicitly destructs the pointed-to `m2` if mode == 1 { @@ -212,7 +212,7 @@ impl Drop for MyDropBuffer { unsafe { _ = *self.ptr; - drop(*self.ptr); // $ MISSING: Source=drop + drop(*self.ptr); // $ MISSING: Source[rust/access-invalid-pointer]=drop _ = *self.ptr; // $ MISSING: Alert[rust/access-invalid-pointer]=drop std::alloc::dealloc(self.ptr, layout); } @@ -239,7 +239,7 @@ fn test_qhelp_example_good(ptr: *mut String) { fn test_qhelp_example_bad(ptr: *mut String) { unsafe { - std::ptr::drop_in_place(ptr); // $ Source=drop_in_place + std::ptr::drop_in_place(ptr); // $ Source[rust/access-invalid-pointer]=drop_in_place } // ... @@ -280,7 +280,7 @@ pub fn test_vec_reserve() { println!(" v1 = {}", v1); } - vec1.reserve(1000); // $ MISSING: Source=reserve + vec1.reserve(1000); // $ MISSING: Source[rust/access-invalid-pointer]=reserve // (may invalidate the pointer) unsafe { @@ -300,7 +300,7 @@ pub fn test_vec_reserve() { } for _i in 0..1000 { - vec2.push(0); // $ MISSING: Source=push + vec2.push(0); // $ MISSING: Source[rust/access-invalid-pointer]=push // (may invalidate the pointer) } diff --git a/rust/ql/test/query-tests/security/CWE-825/lifetime.rs b/rust/ql/test/query-tests/security/CWE-825/lifetime.rs index d7fd8204993a..559b2d96bf2f 100644 --- a/rust/ql/test/query-tests/security/CWE-825/lifetime.rs +++ b/rust/ql/test/query-tests/security/CWE-825/lifetime.rs @@ -18,36 +18,36 @@ impl Drop for MyValue { fn get_local_dangling() -> *const i64 { let my_local1: i64 = 1; - return &my_local1; + return &my_local1; // $ Source[rust/access-after-lifetime-ended]=local1 } // (return value immediately becomes dangling) fn get_local_dangling_mut() -> *mut i64 { let mut my_local2: i64 = 2; - return &mut my_local2; + return &mut my_local2; // $ Source[rust/access-after-lifetime-ended]=local2 } // (return value immediately becomes dangling) fn get_local_dangling_raw_const() -> *const i64 { let my_local3: i64 = 3; - return &raw const my_local3; + return &raw const my_local3; // $ Source[rust/access-after-lifetime-ended]=local3 } // (return value immediately becomes dangling) fn get_local_dangling_raw_mut() -> *mut i64 { let mut my_local4: i64 = 4; - return &raw mut my_local4; + return &raw mut my_local4; // $ Source[rust/access-after-lifetime-ended]=local4 } // (return value immediately becomes dangling) fn get_param_dangling(param5: i64) -> *const i64 { - return ¶m5; + return ¶m5; // $ Source[rust/access-after-lifetime-ended]=param5 } // (return value immediately becomes dangling) fn get_local_field_dangling() -> *const i64 { let val: MyValue; val = MyValue { value: 6 }; - return &val.value; + return &val.value; // $ Source[rust/access-after-lifetime-ended]=localfield } pub fn test_local_dangling() { @@ -60,21 +60,21 @@ pub fn test_local_dangling() { let p7: *const i64; { let my_local7 = 7; - p7 = &raw const my_local7; + p7 = &raw const my_local7; // $ Source[rust/access-after-lifetime-ended]=local7 } // (my_local goes out of scope, thus p7 is dangling) use_the_stack(); unsafe { - let v1 = *p1; // $ MISSING: Alert - let v2 = *p2; // $ MISSING: Alert - let v3 = *p3; // $ MISSING: Alert - let v4 = *p4; // $ MISSING: Alert - let v5 = *p5; // $ MISSING: Alert - let v6 = *p6; // $ MISSING: Alert - let v7 = *p7; // $ MISSING: Alert - *p2 = 8; // $ MISSING: Alert - *p4 = 9; // $ MISSING: Alert + let v1 = *p1; // $ Alert[rust/access-after-lifetime-ended]=local1 + let v2 = *p2; // $ Alert[rust/access-after-lifetime-ended]=local2 + let v3 = *p3; // $ Alert[rust/access-after-lifetime-ended]=local3 + let v4 = *p4; // $ Alert[rust/access-after-lifetime-ended]=local4 + let v5 = *p5; // $ Alert[rust/access-after-lifetime-ended]=param5 + let v6 = *p6; // $ Alert[rust/access-after-lifetime-ended]=localfield + let v7 = *p7; // $ Alert[rust/access-after-lifetime-ended]=local7 + *p2 = 8; // $ Alert[rust/access-after-lifetime-ended]=local2 + *p4 = 9; // $ Alert[rust/access-after-lifetime-ended]=local4 println!(" v1 = {v1} (!)"); // corrupt in practice println!(" v2 = {v2} (!)"); // corrupt in practice @@ -96,7 +96,7 @@ fn use_pointers(p1: *const i64, p2: *mut i64, mode: i32) { use_the_stack(); unsafe { - if (mode == 0) { + if mode == 0 { // reads let v1 = *p1; // GOOD let v2 = *p2; // GOOD @@ -105,7 +105,7 @@ fn use_pointers(p1: *const i64, p2: *mut i64, mode: i32) { println!(" v2 = {v2}"); println!(" v3 = {v3}"); } - if (mode == 200) { + if mode == 200 { // writes *p2 = 2; // GOOD } @@ -142,14 +142,14 @@ pub fn test_static(mode: i32) { use_the_stack(); unsafe { - if (mode == 0) { + if mode == 0 { // reads let v1 = *p1; // GOOD let v2 = *p2; // GOOD println!(" v1 = {v1}"); println!(" v2 = {v2}"); } - if (mode == 210) { + if mode == 210 { // writes *p2 = 3; // GOOD } @@ -169,7 +169,7 @@ fn access_ptr_1(ptr: *const i64) { fn access_ptr_2(ptr: *const i64) { // only called with `ptr` dangling unsafe { - let v2 = *ptr; // $ MISSING: Alert + let v2 = *ptr; // $ Alert[rust/access-after-lifetime-ended]=local1 println!(" v2 = {v2} (!)"); // corrupt in practice } } @@ -184,7 +184,7 @@ fn access_ptr_3(ptr: *const i64) { fn access_and_get_dangling() -> *const i64 { let my_local1 = 1; - let ptr = &my_local1; + let ptr = &my_local1; // $ Source[rust/access-after-lifetime-ended]=local1 access_ptr_1(ptr); access_ptr_3(ptr); @@ -244,54 +244,116 @@ pub fn test_loop() { use_the_stack(); unsafe { - let v1 = (*prev)[0]; // $ MISSING: Alert + let v1 = (*prev)[0]; // $ MISSING: Alert[rust/access-after-lifetime-ended]=local2 println!(" v1 = {v1} (!)"); // incorrect values in practice (except first iteration) } - prev = &my_local2; + prev = &my_local2; // $ Source[rust/access-after-lifetime-ended]=local2 } // (my_local2 goes out of scope, thus prev is dangling) unsafe { - let v2 = (*prev)[0]; // $ MISSING: Alert + let v2 = (*prev)[0]; // $ Alert[rust/access-after-lifetime-ended]=local2 println!(" v2 = {v2} (!)"); // corrupt in practice } } -// --- enum --- +// --- enums --- enum MyEnum { Value(i64), } -impl Drop for MyEnum { - fn drop(&mut self) { - println!(" drop MyEnum"); - } +enum MyEnum2 { + Pointer(*const i64), } -pub fn test_enum() { +pub fn get_pointer_to_enum() -> *const MyEnum { + let e1 = MyEnum::Value(1); + let result: *const MyEnum = &e1; // $ Source[rust/access-after-lifetime-ended]=e1 + + result +} // (e1 goes out of scope, so result is dangling) + +pub fn get_pointer_in_enum() -> MyEnum2 { + let v2 = 2; + let e2 = MyEnum2::Pointer(&v2); // $ MISSING: Source[rust/access-after-lifetime-ended]=v2 + + e2 +} // (v2 goes out of scope, so the contained pointer is dangling) + +pub fn get_pointer_from_enum() -> *const i64 { + let e3 = MyEnum::Value(3); let result: *const i64; - { - let e1 = MyEnum::Value(1); + result = match e3 { + MyEnum::Value(x) => { &x } // $ Source[rust/access-after-lifetime-ended]=match_x + }; // (x goes out of scope, so result is possibly dangling already) - result = match e1 { - MyEnum::Value(x) => { &x } - }; // (x goes out of scope, so result is dangling, I think; seen in real world code) + use_the_stack(); - use_the_stack(); + unsafe { + let v0 = *result; // ? + println!(" v0 = {v0} (?)"); + } - unsafe { - let v1 = *result; // $ MISSING: Alert - println!(" v1 = {v1}"); - } - } // (e1 goes out of scope, so result is definitely dangling now) + result +} // (e3 goes out of scope, so result is definitely dangling now) + +pub fn test_enums() { + let e1 = get_pointer_to_enum(); + let e2 = get_pointer_in_enum(); + let result = get_pointer_from_enum(); use_the_stack(); unsafe { - let v2 = *result; // $ MISSING: Alert - println!(" v2 = {v2}"); // dropped in practice + if let MyEnum::Value(v1) = *e1 { // $ Alert[rust/access-after-lifetime-ended]=e1 + println!(" v1 = {v1} (!)"); // corrupt in practice + } + if let MyEnum2::Pointer(p2) = e2 { + let v2 = unsafe { *p2 }; // $ MISSING: Alert[rust/access-after-lifetime-ended]=v2 + println!(" v2 = {v2} (!)"); // corrupt in practice + } + let v3 = *result; // $ Alert[rust/access-after-lifetime-ended]=match_x + println!(" v3 = {v3} (!)"); // corrupt in practice + } +} + +// --- recursive enum --- + +enum RecursiveEnum { + Wrapper(Box), + Pointer(*const i64), +} + +pub fn get_recursive_enum() -> Box { + let v1 = 1; + let enum1 = RecursiveEnum::Wrapper(Box::new(RecursiveEnum::Pointer(&v1))); // Source[rust/access-after-lifetime-ended]=v1 + let mut ref1 = &enum1; + + while let RecursiveEnum::Wrapper(inner) = ref1 { + println!(" wrapper"); + ref1 = &inner; + } + if let RecursiveEnum::Pointer(ptr) = ref1 { + let v2: i64 = unsafe { **ptr }; // GOOD + println!(" v2 = {v2}"); + } + + return Box::new(enum1); +} // (v1 goes out of scope, thus the contained pointer is dangling) + +pub fn test_recursive_enums() { + let enum1 = *get_recursive_enum(); + let mut ref1 = &enum1; + + while let RecursiveEnum::Wrapper(inner) = ref1 { + println!(" wrapper"); + ref1 = &inner; + } + if let RecursiveEnum::Pointer(ptr) = ref1 { + let v3: i64 = unsafe { **ptr }; // Alert[rust/access-after-lifetime-ended]=v1 + println!(" v3 = {v3} (!)"); // corrupt in practice } } @@ -318,9 +380,9 @@ pub fn test_ptr_to_struct(mode: i32) { { let mut my_pair = MyPair { a: 1, b: 2}; - p1 = std::ptr::addr_of_mut!(my_pair); - p2 = std::ptr::addr_of!(my_pair.a); - p3 = std::ptr::addr_of_mut!(my_pair.b); + p1 = std::ptr::addr_of_mut!(my_pair); // $ Source[rust/access-after-lifetime-ended]=my_pair + p2 = std::ptr::addr_of!(my_pair.a); // $ MISSING: Source[rust/access-after-lifetime-ended]=my_pair_a + p3 = std::ptr::addr_of_mut!(my_pair.b); // $ MISSING: Source[rust/access-after-lifetime-ended]=my_pair_b unsafe { let v1 = (*p1).a; // GOOD @@ -346,12 +408,12 @@ pub fn test_ptr_to_struct(mode: i32) { match mode { 0 => { // read - let v5 = (*p1).a; // $ MISSING: Alert + let v5 = (*p1).a; // $ Alert[rust/access-after-lifetime-ended]=my_pair println!(" v5 = {v5} (!)"); // dropped in practice }, 220 => { // another read - let v6 = (*p1).b; // $ MISSING: Alert + let v6 = (*p1).b; // $ Alert[rust/access-after-lifetime-ended]=my_pair println!(" v6 = {v6} (!)"); // dropped in practice }, 221 => { @@ -363,12 +425,12 @@ pub fn test_ptr_to_struct(mode: i32) { }, 222 => { // writes - (*p1).a = 6; // $ MISSING: Alert + (*p1).a = 6; // $ Alert[rust/access-after-lifetime-ended]=my_pair *p3 = 7; // $ MISSING: Alert }, 223 => { // another write - (*p1).b = 8; // $ MISSING: Alert + (*p1).b = 8; // $ Alert[rust/access-after-lifetime-ended]=my_pair }, _ => {} } @@ -377,7 +439,7 @@ pub fn test_ptr_to_struct(mode: i32) { fn get_ptr_from_ref(val: i32) -> *const i32 { let my_val = val; - let r1: &i32 = &my_val; + let r1: &i32 = &my_val; // $ Source[rust/access-after-lifetime-ended]=my_val let p1: *const i32 = std::ptr::from_ref(r1); unsafe { @@ -394,8 +456,8 @@ pub fn test_ptr_from_ref() { use_the_stack(); unsafe { - let v2 = *p1; // $ MISSING: Alert - let v3 = *get_ptr_from_ref(2); // $ MISSING: Alert + let v2 = *p1; // $ Alert[rust/access-after-lifetime-ended]=my_val + let v3 = *get_ptr_from_ref(2); // $ Alert[rust/access-after-lifetime-ended]=my_val println!(" v2 = {v2} (!)"); // corrupt in practice println!(" v3 = {v3} (!)"); } @@ -429,7 +491,7 @@ pub fn test_rc() { println!(" v3 = {v3}"); println!(" v4 = {v4}"); } - } // rc1 go out of scope, the reference count is 0, so p1, p2 are dangling + } // rc1 goes out of scope, the reference count is 0, so p1, p2 are dangling unsafe { let v5 = *p1; // $ MISSING: Alert @@ -441,3 +503,327 @@ pub fn test_rc() { // note: simialar things are likely possible with Ref, RefMut, RefCell, // Vec and others. } + +// --- closures --- + +fn get_closure(p3: *const i64, p4: *const i64) -> impl FnOnce() { + let my_local1: i64 = 1; + let my_local2: i64 = 2; + let p1: *const i64 = &my_local1; // $ MISSING: Source[rust/access-after-lifetime-ended]=local1 + + return move || { // captures `my_local2`, `p1`, `p3`, `p4` by value (due to `move`) + let p2: *const i64 = &my_local2; + + unsafe { + let v1 = *p1; // $ MISSING: Alert[rust/access-after-lifetime-ended]=local1 + let v2 = *p2; // GOOD + let v3 = *p3; // GOOD + let v4 = *p4; // $ MISSING: Alert[rust/access-after-lifetime-ended]=local4 + println!(" v1 = {v1} (!)"); // corrupt in practice + println!(" v2 = {v2}"); + println!(" v3 = {v3}"); + println!(" v4 = {v4} (!)"); + } + }; +} // (`my_local1` goes out of scope, thus `p1` is dangling) + +fn with_closure(ptr: *const i64, closure: fn(*const i64, *const i64)) { + let my_local5: i64 = 5; + + closure(ptr, + &my_local5); +} + +pub fn test_closures() { + let closure; + let my_local3: i64 = 3; + { + let my_local4: i64 = 4; + closure = get_closure( &my_local3, + &my_local4); // $ MISSING: Source[rust/access-after-lifetime-ended]=local4 + } // (`my_local4` goes out of scope, so `p4` is dangling) + + use_the_stack(); + + closure(); + + with_closure(&my_local3, |p1, p2| { + unsafe { + let v5 = *p1; // GOOD + let v6 = *p2; // $ GOOD + println!(" v5 = {v5}"); + println!(" v6 = {v6}"); + } + }); +} + +// --- async --- + +fn get_async_closure(p3: *const i64, p4: *const i64) -> impl std::future::Future { + let my_local1: i64 = 1; + let my_local2: i64 = 2; + let p1: *const i64 = &my_local1; + + return async move { // captures `my_local2`, `p1`, `p3`, `p4` by value (due to `move`) + let p2: *const i64 = &my_local2; + + unsafe { + let v1 = *p1; // $ MISSING: Alert + let v2 = *p2; // GOOD + let v3 = *p3; // GOOD + let v4 = *p4; // $ MISSING: Alert + println!(" v1 = {v1} (!)"); // corrupt in practice + println!(" v2 = {v2}"); + println!(" v3 = {v3}"); + println!(" v4 = {v4} (!)"); + } + }; +} // (`my_local1` goes out of scope, thus `p1` is dangling) + +pub fn test_async() { + let async_closure; + let my_local3: i64 = 3; + { + let my_local4: i64 = 4; + async_closure = get_async_closure(&my_local3, + &my_local4); + } // (`my_local4` goes out of scope, so `p4` is dangling) + + use_the_stack(); + + futures::executor::block_on(async_closure); +} + +// --- lifetime annotations --- + +fn select_str<'a>(cond: bool, a: &'a str, b: &'a str) -> &'a str { + if cond { a } else { b } +} + +struct MyRefStr<'a> { + ref_str: &'a str, +} + +pub fn test_lifetime_annotations() { + let str1: *const str; + { + let foo = String::from("foo"); + let bar = String::from("bar"); + str1 = select_str(true, foo.as_str(), bar.as_str()); + + unsafe { + let v1 = &*str1; // GOOD + println!(" v1 = {v1}"); + } + } // (`foo`, `bar` go out of scope, the return value of `select_str` has the same lifetime, thus `str1` is dangling) + + unsafe { + let v2 = &*str1; // $ MISSING: Alert + println!(" v2 = {v2} (!)"); // corrupt in practice + } + + let my_ref; + let str2: *const str; + { + let baz = String::from("baz"); + my_ref = MyRefStr { ref_str: baz.as_str() }; + str2 = &*my_ref.ref_str; + + unsafe { + let v3 = &*str2; // GOOD + println!(" v3 = {v3}"); + } + } // (`baz` goes out of scope, `ref_str` has the same lifetime, thus `str2` is dangling) + + use_the_stack(); + + unsafe { + let v4 = &*str2; // $ MISSING: Alert + println!(" v4 = {v4} (!)"); // corrupt in practice + } +} + +// --- implicit dereferences --- + +pub fn test_implicit_derefs() { + let ref1; + { + let str2; + { + let str1 = "bar"; + str2 = "foo".to_string() + &str1; // $ Source[rust/access-after-lifetime-ended]=str1 + ref1 = &raw const str2; // $ Source[rust/access-after-lifetime-ended]=str2 + } // (str1 goes out of scope, but it's been copied into str2) + + unsafe { + let v1 = &*ref1; // $ SPURIOUS: Alert[rust/access-after-lifetime-ended]=str1 + println!(" v1 = {v1}"); + } + } // (str2 goes out of scope, thus ref1 is dangling) + + use_the_stack(); + + unsafe { + let v2 = &*ref1; // $ Alert[rust/access-after-lifetime-ended]=str2 SPURIOUS: Alert[rust/access-after-lifetime-ended]=str1 + println!(" v2 = {v2} (!)"); // corrupt in practice + } +} + +// --- members --- + +struct MyType { + value: i64, +} + +impl MyType { + fn test(&self) { + let r1 = unsafe { + let v1 = &self; + &v1.value + }; + let (r2, r3) = unsafe { + let v2 = &self; + (&v2.value, + &self.value) + }; + + use_the_stack(); + + let v1 = *r1; + let v2 = *r2; + let v3 = *r3; + println!(" v1 = {v1}"); + println!(" v2 = {v2}"); + println!(" v3 = {v3}"); + } +} + +pub fn test_members() { + let mt = MyType { value: 1 }; + mt.test(); +} + +// --- enum members --- + +struct MyValue2 { + value: i64 +} + +enum MyEnum3 { + Value(MyValue2), +} + +impl MyEnum3 { + pub fn test_match(&self) -> &i64 { + let r1 = match self { + MyEnum3::Value(v2) => &v2.value, + }; + + r1 + } +} + +pub fn test_enum_members() { + let v1 = MyValue2 { value: 1 }; + let e1 = MyEnum3::Value(v1); + + let r1 = e1.test_match(); + + use_the_stack(); + + let v3 = *r1; + println!(" v3 = {v3}"); +} + +// --- macros --- + +macro_rules! my_macro1 { + () => { + let ptr: *const i64; + { + let val: i64 = 1; + ptr = &val; + } + + unsafe { + let v = *ptr; + println!(" v = {v}"); + } + } +} + +macro_rules! my_macro2 { + () => { + { + let val: i64 = 1; + let ptr: *const i64 = &val; + ptr + } + } +} + +pub fn test_macros() { + my_macro1!(); + my_macro1!(); + + let ptr = my_macro2!(); + unsafe { + let v = *ptr; + println!(" v = {v}"); + } +} + +// --- unsafe function --- + +fn get_local_for_unsafe_function() -> *const f64 { + let my_local10: f64 = 1.23; + + return &my_local10; // $ Source[rust/access-after-lifetime-ended]=local10 +} // (return value immediately becomes dangling) + +pub unsafe fn test_unsafe_function() { + let p1 = get_local_for_unsafe_function(); + + use_the_stack(); + + let v1 = *p1; // $ Alert[rust/access-after-lifetime-ended]=local10 + println!(" v1 = {v1} (!)"); // corrupt in practice +} + +// --- examples from qhelp --- + +fn get_pointer() -> *const i64 { + let val = 123; + + return &val; // $ Source[rust/access-after-lifetime-ended]=val +} // lifetime of `val` ends here, the pointer becomes dangling + +pub fn test_lifetimes_example_bad() { + let ptr = get_pointer(); + let val; + + use_the_stack(); + + unsafe { + val = *ptr; // $ Alert[rust/access-after-lifetime-ended]=val + } + + println!(" val = {val} (!)"); // corrupt in practice +} + +fn get_box() -> Box { + let val = 123; + + return Box::new(val); +} + +pub fn test_lifetimes_example_good() { + let ptr = get_box(); + let val; + + use_the_stack(); + + val = *ptr; // GOOD + + println!(" val = {val}"); +} diff --git a/rust/ql/test/query-tests/security/CWE-825/main.rs b/rust/ql/test/query-tests/security/CWE-825/main.rs index ec135011f705..5f66313ae85e 100644 --- a/rust/ql/test/query-tests/security/CWE-825/main.rs +++ b/rust/ql/test/query-tests/security/CWE-825/main.rs @@ -154,8 +154,11 @@ fn main() { println!("test_loop:"); test_loop(); - println!("test_enum:"); - test_enum(); + println!("test_enums:"); + test_enums(); + + println!("test_recursive_enums:"); + test_recursive_enums(); println!("test_ptr_to_struct:"); test_ptr_to_struct(mode); @@ -165,4 +168,36 @@ fn main() { println!("test_rc:"); test_rc(); + + println!("test_closures:"); + test_closures(); + + println!("test_async:"); + test_async(); + + println!("test_lifetime_annotations:"); + test_lifetime_annotations(); + + println!("test_implicit_derefs:"); + test_implicit_derefs(); + + println!("test_members:"); + test_members(); + + println!("test_enum_members:"); + test_enum_members(); + + println!("test_macros:"); + test_macros(); + + println!("test_unsafe_function:"); + unsafe { + test_unsafe_function(); + } + + println!("test_lifetimes_example_bad:"); + test_lifetimes_example_bad(); + + println!("test_lifetimes_example_good:"); + test_lifetimes_example_good(); } diff --git a/rust/ql/test/query-tests/security/CWE-825/options.yml b/rust/ql/test/query-tests/security/CWE-825/options.yml index 95a17a53b431..90a51f61a43e 100644 --- a/rust/ql/test/query-tests/security/CWE-825/options.yml +++ b/rust/ql/test/query-tests/security/CWE-825/options.yml @@ -1,3 +1,4 @@ qltest_cargo_check: true qltest_dependencies: - libc = { version = "0.2.11" } + - futures = { version = "0.3" }