Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Safety #9

Closed
srijs opened this issue Dec 10, 2018 · 5 comments
Closed

Safety #9

srijs opened this issue Dec 10, 2018 · 5 comments

Comments

@srijs
Copy link
Contributor

srijs commented Dec 10, 2018

Hi! I was just taking a look at this crate because it seems to be useful for a use-case I am having, where I want to read strings from a Bytes buffer and have them share the underlying allocation.

What struck me is that some of the methods and impls on String seem to be inherently unsafe, because they assume things about the sanity of impls on T which can be easily violated using safe Rust.

As an example, here is a code snippet that leads to a str which contains invalid utf-8 (playground).

extern crate string;

use std::cell::Cell;

struct Corruptor {
    inner: Cell<&'static [u8]>
}

impl AsRef<[u8]> for Corruptor {
    fn as_ref(&self) -> &[u8] {
        self.inner.replace(b"\xc3\x28".as_ref())
    }
}

fn main() {
    let corruptor = Corruptor { inner: Cell::new(&[]) };
    let string: string::String<Corruptor> = string::TryFrom::try_from(corruptor).unwrap();
    
    println!("BOOM: {}", &*string)
}

There's a couple of ways I can think of how this could be fixed:

  • One way to fix this would be to not provide any of the Deref and AsRef impls, but that seems like it would defeat the purpose of this crate.
  • Another way to fix it would be to mark methods as unsafe where appropriate, and remove or specialize trait impls.
  • A third way could be to introduce an unsafe marker trait (similar to what the owning_ref crate does), that is used as a bound for T to ensure that only "sane" implementations are used.

Here are all the ways to construct a String that I believe to be currently unsafe:

  • impl<T> TryFrom<T> for String<T> where T: AsRef<[u8]> (since 0.1.0)
  • impl<T: Default> Default for String<T> (since 0.1.0)
  • pub fn from_str<'a>(src: &'a str) -> String<T> where T: From<&'a [u8]> (since 0.1.2)

Let me know what you think!

@carllerche
Copy link
Owner

Thanks for pointing this out ❤️ Definitely not good... I don't have a great thought right now...

srijs added a commit to srijs/string that referenced this issue Dec 18, 2018
@ehsanmok
Copy link

@srijs may I ask how is it different from impl Sync marker trait? UnsafeCell is not Sync thus cell isn't as well.

@srijs
Copy link
Contributor Author

srijs commented Jan 12, 2019

@ehsanmok I'm not exactly sure what you mean, but I could produce a similar example by replacing Cell with Mutex, and that would be fully Sync. Whether or not this issue exists has nothing to do with thread safety, which is what theSync trait is about.

srijs added a commit to srijs/string that referenced this issue Jan 12, 2019
@carllerche
Copy link
Owner

@srijs could you show me how you would recreate a similar issue with Mutex?

@srijs
Copy link
Contributor Author

srijs commented Jan 12, 2019

@carllerche this should do it I believe?

I mean, the issue here is really interior mutability rather than thread safety. We can do interior mutabiliy in a Sync way via std::sync, and there is no built-in marker trait that indicates the absence of interior mutability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants