Skip to content

Arc and Rc are dropck-unsound #29106

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
apasel422 opened this issue Oct 16, 2015 · 7 comments
Closed

Arc and Rc are dropck-unsound #29106

apasel422 opened this issue Oct 16, 2015 · 7 comments
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@apasel422
Copy link
Contributor

use std::sync::Arc;

struct Foo<'a>(&'a String);

impl<'a> Drop for Foo<'a> {
    fn drop(&mut self) {
        println!("{:?}", self.0);
    }
}

fn main() {
    let (y, x);
    x = "alive".to_string();
    y = Arc::new(Foo(&x));
}

Output after compiling with rustc 1.5.0-nightly (6cdf31b12 2015-10-15):

thread '<main>' panicked at 'index 0 and/or 0 in `�25` do not lie on character boundary', ../src/libcore/str/mod.rs:1444

Compiling with alloc_system and running under Valgrind confirms the use-after-free. Note that replacing Arc::new with Rc::new results in the same invalid runtime behavior, while replacing it with Box::new reports the correct lifetime error at compile time.

I believe this is a result of Arc and Rc failing to include PhantomData<T> in their internals~~, while simultaneously specifying #[unsafe_destructor_blind_to_params] on their destructors~~. Assuming my analysis of the issue is correct, I have a patch for this incoming, which will also address #29037.

CC @pnkfelix @gankro

@alexcrichton
Copy link
Member

triage: I-nominated

@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Oct 16, 2015
@apasel422
Copy link
Contributor Author

Hmm. My proposed fix causes a number of errors in run-pass/dropck_legal_cycles.rs. Investigating now.

@Gankra
Copy link
Contributor

Gankra commented Oct 16, 2015

Fixing this was originally blocked on #26905

Should be fixable now, may require #[unsafe_destructor_blind_to_params] to pass that run-pass test.

@apasel422
Copy link
Contributor Author

@gankro {Arc, Rc} already use that attribute. My only change is essentially to add PhantomData<T> to each of them (via Shared). See https://github.com/rust-lang/rust/compare/master...apasel422:shared?expand=1.

@apasel422
Copy link
Contributor Author

Here are the exact compile errors with my patch:

/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:414:48: 414:52 error: `arc1` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:414     arc0.0.write().unwrap().children.0 = Some(&arc1);
                                                                                                              ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400:32: 442:2 note: reference must be valid for the block suffix following statement 133 at 400:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:401     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:402     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:403     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:404     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:405     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410:51: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 140 at 410:50
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410     let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:411     arc0 = ARCRW::new("arcrw0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:412     arc1 = ARCRW::new("arcrw1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:413     arc2 = ARCRW::new("arcrw2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:414     arc0.0.write().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:415     arc0.0.write().unwrap().children.1 = Some(&arc2);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:415:48: 415:52 error: `arc2` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:415     arc0.0.write().unwrap().children.1 = Some(&arc2);
                                                                                                              ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400:32: 442:2 note: reference must be valid for the block suffix following statement 133 at 400:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:401     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:402     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:403     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:404     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:405     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410:51: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 140 at 410:50
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410     let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:411     arc0 = ARCRW::new("arcrw0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:412     arc1 = ARCRW::new("arcrw1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:413     arc2 = ARCRW::new("arcrw2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:414     arc0.0.write().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:415     arc0.0.write().unwrap().children.1 = Some(&arc2);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:416:48: 416:52 error: `arc0` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:416     arc2.0.write().unwrap().children.0 = Some(&arc0);
                                                                                                              ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400:32: 442:2 note: reference must be valid for the block suffix following statement 133 at 400:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:400     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:401     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:402     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:403     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:404     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:405     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410:51: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 140 at 410:50
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:410     let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:411     arc0 = ARCRW::new("arcrw0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:412     arc1 = ARCRW::new("arcrw1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:413     arc2 = ARCRW::new("arcrw2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:414     arc0.0.write().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:415     arc0.0.write().unwrap().children.1 = Some(&arc2);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:432:47: 432:51 error: `arc1` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:432     arc0.1.lock().unwrap().children.0 = Some(&arc1);
                                                                                                             ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418:32: 442:2 note: reference must be valid for the block suffix following statement 147 at 418:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:419     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:420     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:421     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:422     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:423     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428:48: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 154 at 428:47
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428     let (arc0, arc1, arc2): (ARCM, ARCM, ARCM);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:429     arc0 = ARCM::new("arcm0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:430     arc1 = ARCM::new("arcm1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:431     arc2 = ARCM::new("arcm2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:432     arc0.1.lock().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:433     arc0.1.lock().unwrap().children.1 = Some(&arc2);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:433:47: 433:51 error: `arc2` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:433     arc0.1.lock().unwrap().children.1 = Some(&arc2);
                                                                                                             ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418:32: 442:2 note: reference must be valid for the block suffix following statement 147 at 418:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:419     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:420     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:421     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:422     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:423     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428:48: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 154 at 428:47
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428     let (arc0, arc1, arc2): (ARCM, ARCM, ARCM);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:429     arc0 = ARCM::new("arcm0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:430     arc1 = ARCM::new("arcm1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:431     arc2 = ARCM::new("arcm2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:432     arc0.1.lock().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:433     arc0.1.lock().unwrap().children.1 = Some(&arc2);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:434:47: 434:51 error: `arc0` does not live long enough
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:434     arc2.1.lock().unwrap().children.0 = Some(&arc0);
                                                                                                             ^~~~
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418:32: 442:2 note: reference must be valid for the block suffix following statement 147 at 418:31...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:418     let mut c = c_orig.clone();
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:419     c.control_bits = 0b1;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:420     c.curr_mark = 110;
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:421     assert!(!c.saw_prev_marked);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:422     arc0.descend_into_self(&mut c);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:423     assert!(c.saw_prev_marked);
                                                               ...
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428:48: 442:2 note: ...but borrowed value is only valid for the block suffix following statement 154 at 428:47
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:428     let (arc0, arc1, arc2): (ARCM, ARCM, ARCM);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:429     arc0 = ARCM::new("arcm0");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:430     arc1 = ARCM::new("arcm1");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:431     arc2 = ARCM::new("arcm2");
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:432     arc0.1.lock().unwrap().children.0 = Some(&arc1);
/home/andrew/rust/src/test/run-pass/dropck_legal_cycles.rs:433     arc0.1.lock().unwrap().children.1 = Some(&arc2);
                                                               ...

@apasel422
Copy link
Contributor Author

Since the test errors are only related to {ARCRW, ARCM}, I'm inclined to think that the only problem is that the {RwLock, Mutex} destructors aren't tagged with the attribute. Investigating.

@pnkfelix
Copy link
Member

I can't believe I went through the exercise of writing up that dropck-legal-cycles test (and thus proved to myself the need for the attribute on Arc/Rc) but totally overlooked the missing PhantomData that should have been attached to Rc/Arc ...

(Part of the problem may be that we may not be documenting the right intuitions about what PhantomData denotes, with respect to notions like exclusive versus partial ownership.)

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants