Skip to content

Commit 26f8558

Browse files
committed
Rust: Add qhelp, examples, and examples as tests.
1 parent fe20fb4 commit 26f8558

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
7+
<p>
8+
Dereferencing a pointer after the lifetime of its target has ended causes undefined behavior. Memory
9+
may be corrupted causing the program to crash or behave incorrectly, in some cases exposing the program
10+
to potential attacks.
11+
</p>
12+
13+
</overview>
14+
<recommendation>
15+
16+
<p>
17+
When dereferencing a pointer in <code>unsafe</code> code, take care that the pointer is still valid
18+
at the time it is dereferenced. Code may need to be rearranged or changed to extend lifetimes. If
19+
possible, rewrite the code using safe Rust types to avoid this kind of problem altogether.
20+
</p>
21+
22+
</recommendation>
23+
<example>
24+
25+
<p>
26+
In the following example, <code>val</code> is local to <code>get_pointer</code> so its lifetime
27+
ends when that function returns. However, a pointer to <code>val</code> is returned and dereferenced
28+
after that lifetime has ended, causing undefined behavior:
29+
</p>
30+
31+
<sample src="AccessAfterLifetimeBad.rs" />
32+
33+
<p>
34+
One way to fix this is to change the return type of the function from a pointer to a <code>Box</code>,
35+
which ensures that the value it points to remains on the heap for the lifetime of the <code>Box</code>
36+
itself. Notice that there is no longer a need for an <code>unsafe</code> block as the code no longer
37+
handles pointers directly:
38+
</p>
39+
40+
<sample src="AccessAfterLifetimeGood.rs" />
41+
42+
</example>
43+
<references>
44+
45+
<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>
46+
<li>Rust Documentation: <a href="https://doc.rust-lang.org/std/ptr/index.html#safety">Module ptr - Safety</a>.</li>
47+
<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>
48+
49+
</references>
50+
</qhelp>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
fn get_pointer() -> *const i64 {
3+
let val = 123;
4+
5+
return &val;
6+
} // lifetime of `val` ends here, the pointer becomes dangling
7+
8+
fn example() {
9+
let ptr = get_pointer();
10+
let val;
11+
12+
// ...
13+
14+
unsafe {
15+
val = *ptr; // BAD: dereferences `ptr` after the lifetime of `val` has ended
16+
}
17+
18+
// ...
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
fn get_box() -> Box<i64> {
3+
let val = 123;
4+
5+
return Box::new(val); // copies `val` onto the heap, where it remains for the lifetime of the `Box`.
6+
}
7+
8+
fn example() {
9+
let ptr = get_box();
10+
let val;
11+
12+
// ...
13+
14+
val = *ptr; // GOOD
15+
16+
// ...
17+
}

rust/ql/test/query-tests/security/CWE-825/AccessAfterLifetime.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
| lifetime.rs:692:13:692:14 | r1 | lifetime.rs:682:4:682:12 | &... | lifetime.rs:692:13:692:14 | r1 | Access of a pointer to $@ after it's lifetime has ended. | lifetime.rs:681:8:681:9 | v1 | v1 |
2424
| lifetime.rs:693:13:693:14 | r2 | lifetime.rs:686:5:686:13 | &... | lifetime.rs:693:13:693:14 | r2 | Access of a pointer to $@ after it's lifetime has ended. | lifetime.rs:685:8:685:9 | v2 | v2 |
2525
| lifetime.rs:725:2:725:12 | ptr | lifetime.rs:724:2:724:12 | &val | lifetime.rs:725:2:725:12 | ptr | Access of a pointer to $@ after it's lifetime has ended. | lifetime.rs:724:2:724:12 | val | val |
26+
| lifetime.rs:743:10:743:12 | ptr | lifetime.rs:733:9:733:12 | &val | lifetime.rs:743:10:743:12 | ptr | Access of a pointer to $@ after it's lifetime has ended. | lifetime.rs:731:6:731:8 | val | val |
2627
edges
2728
| deallocation.rs:148:6:148:7 | p1 | deallocation.rs:151:14:151:15 | p1 | provenance | |
2829
| deallocation.rs:148:6:148:7 | p1 | deallocation.rs:158:14:158:15 | p1 | provenance | |
@@ -187,6 +188,10 @@ edges
187188
| lifetime.rs:687:5:687:15 | &... | lifetime.rs:686:4:687:16 | TupleExpr [tuple.1] | provenance | |
188189
| lifetime.rs:724:2:724:12 | &val | lifetime.rs:724:2:724:12 | ptr | provenance | |
189190
| lifetime.rs:724:2:724:12 | ptr | lifetime.rs:725:2:725:12 | ptr | provenance | |
191+
| lifetime.rs:733:2:733:12 | return ... | lifetime.rs:737:12:737:24 | get_pointer(...) | provenance | |
192+
| lifetime.rs:733:9:733:12 | &val | lifetime.rs:733:2:733:12 | return ... | provenance | |
193+
| lifetime.rs:737:6:737:8 | ptr | lifetime.rs:743:10:743:12 | ptr | provenance | |
194+
| lifetime.rs:737:12:737:24 | get_pointer(...) | lifetime.rs:737:6:737:8 | ptr | provenance | |
190195
models
191196
| 1 | Summary: lang:core; crate::ptr::from_ref; Argument[0]; ReturnValue; value |
192197
nodes
@@ -383,4 +388,9 @@ nodes
383388
| lifetime.rs:724:2:724:12 | &val | semmle.label | &val |
384389
| lifetime.rs:724:2:724:12 | ptr | semmle.label | ptr |
385390
| lifetime.rs:725:2:725:12 | ptr | semmle.label | ptr |
391+
| lifetime.rs:733:2:733:12 | return ... | semmle.label | return ... |
392+
| lifetime.rs:733:9:733:12 | &val | semmle.label | &val |
393+
| lifetime.rs:737:6:737:8 | ptr | semmle.label | ptr |
394+
| lifetime.rs:737:12:737:24 | get_pointer(...) | semmle.label | get_pointer(...) |
395+
| lifetime.rs:743:10:743:12 | ptr | semmle.label | ptr |
386396
subpaths

rust/ql/test/query-tests/security/CWE-825/lifetime.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,41 @@ pub fn test_macros() {
724724
my_macro!(); // $ SPURIOUS: Source[rust/access-after-lifetime-ended]
725725
my_macro!(); // $ SPURIOUS: Alert[rust/access-after-lifetime-ended]
726726
}
727+
728+
// --- examples from qhelp ---
729+
730+
fn get_pointer() -> *const i64 {
731+
let val = 123;
732+
733+
return &val; // $ Source[rust/access-after-lifetime-ended]=val
734+
} // lifetime of `val` ends here, the pointer becomes dangling
735+
736+
pub fn test_lifetimes_example_bad() {
737+
let ptr = get_pointer();
738+
let val;
739+
740+
use_the_stack();
741+
742+
unsafe {
743+
val = *ptr; // $ Alert[rust/access-after-lifetime-ended]=val
744+
}
745+
746+
println!(" val = {val} (!)"); // corrupt in practice
747+
}
748+
749+
fn get_box() -> Box<i64> {
750+
let val = 123;
751+
752+
return Box::new(val);
753+
}
754+
755+
pub fn test_lifetimes_example_good() {
756+
let ptr = get_box();
757+
let val;
758+
759+
use_the_stack();
760+
761+
val = *ptr; // GOOD
762+
763+
println!(" val = {val}");
764+
}

rust/ql/test/query-tests/security/CWE-825/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,10 @@ fn main() {
186186

187187
println!("test_macros:");
188188
test_macros();
189+
190+
println!("test_lifetimes_example_bad:");
191+
test_lifetimes_example_bad();
192+
193+
println!("test_lifetimes_example_good:");
194+
test_lifetimes_example_good();
189195
}

0 commit comments

Comments
 (0)