Skip to content
Merged
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
4 changes: 4 additions & 0 deletions rust/ql/lib/change-notes/2025-12-11-read-as-taint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Reading content of a value now carries taint if the value itself is tainted. For instance, if `s` is tainted then `s.field` is also tainted. This generally improves taint flow.
33 changes: 17 additions & 16 deletions rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ private import codeql.rust.dataflow.FlowSummary
private import codeql.rust.dataflow.Ssa
private import Content

predicate encodeContentTupleField(TupleFieldContent c, string arg) {
exists(Addressable a, int pos, string prefix |
arg = prefix + "(" + pos + ")" and prefix = a.getCanonicalPath()
|
c.isStructField(a, pos) or c.isVariantField(a, pos)
)
}

predicate encodeContentStructField(StructFieldContent c, string arg) {
exists(Addressable a, string field | arg = a.getCanonicalPath() + "::" + field |
c.isStructField(a, field) or c.isVariantField(a, field)
)
}

module Input implements InputSig<Location, RustDataFlow> {
private import codeql.rust.frameworks.stdlib.Stdlib

Expand Down Expand Up @@ -58,24 +72,11 @@ module Input implements InputSig<Location, RustDataFlow> {
exists(Content c | cs = TSingletonContentSet(c) |
result = "Field" and
(
exists(Addressable a, int pos, string prefix |
arg = prefix + "(" + pos + ")" and prefix = a.getCanonicalPath()
|
c.(TupleFieldContent).isStructField(a, pos)
or
c.(TupleFieldContent).isVariantField(a, pos)
)
encodeContentTupleField(c, arg)
or
exists(Addressable a, string field | arg = a.getCanonicalPath() + "::" + field |
c.(StructFieldContent).isStructField(a, field)
or
c.(StructFieldContent).isVariantField(a, field)
)
encodeContentStructField(c, arg)
or
exists(int pos |
c = TTuplePositionContent(pos) and
arg = pos.toString()
)
exists(int pos | c = TTuplePositionContent(pos) and arg = pos.toString())
)
or
result = "Reference" and
Expand Down
37 changes: 32 additions & 5 deletions rust/ql/lib/codeql/rust/dataflow/internal/TaintTrackingImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ private import Node as Node
private import Content
private import FlowSummaryImpl as FlowSummaryImpl
private import codeql.rust.internal.CachedStages
private import codeql.rust.internal.TypeInference as TypeInference
private import codeql.rust.internal.Type as Type
private import codeql.rust.frameworks.stdlib.Builtins as Builtins

/**
* Holds if the field `field` should, by default, be excluded from taint steps
* from the containing type to reads of the field. The models-as-data syntax
* used to denote the field is the same as for `Field[]` access path elements.
*/
extensible predicate excludeFieldTaintStep(string field);

/**
* Holds if the content `c` corresponds to a field that has explicitly been
* excluded as a taint step.
*/
private predicate excludedTaintStepContent(Content c) {
exists(string arg | excludeFieldTaintStep(arg) |
FlowSummaryImpl::encodeContentStructField(c, arg) or
FlowSummaryImpl::encodeContentTupleField(c, arg)
)
}

module RustTaintTracking implements InputSig<Location, RustDataFlow> {
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
Expand All @@ -28,11 +49,17 @@ module RustTaintTracking implements InputSig<Location, RustDataFlow> {
succ.asExpr() = index
)
or
// Although data flow through collections and references is modeled using
// stores/reads, we also allow taint to flow out of a tainted collection
// or reference.
// This is needed in order to support taint-tracking configurations where
// the source is a collection or reference.
// Read steps give rise to taint steps. This has the effect that if `foo`
// is tainted and an operation reads from `foo` (e.g., `foo.bar`) then
// taint is propagated.
exists(Content c |
RustDataFlow::readContentStep(pred, c, succ) and
not excludedTaintStepContent(c)
)
or
// In addition to the above, for element and reference content we let
// _all_ read steps (including those from flow summaries and those that
// result in small primitive types) give rise to taint steps.
Copy link
Contributor

Choose a reason for hiding this comment

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

You've lost some context from the original comment here, I think.

exists(SingletonContentSet cs | RustDataFlow::readStep(pred, cs, succ) |
cs.getContent() instanceof ElementContent
or
Expand Down
7 changes: 1 addition & 6 deletions rust/ql/lib/codeql/rust/frameworks/actix-web.model.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,4 @@ extensions:
pack: codeql/rust-all
extensible: summaryModel
data:
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue.Field[0]", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue.Field[1]", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue.Field[2]", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue.Field[3]", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue.Field[4]", "taint", "manual"]
- ["<actix_web::types::path::Path>::into_inner", "Argument[self]", "ReturnValue", "taint", "manual"]
6 changes: 6 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 @@ -141,3 +141,9 @@ extensions:
- ["core::ptr::write_bytes", "Argument[0]", "pointer-access", "manual"]
- ["core::ptr::write_unaligned", "Argument[0]", "pointer-access", "manual"]
- ["core::ptr::write_volatile", "Argument[0]", "pointer-access", "manual"]
- addsTo:
pack: codeql/rust-all
extensible: excludeFieldTaintStep
data:
- ["core::ops::range::RangeInclusive::start"]
- ["core::ops::range::RangeInclusive::end"]
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,13 @@ edges
| main.rs:306:30:306:56 | ...::take_second(...) [MyInt] | main.rs:306:9:306:26 | MyInt {...} [MyInt] | provenance | |
| main.rs:306:55:306:55 | b [MyInt] | main.rs:293:26:293:37 | ...: MyInt [MyInt] | provenance | |
| main.rs:306:55:306:55 | b [MyInt] | main.rs:306:30:306:56 | ...::take_second(...) [MyInt] | provenance | |
| main.rs:315:32:319:1 | { ... } | main.rs:322:13:322:26 | async_source(...) | provenance | |
| main.rs:315:32:319:1 | { ... } | main.rs:334:41:334:54 | async_source(...) | provenance | |
| main.rs:316:9:316:9 | a | main.rs:315:32:319:1 | { ... } | provenance | |
| main.rs:316:9:316:9 | a | main.rs:317:10:317:10 | a | provenance | |
| main.rs:316:13:316:21 | source(...) | main.rs:316:9:316:9 | a | provenance | |
| main.rs:322:9:322:9 | a | main.rs:323:10:323:10 | a | provenance | |
| main.rs:322:13:322:26 | async_source(...) | main.rs:322:9:322:9 | a | provenance | |
| main.rs:326:13:326:13 | c | main.rs:327:14:327:14 | c | provenance | |
| main.rs:326:17:326:25 | source(...) | main.rs:326:13:326:13 | c | provenance | |
| main.rs:334:9:334:9 | a | main.rs:335:10:335:10 | a | provenance | |
Expand Down Expand Up @@ -419,6 +422,9 @@ nodes
| main.rs:316:9:316:9 | a | semmle.label | a |
| main.rs:316:13:316:21 | source(...) | semmle.label | source(...) |
| main.rs:317:10:317:10 | a | semmle.label | a |
| main.rs:322:9:322:9 | a | semmle.label | a |
| main.rs:322:13:322:26 | async_source(...) | semmle.label | async_source(...) |
| main.rs:323:10:323:10 | a | semmle.label | a |
| main.rs:326:13:326:13 | c | semmle.label | c |
| main.rs:326:17:326:25 | source(...) | semmle.label | source(...) |
| main.rs:327:14:327:14 | c | semmle.label | c |
Expand Down Expand Up @@ -503,6 +509,7 @@ testFailures
| main.rs:302:10:302:10 | c | main.rs:299:28:299:36 | source(...) | main.rs:302:10:302:10 | c | $@ | main.rs:299:28:299:36 | source(...) | source(...) |
| main.rs:307:10:307:10 | c | main.rs:305:28:305:37 | source(...) | main.rs:307:10:307:10 | c | $@ | main.rs:305:28:305:37 | source(...) | source(...) |
| main.rs:317:10:317:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:317:10:317:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |
| main.rs:323:10:323:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:323:10:323:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |
| main.rs:327:14:327:14 | c | main.rs:326:17:326:25 | source(...) | main.rs:327:14:327:14 | c | $@ | main.rs:326:17:326:25 | source(...) | source(...) |
| main.rs:335:10:335:10 | a | main.rs:316:13:316:21 | source(...) | main.rs:335:10:335:10 | a | $@ | main.rs:316:13:316:21 | source(...) | source(...) |
| main.rs:384:14:384:15 | n1 | main.rs:359:13:359:21 | source(...) | main.rs:384:14:384:15 | n1 | $@ | main.rs:359:13:359:21 | source(...) | source(...) |
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/test/library-tests/dataflow/global/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ async fn async_source() -> i64 {

async fn test_async_await_async_part() {
let a = async_source().await;
sink(a); // $ MISSING: hasValueFlow=1
sink(a); // $ hasTaintFlow=1 MISSING: hasValueFlow=1

let b = async {
let c = source(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ edges
| test.rs:42:20:42:21 | t1 [element] | test.rs:42:13:42:15 | row | provenance | |
| test.rs:48:22:48:30 | query_map | test.rs:50:14:50:24 | ...: i64 | provenance | Src:MaD:3 |
| test.rs:50:14:50:24 | ...: i64 | test.rs:51:22:51:27 | values | provenance | |
| test.rs:55:22:55:30 | query_map | test.rs:57:14:57:39 | ...: ... | provenance | Src:MaD:3 |
| test.rs:57:14:57:39 | ...: ... | test.rs:58:22:58:29 | values.0 | provenance | |
| test.rs:57:14:57:39 | ...: ... | test.rs:59:22:59:29 | values.1 | provenance | |
| test.rs:57:14:57:39 | ...: ... | test.rs:60:22:60:29 | values.2 | provenance | |
| test.rs:64:13:64:17 | total | test.rs:68:14:68:18 | total | provenance | |
| test.rs:64:21:67:10 | conn.query_fold(...) [Ok] | test.rs:64:21:67:11 | TryExpr | provenance | |
| test.rs:64:21:67:11 | TryExpr | test.rs:64:13:64:17 | total | provenance | |
Expand All @@ -61,6 +65,13 @@ edges
| test.rs:66:19:66:21 | row | test.rs:66:13:66:21 | ... + ... | provenance | MaD:11 |
| test.rs:66:19:66:21 | row | test.rs:66:13:66:21 | ... + ... | provenance | MaD:12 |
| test.rs:66:19:66:21 | row | test.rs:66:13:66:21 | ... + ... | provenance | MaD:15 |
| test.rs:70:22:70:31 | query_fold | test.rs:70:83:70:105 | ...: ... | provenance | Src:MaD:2 |
| test.rs:70:83:70:105 | ...: ... | test.rs:71:17:71:18 | id | provenance | |
| test.rs:70:83:70:105 | ...: ... | test.rs:72:17:72:20 | name | provenance | |
| test.rs:70:83:70:105 | ...: ... | test.rs:73:17:73:19 | age | provenance | |
| test.rs:71:17:71:18 | id | test.rs:74:18:74:19 | id | provenance | |
| test.rs:72:17:72:20 | name | test.rs:75:18:75:21 | name | provenance | |
| test.rs:73:17:73:19 | age | test.rs:76:18:76:20 | age | provenance | |
| test.rs:105:13:105:14 | v1 | test.rs:106:14:106:15 | v1 | provenance | |
| test.rs:105:24:105:33 | row.get(...) [Some] | test.rs:105:24:105:42 | ... .unwrap() | provenance | MaD:16 |
| test.rs:105:24:105:42 | ... .unwrap() | test.rs:105:13:105:14 | v1 | provenance | |
Expand All @@ -81,6 +92,10 @@ edges
| test.rs:114:28:114:35 | take_opt | test.rs:114:24:114:38 | row.take_opt(...) [Some, Ok] | provenance | Src:MaD:10 |
| test.rs:135:22:135:30 | query_map | test.rs:137:14:137:24 | ...: i64 | provenance | Src:MaD:5 |
| test.rs:137:14:137:24 | ...: i64 | test.rs:138:22:138:27 | values | provenance | |
| test.rs:142:22:142:30 | query_map | test.rs:144:14:144:39 | ...: ... | provenance | Src:MaD:5 |
| test.rs:144:14:144:39 | ...: ... | test.rs:145:22:145:29 | values.0 | provenance | |
| test.rs:144:14:144:39 | ...: ... | test.rs:146:22:146:29 | values.1 | provenance | |
| test.rs:144:14:144:39 | ...: ... | test.rs:147:22:147:29 | values.2 | provenance | |
| test.rs:151:13:151:17 | total | test.rs:155:14:155:18 | total | provenance | |
| test.rs:151:21:154:10 | conn.query_fold(...) [future, Ok] | test.rs:151:21:154:16 | await ... [Ok] | provenance | |
| test.rs:151:21:154:16 | await ... [Ok] | test.rs:151:21:154:17 | TryExpr | provenance | |
Expand All @@ -93,6 +108,13 @@ edges
| test.rs:153:19:153:21 | row | test.rs:153:13:153:21 | ... + ... | provenance | MaD:11 |
| test.rs:153:19:153:21 | row | test.rs:153:13:153:21 | ... + ... | provenance | MaD:12 |
| test.rs:153:19:153:21 | row | test.rs:153:13:153:21 | ... + ... | provenance | MaD:15 |
| test.rs:157:22:157:31 | query_fold | test.rs:157:83:157:105 | ...: ... | provenance | Src:MaD:4 |
| test.rs:157:83:157:105 | ...: ... | test.rs:158:17:158:18 | id | provenance | |
| test.rs:157:83:157:105 | ...: ... | test.rs:159:17:159:20 | name | provenance | |
| test.rs:157:83:157:105 | ...: ... | test.rs:160:17:160:19 | age | provenance | |
| test.rs:158:17:158:18 | id | test.rs:161:18:161:19 | id | provenance | |
| test.rs:159:17:159:20 | name | test.rs:162:18:162:21 | name | provenance | |
| test.rs:160:17:160:19 | age | test.rs:163:18:163:20 | age | provenance | |
nodes
| test.rs:18:13:18:14 | v1 | semmle.label | v1 |
| test.rs:18:24:18:33 | row.get(...) [Some] | semmle.label | row.get(...) [Some] |
Expand Down Expand Up @@ -135,6 +157,11 @@ nodes
| test.rs:48:22:48:30 | query_map | semmle.label | query_map |
| test.rs:50:14:50:24 | ...: i64 | semmle.label | ...: i64 |
| test.rs:51:22:51:27 | values | semmle.label | values |
| test.rs:55:22:55:30 | query_map | semmle.label | query_map |
| test.rs:57:14:57:39 | ...: ... | semmle.label | ...: ... |
| test.rs:58:22:58:29 | values.0 | semmle.label | values.0 |
| test.rs:59:22:59:29 | values.1 | semmle.label | values.1 |
| test.rs:60:22:60:29 | values.2 | semmle.label | values.2 |
| test.rs:64:13:64:17 | total | semmle.label | total |
| test.rs:64:21:67:10 | conn.query_fold(...) [Ok] | semmle.label | conn.query_fold(...) [Ok] |
| test.rs:64:21:67:11 | TryExpr | semmle.label | TryExpr |
Expand All @@ -145,6 +172,14 @@ nodes
| test.rs:66:13:66:21 | ... + ... | semmle.label | ... + ... |
| test.rs:66:19:66:21 | row | semmle.label | row |
| test.rs:68:14:68:18 | total | semmle.label | total |
| test.rs:70:22:70:31 | query_fold | semmle.label | query_fold |
| test.rs:70:83:70:105 | ...: ... | semmle.label | ...: ... |
| test.rs:71:17:71:18 | id | semmle.label | id |
| test.rs:72:17:72:20 | name | semmle.label | name |
| test.rs:73:17:73:19 | age | semmle.label | age |
| test.rs:74:18:74:19 | id | semmle.label | id |
| test.rs:75:18:75:21 | name | semmle.label | name |
| test.rs:76:18:76:20 | age | semmle.label | age |
| test.rs:105:13:105:14 | v1 | semmle.label | v1 |
| test.rs:105:24:105:33 | row.get(...) [Some] | semmle.label | row.get(...) [Some] |
| test.rs:105:24:105:42 | ... .unwrap() | semmle.label | ... .unwrap() |
Expand All @@ -170,6 +205,11 @@ nodes
| test.rs:135:22:135:30 | query_map | semmle.label | query_map |
| test.rs:137:14:137:24 | ...: i64 | semmle.label | ...: i64 |
| test.rs:138:22:138:27 | values | semmle.label | values |
| test.rs:142:22:142:30 | query_map | semmle.label | query_map |
| test.rs:144:14:144:39 | ...: ... | semmle.label | ...: ... |
| test.rs:145:22:145:29 | values.0 | semmle.label | values.0 |
| test.rs:146:22:146:29 | values.1 | semmle.label | values.1 |
| test.rs:147:22:147:29 | values.2 | semmle.label | values.2 |
| test.rs:151:13:151:17 | total | semmle.label | total |
| test.rs:151:21:154:10 | conn.query_fold(...) [future, Ok] | semmle.label | conn.query_fold(...) [future, Ok] |
| test.rs:151:21:154:16 | await ... [Ok] | semmle.label | await ... [Ok] |
Expand All @@ -181,6 +221,14 @@ nodes
| test.rs:153:13:153:21 | ... + ... | semmle.label | ... + ... |
| test.rs:153:19:153:21 | row | semmle.label | row |
| test.rs:155:14:155:18 | total | semmle.label | total |
| test.rs:157:22:157:31 | query_fold | semmle.label | query_fold |
| test.rs:157:83:157:105 | ...: ... | semmle.label | ...: ... |
| test.rs:158:17:158:18 | id | semmle.label | id |
| test.rs:159:17:159:20 | name | semmle.label | name |
| test.rs:160:17:160:19 | age | semmle.label | age |
| test.rs:161:18:161:19 | id | semmle.label | id |
| test.rs:162:18:162:21 | name | semmle.label | name |
| test.rs:163:18:163:20 | age | semmle.label | age |
subpaths
testFailures
#select
Expand All @@ -192,12 +240,24 @@ testFailures
| test.rs:41:14:41:70 | ... .unwrap() | test.rs:41:42:41:44 | get | test.rs:41:14:41:70 | ... .unwrap() | $@ | test.rs:41:42:41:44 | get | get |
| test.rs:44:22:44:22 | v | test.rs:40:27:40:35 | exec_iter | test.rs:44:22:44:22 | v | $@ | test.rs:40:27:40:35 | exec_iter | exec_iter |
| test.rs:51:22:51:27 | values | test.rs:48:22:48:30 | query_map | test.rs:51:22:51:27 | values | $@ | test.rs:48:22:48:30 | query_map | query_map |
| test.rs:58:22:58:29 | values.0 | test.rs:55:22:55:30 | query_map | test.rs:58:22:58:29 | values.0 | $@ | test.rs:55:22:55:30 | query_map | query_map |
| test.rs:59:22:59:29 | values.1 | test.rs:55:22:55:30 | query_map | test.rs:59:22:59:29 | values.1 | $@ | test.rs:55:22:55:30 | query_map | query_map |
| test.rs:60:22:60:29 | values.2 | test.rs:55:22:55:30 | query_map | test.rs:60:22:60:29 | values.2 | $@ | test.rs:55:22:55:30 | query_map | query_map |
| test.rs:65:18:65:20 | row | test.rs:64:26:64:35 | query_fold | test.rs:65:18:65:20 | row | $@ | test.rs:64:26:64:35 | query_fold | query_fold |
| test.rs:68:14:68:18 | total | test.rs:64:26:64:35 | query_fold | test.rs:68:14:68:18 | total | $@ | test.rs:64:26:64:35 | query_fold | query_fold |
| test.rs:74:18:74:19 | id | test.rs:70:22:70:31 | query_fold | test.rs:74:18:74:19 | id | $@ | test.rs:70:22:70:31 | query_fold | query_fold |
| test.rs:75:18:75:21 | name | test.rs:70:22:70:31 | query_fold | test.rs:75:18:75:21 | name | $@ | test.rs:70:22:70:31 | query_fold | query_fold |
| test.rs:76:18:76:20 | age | test.rs:70:22:70:31 | query_fold | test.rs:76:18:76:20 | age | $@ | test.rs:70:22:70:31 | query_fold | query_fold |
| test.rs:106:14:106:15 | v1 | test.rs:105:28:105:30 | get | test.rs:106:14:106:15 | v1 | $@ | test.rs:105:28:105:30 | get | get |
| test.rs:109:14:109:15 | v2 | test.rs:108:28:108:34 | get_opt | test.rs:109:14:109:15 | v2 | $@ | test.rs:108:28:108:34 | get_opt | get_opt |
| test.rs:112:14:112:15 | v3 | test.rs:111:28:111:31 | take | test.rs:112:14:112:15 | v3 | $@ | test.rs:111:28:111:31 | take | take |
| test.rs:115:14:115:15 | v4 | test.rs:114:28:114:35 | take_opt | test.rs:115:14:115:15 | v4 | $@ | test.rs:114:28:114:35 | take_opt | take_opt |
| test.rs:138:22:138:27 | values | test.rs:135:22:135:30 | query_map | test.rs:138:22:138:27 | values | $@ | test.rs:135:22:135:30 | query_map | query_map |
| test.rs:145:22:145:29 | values.0 | test.rs:142:22:142:30 | query_map | test.rs:145:22:145:29 | values.0 | $@ | test.rs:142:22:142:30 | query_map | query_map |
| test.rs:146:22:146:29 | values.1 | test.rs:142:22:142:30 | query_map | test.rs:146:22:146:29 | values.1 | $@ | test.rs:142:22:142:30 | query_map | query_map |
| test.rs:147:22:147:29 | values.2 | test.rs:142:22:142:30 | query_map | test.rs:147:22:147:29 | values.2 | $@ | test.rs:142:22:142:30 | query_map | query_map |
| test.rs:152:18:152:20 | row | test.rs:151:26:151:35 | query_fold | test.rs:152:18:152:20 | row | $@ | test.rs:151:26:151:35 | query_fold | query_fold |
| test.rs:155:14:155:18 | total | test.rs:151:26:151:35 | query_fold | test.rs:155:14:155:18 | total | $@ | test.rs:151:26:151:35 | query_fold | query_fold |
| test.rs:161:18:161:19 | id | test.rs:157:22:157:31 | query_fold | test.rs:161:18:161:19 | id | $@ | test.rs:157:22:157:31 | query_fold | query_fold |
| test.rs:162:18:162:21 | name | test.rs:157:22:157:31 | query_fold | test.rs:162:18:162:21 | name | $@ | test.rs:157:22:157:31 | query_fold | query_fold |
| test.rs:163:18:163:20 | age | test.rs:157:22:157:31 | query_fold | test.rs:163:18:163:20 | age | $@ | test.rs:157:22:157:31 | query_fold | query_fold |
Loading