Merge branch 'main' into blockon

This commit is contained in:
Geoffrey White
2025-04-07 09:03:55 +01:00
18 changed files with 1255 additions and 11 deletions

View File

@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/rust-all
extensible: sourceModel
data:
# Alloc
- ["repo:https://github.com/rust-lang/libc:libc", "::free", "Argument[0]", "pointer-invalidate", "manual"]

View File

@@ -0,0 +1,17 @@
extensions:
- addsTo:
pack: codeql/rust-all
extensible: summaryModel
data:
# Fmt
- ["lang:alloc", "crate::fmt::format", "Argument[0]", "ReturnValue", "taint", "manual"]
# String
- ["lang:alloc", "<crate::string::String>::as_str", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["lang:alloc", "<crate::string::String>::as_bytes", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["lang:alloc", "<_ as crate::string::ToString>::to_string", "Argument[self]", "ReturnValue", "taint", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: sourceModel
data:
# Alloc
- ["lang:alloc", "crate::alloc::dealloc", "Argument[0]", "pointer-invalidate", "manual"]

View File

@@ -3,8 +3,6 @@ extensions:
pack: codeql/rust-all
extensible: summaryModel
data:
# Fmt
- ["lang:alloc", "crate::fmt::format", "Argument[0]", "ReturnValue", "taint", "manual"]
# Iterator
- ["lang:core", "<[_]>::iter", "Argument[Self].Element", "ReturnValue.Element", "value", "manual"]
- ["lang:core", "<[_]>::iter_mut", "Argument[Self].Element", "ReturnValue.Element", "value", "manual"]
@@ -19,7 +17,7 @@ extensions:
- ["lang:core", "<crate::slice::iter::Iter as crate::iter::traits::iterator::Iterator>::collect", "Argument[self].Element", "ReturnValue.Element", "value", "manual"]
- ["lang:core", "<crate::slice::iter::Iter as crate::iter::traits::iterator::Iterator>::map", "Argument[self].Element", "Argument[0].Parameter[0]", "value", "manual"]
- ["lang:core", "<crate::slice::iter::Iter as crate::iter::traits::iterator::Iterator>::for_each", "Argument[self].Element", "Argument[0].Parameter[0]", "value", "manual"]
# ptr
# Ptr
- ["lang:core", "crate::ptr::read", "Argument[0].Reference", "ReturnValue", "value", "manual"]
- ["lang:core", "crate::ptr::read_unaligned", "Argument[0].Reference", "ReturnValue", "value", "manual"]
- ["lang:core", "crate::ptr::read_volatile", "Argument[0].Reference", "ReturnValue", "value", "manual"]
@@ -28,7 +26,24 @@ extensions:
- ["lang:core", "crate::ptr::write_volatile", "Argument[1]", "Argument[0].Reference", "value", "manual"]
# Str
- ["lang:core", "<str>::parse", "Argument[self]", "ReturnValue.Field[crate::result::Result::Ok(0)]", "taint", "manual"]
# String
- ["lang:alloc", "<crate::string::String>::as_str", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["lang:alloc", "<crate::string::String>::as_bytes", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["lang:alloc", "<_ as crate::string::ToString>::to_string", "Argument[self]", "ReturnValue", "taint", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: sourceModel
data:
# Ptr
- ["lang:core", "crate::ptr::drop_in_place", "Argument[0]", "pointer-invalidate", "manual"]
- ["lang:core", "crate::ptr::dangling", "ReturnValue", "pointer-invalidate", "manual"]
- ["lang:core", "crate::ptr::dangling_mut", "ReturnValue", "pointer-invalidate", "manual"]
- ["lang:core", "crate::ptr::null", "ReturnValue", "pointer-invalidate", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: sinkModel
data:
# Ptr
- ["lang:core", "crate::ptr::read", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::read_unaligned", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::read_volatile", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::write", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::write_bytes", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::write_unaligned", "Argument[0]", "pointer-access", "manual"]
- ["lang:core", "crate::ptr::write_volatile", "Argument[0]", "pointer-access", "manual"]

View File

@@ -0,0 +1,64 @@
/**
* Provides classes and predicates for reasoning about accesses to invalid
* pointers.
*/
import rust
private import codeql.rust.dataflow.DataFlow
private import codeql.rust.dataflow.FlowSource
private import codeql.rust.dataflow.FlowSink
private import codeql.rust.Concepts
private import codeql.rust.dataflow.internal.Node
/**
* Provides default sources, sinks and barriers for detecting accesses to
* invalid pointers, as well as extension points for adding your own.
*/
module AccessInvalidPointer {
/**
* A data flow source for invalid pointer accesses, that is, an operation
* where a pointer becomes invalid.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for invalid pointer accesses, that is, a pointer
* dereference.
*/
abstract class Sink extends QuerySink::Range {
override string getSinkType() { result = "AccessInvalidPointer" }
}
/**
* A barrier for invalid pointer accesses.
*/
abstract class Barrier extends DataFlow::Node { }
/**
* A pointer invalidation from model data.
*
* Note: we don't currently support invalidation via the object itself rather than via a pointer, such as:
* ```
* drop(obj)
* ```
*/
private class ModelsAsDataSource extends Source {
ModelsAsDataSource() { sourceNode(this, "pointer-invalidate") }
}
/**
* A pointer access using the unary `*` operator.
*/
private class DereferenceSink extends Sink {
DereferenceSink() {
exists(PrefixExpr p | p.getOperatorName() = "*" and p.getExpr() = this.asExpr().getExpr())
}
}
/**
* A pointer access from model data.
*/
private class ModelsAsDataSink extends Sink {
ModelsAsDataSink() { sinkNode(this, "pointer-access") }
}
}

View File

@@ -0,0 +1,49 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Dereferencing an invalid or dangling pointer may cause undefined behavior. Memory may be corrupted
causing the program to crash or behave incorrectly, in some cases exposing the program to
potential attacks.
</p>
</overview>
<recommendation>
<p>
When dereferencing a pointer in <code>unsafe</code> code, take care that the pointer is valid and
points to the intended data. Code may need to be rearranged or additional checks added to ensure
safety in all circumstances. If possible, rewrite the code using safe Rust types to avoid this
kind of problem altogether.
</p>
</recommendation>
<example>
<p>
In the following example, <code>std::ptr::drop_in_place</code> is used to execute the destructor
of an object. However, a pointer to that object is dereferenced later in the program, causing
undefined behavior:
</p>
<sample src="AccessInvalidPointerBad.rs" />
<p>
In this case, undefined behavior can be avoided by rearranging the code so that the dereferencing
comes before the call to <code>std::ptr::drop_in_place</code>:
</p>
<sample src="AccessInvalidPointerGood.rs" />
</example>
<references>
<li>Rust Documentation: <a href="https://doc.rust-lang.org/reference/behavior-considered-undefined.html#dangling-pointers">Behavior considered undefined &gt;&gt; Dangling pointers</a>.</li>
<li>Rust Documentation: <a href="https://doc.rust-lang.org/std/ptr/index.html#safety">Module ptr - Safety</a>.</li>
<li>Massachusetts Institute of Technology: <a href="https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/second-edition/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer">Unsafe Rust - Dereferencing a Raw Pointer</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,42 @@
/**
* @name Access of invalid pointer
* @description Dereferencing an invalid or dangling pointer causes undefined behavior and may result in memory corruption.
* @kind path-problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id rust/access-invalid-pointer
* @tags reliability
* security
* external/cwe/cwe-476
* external/cwe/cwe-825
*/
import rust
import codeql.rust.dataflow.DataFlow
import codeql.rust.dataflow.TaintTracking
import codeql.rust.security.AccessInvalidPointerExtensions
import AccessInvalidPointerFlow::PathGraph
/**
* A data flow configuration for accesses to invalid pointers.
*/
module AccessInvalidPointerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node instanceof AccessInvalidPointer::Source }
predicate isSink(DataFlow::Node node) { node instanceof AccessInvalidPointer::Sink }
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof AccessInvalidPointer::Barrier }
predicate isBarrierOut(DataFlow::Node node) {
// make sinks barriers so that we only report the closest instance
isSink(node)
}
}
module AccessInvalidPointerFlow = TaintTracking::Global<AccessInvalidPointerConfig>;
from AccessInvalidPointerFlow::PathNode sourceNode, AccessInvalidPointerFlow::PathNode sinkNode
where AccessInvalidPointerFlow::flowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode,
"This operation dereferences a pointer that may be $@.", sourceNode.getNode(), "invalid"

View File

@@ -0,0 +1,10 @@
unsafe {
std::ptr::drop_in_place(ptr); // executes the destructor of `*ptr`
}
// ...
unsafe {
do_something(&*ptr); // BAD: dereferences `ptr`
}

View File

@@ -0,0 +1,10 @@
unsafe {
do_something(&*ptr); // GOOD: dereferences `ptr` while it is still valid
}
// ...
{
std::ptr::drop_in_place(ptr); // executes the destructor of `*ptr`
}

View File

@@ -15,6 +15,7 @@ private import codeql.rust.Diagnostics
private import codeql.rust.security.SensitiveData
private import TaintReach
// import all query extensions files, so that all extensions of `QuerySink` are found
private import codeql.rust.security.AccessInvalidPointerExtensions
private import codeql.rust.security.CleartextLoggingExtensions
private import codeql.rust.security.SqlInjectionExtensions
private import codeql.rust.security.WeakSensitiveDataHashingExtensions

View File

@@ -2,15 +2,15 @@
| main.rs:6:25:6:30 | &regex | main.rs:4:20:4:32 | ...::var | main.rs:6:25:6:30 | &regex | This regular expression is constructed from a $@. | main.rs:4:20:4:32 | ...::var | user-provided value |
edges
| main.rs:4:9:4:16 | username | main.rs:5:25:5:44 | MacroExpr | provenance | |
| main.rs:4:20:4:32 | ...::var | main.rs:4:20:4:40 | ...::var(...) [Ok] | provenance | Src:MaD:65 |
| main.rs:4:20:4:40 | ...::var(...) [Ok] | main.rs:4:20:4:66 | ... .unwrap_or(...) | provenance | MaD:1628 |
| main.rs:4:20:4:32 | ...::var | main.rs:4:20:4:40 | ...::var(...) [Ok] | provenance | Src:MaD:66 |
| main.rs:4:20:4:40 | ...::var(...) [Ok] | main.rs:4:20:4:66 | ... .unwrap_or(...) | provenance | MaD:1641 |
| main.rs:4:20:4:66 | ... .unwrap_or(...) | main.rs:4:9:4:16 | username | provenance | |
| main.rs:5:9:5:13 | regex | main.rs:6:26:6:30 | regex | provenance | |
| main.rs:5:17:5:45 | res | main.rs:5:25:5:44 | { ... } | provenance | |
| main.rs:5:25:5:44 | ...::format(...) | main.rs:5:17:5:45 | res | provenance | |
| main.rs:5:25:5:44 | ...::must_use(...) | main.rs:5:9:5:13 | regex | provenance | |
| main.rs:5:25:5:44 | MacroExpr | main.rs:5:25:5:44 | ...::format(...) | provenance | MaD:101 |
| main.rs:5:25:5:44 | { ... } | main.rs:5:25:5:44 | ...::must_use(...) | provenance | MaD:3051 |
| main.rs:5:25:5:44 | MacroExpr | main.rs:5:25:5:44 | ...::format(...) | provenance | MaD:102 |
| main.rs:5:25:5:44 | { ... } | main.rs:5:25:5:44 | ...::must_use(...) | provenance | MaD:3064 |
| main.rs:6:26:6:30 | regex | main.rs:6:25:6:30 | &regex | provenance | |
nodes
| main.rs:4:9:4:16 | username | semmle.label | username |

View File

@@ -0,0 +1,94 @@
#select
| deallocation.rs:26:15:26:16 | m1 | deallocation.rs:20:3:20:21 | ...::dealloc | deallocation.rs:26:15:26:16 | m1 | This operation dereferences a pointer that may be $@. | deallocation.rs:20:3:20:21 | ...::dealloc | invalid |
| deallocation.rs:37:14:37:33 | ...::read::<...> | deallocation.rs:20:3:20:21 | ...::dealloc | deallocation.rs:37:14:37:33 | ...::read::<...> | This operation dereferences a pointer that may be $@. | deallocation.rs:20:3:20:21 | ...::dealloc | invalid |
| deallocation.rs:44:6:44:7 | m1 | deallocation.rs:20:3:20:21 | ...::dealloc | deallocation.rs:44:6:44:7 | m1 | This operation dereferences a pointer that may be $@. | deallocation.rs:20:3:20:21 | ...::dealloc | invalid |
| deallocation.rs:49:5:49:25 | ...::write::<...> | deallocation.rs:20:3:20:21 | ...::dealloc | deallocation.rs:49:5:49:25 | ...::write::<...> | This operation dereferences a pointer that may be $@. | deallocation.rs:20:3:20:21 | ...::dealloc | invalid |
| deallocation.rs:76:16:76:17 | m2 | deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:76:16:76:17 | m2 | This operation dereferences a pointer that may be $@. | deallocation.rs:70:3:70:21 | ...::dealloc | invalid |
| deallocation.rs:81:16:81:17 | m2 | deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:81:16:81:17 | m2 | This operation dereferences a pointer that may be $@. | deallocation.rs:70:3:70:21 | ...::dealloc | invalid |
| deallocation.rs:86:7:86:8 | m2 | deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:86:7:86:8 | m2 | This operation dereferences a pointer that may be $@. | deallocation.rs:70:3:70:21 | ...::dealloc | invalid |
| deallocation.rs:90:7:90:8 | m2 | deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:90:7:90:8 | m2 | This operation dereferences a pointer that may be $@. | deallocation.rs:70:3:70:21 | ...::dealloc | invalid |
| deallocation.rs:95:5:95:31 | ...::write::<...> | deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:95:5:95:31 | ...::write::<...> | This operation dereferences a pointer that may be $@. | deallocation.rs:70:3:70:21 | ...::dealloc | invalid |
| deallocation.rs:115:13:115:18 | my_ptr | deallocation.rs:112:3:112:12 | ...::free | deallocation.rs:115:13:115:18 | my_ptr | This operation dereferences a pointer that may be $@. | deallocation.rs:112:3:112:12 | ...::free | invalid |
| deallocation.rs:130:14:130:15 | p1 | deallocation.rs:123:23:123:40 | ...::dangling | deallocation.rs:130:14:130:15 | p1 | This operation dereferences a pointer that may be $@. | deallocation.rs:123:23:123:40 | ...::dangling | invalid |
| deallocation.rs:131:14:131:15 | p2 | deallocation.rs:124:21:124:42 | ...::dangling_mut | deallocation.rs:131:14:131:15 | p2 | This operation dereferences a pointer that may be $@. | deallocation.rs:124:21:124:42 | ...::dangling_mut | invalid |
| deallocation.rs:132:14:132:15 | p3 | deallocation.rs:125:23:125:36 | ...::null | deallocation.rs:132:14:132:15 | p3 | This operation dereferences a pointer that may be $@. | deallocation.rs:125:23:125:36 | ...::null | invalid |
| deallocation.rs:180:15:180:16 | p1 | deallocation.rs:176:3:176:25 | ...::drop_in_place | deallocation.rs:180:15:180:16 | p1 | This operation dereferences a pointer that may be $@. | deallocation.rs:176:3:176:25 | ...::drop_in_place | invalid |
| deallocation.rs:248:18:248:20 | ptr | deallocation.rs:242:3:242:25 | ...::drop_in_place | deallocation.rs:248:18:248:20 | ptr | This operation dereferences a pointer that may be $@. | deallocation.rs:242:3:242:25 | ...::drop_in_place | invalid |
edges
| deallocation.rs:20:3:20:21 | ...::dealloc | deallocation.rs:20:23:20:24 | [post] m1 | provenance | Src:MaD:3 MaD:3 |
| deallocation.rs:20:23:20:24 | [post] m1 | deallocation.rs:26:15:26:16 | m1 | provenance | |
| deallocation.rs:20:23:20:24 | [post] m1 | deallocation.rs:37:35:37:36 | m1 | provenance | |
| deallocation.rs:20:23:20:24 | [post] m1 | deallocation.rs:44:6:44:7 | m1 | provenance | |
| deallocation.rs:20:23:20:24 | [post] m1 | deallocation.rs:49:27:49:28 | m1 | provenance | |
| deallocation.rs:37:35:37:36 | m1 | deallocation.rs:37:14:37:33 | ...::read::<...> | provenance | MaD:1 Sink:MaD:1 |
| deallocation.rs:49:27:49:28 | m1 | deallocation.rs:49:5:49:25 | ...::write::<...> | provenance | MaD:2 Sink:MaD:2 |
| deallocation.rs:70:3:70:21 | ...::dealloc | deallocation.rs:70:23:70:35 | [post] m2 as ... | provenance | Src:MaD:3 MaD:3 |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | deallocation.rs:76:16:76:17 | m2 | provenance | |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | deallocation.rs:81:16:81:17 | m2 | provenance | |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | deallocation.rs:86:7:86:8 | m2 | provenance | |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | deallocation.rs:90:7:90:8 | m2 | provenance | |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | deallocation.rs:95:33:95:34 | m2 | provenance | |
| deallocation.rs:95:33:95:34 | m2 | deallocation.rs:95:5:95:31 | ...::write::<...> | provenance | MaD:2 Sink:MaD:2 |
| deallocation.rs:112:3:112:12 | ...::free | deallocation.rs:112:14:112:40 | [post] my_ptr as ... | provenance | Src:MaD:8 MaD:8 |
| deallocation.rs:112:14:112:40 | [post] my_ptr as ... | deallocation.rs:115:13:115:18 | my_ptr | provenance | |
| deallocation.rs:123:6:123:7 | p1 | deallocation.rs:130:14:130:15 | p1 | provenance | |
| deallocation.rs:123:23:123:40 | ...::dangling | deallocation.rs:123:23:123:42 | ...::dangling(...) | provenance | Src:MaD:4 MaD:4 |
| deallocation.rs:123:23:123:42 | ...::dangling(...) | deallocation.rs:123:6:123:7 | p1 | provenance | |
| deallocation.rs:124:6:124:7 | p2 | deallocation.rs:131:14:131:15 | p2 | provenance | |
| deallocation.rs:124:21:124:42 | ...::dangling_mut | deallocation.rs:124:21:124:44 | ...::dangling_mut(...) | provenance | Src:MaD:5 MaD:5 |
| deallocation.rs:124:21:124:44 | ...::dangling_mut(...) | deallocation.rs:124:6:124:7 | p2 | provenance | |
| deallocation.rs:125:6:125:7 | p3 | deallocation.rs:132:14:132:15 | p3 | provenance | |
| deallocation.rs:125:23:125:36 | ...::null | deallocation.rs:125:23:125:38 | ...::null(...) | provenance | Src:MaD:7 MaD:7 |
| deallocation.rs:125:23:125:38 | ...::null(...) | deallocation.rs:125:6:125:7 | p3 | provenance | |
| deallocation.rs:176:3:176:25 | ...::drop_in_place | deallocation.rs:176:27:176:28 | [post] p1 | provenance | Src:MaD:6 MaD:6 |
| deallocation.rs:176:27:176:28 | [post] p1 | deallocation.rs:180:15:180:16 | p1 | provenance | |
| deallocation.rs:242:3:242:25 | ...::drop_in_place | deallocation.rs:242:27:242:29 | [post] ptr | provenance | Src:MaD:6 MaD:6 |
| deallocation.rs:242:27:242:29 | [post] ptr | deallocation.rs:248:18:248:20 | ptr | provenance | |
models
| 1 | Sink: lang:core; crate::ptr::read; pointer-access; Argument[0] |
| 2 | Sink: lang:core; crate::ptr::write; pointer-access; Argument[0] |
| 3 | Source: lang:alloc; crate::alloc::dealloc; pointer-invalidate; Argument[0] |
| 4 | Source: lang:core; crate::ptr::dangling; pointer-invalidate; ReturnValue |
| 5 | Source: lang:core; crate::ptr::dangling_mut; pointer-invalidate; ReturnValue |
| 6 | Source: lang:core; crate::ptr::drop_in_place; pointer-invalidate; Argument[0] |
| 7 | Source: lang:core; crate::ptr::null; pointer-invalidate; ReturnValue |
| 8 | Source: repo:https://github.com/rust-lang/libc:libc; ::free; pointer-invalidate; Argument[0] |
nodes
| deallocation.rs:20:3:20:21 | ...::dealloc | semmle.label | ...::dealloc |
| deallocation.rs:20:23:20:24 | [post] m1 | semmle.label | [post] m1 |
| deallocation.rs:26:15:26:16 | m1 | semmle.label | m1 |
| deallocation.rs:37:14:37:33 | ...::read::<...> | semmle.label | ...::read::<...> |
| deallocation.rs:37:35:37:36 | m1 | semmle.label | m1 |
| deallocation.rs:44:6:44:7 | m1 | semmle.label | m1 |
| deallocation.rs:49:5:49:25 | ...::write::<...> | semmle.label | ...::write::<...> |
| deallocation.rs:49:27:49:28 | m1 | semmle.label | m1 |
| deallocation.rs:70:3:70:21 | ...::dealloc | semmle.label | ...::dealloc |
| deallocation.rs:70:23:70:35 | [post] m2 as ... | semmle.label | [post] m2 as ... |
| deallocation.rs:76:16:76:17 | m2 | semmle.label | m2 |
| deallocation.rs:81:16:81:17 | m2 | semmle.label | m2 |
| deallocation.rs:86:7:86:8 | m2 | semmle.label | m2 |
| deallocation.rs:90:7:90:8 | m2 | semmle.label | m2 |
| deallocation.rs:95:5:95:31 | ...::write::<...> | semmle.label | ...::write::<...> |
| deallocation.rs:95:33:95:34 | m2 | semmle.label | m2 |
| deallocation.rs:112:3:112:12 | ...::free | semmle.label | ...::free |
| deallocation.rs:112:14:112:40 | [post] my_ptr as ... | semmle.label | [post] my_ptr as ... |
| deallocation.rs:115:13:115:18 | my_ptr | semmle.label | my_ptr |
| deallocation.rs:123:6:123:7 | p1 | semmle.label | p1 |
| deallocation.rs:123:23:123:40 | ...::dangling | semmle.label | ...::dangling |
| deallocation.rs:123:23:123:42 | ...::dangling(...) | semmle.label | ...::dangling(...) |
| deallocation.rs:124:6:124:7 | p2 | semmle.label | p2 |
| deallocation.rs:124:21:124:42 | ...::dangling_mut | semmle.label | ...::dangling_mut |
| deallocation.rs:124:21:124:44 | ...::dangling_mut(...) | semmle.label | ...::dangling_mut(...) |
| deallocation.rs:125:6:125:7 | p3 | semmle.label | p3 |
| deallocation.rs:125:23:125:36 | ...::null | semmle.label | ...::null |
| deallocation.rs:125:23:125:38 | ...::null(...) | semmle.label | ...::null(...) |
| deallocation.rs:130:14:130:15 | p1 | semmle.label | p1 |
| deallocation.rs:131:14:131:15 | p2 | semmle.label | p2 |
| deallocation.rs:132:14:132:15 | p3 | semmle.label | p3 |
| deallocation.rs:176:3:176:25 | ...::drop_in_place | semmle.label | ...::drop_in_place |
| deallocation.rs:176:27:176:28 | [post] p1 | semmle.label | [post] p1 |
| deallocation.rs:180:15:180:16 | p1 | semmle.label | p1 |
| deallocation.rs:242:3:242:25 | ...::drop_in_place | semmle.label | ...::drop_in_place |
| deallocation.rs:242:27:242:29 | [post] ptr | semmle.label | [post] ptr |
| deallocation.rs:248:18:248:20 | ptr | semmle.label | ptr |
subpaths

View File

@@ -0,0 +1,4 @@
query: queries/security/CWE-825/AccessInvalidPointer.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -0,0 +1,311 @@
// --- std::alloc ---
pub fn test_alloc(mode: i32) {
let layout = std::alloc::Layout::new::<i64>();
unsafe {
let m1 = std::alloc::alloc(layout); // *mut u8
let m2 = m1 as *mut i64; // *mut i64
*m2 = 1; // GOOD
let v1 = *m1; // GOOD
let v2 = *m2; // GOOD
let v3 = std::ptr::read::<u8>(m1); // GOOD
let v4 = std::ptr::read::<i64>(m2); // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
println!(" v3 = {v3}");
println!(" v4 = {v4}");
std::alloc::dealloc(m1, layout); // $ Source=dealloc
// (m1, m2 are now dangling)
match mode {
0 => {
// reads
let v5 = *m1; // $ Alert[rust/access-invalid-pointer]=dealloc
let v6 = *m2; // $ MISSING: Alert
println!(" v5 = {v5} (!)"); // corrupt in practice
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;
},
100 => {
// more reads
let v7 = std::ptr::read::<u8>(m1); // $ Alert[rust/access-invalid-pointer]=dealloc
let v8 = std::ptr::read::<i64>(m2); // $ MISSING: Alert
println!(" v7 = {v7} (!)"); // corrupt in practice
println!(" v8 = {v8} (!)"); // corrupt in practice
},
101 => {
// writes
*m1 = 2; // $ Alert[rust/access-invalid-pointer]=dealloc
*m2 = 3; // $ MISSING: Alert
},
102 => {
// more writes
std::ptr::write::<u8>(m1, 4); // $ Alert[rust/access-invalid-pointer]=dealloc
std::ptr::write::<i64>(m2, 5); // $ MISSING: Alert
},
_ => {}
}
}
}
pub fn test_alloc_array(mode: i32) {
let layout = std::alloc::Layout::new::<[u8; 10]>();
unsafe {
let m1 = std::alloc::alloc(layout);
let m2 = m1 as *mut [u8; 10];
(*m2)[0] = 4; // GOOD
(*m2)[1] = 5; // GOOD
let v1 = (*m2)[0]; // GOOD
let v2 = (*m2)[1]; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
std::alloc::dealloc(m2 as *mut u8, layout); // $ Source=dealloc_array
// m1, m2 are now dangling
match mode {
0 => {
// read
let v3 = (*m2)[0]; // $ Alert[rust/access-invalid-pointer]=dealloc_array
println!(" v3 = {v3} (!)"); // corrupt in practice
},
110 => {
// another read
let v4 = (*m2)[1]; // $ Alert[rust/access-invalid-pointer]=dealloc_array
println!(" v4 = {v4} (!)"); // corrupt in practice
},
111 => {
// write
(*m2)[0] = 3; // $ Alert[rust/access-invalid-pointer]=dealloc_array
},
112 => {
// another write
(*m2)[1] = 4; // $ Alert[rust/access-invalid-pointer]=dealloc_array
},
113 => {
// more writes
std::ptr::write::<u8>(m1, 5); // $ MISSING: Alert
std::ptr::write::<[u8; 10]>(m2, [6; 10]); // $ Alert[rust/access-invalid-pointer]=dealloc_array
},
_ => {}
}
}
}
// --- libc::malloc ---
pub fn test_libc() {
unsafe {
let my_ptr = libc::malloc(256) as *mut i64;
*my_ptr = 10;
let v1 = *my_ptr; // GOOD
println!(" v1 = {v1}");
libc::free(my_ptr as *mut libc::c_void); // $ Source=free
// (my_ptr is now dangling)
let v2 = *my_ptr; // $ Alert[rust/access-invalid-pointer]=free
println!(" v2 = {v2} (!)"); // corrupt in practice
}
}
// --- 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
if mode == 120 {
unsafe {
// (a segmentation fault occurs in the code below)
let v1 = *p1; // $ Alert[rust/access-invalid-pointer]=dangling
let v2 = *p2; // $ Alert[rust/access-invalid-pointer]=dangling_mut
let v3 = *p3; // $ Alert[rust/access-invalid-pointer]=null
println!(" v1 = {v1} (!)");
println!(" v2 = {v2} (!)");
println!(" v3 = {v3} (!)");
}
}
}
// --- drop ---
struct MyBuffer {
data: Vec<u8>
}
pub fn test_drop() {
let my_buffer = MyBuffer { data: vec!(1, 2, 3) };
let p1 = std::ptr::addr_of!(my_buffer);
unsafe {
let v1 = (*p1).data[0]; // GOOD
println!(" v1 = {v1}");
}
drop(my_buffer); // explicitly destructs the `my_buffer` variable
unsafe {
let v2 = (*p1).data[0]; // $ MISSING: Alert
println!(" v2 = {v2} (!)"); // corrupt in practice
}
}
pub fn test_ptr_drop(mode: i32) {
let layout = std::alloc::Layout::new::<Vec<i64>>();
unsafe {
let p1 = std::alloc::alloc(layout) as *mut Vec<i64>;
let p2 = p1;
*p1 = vec!(1, 2, 3);
let v1 = (*p1)[0]; // GOOD
let v2 = (*p2)[0]; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
std::ptr::drop_in_place(p1); // $ Source=drop_in_place
// explicitly destructs the pointed-to `m2`
if mode == 1 {
let v3 = (*p1)[0]; // $ Alert[rust/access-invalid-pointer]=drop_in_place
println!(" v3 = {v3} (!)"); // corrupt in practice
}
if mode == 130 {
let v4 = (*p2)[0]; // $ MISSING: Alert
println!(" v4 = {v4} (!)"); // corrupt in practice
}
let p3 = std::alloc::alloc(layout) as *mut Vec<i64>;
std::ptr::drop_in_place((*p3).as_mut_ptr()); // GOOD
}
}
struct MyDropBuffer {
ptr: *mut u8,
}
impl MyDropBuffer {
unsafe fn new() -> MyDropBuffer {
let layout = std::alloc::Layout::from_size_align(1024, 1).unwrap();
MyDropBuffer {
ptr: std::alloc::alloc(layout),
}
// ...
}
}
impl Drop for MyDropBuffer {
fn drop(&mut self) {
let layout = std::alloc::Layout::from_size_align(1024, 1).unwrap();
unsafe {
_ = *self.ptr;
drop(*self.ptr); // $ MISSING: Source=drop
_ = *self.ptr; // $ MISSING: Alert[rust/access-invalid-pointer]=drop
std::alloc::dealloc(self.ptr, layout);
}
}
}
// --- qhelp examples ---
fn do_something(s: &String) {
println!(" s = {}", s);
}
fn test_qhelp_example_good(ptr: *mut String) {
unsafe {
do_something(&*ptr);
}
// ...
unsafe {
std::ptr::drop_in_place(ptr);
}
}
fn test_qhelp_example_bad(ptr: *mut String) {
unsafe {
std::ptr::drop_in_place(ptr); // $ Source=drop_in_place
}
// ...
unsafe {
do_something(&*ptr); // $ Alert[rust/access-invalid-pointer]=drop_in_place
}
}
pub fn test_qhelp_examples() {
let layout = std::alloc::Layout::new::<[String; 2]>();
unsafe {
let ptr = std::alloc::alloc(layout);
let ptr_s = ptr as *mut [String; 2];
let ptr1 = &raw mut (*ptr_s)[0];
let ptr2 = &raw mut (*ptr_s)[1];
*ptr1 = String::from("123");
*ptr2 = String::from("456");
test_qhelp_example_good(ptr1);
test_qhelp_example_bad(ptr2);
std::alloc::dealloc(ptr, layout);
}
}
// --- Vec ---
pub fn test_vec_reserve() {
let mut vec1 = Vec::<u16>::new();
vec1.push(100);
let ptr1 = &raw mut vec1[0];
unsafe {
let v1 = *ptr1;
println!(" v1 = {}", v1);
}
vec1.reserve(1000); // $ MISSING: Source=reserve
// (may invalidate the pointer)
unsafe {
let v2 = *ptr1; // $ MISSING: Alert[rust/access-invalid-pointer]=reserve
println!(" v2 = {}", v2); // corrupt in practice
}
// -
let mut vec2 = Vec::<u16>::new();
vec2.push(200);
let ptr2 = &raw mut vec2[0];
unsafe {
let v3 = *ptr2;
println!(" v3 = {}", v3);
}
for _i in 0..1000 {
vec2.push(0); // $ MISSING: Source=push
// (may invalidate the pointer)
}
unsafe {
let v4 = *ptr2; // $ MISSING: Alert[rust/access-invalid-pointer]=push
println!(" v4 = {}", v4); // corrupt in practice
}
}

View File

@@ -0,0 +1,443 @@
fn use_the_stack() {
let _buffer: [u8; 256] = [0xFF; 256];
}
struct MyValue {
value: i64
}
impl Drop for MyValue {
fn drop(&mut self) {
println!(" drop MyValue '{}'", self.value)
}
}
// --- local dangling ---
fn get_local_dangling() -> *const i64 {
let my_local1: i64 = 1;
return &my_local1;
} // (return value immediately becomes dangling)
fn get_local_dangling_mut() -> *mut i64 {
let mut my_local2: i64 = 2;
return &mut my_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 value immediately becomes dangling)
fn get_local_dangling_raw_mut() -> *mut i64 {
let mut my_local4: i64 = 4;
return &raw mut my_local4;
} // (return value immediately becomes dangling)
fn get_param_dangling(param5: i64) -> *const i64 {
return &param5;
} // (return value immediately becomes dangling)
fn get_local_field_dangling() -> *const i64 {
let val: MyValue;
val = MyValue { value: 6 };
return &val.value;
}
pub fn test_local_dangling() {
let p1 = get_local_dangling();
let p2 = get_local_dangling_mut();
let p3 = get_local_dangling_raw_const();
let p4 = get_local_dangling_raw_mut();
let p5 = get_param_dangling(5);
let p6 = get_local_field_dangling();
let p7: *const i64;
{
let my_local7 = 7;
p7 = &raw const my_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
println!(" v1 = {v1} (!)"); // corrupt in practice
println!(" v2 = {v2} (!)"); // corrupt in practice
println!(" v3 = {v3} (!)"); // corrupt in practice
println!(" v4 = {v4} (!)"); // corrupt in practice
println!(" v5 = {v5} (!)"); // corrupt in practice
println!(" v6 = {v6} (!)"); // corrupt in practice
println!(" v7 = {v7} (!)");
}
}
// --- local in scope ---
fn use_pointers(p1: *const i64, p2: *mut i64, mode: i32) {
let p3: *const i64;
let my_local1 = 1;
p3 = &my_local1;
use_the_stack();
unsafe {
if (mode == 0) {
// reads
let v1 = *p1; // GOOD
let v2 = *p2; // GOOD
let v3 = *p3; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
println!(" v3 = {v3}");
}
if (mode == 200) {
// writes
*p2 = 2; // GOOD
}
}
}
pub fn test_local_in_scope(mode: i32) {
let my_local3: i64 = 3;
let mut my_local_mut4: i64 = 4;
use_pointers(&my_local3, &mut my_local_mut4, mode);
}
// --- static lifetime ---
const MY_GLOBAL_CONST: i64 = 1;
fn get_const() -> *const i64 {
return &MY_GLOBAL_CONST;
}
static mut MY_GLOBAL_STATIC: i64 = 2;
fn get_static_mut() -> *mut i64 {
unsafe {
return &mut MY_GLOBAL_STATIC;
}
}
pub fn test_static(mode: i32) {
let p1 = get_const();
let p2 = get_static_mut();
use_the_stack();
unsafe {
if (mode == 0) {
// reads
let v1 = *p1; // GOOD
let v2 = *p2; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
}
if (mode == 210) {
// writes
*p2 = 3; // GOOD
}
}
}
// --- call contexts ---
fn access_ptr_1(ptr: *const i64) {
// only called with `ptr` safe
unsafe {
let v1 = *ptr; // GOOD
println!(" v1 = {v1}");
}
}
fn access_ptr_2(ptr: *const i64) {
// only called with `ptr` dangling
unsafe {
let v2 = *ptr; // $ MISSING: Alert
println!(" v2 = {v2} (!)"); // corrupt in practice
}
}
fn access_ptr_3(ptr: *const i64) {
// called from contexts with `ptr` safe and dangling
unsafe {
let v3 = *ptr; // $ MISSING: Alert
println!(" v3 = {v3} (!)"); // corrupt in practice (in one context)
}
}
fn access_and_get_dangling() -> *const i64 {
let my_local1 = 1;
let ptr = &my_local1;
access_ptr_1(ptr);
access_ptr_3(ptr);
return ptr;
} // (returned pointer becomes dangling)
pub fn test_call_contexts() {
let ptr = access_and_get_dangling();
use_the_stack();
access_ptr_2(ptr);
access_ptr_3(ptr);
}
// --- call contexts (recursive) ---
fn access_ptr_rec(ptr_up: *const i64, count: i64) -> *const i64 {
let my_local_rec = count;
let ptr_ours = &my_local_rec;
if count < 5 {
let ptr_down = access_ptr_rec(ptr_ours, count + 1);
use_the_stack();
unsafe {
let v_up = *ptr_up; // GOOD
let v_ours = *ptr_ours; // GOOD
let v_down = *ptr_down; // $ MISSING: Alert
println!(" v_up = {v_up}");
println!(" v_ours = {v_ours}");
println!(" v_down = {v_down} (!)");
}
}
return ptr_ours;
} // (returned pointer becomes dangling)
pub fn test_call_contexts_rec() {
let my_local_rec2 = 1;
let ptr_start = &my_local_rec2;
_ = access_ptr_rec(ptr_start, 2);
}
// --- loops ---
pub fn test_loop() {
let my_local1 = vec!(0);
let mut prev: *const Vec<i32> = &my_local1;
for i in 1..5 {
let my_local2 = vec!(i);
use_the_stack();
unsafe {
let v1 = (*prev)[0]; // $ MISSING: Alert
println!(" v1 = {v1} (!)"); // incorrect values in practice (except first iteration)
}
prev = &my_local2;
} // (my_local2 goes out of scope, thus prev is dangling)
unsafe {
let v2 = (*prev)[0]; // $ MISSING: Alert
println!(" v2 = {v2} (!)"); // corrupt in practice
}
}
// --- enum ---
enum MyEnum {
Value(i64),
}
impl Drop for MyEnum {
fn drop(&mut self) {
println!(" drop MyEnum");
}
}
pub fn test_enum() {
let result: *const i64;
{
let e1 = MyEnum::Value(1);
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();
unsafe {
let v1 = *result; // $ MISSING: Alert
println!(" v1 = {v1}");
}
} // (e1 goes out of scope, so result is definitely dangling now)
use_the_stack();
unsafe {
let v2 = *result; // $ MISSING: Alert
println!(" v2 = {v2}"); // dropped in practice
}
}
// --- std::ptr ---
#[derive(Debug)]
struct MyPair {
a: i64,
b: i64
}
impl Drop for MyPair {
fn drop(&mut self) {
println!(" drop MyPair '{} {}'", self.a, self.b);
self.a = -1;
self.b = -1;
}
}
pub fn test_ptr_to_struct(mode: i32) {
let p1: *mut MyPair;
let p2: *const i64;
let p3: *mut i64;
{
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);
unsafe {
let v1 = (*p1).a; // GOOD
println!(" v1 = {v1}");
let v2 = (*p1).b; // GOOD
println!(" v2 = {v2}");
let v3 = *p2; // GOOD
let v4 = *p3; // GOOD
println!(" v3 = {v3}");
println!(" v4 = {v4}");
(*p1).a = 3; // GOOD
*p3 = 4; // GOOD
(*p1).b = 5; // GOOD
}
}; // my_pair goes out of scope, thus p1, p2, p3 are dangling
use_the_stack();
unsafe {
match mode {
0 => {
// read
let v5 = (*p1).a; // $ MISSING: Alert
println!(" v5 = {v5} (!)"); // dropped in practice
},
220 => {
// another read
let v6 = (*p1).b; // $ MISSING: Alert
println!(" v6 = {v6} (!)"); // dropped in practice
},
221 => {
// more reads
let v7 = *p2; // $ MISSING: Alert
let v8 = *p3; // $ MISSING: Alert
println!(" v7 = {v7} (!)"); // dropped in practice
println!(" v8 = {v8} (!)"); // dropped in practice
},
222 => {
// writes
(*p1).a = 6; // $ MISSING: Alert
*p3 = 7; // $ MISSING: Alert
},
223 => {
// another write
(*p1).b = 8; // $ MISSING: Alert
},
_ => {}
}
}
}
fn get_ptr_from_ref(val: i32) -> *const i32 {
let my_val = val;
let r1: &i32 = &my_val;
let p1: *const i32 = std::ptr::from_ref(r1);
unsafe {
let v1 = *p1; // GOOD
println!(" v1 = {v1}");
}
return p1;
} // (returned pointer becomes dangling)
pub fn test_ptr_from_ref() {
let p1 = get_ptr_from_ref(1);
use_the_stack();
unsafe {
let v2 = *p1; // $ MISSING: Alert
let v3 = *get_ptr_from_ref(2); // $ MISSING: Alert
println!(" v2 = {v2} (!)"); // corrupt in practice
println!(" v3 = {v3} (!)");
}
}
// --- std::rc (reference counting pointer) ---
pub fn test_rc() {
let p1: *const i64;
let p2: *const i64;
{
let rc1: std::rc::Rc<i64> = std::rc::Rc::new(1);
p1 = std::rc::Rc::<i64>::as_ptr(&rc1);
{
let rc2: std::rc::Rc<i64> = std::rc::Rc::clone(&rc1);
p2 = std::rc::Rc::<i64>::as_ptr(&rc2);
unsafe {
let v1 = *p1; // GOOD
let v2 = *p2; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
}
} // rc2 goes out of scope, but the reference count is still 1 so the pointer remains valid
unsafe {
let v3 = *p1; // GOOD
let v4 = *p2; // GOOD
println!(" v3 = {v3}");
println!(" v4 = {v4}");
}
} // rc1 go out of scope, the reference count is 0, so p1, p2 are dangling
unsafe {
let v5 = *p1; // $ MISSING: Alert
let v6 = *p2; // $ MISSING: Alert
println!(" v5 = {v5} (!)"); // corrupt in practice
println!(" v6 = {v6} (!)"); // corrupt in practice
}
// note: simialar things are likely possible with Ref, RefMut, RefCell,
// Vec and others.
}

View File

@@ -0,0 +1,168 @@
#![feature(box_as_ptr)]
#![feature(box_into_inner)]
mod deallocation;
use deallocation::*;
mod lifetime;
use lifetime::*;
fn use_the_heap() {
let _a = Box::new(0x7FFFFFFF);
let _b = Box::new(0x7FFFFFFF);
}
// --- boxes ---
pub fn test_boxes_into() {
let b1: Box<i64> = Box::new(7); // b1 owns the memory for '50'
let p1 = Box::as_ptr(&b1); // b1 still owns the memory
unsafe {
let v1 = *p1; // GOOD
println!(" v1 = {v1}");
}
let v2 = Box::into_inner(b1); // b1 is explicitly freed here, thus p1 is dangling
println!(" v2 = {v2}");
unsafe {
let v3 = *p1; // $ MISSING: Alert
println!(" v3 = {v3} (!)"); // corrupt in practice
}
}
pub fn test_boxes_1(mode: i32) {
let p1: *const i64;
let p2: *const i64;
let p3: *mut i64;
{
let b1: Box<i64> = Box::new(1);
p1 = Box::into_raw(b1); // now owned by p1
let b2: Box<i64> = Box::new(2);
p2 = Box::as_ptr(&b2); // still owned by b2
let mut b3: Box<i64> = Box::new(3);
p3 = Box::as_mut_ptr(&mut b3); // still owned by b3
unsafe {
let v1 = *p1; // GOOD
let v2 = *p2; // GOOD
let v3 = *p3; // GOOD
println!(" v1 = {v1}");
println!(" v2 = {v2}");
println!(" v3 = {v3}");
*p3 = 4;
}
} // (b2, b3 go out of scope, thus p2, p3 are dangling)
unsafe {
if mode == 0 {
// reads
let v4 = *p1; // GOOD
let v5 = *p2; // $ MISSING: Alert
let v6 = *p3; // $ MISSING: Alert
println!(" v4 = {v4}");
println!(" v5 = {v5} (!)"); // corrupt in practice
println!(" v6 = {v6} (!)"); // corrupt in practice
}
if mode == 10 {
// write
*p3 = 5; // $ MISSING: Alert
use_the_heap(); // "malloc: Heap corruption detected"
}
}
}
pub fn test_boxes_2() {
let b1: Box<i64> = Box::new(6); // b1 owns the memory
let p1 = Box::into_raw(b1); // now p1 owns the memory
unsafe {
let _b2 = Box::from_raw(p1); // now _b2 owns the memory
let v1 = *p1; // GOOD
println!(" v1 = {v1}");
} // (_b2 goes out of scope, thus the memory is freed and p1 is dangling)
unsafe {
let v2 = *p1; // $ MISSING: Alert
println!(" v2 = {v2} (!)"); // corrupt in practice
}
}
// --- main ---
fn main() {
let mode = std::env::args().nth(1).unwrap_or("0".to_string()).parse::<i32>().unwrap_or(0);
// mode = which test cases to explore (0 should be safe; some will crash / segfault).
println!("mode = {mode}");
println!("test_boxes_into:");
test_boxes_into();
println!("test_boxes_1:");
test_boxes_1(mode);
println!("test_boxes_2:");
test_boxes_2();
// ---
println!("test_alloc:");
test_alloc(mode);
println!("test_alloc_array:");
test_alloc_array(mode);
println!("test_libc:");
test_libc();
println!("test_ptr_invalid:");
test_ptr_invalid(mode);
println!("test_drop:");
test_drop();
println!("test_ptr_drop:");
test_ptr_drop(mode);
println!("test_qhelp_tests:");
test_qhelp_examples();
println!("test_vec_reserve:");
test_vec_reserve();
// ---
println!("test_local_dangling:");
test_local_dangling();
println!("test_local_in_scope:");
test_local_in_scope(mode);
println!("test_static:");
test_static(mode);
println!("test_call_contexts:");
test_call_contexts();
println!("test_call_contexts_rec:");
test_call_contexts_rec();
println!("test_loop:");
test_loop();
println!("test_enum:");
test_enum();
println!("test_ptr_to_struct:");
test_ptr_to_struct(mode);
println!("test_ptr_from_ref:");
test_ptr_from_ref();
println!("test_rc:");
test_rc();
}

View File

@@ -0,0 +1,3 @@
qltest_cargo_check: true
qltest_dependencies:
- libc = { version = "0.2.11" }

View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly-2025-03-17"

View File

@@ -10,6 +10,7 @@ use core::{hint, mem};
// summary=repo::test;crate::option::replace;Argument[0].Reference;ReturnValue;value;dfc-generated
// summary=repo::test;crate::option::replace;Argument[1];Argument[0].Reference;value;dfc-generated
// sink=repo::test;crate::option::replace;Argument[0];pointer-access;df-generated
pub fn replace<T>(dest: &mut T, src: T) -> T {
unsafe {
let result = ptr::read(dest);
@@ -338,6 +339,7 @@ impl<T> MyOption<T> {
}
// summary=repo::test;<crate::option::MyOption>::take;Argument[self].Reference;ReturnValue;value;dfc-generated
// sink=repo::test;<crate::option::MyOption>::take;Argument[self];pointer-access;df-generated
pub fn take(&mut self) -> MyOption<T> {
// FIXME(const-hack) replace `mem::replace` by `mem::take` when the latter is const ready
replace(self, MyNone)
@@ -345,6 +347,7 @@ impl<T> MyOption<T> {
// summary=repo::test;<crate::option::MyOption>::take_if;Argument[self].Reference.Field[crate::option::MyOption::MySome(0)];Argument[0].Parameter[0].Reference;value;dfc-generated
// summary=repo::test;<crate::option::MyOption>::take_if;Argument[self].Reference;ReturnValue;value;dfc-generated
// sink=repo::test;<crate::option::MyOption>::take_if;Argument[self];pointer-access;df-generated
pub fn take_if<P>(&mut self, predicate: P) -> MyOption<T>
where
P: FnOnce(&mut T) -> bool,
@@ -358,6 +361,7 @@ impl<T> MyOption<T> {
// summary=repo::test;<crate::option::MyOption>::replace;Argument[0];Argument[self].Reference.Field[crate::option::MyOption::MySome(0)];value;dfc-generated
// summary=repo::test;<crate::option::MyOption>::replace;Argument[self].Reference;ReturnValue;value;dfc-generated
// sink=repo::test;<crate::option::MyOption>::replace;Argument[self];pointer-access;df-generated
pub fn replace(&mut self, value: T) -> MyOption<T> {
replace(self, MySome(value))
}