Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added data-flow models for `core::fmt::Write`. This may improve detection of vulnerabilities where tainted data is written to a formatted output buffer.
5 changes: 5 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/core.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ extensions:
- ["<core::str>::parse", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
- ["<core::str>::trim", "Argument[self].Reference", "ReturnValue.Reference", "taint", "manual"]
- ["<core::str>::to_string", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
# Fmt
- ["<_ as core::fmt::Write>::write_fmt", "Argument[0]", "Argument[self].Reference", "taint", "manual"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a way of getting taint into an Arguments struct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have test cases along the lines of format_args!("{}", source()). Do you mean that or something else?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I see that it somehow works, I'm just curious which model enables taint to get into an Arguments struct.

- ["<_ as core::fmt::Write>::write_str", "Argument[0].Reference", "Argument[self].Reference", "taint", "manual"]
- ["<_ as core::fmt::Write>::write_char", "Argument[0]", "Argument[self].Reference", "taint", "manual"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to test every single model individually.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will have a semantic merge conflict with #22376.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot. I suggest when either #22376 or #22346 is merge, we update the other for this and any other outdated models.

- ["core::fmt::write", "Argument[1]", "Argument[0].Reference", "taint", "manual"]
# Ord
- ["<_ as core::cmp::Ord>::min", "Argument[self,0]", "ReturnValue", "value", "manual"]
- ["<_ as core::cmp::Ord>::max", "Argument[self,0]", "ReturnValue", "value", "manual"]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
| main.rs:13:10:13:25 | FormatArgsExpr | 1 |
| main.rs:16:10:16:23 | FormatArgsExpr | 0 |
| main.rs:19:13:19:36 | FormatArgsExpr | 1 |
| main.rs:33:25:33:33 | FormatArgsExpr | 1 |
| main.rs:38:13:38:32 | FormatArgsExpr | 1 |
| main.rs:39:13:39:32 | FormatArgsExpr | 0 |
| main.rs:49:5:49:27 | FormatArgsExpr | 1 |
| main.rs:50:5:50:26 | FormatArgsExpr | 0 |
| main.rs:51:5:51:25 | FormatArgsExpr | 1 |
| main.rs:52:5:52:24 | FormatArgsExpr | 0 |
| main.rs:27:25:27:33 | FormatArgsExpr | 1 |
| main.rs:38:40:38:46 | FormatArgsExpr | 0 |
| main.rs:43:52:43:58 | FormatArgsExpr | 0 |
| main.rs:49:13:49:33 | FormatArgsExpr | 1 |
| main.rs:54:13:54:33 | FormatArgsExpr | 0 |
| main.rs:64:5:64:27 | FormatArgsExpr | 1 |
| main.rs:65:5:65:26 | FormatArgsExpr | 0 |
| main.rs:66:5:66:25 | FormatArgsExpr | 1 |
| main.rs:67:5:67:24 | FormatArgsExpr | 0 |
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
#select
| main.rs:49:5:49:27 | FormatArgsExpr | main.rs:47:19:47:45 | ...::var(...) | main.rs:49:5:49:27 | FormatArgsExpr | Log entry depends on a $@. | main.rs:47:19:47:45 | ...::var(...) | user-provided value |
| main.rs:50:5:50:26 | FormatArgsExpr | main.rs:47:19:47:45 | ...::var(...) | main.rs:50:5:50:26 | FormatArgsExpr | Log entry depends on a $@. | main.rs:47:19:47:45 | ...::var(...) | user-provided value |
| main.rs:51:5:51:25 | FormatArgsExpr | main.rs:47:19:47:45 | ...::var(...) | main.rs:51:5:51:25 | FormatArgsExpr | Log entry depends on a $@. | main.rs:47:19:47:45 | ...::var(...) | user-provided value |
| main.rs:52:5:52:24 | FormatArgsExpr | main.rs:47:19:47:45 | ...::var(...) | main.rs:52:5:52:24 | FormatArgsExpr | Log entry depends on a $@. | main.rs:47:19:47:45 | ...::var(...) | user-provided value |
| main.rs:64:5:64:27 | FormatArgsExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:64:5:64:27 | FormatArgsExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value |
| main.rs:65:5:65:26 | FormatArgsExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:65:5:65:26 | FormatArgsExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value |
| main.rs:66:5:66:25 | FormatArgsExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:66:5:66:25 | FormatArgsExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value |
| main.rs:67:5:67:24 | FormatArgsExpr | main.rs:62:19:62:45 | ...::var(...) | main.rs:67:5:67:24 | FormatArgsExpr | Log entry depends on a $@. | main.rs:62:19:62:45 | ...::var(...) | user-provided value |
edges
| main.rs:47:9:47:15 | tainted | main.rs:49:5:49:27 | FormatArgsExpr | provenance | Sink:MaD:2 |
| main.rs:47:9:47:15 | tainted | main.rs:50:5:50:26 | FormatArgsExpr | provenance | Sink:MaD:1 |
| main.rs:47:9:47:15 | tainted | main.rs:51:5:51:25 | FormatArgsExpr | provenance | Sink:MaD:2 |
| main.rs:47:9:47:15 | tainted | main.rs:52:5:52:24 | FormatArgsExpr | provenance | Sink:MaD:1 |
| main.rs:47:19:47:45 | ...::var(...) | main.rs:47:19:47:45 | ...::var(...) [Ok] | provenance | Src:MaD:3 |
| main.rs:47:19:47:45 | ...::var(...) [Ok] | main.rs:47:19:47:65 | ... .unwrap_or_default() | provenance | MaD:4 |
| main.rs:47:19:47:65 | ... .unwrap_or_default() | main.rs:47:9:47:15 | tainted | provenance | |
| main.rs:62:9:62:15 | tainted | main.rs:64:5:64:27 | FormatArgsExpr | provenance | Sink:MaD:2 |
| main.rs:62:9:62:15 | tainted | main.rs:65:5:65:26 | FormatArgsExpr | provenance | Sink:MaD:1 |
| main.rs:62:9:62:15 | tainted | main.rs:66:5:66:25 | FormatArgsExpr | provenance | Sink:MaD:2 |
| main.rs:62:9:62:15 | tainted | main.rs:67:5:67:24 | FormatArgsExpr | provenance | Sink:MaD:1 |
| main.rs:62:19:62:45 | ...::var(...) | main.rs:62:19:62:45 | ...::var(...) [Ok] | provenance | Src:MaD:3 |
| main.rs:62:19:62:45 | ...::var(...) [Ok] | main.rs:62:19:62:65 | ... .unwrap_or_default() | provenance | MaD:4 |
| main.rs:62:19:62:65 | ... .unwrap_or_default() | main.rs:62:9:62:15 | tainted | provenance | |
models
| 1 | Sink: std::io::stdio::_eprint; Argument[0]; log-injection |
| 2 | Sink: std::io::stdio::_print; Argument[0]; log-injection |
| 3 | Source: std::env::var; ReturnValue.Field[core::result::Result::Ok(0)]; environment |
| 4 | Summary: <core::result::Result>::unwrap_or_default; Argument[self].Field[core::result::Result::Ok(0)]; ReturnValue; value |
nodes
| main.rs:47:9:47:15 | tainted | semmle.label | tainted |
| main.rs:47:19:47:45 | ...::var(...) | semmle.label | ...::var(...) |
| main.rs:47:19:47:45 | ...::var(...) [Ok] | semmle.label | ...::var(...) [Ok] |
| main.rs:47:19:47:65 | ... .unwrap_or_default() | semmle.label | ... .unwrap_or_default() |
| main.rs:49:5:49:27 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:50:5:50:26 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:51:5:51:25 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:52:5:52:24 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:62:9:62:15 | tainted | semmle.label | tainted |
| main.rs:62:19:62:45 | ...::var(...) | semmle.label | ...::var(...) |
| main.rs:62:19:62:45 | ...::var(...) [Ok] | semmle.label | ...::var(...) [Ok] |
| main.rs:62:19:62:65 | ... .unwrap_or_default() | semmle.label | ... .unwrap_or_default() |
| main.rs:64:5:64:27 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:65:5:65:26 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:66:5:66:25 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:67:5:67:24 | FormatArgsExpr | semmle.label | FormatArgsExpr |
subpaths
Original file line number Diff line number Diff line change
@@ -1,19 +1,51 @@
models
| 1 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
| 1 | Summary: <_ as core::fmt::Write>::write_fmt; Argument[0]; Argument[self].Reference; taint |
| 2 | Summary: <_ as core::fmt::Write>::write_str; Argument[0].Reference; Argument[self].Reference; taint |
| 3 | Summary: <alloc::string::String>::as_str; Argument[self].Reference; ReturnValue.Reference; taint |
| 4 | Summary: alloc::fmt::format; Argument[0]; ReturnValue; taint |
| 5 | Summary: core::fmt::write; Argument[1]; Argument[0].Reference; taint |
edges
| main.rs:12:9:12:9 | a | main.rs:13:10:13:25 | FormatArgsExpr | provenance | |
| main.rs:12:13:12:21 | source(...) | main.rs:12:9:12:9 | a | provenance | |
| main.rs:13:10:13:25 | ...::format(...) | main.rs:13:10:13:25 | MacroExpr | provenance | |
| main.rs:13:10:13:25 | FormatArgsExpr | main.rs:13:10:13:25 | ...::format(...) | provenance | MaD:1 |
| main.rs:13:10:13:25 | FormatArgsExpr | main.rs:13:10:13:25 | ...::format(...) | provenance | MaD:4 |
| main.rs:15:9:15:9 | b | main.rs:16:10:16:23 | FormatArgsExpr | provenance | |
| main.rs:15:13:15:21 | source(...) | main.rs:15:9:15:9 | b | provenance | |
| main.rs:16:10:16:23 | ...::format(...) | main.rs:16:10:16:23 | MacroExpr | provenance | |
| main.rs:16:10:16:23 | FormatArgsExpr | main.rs:16:10:16:23 | ...::format(...) | provenance | MaD:1 |
| main.rs:16:10:16:23 | FormatArgsExpr | main.rs:16:10:16:23 | ...::format(...) | provenance | MaD:4 |
| main.rs:18:9:18:9 | c | main.rs:19:13:19:36 | FormatArgsExpr | provenance | |
| main.rs:18:13:18:21 | source(...) | main.rs:18:9:18:9 | c | provenance | |
| main.rs:19:9:19:9 | s | main.rs:20:10:20:10 | s | provenance | |
| main.rs:19:13:19:36 | ...::format(...) | main.rs:19:9:19:9 | s | provenance | |
| main.rs:19:13:19:36 | FormatArgsExpr | main.rs:19:13:19:36 | ...::format(...) | provenance | MaD:1 |
| main.rs:19:13:19:36 | FormatArgsExpr | main.rs:19:13:19:36 | ...::format(...) | provenance | MaD:4 |
| main.rs:26:9:26:9 | a | main.rs:27:9:27:9 | b | provenance | |
| main.rs:26:13:26:21 | source(...) | main.rs:26:9:26:9 | a | provenance | |
| main.rs:27:9:27:9 | b | main.rs:28:30:28:30 | b | provenance | |
| main.rs:28:9:28:9 | c | main.rs:29:10:29:10 | c | provenance | |
| main.rs:28:13:28:31 | ...::format(...) | main.rs:28:9:28:9 | c | provenance | |
| main.rs:28:30:28:30 | b | main.rs:28:13:28:31 | ...::format(...) | provenance | MaD:4 |
| main.rs:32:9:32:9 | d | main.rs:33:28:33:28 | d | provenance | |
| main.rs:32:13:32:21 | source(...) | main.rs:32:9:32:9 | d | provenance | |
| main.rs:33:13:33:16 | [post] buf1 | main.rs:34:10:34:13 | buf1 | provenance | |
| main.rs:33:28:33:28 | d | main.rs:33:28:33:37 | d.as_str() [&ref] | provenance | MaD:3 |
| main.rs:33:28:33:37 | d.as_str() [&ref] | main.rs:33:13:33:16 | [post] buf1 | provenance | MaD:2 |
| main.rs:37:9:37:9 | e | main.rs:38:28:38:46 | MacroExpr | provenance | |
| main.rs:37:13:37:21 | source(...) | main.rs:37:9:37:9 | e | provenance | |
| main.rs:38:13:38:16 | [post] buf2 | main.rs:39:10:39:13 | buf2 | provenance | |
| main.rs:38:28:38:46 | MacroExpr | main.rs:38:13:38:16 | [post] buf2 | provenance | MaD:1 |
| main.rs:42:9:42:9 | f | main.rs:43:40:43:58 | MacroExpr | provenance | |
| main.rs:42:13:42:21 | source(...) | main.rs:42:9:42:9 | f | provenance | |
| main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | main.rs:43:34:43:37 | [post] buf3 | provenance | |
| main.rs:43:34:43:37 | [post] buf3 | main.rs:44:10:44:13 | buf3 | provenance | |
| main.rs:43:40:43:58 | MacroExpr | main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | provenance | MaD:5 |
| main.rs:48:9:48:9 | g | main.rs:49:13:49:33 | FormatArgsExpr | provenance | |
| main.rs:48:13:48:21 | source(...) | main.rs:48:9:48:9 | g | provenance | |
| main.rs:49:13:49:33 | FormatArgsExpr | main.rs:49:20:49:23 | [post] buf4 | provenance | MaD:1 |
| main.rs:49:20:49:23 | [post] buf4 | main.rs:50:10:50:13 | buf4 | provenance | |
| main.rs:53:9:53:9 | h | main.rs:54:13:54:33 | FormatArgsExpr | provenance | |
| main.rs:53:13:53:21 | source(...) | main.rs:53:9:53:9 | h | provenance | |
| main.rs:54:13:54:33 | FormatArgsExpr | main.rs:54:22:54:25 | [post] buf5 | provenance | MaD:1 |
| main.rs:54:22:54:25 | [post] buf5 | main.rs:55:10:55:13 | buf5 | provenance | |
nodes
| main.rs:12:9:12:9 | a | semmle.label | a |
| main.rs:12:13:12:21 | source(...) | semmle.label | source(...) |
Expand All @@ -31,9 +63,49 @@ nodes
| main.rs:19:13:19:36 | ...::format(...) | semmle.label | ...::format(...) |
| main.rs:19:13:19:36 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:20:10:20:10 | s | semmle.label | s |
| main.rs:26:9:26:9 | a | semmle.label | a |
| main.rs:26:13:26:21 | source(...) | semmle.label | source(...) |
| main.rs:27:9:27:9 | b | semmle.label | b |
| main.rs:28:9:28:9 | c | semmle.label | c |
| main.rs:28:13:28:31 | ...::format(...) | semmle.label | ...::format(...) |
| main.rs:28:30:28:30 | b | semmle.label | b |
| main.rs:29:10:29:10 | c | semmle.label | c |
| main.rs:32:9:32:9 | d | semmle.label | d |
| main.rs:32:13:32:21 | source(...) | semmle.label | source(...) |
| main.rs:33:13:33:16 | [post] buf1 | semmle.label | [post] buf1 |
| main.rs:33:28:33:28 | d | semmle.label | d |
| main.rs:33:28:33:37 | d.as_str() [&ref] | semmle.label | d.as_str() [&ref] |
| main.rs:34:10:34:13 | buf1 | semmle.label | buf1 |
| main.rs:37:9:37:9 | e | semmle.label | e |
| main.rs:37:13:37:21 | source(...) | semmle.label | source(...) |
| main.rs:38:13:38:16 | [post] buf2 | semmle.label | [post] buf2 |
| main.rs:38:28:38:46 | MacroExpr | semmle.label | MacroExpr |
| main.rs:39:10:39:13 | buf2 | semmle.label | buf2 |
| main.rs:42:9:42:9 | f | semmle.label | f |
| main.rs:42:13:42:21 | source(...) | semmle.label | source(...) |
| main.rs:43:29:43:37 | [post] &mut buf3 [&ref] | semmle.label | [post] &mut buf3 [&ref] |
| main.rs:43:34:43:37 | [post] buf3 | semmle.label | [post] buf3 |
| main.rs:43:40:43:58 | MacroExpr | semmle.label | MacroExpr |
| main.rs:44:10:44:13 | buf3 | semmle.label | buf3 |
| main.rs:48:9:48:9 | g | semmle.label | g |
| main.rs:48:13:48:21 | source(...) | semmle.label | source(...) |
| main.rs:49:13:49:33 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:49:20:49:23 | [post] buf4 | semmle.label | [post] buf4 |
| main.rs:50:10:50:13 | buf4 | semmle.label | buf4 |
| main.rs:53:9:53:9 | h | semmle.label | h |
| main.rs:53:13:53:21 | source(...) | semmle.label | source(...) |
| main.rs:54:13:54:33 | FormatArgsExpr | semmle.label | FormatArgsExpr |
| main.rs:54:22:54:25 | [post] buf5 | semmle.label | [post] buf5 |
| main.rs:55:10:55:13 | buf5 | semmle.label | buf5 |
subpaths
testFailures
#select
| main.rs:13:10:13:25 | MacroExpr | main.rs:12:13:12:21 | source(...) | main.rs:13:10:13:25 | MacroExpr | $@ | main.rs:12:13:12:21 | source(...) | source(...) |
| main.rs:16:10:16:23 | MacroExpr | main.rs:15:13:15:21 | source(...) | main.rs:16:10:16:23 | MacroExpr | $@ | main.rs:15:13:15:21 | source(...) | source(...) |
| main.rs:20:10:20:10 | s | main.rs:18:13:18:21 | source(...) | main.rs:20:10:20:10 | s | $@ | main.rs:18:13:18:21 | source(...) | source(...) |
| main.rs:29:10:29:10 | c | main.rs:26:13:26:21 | source(...) | main.rs:29:10:29:10 | c | $@ | main.rs:26:13:26:21 | source(...) | source(...) |
| main.rs:34:10:34:13 | buf1 | main.rs:32:13:32:21 | source(...) | main.rs:34:10:34:13 | buf1 | $@ | main.rs:32:13:32:21 | source(...) | source(...) |
| main.rs:39:10:39:13 | buf2 | main.rs:37:13:37:21 | source(...) | main.rs:39:10:39:13 | buf2 | $@ | main.rs:37:13:37:21 | source(...) | source(...) |
| main.rs:44:10:44:13 | buf3 | main.rs:42:13:42:21 | source(...) | main.rs:44:10:44:13 | buf3 | $@ | main.rs:42:13:42:21 | source(...) | source(...) |
| main.rs:50:10:50:13 | buf4 | main.rs:48:13:48:21 | source(...) | main.rs:50:10:50:13 | buf4 | $@ | main.rs:48:13:48:21 | source(...) | source(...) |
| main.rs:55:10:55:13 | buf5 | main.rs:53:13:53:21 | source(...) | main.rs:55:10:55:13 | buf5 | $@ | main.rs:53:13:53:21 | source(...) | source(...) |
47 changes: 31 additions & 16 deletions rust/ql/test/library-tests/format-macros-legacy/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn source(i: i64) -> String {
i.to_string()
}

fn sink(s: String) {}
fn sink(_s: String) {}

pub fn format_flow() {
let a = source(1);
Expand All @@ -21,23 +21,38 @@ pub fn format_flow() {
}

pub fn exercises_reconstruction() {
// No flow assertions here: these exercise reconstruction of the rest of the
// family, including the writer-argument handling of `write!`/`writeln!`.
//
// Flow into a writer buffer is not recovered on <1.94 (the
// `buf.write_fmt(..)` desugaring is absent), but this matches native
// behavior on >=1.94, where the `Write::write_fmt` content-to-self taint
// model is also missing. So it is a pre-existing model gap, not a
// regression from the reconstruction.
let d = source(4);
let _ = format_args!("{}", d);
// these exercise reconstruction of the rest of the family, including the
// writer-argument handling of `write!`/`writeln!`.
let a = source(4);
let b = format_args!("{}", a);
let c = std::fmt::format(b);
sink(c); // $ hasTaintFlow=4
Comment thread
geoffw0 marked this conversation as resolved.

let mut buf1 = String::new();
let d = source(5);
let _ = buf1.write_str(d.as_str());
sink(buf1); // $ hasTaintFlow=5

let mut buf2 = String::new();
let e = source(6);
let _ = buf2.write_fmt(format_args!("{e}"));
sink(buf2); // $ hasTaintFlow=6

let mut buf3 = String::new();
let f = source(7);
let _ = std::fmt::write(&mut buf3, format_args!("{f}"));
sink(buf3); // $ hasTaintFlow=7

use std::fmt::Write;
let mut buf = String::new();
let e = source(5);
let _ = write!(buf, "{}", e);
let _ = writeln!(buf, "{e}");
sink(buf);
let mut buf4 = String::new();
let g = source(8);
let _ = write!(buf4, "{}", g);
sink(buf4); // $ hasTaintFlow=8

let mut buf5 = String::new();
let h = source(9);
let _ = writeln!(buf5, "{h}");
sink(buf5); // $ hasTaintFlow=9
}

// The log-injection sinks (`println!`/`eprintln!`/`panic!`) are reconstructed
Expand Down
Loading
Loading