From 11055760a442deaf583d7be8a1bf512be0c23821 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Fri, 7 Feb 2025 13:53:17 +0100 Subject: [PATCH] Rust: Handle writes to references and add encoding of reference content --- .../rust/dataflow/internal/DataFlowImpl.qll | 15 +++++++++ .../dataflow/internal/FlowSummaryImpl.qll | 4 +++ .../modelgenerator/internal/CaptureModels.qll | 5 ++- .../dataflow/global/inline-flow.expected | 17 ++++++++++ .../library-tests/dataflow/global/main.rs | 2 +- .../dataflow/pointers/inline-flow.expected | 8 +++++ .../library-tests/dataflow/pointers/main.rs | 2 +- .../test/utils-tests/modelgenerator/option.rs | 32 +++++++++++-------- .../utils-tests/modelgenerator/summaries.rs | 3 +- 9 files changed, 69 insertions(+), 19 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 6c539e9642df..70736818cad6 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -1211,6 +1211,17 @@ module RustDataFlow implements InputSig { ) } + pragma[nomagic] + private predicate referenceAssignment(Node node1, Node node2, ReferenceContent c) { + exists(AssignmentExprCfgNode assignment, PrefixExprCfgNode deref | + assignment.getLhs() = deref and + deref.getOperatorName() = "*" and + node1.asExpr() = assignment.getRhs() and + node2.asExpr() = deref.getExpr() and + exists(c) + ) + } + pragma[nomagic] private predicate storeContentStep(Node node1, Content c, Node node2) { exists(CallExprCfgNode call, int pos | @@ -1242,6 +1253,8 @@ module RustDataFlow implements InputSig { or fieldAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c) or + referenceAssignment(node1, node2.(PostUpdateNode).getPreUpdateNode(), c) + or exists(AssignmentExprCfgNode assignment, IndexExprCfgNode index | c instanceof ElementContent and assignment.getLhs() = index and @@ -1285,6 +1298,8 @@ module RustDataFlow implements InputSig { predicate clearsContent(Node n, ContentSet cs) { fieldAssignment(_, n, cs.(SingletonContentSet).getContent()) or + referenceAssignment(_, n, cs.(SingletonContentSet).getContent()) + or FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(Node::FlowSummaryNode).getSummaryNode(), cs) or diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll index b716e81b129e..8ef1473b62e9 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/FlowSummaryImpl.qll @@ -89,6 +89,10 @@ module Input implements InputSig { arg = v.getExtendedCanonicalPath() + "(" + v.getPosition() + ")" ) or + result = "Reference" and + c = TReferenceContent() and + arg = "" + or result = "Element" and c = TElementContent() and arg = "" diff --git a/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll index 21269cacec25..a04367055f66 100644 --- a/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/rust/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -81,9 +81,8 @@ module ModelGeneratorInput implements ModelGeneratorInputSig MyOption { } } - // summary=repo::test;::as_ref;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // NOTE: The returned value inside the variant should be inside a `Reference`, requires handling + // `ref` in patterns. + // summary=repo::test;::as_ref;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated pub fn as_ref(&self) -> MyOption<&T> { match *self { MySome(ref x) => MySome(x), @@ -52,7 +54,7 @@ impl MyOption { } } - // summary=repo::test;::as_mut;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::as_mut;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated pub fn as_mut(&mut self) -> MyOption<&mut T> { match *self { MySome(ref mut x) => MySome(x), @@ -285,8 +287,11 @@ impl MyOption { } } - // MISSING: summary=repo::test;::insert;Argument[0];ReturnValue;value;dfc-generated - // SPURIOUS-summary=repo::test;::insert;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated + // summary=repo::test;::insert;Argument[0];Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // The below should be `ReturnValue.Reference` and not just `ReturnValue`. + // SPURIOUS-summary=repo::test;::insert;Argument[0];ReturnValue;value;dfc-generated + // The content of `self` is overwritten so it does not flow to the return value. + // SPURIOUS-summary=repo::test;::insert;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated pub fn insert(&mut self, value: T) -> &mut T { *self = MySome(value); @@ -294,13 +299,14 @@ impl MyOption { unsafe { self.as_mut().unwrap_unchecked() } } - // summary=repo::test;::get_or_insert;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated - // MISSING: repo::test;::get_or_insert;Argument[0];ReturnValue;value;dfc-generated + // summary=repo::test;::get_or_insert;Argument[0];Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::get_or_insert;Argument[0];ReturnValue;value;dfc-generated + // summary=repo::test;::get_or_insert;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated pub fn get_or_insert(&mut self, value: T) -> &mut T { self.get_or_insert_with(|| value) } - // summary=repo::test;::get_or_insert_default;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated + // summary=repo::test;::get_or_insert_default;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated pub fn get_or_insert_default(&mut self) -> &mut T where T: Default, @@ -308,7 +314,7 @@ impl MyOption { self.get_or_insert_with(T::default) } - // summary=repo::test;::get_or_insert_with;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated + // summary=repo::test;::get_or_insert_with;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue;value;dfc-generated // MISSING: Mutating `self` parameter. pub fn get_or_insert_with(&mut self, f: F) -> &mut T where @@ -329,7 +335,7 @@ impl MyOption { mem::replace(self, MyNone) } - // summary=repo::test;::take_if;Argument[self].Variant[crate::option::MyOption::MySome(0)];Argument[0].Parameter[0];value;dfc-generated + // summary=repo::test;::take_if;Argument[self].Reference.Variant[crate::option::MyOption::MySome(0)];Argument[0].Parameter[0];value;dfc-generated // MISSING: Uses `take` which doesn't have flow pub fn take_if

(&mut self, predicate: P) -> MyOption where @@ -378,7 +384,7 @@ impl MyOption<(T, U)> { } impl MyOption<&T> { - // summary=repo::test;::copied;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::copied;Argument[self].Variant[crate::option::MyOption::MySome(0)].Reference;ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated pub fn copied(self) -> MyOption where T: Copy, @@ -404,7 +410,7 @@ impl MyOption<&T> { } impl MyOption<&mut T> { - // summary=repo::test;::copied;Argument[self].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::copied;Argument[self].Variant[crate::option::MyOption::MySome(0)].Reference;ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated pub fn copied(self) -> MyOption where T: Copy, @@ -474,14 +480,14 @@ impl From for MyOption { } impl<'a, T> From<&'a MyOption> for MyOption<&'a T> { - // summary=repo::test;::from;Argument[0].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::from;Argument[0].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated fn from(o: &'a MyOption) -> MyOption<&'a T> { o.as_ref() } } impl<'a, T> From<&'a mut MyOption> for MyOption<&'a mut T> { - // summary=repo::test;::from;Argument[0].Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated + // summary=repo::test;::from;Argument[0].Reference.Variant[crate::option::MyOption::MySome(0)];ReturnValue.Variant[crate::option::MyOption::MySome(0)];value;dfc-generated fn from(o: &'a mut MyOption) -> MyOption<&'a mut T> { o.as_mut() } diff --git a/rust/ql/test/utils-tests/modelgenerator/summaries.rs b/rust/ql/test/utils-tests/modelgenerator/summaries.rs index db1158640c25..3e6b0995971f 100644 --- a/rust/ql/test/utils-tests/modelgenerator/summaries.rs +++ b/rust/ql/test/utils-tests/modelgenerator/summaries.rs @@ -79,11 +79,12 @@ pub fn apply(n: i64, f: F) -> i64 where F : FnOnce(i64) -> i64 { // Flow out of mutated arguments +// summary=repo::test;crate::summaries::set_int;Argument[1];Argument[0].Reference;value;dfc-generated pub fn set_int(n: &mut i64, c: i64) { *n = c; } -// summary=repo::test;crate::summaries::read_int;Argument[0];ReturnValue;value;dfc-generated +// summary=repo::test;crate::summaries::read_int;Argument[0].Reference;ReturnValue;value;dfc-generated pub fn read_int(n: &mut i64) -> i64 { *n }