-
Notifications
You must be signed in to change notification settings - Fork 13.3k
avoiding races with native module initializers #2267
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
Comments
TL;DR I agree we need global data, possibly a mutex type. Let's push libraries as far as we can before adding too many unloved language features. This is similar to the problem that @olsonjeffery is encountering with the uv bindings. With uv, the standard library needs access to a single global resource (a uv loop) that is available to any task, must be initialized zero or one times, and shutdown when the runtime exits. The solution we are currently using uses two library functions to create a lazily-initialized, globally accessible task. What uv is trying to doThis is how the resource is initialized:
This is how the resource is deinitialized:
This could work for other cases too. For instance, we discussed earlier on IRC how it could be used for a getenv/setenv daemon. It may also work for initializing crypto libraries and serializing access to them if necessary. What ssl could do similarlyHere's how we could possibly use these functions to provide safe access to a crypto library that requires initialization, deinitialization and isn't thread safe if we allow for one rule
We would guarantee single initialization and deinitialization the same way as uv. And I could imagine a crypto API that takes a unique closure and lets you execute arbitrary calls within the context of the crypto scheduler, then further APIs that dress that up in synchronous function calls. If being forced into another task to do crypto is truly intolerable then we would need some kind of mutex as you suggest. Global dataEven the scheme I'm using requires access to global data; there is clearly a need. The way uv handles it is by stashing a word-sized global in the runtime kernel, but this isn't a solution for everybody. Probably unsafe global variables is inevitable. The one thing that really weirds me out about this is that this language will allow rust data to "leak" out of the runtime's control in a way that feels different from any other part of the language. In particular, if there are two Rust runtimes running at the same time, then they both will see the same global data. For the purpose of most libraries this means they will also need to coordinate truly global initialization and deinitialization between runtimes and shield runtimes' data from each other. Of course we can't actually run multiple runtimes, but the runtime will be embeddable, and I don't want to be one of these lame libraries that isn't threadsafe. Rust mutexesIt's come up a few times now where it would be nice to have a mutex in Rust. We can't just use pthread mutexes because they don't understand the Rust scheduler (if the scheduler deschedules a task while it holds a lock then ... I can't even imagine the consequences but surely they aren't good). I have not put much thought into it yet because I think we should push actor-based programming as far as we can. Most of the time when you want a lock the code can be expressed with actors. Tasks and messages are the bet we've made. We can create inefficient locks on top of tasks where synchronization is really easier to express with locks. If we find that we must have fast mutexes then it will require serious thinking. Static initializationPlease let's resist adding this to the language with our very most. I believe we can solve this in the library for many use cases. |
Great response. I agree I'd hate to have static initialization. The globally accessible, lazily created task would be perfect for these SSL libraries. I believe all I'd need really is something like:
Where I don't think we need to run the SSL functions inside their own thread though. Beyond synchronizing the init functions, the rest of the API is thread safe, with one exception. The SSL socket wrappers can't be used by more than one thread at a time, but we can already protect against that in rust. |
I think with unsafe global data the lazy initialization problem is solved trivially with a global mutex and a flag. Global shutdown is trickier, especially if you assume that a single process may host multiple Rust 'runtime' instances (which I do). |
I'm going to close this because I think any customizable before-main initialization is a non-starter, and the other possible solutions either exist or are planned. Feel free to reopen if you disagree. |
rustup I cannot reproduce rust-lang#98493 so let's see what CI says.
…default unwind (rust-lang#2267) I made a few improvements while trying to use assess: - Allow assess to keep building packages even when one or more targets failed to build. Instead, just report how many failed and how many succeeded. - Fix test result reporting for cases where CBMC crashes. We were counting them as success. Now we report the error code. - Allow users to set the default unwind value using --default-unwind. By default, we still use value 1. - Only report as analyzed crates that have any kani-metadata.json file.
The three SSL libraries I've looked at so far, OpenSSL, GnuTLS, and NSS, each require a function to be called (and in some cases, mutexes to be passed in) to initialize their global data structures (ugh). Even better, these functions aren't thread safe, so initialization needs to be pushed up to the executable in order for libraries to not stomp on each other's toes. Now, if a user happens to forget to initialize the library, then they are exposed to race conditions and segfaults.
I'd like to avoid that. Here's what I came up with to avoid these races:
The text was updated successfully, but these errors were encountered: