Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRwLock and Mutex on Window theoretically allows undefined behavior in safe code #35836
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I don't see any easy way out of this. Consider this example: let lock = RwLock::new();
mem::forget(lock.read());
lock.read();To avoid undefined behavior we would have to either:
|
This comment has been minimized.
This comment has been minimized.
|
…or document recursive reader lock as UB for now? |
This comment has been minimized.
This comment has been minimized.
|
@nagisa So we're going to accept, even temporarily, that a safe function in std can invoke UB? |
This comment has been minimized.
This comment has been minimized.
|
@nagisa But the code I've just written is entirely safe code. We can't allow UB in safe code. |
alexcrichton
added
I-nominated
T-libs
labels
Aug 20, 2016
This comment has been minimized.
This comment has been minimized.
|
Is there not a non-slim R/W lock primitive on Windows? |
This comment has been minimized.
This comment has been minimized.
|
No, there isn't. On Aug 20, 2016 5:44 AM, "Alex Burka" notifications@github.com wrote:
|
This comment has been minimized.
This comment has been minimized.
andlabs
commented
Aug 20, 2016
•
|
Even though the Windows API might not have non-slim RW locks, it does have loads of other synchronization objects that you can combine to form a recursive RW lock implementation. I wouldn't know what the best ones to use are, sorry. |
This comment has been minimized.
This comment has been minimized.
yilangmok
commented
Aug 20, 2016
|
Boost.Interprocess provides a very complete mutex implementation that allows recursive lock acquisition and upgradeability. (I am not familiar with Rust, but I'd assume you have some way to call into C/C++ code if you're able to call Windows functions). |
This comment has been minimized.
This comment has been minimized.
|
We are definitely not depending on Boost. We can however look at their implementation to create our own mutex inspired by it. |
This comment has been minimized.
This comment has been minimized.
|
That page is for inter-process implementations of synchronisation primitives. It has considerably different design constraints compared to inter-thread stuff (documented here). The implementation of RWLocks in Boost seems to use a number (3) of semaphores in addition to some state. |
This comment has been minimized.
This comment has been minimized.
yilangmok
commented
Aug 21, 2016
|
Thanks for the correction @nagisa. I knew Boost had a RWLock, so I assumed the first page I found would be the right one. Oops! @retep998, of course it's up to the Rust community to decide what to use or not use. However, I've found that for some areas (including concurrency and cryptography), it's best to use an existing library that has been battle tested. Boost provides primitives that are well tested, performant and cross-platform. Why not consider it? |
This comment has been minimized.
This comment has been minimized.
|
@yilangmok Because Boost is a massive dependency to pull in. Right now libstd is lightweight and pure Rust. Pulling in Boost would be hugely detrimental to libstd's weight. Also Boost is C++ so it would depend on the C++ runtime and standard library as well. Furthermore Rust is only able to easily work with C APIs, C++ is a massively complicated beast. Basically, I don't see any chances of Rust depending on Boost for something as simple as mutexes. |
This comment has been minimized.
This comment has been minimized.
|
Discussion at @rust-lang/libs triage today concluded that this seems like a good bug to fix, but not high-enough priority for P-high |
alexcrichton
added
P-medium
E-help-wanted
and removed
I-nominated
labels
Aug 23, 2016
This comment has been minimized.
This comment has been minimized.
|
A nice suggestion from the comment section of the linked blog post:
Extending the Windows RWLock structure as thus:
would allow to fix the issue. Of course the TLS safeguard could be moved to any of the layers above plain wrapper; idea stays the same. Alas, this limits the number of locks user could have at the same time to number of available TLS slots, which is 1088. |
This comment has been minimized.
This comment has been minimized.
mwinterb
commented
Aug 25, 2016
|
Is the license for the source code in David Butenhof's "Programming with POSIX Threads" book known? The "best" information that I could find was from the pthreads-win32 COPYING file which has this to say:
Currently, the source is available on the Informit.com website, which appears to be associated with the current publisher: If the license is acceptable for use in Rust, I've written a fairly direct translation of his As I'm not a Rust developer, it's in C (maybe with some accidental C++ constructs). Someone more experienced in Rust could likely translate it, but that task feels like a bad fit for "my first Rust program". The translation is available here: It's likely not the most efficient implementation of a reader/writer mutex as it is 10 years old, but it's O(1) space and has no dynamic memory allocations, so presumably † Internally, some counters use |
This comment has been minimized.
This comment has been minimized.
|
@nagisa That could work with my thread_local crate which gives you per-object thread-local storage. However I still think that the proper solution to this is to use the parking_lot crate which provides implementations of |
This comment has been minimized.
This comment has been minimized.
|
On 2016-08-25 18:26:57-0700, Amanieu wrote:
I do not disagree. I’m just brainstorming for simple and quick-to-implement fixes which could paper
|
This comment has been minimized.
This comment has been minimized.
|
I'm interested in fixing this. I think the best long-term solution is to use parking_lot in the stdlib. For now though, @nagisa's solution (above) is much better than UB, no? It's very unlikely that anyone is using more than 1,088 TLS + RwLock, and we can at least panic if that happens. I'll send in a PR to do that unless someone has a better idea. |
This comment has been minimized.
This comment has been minimized.
|
Making RwLock::new() non-
except panicking.rs in libstd uses the underlying const-fn impl now that Not an insurmountable problem but there's going to have to be some hackery, like an ad-hoc reimplementation of |
This comment has been minimized.
This comment has been minimized.
|
@mattico I think such a solution would limit a process to at most 1088 instances of RwLock, right? If that's so that seems... unfortunately too low :( |
This comment has been minimized.
This comment has been minimized.
|
Oh that's per process, I thought it was per-thread. That might actually be an issue, yes. |
This comment has been minimized.
This comment has been minimized.
|
Maybe allocate an array per thread (how big?) to use as a bit-set for the RwLock flags, and store a pointer to it in TLS. Then add an index into the set to each RwLock. Now we're only using one TLS per thread that has a RwLock, but it's a bit more of a hack |
This comment has been minimized.
This comment has been minimized.
|
@mattico Have a look at the thread_local crate. It allows you to have per-object TLS without using up TLS indexes. |
This comment has been minimized.
This comment has been minimized.
|
It might be better to handle this in the |
Mark-Simulacrum
added
the
C-bug
label
Jul 26, 2017
This comment has been minimized.
This comment has been minimized.
CodesInChaos
commented
Feb 15, 2018
|
Shouldn't this be tagged as a soundness issue since this it allows UB in safe code? |
This comment has been minimized.
This comment has been minimized.
|
Nobody has been able to actually demonstrate UB in safe code using this yet. It's only theoretical at the moment. We really just need to migrate to |
This comment has been minimized.
This comment has been minimized.
|
What is that supposed to mean? It's either defined or not. Do you mean it won't be tagged I-unsound unless someone demonstrates memory corruption? |
This comment has been minimized.
This comment has been minimized.
The burden of proof is on the code author to show that their code does not have UB. If the code has UB, it might do anything, including working perfectly well. One cannot show absence of UB by example. The Windows Mutex implementation is also wrong (lack of proper initialization), and RwLock is UB on macOS as well (and possibly other non-Linux POSIX platforms) because macOS also says recursive locking of an RwLock is UB when a write lock is involved. (Only recursive write and read-write locking though. Making recursive reads UB is just... wtf, what were they thinking?? And the documentation for the relevant function does not even mention this "detail". Wow.) Oh and the Mutex situation is horrible on all POSIX platforms because there is no I implemented a reentrancy checker for Mutex (which is easier than RwLock) because there are no read locks), and it makes my microbenchmarks slower by ~20%. Not great. |
This comment has been minimized.
This comment has been minimized.
|
Oh, and we are also using SRWLock for
I have been thinking about implementing a lazily initialized But, given all the other problems in particular around RwLock (where at least two major platforms just do not have a reasonable platform-specific implementation) -- it seems more and more reasonable to just roll our own implementation. I also hear that So, how realistic is it to use |
This was referenced Aug 7, 2018
RalfJung
changed the title
RwLock on Windows theoretically allows undefined behavior in safe code
RwLock and Mutex on Window theoretically allows undefined behavior in safe code
Aug 13, 2018
bors
added a commit
that referenced
this issue
Aug 23, 2018
kennytm
added a commit
to kennytm/rust
that referenced
this issue
Aug 24, 2018
This comment has been minimized.
This comment has been minimized.
|
I've opened an internals post to continue more long-form discussion on the topic of continuing to fix these issues. |
retep998 commentedAug 19, 2016
•
edited
According to this back and forth on a Microsoft blog post, it is currently undefined behavior to even try to acquire an SRWLock recursively, even recursive read locks.
Also apparently NT keyed events have no stability guarantee so the current implementation ofNo longer an issue asparking_loton Windows could theoretically break with a new version of Windows.parking_lotuses the stableWaitOnAddresson newer Windows.So uh, what do?