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

Soundness Bug - StreamContextInfo is not safe to send across threads #324

Closed
erickt opened this issue Jun 1, 2021 · 6 comments · Fixed by #325
Closed

Soundness Bug - StreamContextInfo is not safe to send across threads #324

erickt opened this issue Jun 1, 2021 · 6 comments · Fixed by #325

Comments

@erickt
Copy link
Contributor

erickt commented Jun 1, 2021

We found another soundness issue. In src/fsevent.rs we have this definition:

struct StreamContextInfo {
    event_fn: Arc<dyn EventFn>,
    recursive_info: HashMap<PathBuf, bool>,
}

This type is passed across a thread that actually listens to file changes. Unfortunately this type isn't actually send because while pub trait EventFn: 'static + Fn(Result<Event>) + Send {} implements Send, Arc<dyn EventFn> is not Send. That also requires EventFn to also implement Sync. Unfortunately just adding Sync doesn't compile because of some other conflicts.

@0xpr03
Copy link
Member

0xpr03 commented Jun 1, 2021

To clarify, from my point of understanding Arc is only Send when T is Send. The trait itself is send, so Arc should be Send which is fine. But that isn't enough because of what ?

We could just wrap it inside a Mutex, but AFAIK whether we use a Mutex or a Send Trait doesn't make a difference (see above). Making it Sync is somewhat required as we otherwise have to guarantee that nobody else is calling the Trait (datarace)? At which point it'd be better to just move the whole dyn Trait to the thread instead of sharing a reference and hoping for the best?

@0xpr03
Copy link
Member

0xpr03 commented Jun 1, 2021

Never mind, I just realized the core issue, guess it's a little bit late for me.

Edit: See also this answer on SO about Drop requirements for why it's always both..

@0xpr03
Copy link
Member

0xpr03 commented Jun 1, 2021

Reading over the code: Is there any reason to have this inside an Arc instead of a raw Box that is kept by StreamContextInfo for the callback ? We should have some guarantee about whether it's ok for the callback to a non-sync call or not.

@0xpr03
Copy link
Member

0xpr03 commented Jun 1, 2021

If we would simply expose a channel instead of a callback this could be completely eliminated. It would also allow #189. Though it's coming with its own limitations (and will introduce locking inside the callback).

@erickt
Copy link
Contributor Author

erickt commented Jun 2, 2021

StreamContextInfo has event_fn in an Arc because it needs to clone it and pass it into the event thread. We can't change it into a Box without either making EventFn cloneable, or restructuring the types to make Watcher take ownership over self, which might be pretty heavy.

I'm not familiar with #189, but the simplest solution for this would be to change StreamContextInfo's event_fn in Arc<RwLock<dyn EventFn>>, since EventFn doesn't allow mutability, so we'd always be grabbing a read lock, which should be quite cheap.

@erickt
Copy link
Contributor Author

erickt commented Jun 2, 2021

Actually I'm wrong, RwLock is only Sync if T is Sync. So the smallest fix for this would be to use Arc<Mutex<T>>. I suggest we do that to address the immediate soundness issues, then explore alternatives to get rid of the mutex.

erickt added a commit to erickt/notify that referenced this issue Jun 2, 2021
In notify-rs#324, we found that we were passing StreamContextInfo to another
thread, even though the type is not actually Send.

Fixes notify-rs#324
0xpr03 pushed a commit that referenced this issue Jun 2, 2021
In #324, we found that we were passing StreamContextInfo to another
thread, even though the type is not actually Send.

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

Successfully merging a pull request may close this issue.

2 participants