Closed
Description
fmt::Arguments<'a>
is only parameterized on lifetime. The types of the captured arguments are erased in the contained [ArgumentV1<'a>]
. Nothing restricts the Arguments
aggregate from being Send
or Sync
, so by OIBIT it's both. Thus this compiles:
fn send<T: Send>(_: T) {}
fn sync<T: Sync>(_: T) {}
fn main() {
// `Cell` is not `Sync`, so `&Cell` is neither `Sync` nor `Send`,
// yet `std::fmt::Arguments` forgets this...
let c = std::cell::Cell::new(42);
send(format_args!("{:?}", c));
sync(format_args!("{:?}", c));
}
I'm not sure if there are any realistic ways to accidentally abuse this, but here's a deliberate example. The spawned thread will read the Cell
through the arguments, even while the main thread modifies it.
extern crate crossbeam;
use std::io::Write;
use std::cell::Cell;
fn main() {
let c = Cell::new(1);
threader(&c, format_args!("{:?}\n", c));
}
fn threader(c: &Cell<i32>, a: std::fmt::Arguments) {
crossbeam::scope(|scope| {
let delay = std::time::Duration::from_millis(100);
let guard = scope.spawn(move || {
for _ in 0..10 {
std::thread::sleep(delay);
std::io::stdout().write_fmt(a).unwrap();
}
});
for _ in 0..10 {
std::thread::sleep(delay);
c.set(c.get() * 2);
}
guard.join();
});
}