From 544af7f7ca92cfaa9ed9a8ffcddf7cbdc857a2be Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 12 May 2025 18:39:43 +0100 Subject: [PATCH 1/8] Rust: Add tests for sources involving futures-rustls and futures-io. --- .../dataflow/sources/options.yml | 3 + .../dataflow/sources/test_futures_io.rs | 159 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs diff --git a/rust/ql/test/library-tests/dataflow/sources/options.yml b/rust/ql/test/library-tests/dataflow/sources/options.yml index a9d9354529eb..a05a970f7b87 100644 --- a/rust/ql/test/library-tests/dataflow/sources/options.yml +++ b/rust/ql/test/library-tests/dataflow/sources/options.yml @@ -13,3 +13,6 @@ qltest_dependencies: - actix-web = { version = "4.10.2" } - axum = { version = "0.8.4" } - serde_json = { version = "1.0.140" } + - rustls = { version = "0.23.27" } + - futures-rustls = { version = "0.26.0" } + - async-std = { version = "1.13.1" } diff --git a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs new file mode 100644 index 000000000000..0b006546893c --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs @@ -0,0 +1,159 @@ +fn sink(_: T) { } + +// --- tests --- + +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::io; +use futures::io::AsyncRead; +use futures::io::AsyncReadExt; +use futures::io::AsyncBufRead; +use futures::io::AsyncBufReadExt; +use futures::StreamExt; +use futures_rustls::{TlsConnector}; +use async_std::sync::Arc; +use async_std::net::TcpStream; + +async fn test_futures_rustls_futures_io() -> io::Result<()> { + let url = "www.example.com:443"; + let tcp = TcpStream::connect(url).await?; // $ MISSING: Alert[rust/summary/taint-sources] + sink(&tcp); // $ MISSING: hasTaintFlow + let config = rustls::ClientConfig::builder() + .with_root_certificates(rustls::RootCertStore::empty()) + .with_no_client_auth(); + let connector = TlsConnector::from(Arc::new(config)); + let server_name = rustls::pki_types::ServerName::try_from("www.example.com").unwrap(); + let mut reader = connector.connect(server_name, tcp).await?; + sink(&reader); // $ MISSING: hasTaintFlow + + { + // using the `AsyncRead` trait (low-level) + let mut buffer = [0u8; 64]; + let mut pinned = Pin::new(&mut reader); + sink(&pinned); // $ MISSING: hasTaintFlow + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let bytes_read = pinned.poll_read(&mut cx, &mut buffer); + if let Poll::Ready(Ok(n)) = bytes_read { + sink(&buffer); // $ MISSING: hasTaintFlow=url + sink(&buffer[..n]); // $ MISSING: hasTaintFlow=url + } + } + + { + // using the `AsyncReadExt::read` extension method (higher-level) + let mut buffer1 = [0u8; 64]; + let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader, &mut buffer1).await?; + sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow + + let mut buffer2 = [0u8; 64]; + let bytes_read2 = reader.read(&mut buffer2).await?; + sink(&buffer2[..bytes_read2]); // $ MISSING: hasTaintFlow + } + + let mut reader2 = futures::io::BufReader::new(reader); + sink(&reader2); // $ MISSING: hasTaintFlow + + { + // using the `AsyncBufRead` trait (low-level) + let mut pinned = Pin::new(&mut reader2); + sink(&pinned); // $ MISSING: hasTaintFlow + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let buffer = pinned.poll_fill_buf(&mut cx); + if let Poll::Ready(Ok(buf)) = buffer { + sink(&buffer); // $ MISSING: hasTaintFlow=url + sink(buf); // $ MISSING: hasTaintFlow=url + } + + // using the `AsyncBufRead` trait (alternative syntax) + let buffer2 = Pin::new(&mut reader2).poll_fill_buf(&mut cx); + match (buffer2) { + Poll::Ready(Ok(buf)) => { + sink(&buffer2); // $ MISSING: hasTaintFlow=url + sink(buf); // $ MISSING: hasTaintFlow=url + } + _ => { + // ... + } + } + } + + { + // using the `AsyncBufReadExt::fill_buf` extension method (higher-level) + let buffer = reader2.fill_buf().await?; + sink(buffer); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncRead` trait (low-level) + let mut buffer = [0u8; 64]; + let mut pinned = Pin::new(&mut reader2); + sink(&pinned); // $ MISSING: hasTaintFlow + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let bytes_read = pinned.poll_read(&mut cx, &mut buffer); + sink(&buffer); // $ MISSING: hasTaintFlow=url + if let Poll::Ready(Ok(n)) = bytes_read { + sink(&buffer[..n]); // $ MISSING: hasTaintFlow=url + } + } + + { + // using the `AsyncReadExt::read` extension method (higher-level) + let mut buffer1 = [0u8; 64]; + let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader2, &mut buffer1).await?; + sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow + + let mut buffer2 = [0u8; 64]; + let bytes_read2 = reader2.read(&mut buffer2).await?; + sink(&buffer2[..bytes_read2]); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncBufRead` trait (low-level) + let mut pinned = Pin::new(&mut reader2); + sink(&pinned); // $ MISSING: hasTaintFlow + let mut cx = Context::from_waker(futures::task::noop_waker_ref()); + let buffer = pinned.poll_fill_buf(&mut cx); + sink(&buffer); // $ MISSING: hasTaintFlow=url + if let Poll::Ready(Ok(buf)) = buffer { + sink(buf); // $ MISSING: hasTaintFlow=url + } + } + + { + // using the `AsyncBufReadExt::fill_buf` extension method (higher-level) + let buffer = reader2.fill_buf().await?; + sink(buffer); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncBufReadExt::read_until` extension method + let mut line = Vec::new(); + let _bytes_read = reader2.read_until(b'\n', &mut line).await?; + sink(&line); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncBufReadExt::read_line` extension method + let mut line = String::new(); + let _bytes_read = reader2.read_line(&mut line).await?; + sink(&line); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncBufReadExt::read_to_end` extension method + let mut buffer = Vec::with_capacity(1024); + let _bytes_read = reader2.read_to_end(&mut buffer).await?; + sink(&buffer); // $ MISSING: hasTaintFlow + } + + { + // using the `AsyncBufReadExt::lines` extension method + let mut lines_stream = reader2.lines(); + sink(lines_stream.next().await.unwrap()); // $ MISSING: hasTaintFlow + while let Some(line) = lines_stream.next().await { + sink(line.unwrap()); // $ MISSING: hasTaintFlow + } + } + + Ok(()) +} From a5e1702d4b208674ae08997b4ae2995bccab6ff4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 15 May 2025 17:07:39 +0100 Subject: [PATCH 2/8] Rust: Add tests for sources involving regular rustls as well. --- .../dataflow/sources/TaintSources.expected | 4 +- .../library-tests/dataflow/sources/test.rs | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected index da3b69eb0507..9f96f4f3f132 100644 --- a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected +++ b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected @@ -75,8 +75,8 @@ | test.rs:619:26:619:61 | ...::connect_timeout | Flow source 'RemoteSource' of type remote (DEFAULT). | | test.rs:671:28:671:57 | ...::connect | Flow source 'RemoteSource' of type remote (DEFAULT). | | test.rs:753:22:753:49 | ...::connect | Flow source 'RemoteSource' of type remote (DEFAULT). | -| test.rs:775:16:775:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | -| test.rs:775:16:775:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | +| test.rs:806:16:806:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | +| test.rs:806:16:806:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | | web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | | web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | | web_frameworks.rs:21:31:21:35 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). | diff --git a/rust/ql/test/library-tests/dataflow/sources/test.rs b/rust/ql/test/library-tests/dataflow/sources/test.rs index 370f3d4c9b66..e6854093bc55 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test.rs @@ -770,6 +770,37 @@ async fn test_std_to_tokio_tcpstream() -> std::io::Result<()> { Ok(()) } +fn test_rustls() -> std::io::Result<()> { + let config = rustls::ClientConfig::builder() + .with_root_certificates(rustls::RootCertStore::empty()) + .with_no_client_auth(); + let server_name = rustls::pki_types::ServerName::try_from("www.example.com").unwrap(); + let config_arc = std::sync::Arc::new(config); + let mut client = rustls::ClientConnection::new(config_arc, server_name).unwrap(); // $ MISSING: Alert[rust/summary/taint-sources] + let mut reader = client.reader(); + sink(&reader); // $ MISSING: hasTaintFlow + + { + let mut buffer = [0u8; 100]; + let _bytes = reader.read(&mut buffer)?; + sink(&buffer); // $ MISSING: hasTaintFlow + } + + { + let mut buffer = Vec::::new(); + let _bytes = reader.read_to_end(&mut buffer)?; + sink(&buffer); // $ MISSING: hasTaintFlow + } + + { + let mut buffer = String::new(); + let _bytes = reader.read_to_string(&mut buffer)?; + sink(&buffer); // $ MISSING: hasTaintFlow + } + + Ok(()) +} + #[tokio::main] async fn main() -> Result<(), Box> { let case = std::env::args().nth(1).unwrap_or(String::from("1")).parse::().unwrap(); // $ Alert[rust/summary/taint-sources] @@ -849,5 +880,11 @@ async fn main() -> Result<(), Box> { Err(e) => println!("error: {}", e), } + println!("test_rustls..."); + match test_rustls() { + Ok(_) => println!("complete"), + Err(e) => println!("error: {}", e), + } + Ok(()) } From b78d51e079e9f0b8ebe15af9a50501da9795c05c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 22 May 2025 11:45:00 +0100 Subject: [PATCH 3/8] Rust: Fix a bug in InlineFlow.ql that was excluding some sinks. --- rust/ql/test/library-tests/dataflow/sources/InlineFlow.ql | 2 +- .../test/library-tests/dataflow/sources/web_frameworks.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/ql/test/library-tests/dataflow/sources/InlineFlow.ql b/rust/ql/test/library-tests/dataflow/sources/InlineFlow.ql index 7d5f4fb926f5..f0a38e29f194 100644 --- a/rust/ql/test/library-tests/dataflow/sources/InlineFlow.ql +++ b/rust/ql/test/library-tests/dataflow/sources/InlineFlow.ql @@ -10,7 +10,7 @@ module MyFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source instanceof ThreatModelSource } predicate isSink(DataFlow::Node sink) { - any(CallExpr call | call.getFunction().(PathExpr).getResolvedPath() = "crate::test::sink") + any(CallExpr call | call.getFunction().(PathExpr).getResolvedPath().matches("%::sink")) .getArgList() .getAnArg() = sink.asExpr().getExpr() } diff --git a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs index 6bfee08a3d2d..f1bf3ab6b0bb 100644 --- a/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs +++ b/rust/ql/test/library-tests/dataflow/sources/web_frameworks.rs @@ -10,9 +10,9 @@ mod poem_test { #[handler] fn my_poem_handler_1(Path(a): Path) -> String { // $ Alert[rust/summary/taint-sources] - sink(a.as_str()); // $ MISSING: hasTaintFlow - sink(a.as_bytes()); // $ MISSING: hasTaintFlow - sink(a); // $ MISSING: hasTaintFlow + sink(a.as_str()); // $ hasTaintFlow + sink(a.as_bytes()); // $ hasTaintFlow + sink(a); // $ hasTaintFlow "".to_string() } @@ -59,7 +59,7 @@ mod poem_test { fn my_poem_handler_6( Query(a): Query, // $ Alert[rust/summary/taint-sources] ) -> String { - sink(a); // $ MISSING: hasTaintFlow + sink(a); // $ hasTaintFlow "".to_string() } From 13f6de99247dd8dc365e2148dfd6e8f9fdfec4ae Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 15 May 2025 17:31:01 +0100 Subject: [PATCH 4/8] Rust: Add source / basic basic models. --- .../codeql/rust/frameworks/async-rs.model.yml | 6 ++++++ .../lib/codeql/rust/frameworks/futures.model.yml | 1 + .../lib/codeql/rust/frameworks/rustls.model.yml | 12 ++++++++++++ .../dataflow/sources/TaintSources.expected | 2 ++ .../test/library-tests/dataflow/sources/test.rs | 8 ++++---- .../dataflow/sources/test_futures_io.rs | 16 ++++++++-------- 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/frameworks/async-rs.model.yml create mode 100644 rust/ql/lib/codeql/rust/frameworks/rustls.model.yml diff --git a/rust/ql/lib/codeql/rust/frameworks/async-rs.model.yml b/rust/ql/lib/codeql/rust/frameworks/async-rs.model.yml new file mode 100644 index 000000000000..2217c30fce36 --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/async-rs.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["repo:https://github.com/async-rs/async-std:async-std", "::connect", "ReturnValue.Future.Field[crate::result::Result::Ok(0)]", "remote", "manual"] diff --git a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml index 1361ff9aeb2e..2bf444da9c72 100644 --- a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml @@ -4,3 +4,4 @@ extensions: extensible: summaryModel data: - ["repo:https://github.com/rust-lang/futures-rs:futures-executor", "crate::local_pool::block_on", "Argument[0]", "ReturnValue", "value", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "::new", "Argument[0]", "ReturnValue", "taint", "manual"] diff --git a/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml b/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml new file mode 100644 index 000000000000..1244e05dce90 --- /dev/null +++ b/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml @@ -0,0 +1,12 @@ +extensions: + - addsTo: + pack: codeql/rust-all + extensible: sourceModel + data: + - ["repo:https://github.com/rustls/rustls:rustls", "::new", "ReturnValue.Field[crate::result::Result::Ok(0)]", "remote", "manual"] + - addsTo: + pack: codeql/rust-all + extensible: summaryModel + data: + - ["repo:https://github.com/quininer/futures-rustls:futures-rustls", "::connect", "Argument[1]", "ReturnValue.Future.Field[crate::result::Result::Ok(0)]", "taint", "manual"] + - ["repo:https://github.com/rustls/rustls:rustls", "::reader", "Argument[self]", "ReturnValue", "taint", "manual"] diff --git a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected index 9f96f4f3f132..c244167fd804 100644 --- a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected +++ b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected @@ -75,8 +75,10 @@ | test.rs:619:26:619:61 | ...::connect_timeout | Flow source 'RemoteSource' of type remote (DEFAULT). | | test.rs:671:28:671:57 | ...::connect | Flow source 'RemoteSource' of type remote (DEFAULT). | | test.rs:753:22:753:49 | ...::connect | Flow source 'RemoteSource' of type remote (DEFAULT). | +| test.rs:779:22:779:50 | ...::new | Flow source 'RemoteSource' of type remote (DEFAULT). | | test.rs:806:16:806:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | | test.rs:806:16:806:29 | ...::args | Flow source 'CommandLineArgs' of type commandargs (DEFAULT). | +| test_futures_io.rs:19:15:19:32 | ...::connect | Flow source 'RemoteSource' of type remote (DEFAULT). | | web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | | web_frameworks.rs:12:31:12:31 | a | Flow source 'RemoteSource' of type remote (DEFAULT). | | web_frameworks.rs:21:31:21:35 | TuplePat | Flow source 'RemoteSource' of type remote (DEFAULT). | diff --git a/rust/ql/test/library-tests/dataflow/sources/test.rs b/rust/ql/test/library-tests/dataflow/sources/test.rs index e6854093bc55..604dc86c6d87 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test.rs @@ -776,9 +776,9 @@ fn test_rustls() -> std::io::Result<()> { .with_no_client_auth(); let server_name = rustls::pki_types::ServerName::try_from("www.example.com").unwrap(); let config_arc = std::sync::Arc::new(config); - let mut client = rustls::ClientConnection::new(config_arc, server_name).unwrap(); // $ MISSING: Alert[rust/summary/taint-sources] + let mut client = rustls::ClientConnection::new(config_arc, server_name).unwrap(); // $ Alert[rust/summary/taint-sources] let mut reader = client.reader(); - sink(&reader); // $ MISSING: hasTaintFlow + sink(&reader); // $ hasTaintFlow=config_arc { let mut buffer = [0u8; 100]; @@ -789,13 +789,13 @@ fn test_rustls() -> std::io::Result<()> { { let mut buffer = Vec::::new(); let _bytes = reader.read_to_end(&mut buffer)?; - sink(&buffer); // $ MISSING: hasTaintFlow + sink(&buffer); // $ hasTaintFlow=config_arc } { let mut buffer = String::new(); let _bytes = reader.read_to_string(&mut buffer)?; - sink(&buffer); // $ MISSING: hasTaintFlow + sink(&buffer); // $ hasTaintFlow=config_arc } Ok(()) diff --git a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs index 0b006546893c..5dc3bec184d5 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs @@ -16,21 +16,21 @@ use async_std::net::TcpStream; async fn test_futures_rustls_futures_io() -> io::Result<()> { let url = "www.example.com:443"; - let tcp = TcpStream::connect(url).await?; // $ MISSING: Alert[rust/summary/taint-sources] - sink(&tcp); // $ MISSING: hasTaintFlow + let tcp = TcpStream::connect(url).await?; // $ Alert[rust/summary/taint-sources] + sink(&tcp); // $ hasTaintFlow=url let config = rustls::ClientConfig::builder() .with_root_certificates(rustls::RootCertStore::empty()) .with_no_client_auth(); let connector = TlsConnector::from(Arc::new(config)); let server_name = rustls::pki_types::ServerName::try_from("www.example.com").unwrap(); let mut reader = connector.connect(server_name, tcp).await?; - sink(&reader); // $ MISSING: hasTaintFlow + sink(&reader); // $ hasTaintFlow=url { // using the `AsyncRead` trait (low-level) let mut buffer = [0u8; 64]; let mut pinned = Pin::new(&mut reader); - sink(&pinned); // $ MISSING: hasTaintFlow + sink(&pinned); // $ hasTaintFlow=url let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let bytes_read = pinned.poll_read(&mut cx, &mut buffer); if let Poll::Ready(Ok(n)) = bytes_read { @@ -51,12 +51,12 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { } let mut reader2 = futures::io::BufReader::new(reader); - sink(&reader2); // $ MISSING: hasTaintFlow + sink(&reader2); // $ hasTaintFlow=url { // using the `AsyncBufRead` trait (low-level) let mut pinned = Pin::new(&mut reader2); - sink(&pinned); // $ MISSING: hasTaintFlow + sink(&pinned); // $ hasTaintFlow=url let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let buffer = pinned.poll_fill_buf(&mut cx); if let Poll::Ready(Ok(buf)) = buffer { @@ -87,7 +87,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { // using the `AsyncRead` trait (low-level) let mut buffer = [0u8; 64]; let mut pinned = Pin::new(&mut reader2); - sink(&pinned); // $ MISSING: hasTaintFlow + sink(&pinned); // $ hasTaintFlow=url let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let bytes_read = pinned.poll_read(&mut cx, &mut buffer); sink(&buffer); // $ MISSING: hasTaintFlow=url @@ -110,7 +110,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { { // using the `AsyncBufRead` trait (low-level) let mut pinned = Pin::new(&mut reader2); - sink(&pinned); // $ MISSING: hasTaintFlow + sink(&pinned); // $ hasTaintFlow=url let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let buffer = pinned.poll_fill_buf(&mut cx); sink(&buffer); // $ MISSING: hasTaintFlow=url From 84c72f68af8b2820e85edb8f7e1e573da699cf51 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 16 May 2025 13:28:22 +0100 Subject: [PATCH 5/8] Rust: Add models for read methods. --- rust/ql/lib/codeql/rust/frameworks/futures.model.yml | 4 ++++ .../library-tests/dataflow/sources/test_futures_io.rs | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml index 2bf444da9c72..92affb8dd1fc 100644 --- a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml @@ -5,3 +5,7 @@ extensions: data: - ["repo:https://github.com/rust-lang/futures-rs:futures-executor", "crate::local_pool::block_on", "Argument[0]", "ReturnValue", "value", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "::new", "Argument[0]", "ReturnValue", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read_to_end", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_line", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_until", "Argument[self]", "Argument[1].Reference", "taint", "manual"] diff --git a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs index 5dc3bec184d5..99ea8004e6a2 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs @@ -47,7 +47,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { let mut buffer2 = [0u8; 64]; let bytes_read2 = reader.read(&mut buffer2).await?; - sink(&buffer2[..bytes_read2]); // $ MISSING: hasTaintFlow + sink(&buffer2[..bytes_read2]); // $ hasTaintFlow=url } let mut reader2 = futures::io::BufReader::new(reader); @@ -104,7 +104,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { let mut buffer2 = [0u8; 64]; let bytes_read2 = reader2.read(&mut buffer2).await?; - sink(&buffer2[..bytes_read2]); // $ MISSING: hasTaintFlow + sink(&buffer2[..bytes_read2]); // $ hasTaintFlow=url } { @@ -129,21 +129,21 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { // using the `AsyncBufReadExt::read_until` extension method let mut line = Vec::new(); let _bytes_read = reader2.read_until(b'\n', &mut line).await?; - sink(&line); // $ MISSING: hasTaintFlow + sink(&line); // $ hasTaintFlow=url } { // using the `AsyncBufReadExt::read_line` extension method let mut line = String::new(); let _bytes_read = reader2.read_line(&mut line).await?; - sink(&line); // $ MISSING: hasTaintFlow + sink(&line); // $ hasTaintFlow=url } { // using the `AsyncBufReadExt::read_to_end` extension method let mut buffer = Vec::with_capacity(1024); let _bytes_read = reader2.read_to_end(&mut buffer).await?; - sink(&buffer); // $ MISSING: hasTaintFlow + sink(&buffer); // $ hasTaintFlow=url } { From 4d51a15cc4fca88223a4995770d30ffc0a3824e4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 16 May 2025 13:30:53 +0100 Subject: [PATCH 6/8] Rust: Add model variants for when the qualifier is expressed as an arg (reference). We shouldn't need these. --- rust/ql/lib/codeql/rust/frameworks/futures.model.yml | 4 ++++ .../ql/test/library-tests/dataflow/sources/test_futures_io.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml index 92affb8dd1fc..e098bd5de14e 100644 --- a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml @@ -6,6 +6,10 @@ extensions: - ["repo:https://github.com/rust-lang/futures-rs:futures-executor", "crate::local_pool::block_on", "Argument[0]", "ReturnValue", "value", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "::new", "Argument[0]", "ReturnValue", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read_to_end", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncReadExt::read_to_end", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_line", "Argument[self]", "Argument[0].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_line", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_until", "Argument[self]", "Argument[1].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_until", "Argument[self].Reference", "Argument[1].Reference", "taint", "manual"] diff --git a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs index 99ea8004e6a2..e1f19658e0ac 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs @@ -43,7 +43,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { // using the `AsyncReadExt::read` extension method (higher-level) let mut buffer1 = [0u8; 64]; let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader, &mut buffer1).await?; - sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow + sink(&buffer1[..bytes_read1]); // $ hasTaintFlow=url let mut buffer2 = [0u8; 64]; let bytes_read2 = reader.read(&mut buffer2).await?; @@ -100,7 +100,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { // using the `AsyncReadExt::read` extension method (higher-level) let mut buffer1 = [0u8; 64]; let bytes_read1 = futures::io::AsyncReadExt::read(&mut reader2, &mut buffer1).await?; - sink(&buffer1[..bytes_read1]); // $ MISSING: hasTaintFlow + sink(&buffer1[..bytes_read1]); // $ hasTaintFlow=url let mut buffer2 = [0u8; 64]; let bytes_read2 = reader2.read(&mut buffer2).await?; From 10f894b9a1e8fa3edfd0ee1b6956101684063fe4 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 16 May 2025 16:50:55 +0100 Subject: [PATCH 7/8] Rust: Model more methods. --- rust/ql/lib/codeql/rust/frameworks/futures.model.yml | 4 ++++ rust/ql/lib/codeql/rust/frameworks/rustls.model.yml | 2 ++ rust/ql/test/library-tests/dataflow/sources/test.rs | 2 +- .../library-tests/dataflow/sources/test_futures_io.rs | 10 +++++----- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml index e098bd5de14e..02f29d23494d 100644 --- a/rust/ql/lib/codeql/rust/frameworks/futures.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/futures.model.yml @@ -13,3 +13,7 @@ extensions: - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_line", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_until", "Argument[self]", "Argument[1].Reference", "taint", "manual"] - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::read_until", "Argument[self].Reference", "Argument[1].Reference", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::fill_buf", "Argument[self]", "ReturnValue.Future.Field[crate::result::Result::Ok(0)]", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::io::AsyncBufReadExt::lines", "Argument[self]", "ReturnValue", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "crate::stream::stream::StreamExt::next", "Argument[self]", "ReturnValue.Future.Field[crate::option::Option::Some(0)]", "taint", "manual"] + - ["repo:https://github.com/rust-lang/futures-rs:futures-util", "::poll_fill_buf", "Argument[self].Reference", "ReturnValue.Field[crate::task::poll::Poll::Ready(0)].Field[crate::result::Result::Ok(0)]", "taint", "manual"] diff --git a/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml b/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml index 1244e05dce90..baa5615d458f 100644 --- a/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml +++ b/rust/ql/lib/codeql/rust/frameworks/rustls.model.yml @@ -9,4 +9,6 @@ extensions: extensible: summaryModel data: - ["repo:https://github.com/quininer/futures-rustls:futures-rustls", "::connect", "Argument[1]", "ReturnValue.Future.Field[crate::result::Result::Ok(0)]", "taint", "manual"] + - ["repo:https://github.com/quininer/futures-rustls:futures-rustls", "::poll_read", "Argument[self].Reference", "Argument[1].Reference", "taint", "manual"] - ["repo:https://github.com/rustls/rustls:rustls", "::reader", "Argument[self]", "ReturnValue", "taint", "manual"] + - ["repo:https://github.com/rustls/rustls:rustls", "::read", "Argument[self]", "Argument[0].Reference", "taint", "manual"] diff --git a/rust/ql/test/library-tests/dataflow/sources/test.rs b/rust/ql/test/library-tests/dataflow/sources/test.rs index 604dc86c6d87..342efbba69ea 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test.rs @@ -783,7 +783,7 @@ fn test_rustls() -> std::io::Result<()> { { let mut buffer = [0u8; 100]; let _bytes = reader.read(&mut buffer)?; - sink(&buffer); // $ MISSING: hasTaintFlow + sink(&buffer); // $ hasTaintFlow=config_arc } { diff --git a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs index e1f19658e0ac..6e7747424d60 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test_futures_io.rs @@ -34,8 +34,8 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let bytes_read = pinned.poll_read(&mut cx, &mut buffer); if let Poll::Ready(Ok(n)) = bytes_read { - sink(&buffer); // $ MISSING: hasTaintFlow=url - sink(&buffer[..n]); // $ MISSING: hasTaintFlow=url + sink(&buffer); // $ hasTaintFlow=url + sink(&buffer[..n]); // $ hasTaintFlow=url } } @@ -80,7 +80,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { { // using the `AsyncBufReadExt::fill_buf` extension method (higher-level) let buffer = reader2.fill_buf().await?; - sink(buffer); // $ MISSING: hasTaintFlow + sink(buffer); // $ hasTaintFlow=url } { @@ -122,7 +122,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { { // using the `AsyncBufReadExt::fill_buf` extension method (higher-level) let buffer = reader2.fill_buf().await?; - sink(buffer); // $ MISSING: hasTaintFlow + sink(buffer); // $ hasTaintFlow=url } { @@ -149,7 +149,7 @@ async fn test_futures_rustls_futures_io() -> io::Result<()> { { // using the `AsyncBufReadExt::lines` extension method let mut lines_stream = reader2.lines(); - sink(lines_stream.next().await.unwrap()); // $ MISSING: hasTaintFlow + sink(lines_stream.next().await.unwrap()); // $ hasTaintFlow=url while let Some(line) = lines_stream.next().await { sink(line.unwrap()); // $ MISSING: hasTaintFlow } From 49dabdb8a53ac3c70d30948318067e6a8f7b9373 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 29 May 2025 17:51:44 +0100 Subject: [PATCH 8/8] Rust: Accept consistency test failures. --- .../PathResolutionConsistency.expected | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rust/ql/test/library-tests/dataflow/sources/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/library-tests/dataflow/sources/CONSISTENCY/PathResolutionConsistency.expected index 0aa771632529..b9189f05dc9d 100644 --- a/rust/ql/test/library-tests/dataflow/sources/CONSISTENCY/PathResolutionConsistency.expected +++ b/rust/ql/test/library-tests/dataflow/sources/CONSISTENCY/PathResolutionConsistency.expected @@ -1,3 +1,25 @@ +multipleMethodCallTargets +| test.rs:618:25:618:49 | address.to_socket_addrs() | file://:0:0:0:0 | fn to_socket_addrs | +| test.rs:618:25:618:49 | address.to_socket_addrs() | file://:0:0:0:0 | fn to_socket_addrs | +| test_futures_io.rs:35:26:35:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:35:26:35:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:35:26:35:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:61:22:61:50 | pinned.poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +| test_futures_io.rs:61:22:61:50 | pinned.poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +| test_futures_io.rs:68:23:68:67 | ... .poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +| test_futures_io.rs:68:23:68:67 | ... .poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +| test_futures_io.rs:92:26:92:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:92:26:92:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:92:26:92:63 | pinned.poll_read(...) | file://:0:0:0:0 | fn poll_read | +| test_futures_io.rs:115:22:115:50 | pinned.poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +| test_futures_io.rs:115:22:115:50 | pinned.poll_fill_buf(...) | file://:0:0:0:0 | fn poll_fill_buf | +multiplePathResolutions +| test.rs:777:23:777:61 | ...::try_from | file://:0:0:0:0 | fn try_from | +| test.rs:777:23:777:61 | ...::try_from | file://:0:0:0:0 | fn try_from | +| test.rs:777:23:777:61 | ...::try_from | file://:0:0:0:0 | fn try_from | +| test_futures_io.rs:25:23:25:61 | ...::try_from | file://:0:0:0:0 | fn try_from | +| test_futures_io.rs:25:23:25:61 | ...::try_from | file://:0:0:0:0 | fn try_from | +| test_futures_io.rs:25:23:25:61 | ...::try_from | file://:0:0:0:0 | fn try_from | multipleCanonicalPaths | file://:0:0:0:0 | fn to_ordering | file://:0:0:0:0 | Crate(typenum@1.18.0) | ::to_ordering | | file://:0:0:0:0 | fn to_ordering | file://:0:0:0:0 | Crate(typenum@1.18.0) | ::to_ordering |