Skip to content

Commit 89150f9

Browse files
committed
Fix 'LocalRequest::clone()' soundness issue.
The existing implementation of 'LocalRequest::clone()' mistakenly copied the internal 'Request' pointer from the existing 'LocalRequest' to the cloned 'LocalRequest'. This resulted in an aliased '*mut Request' pointer, a clear soundness issue. The fix in this commit is to clone the internal 'Request', replacing the internal pointer with the newly cloned 'Request' when producing the cloned 'LocalRequest'. A fix that removes all 'unsafe' code should be explored. Fixes #1312.
1 parent ccb5eb1 commit 89150f9

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

core/lib/src/local/request.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,19 +476,35 @@ impl<'c> fmt::Debug for LocalResponse<'c> {
476476

477477
impl<'c> Clone for LocalRequest<'c> {
478478
fn clone(&self) -> LocalRequest<'c> {
479+
// Don't alias the existing `Request`. See #1312.
480+
let mut request = Rc::new(self.inner().clone());
481+
let ptr = Rc::get_mut(&mut request).unwrap() as *mut Request<'_>;
482+
479483
LocalRequest {
484+
ptr, request,
480485
client: self.client,
481-
ptr: self.ptr,
482-
request: self.request.clone(),
483486
data: self.data.clone(),
484487
uri: self.uri.clone()
485488
}
486489
}
487490
}
488491

489-
// #[cfg(test)]
492+
#[cfg(test)]
490493
mod tests {
491-
// Someday...
494+
use crate::Request;
495+
use crate::local::Client;
496+
497+
#[test]
498+
fn clone_unique_ptr() {
499+
let client = Client::new(crate::ignite()).unwrap();
500+
let r1 = client.get("/");
501+
let r2 = r1.clone();
502+
503+
assert_ne!(
504+
r1.inner() as *const Request<'_>,
505+
r2.inner() as *const Request<'_>
506+
);
507+
}
492508

493509
// #[test]
494510
// #[compile_fail]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
extern crate rocket;
2+
3+
use rocket::http::Header;
4+
use rocket::local::Client;
5+
6+
#[test]
7+
fn test_local_request_clone_soundness() {
8+
let client = Client::new(rocket::ignite()).unwrap();
9+
10+
// creates two LocalRequest instances that shouldn't share the same req
11+
let r1 = client.get("/").header(Header::new("key", "val1"));
12+
let mut r2 = r1.clone();
13+
14+
// save the iterator, which internally holds a slice
15+
let mut iter = r1.inner().headers().get("key");
16+
17+
// insert headers to force header map reallocation.
18+
for i in 0..100 {
19+
r2.add_header(Header::new(i.to_string(), i.to_string()));
20+
}
21+
22+
// Replace the original key/val.
23+
r2.add_header(Header::new("key", "val2"));
24+
25+
// Heap massage: so we've got crud to print.
26+
let _: Vec<usize> = vec![0, 0xcafebabe, 31337, 0];
27+
28+
// Ensure we're good.
29+
let s = iter.next().unwrap();
30+
println!("{}", s);
31+
32+
// And that we've got the right data.
33+
assert_eq!(r1.inner().headers().get("key").collect::<Vec<_>>(), vec!["val1"]);
34+
assert_eq!(r2.inner().headers().get("key").collect::<Vec<_>>(), vec!["val1", "val2"]);
35+
}

0 commit comments

Comments
 (0)