-
Notifications
You must be signed in to change notification settings - Fork 271
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
preallocated_gen_new
is unsound
#543
Comments
For raw pointers that can never be null Rust provides the `core::ptr::NonNull` type. Our `Secp256k1` type has an inner field that is a non-null pointer; use `NonNull` for it. Fix: #534
Wow, nice catch! It took me a few reads but actually this looks like a fairly basic fuckup on our part, matching up lifetimes. My intuition is that we should be able to put enough restrictions on |
Oh, yeah, actually at least |
Also I'm not sure what the correct variance for the lifetime is. Probably covariant but maybe it'll need invariant when we impl rerandomization? Again, I'm not fresh. |
I think, because rerandomization can't cause any reallocations or moves, we can still use invariance even with a mutable context. But we need to promise, at penalty of unsoundness, that we won't ever change rerandomization to modify the pointer in a lifetime-changing way, because Rust will let us do it. The relationship between variance and mutability is maybe easier to see in terms of subtypes ... if we need an immutable X, it's fine if somebody gives us a subtype of X (e.g. X is a &'a and the subtype is a &'b where 'b outlives 'a), since any subtype of X is certainly an X. But if we need a mutable X then it's not safe for a user to provide a subtype of X ... because we may mutate it in a way that moves it outside of the subtype. The classic example (from the nomicon?) is that if we want an &Animal and the user actually provides a &Dog, we don't care. But if we want a &mut Animal, and the user provides a &mut Dog which we then do |
Oh yeah, |
My original idea was to redefine
The issue is that
I'm not sure what you mean by this. |
Oh, I think I got it! |
@Kixunil I can't find a nice way to make it generic, at least with our MSRV ... but I believe that the existing |
Eh, nah, I'll just duplicate the code. It's not very long and we only need 3 copies. And it prevents your thing from compiling :) |
Stop this from being a generic function over all contexts, to only a function generic over contexts where we can bound the lifetime precisely. Fixes rust-bitcoin#543
Stop this from being a generic function over all contexts, to only a function generic over contexts where we can bound the lifetime precisely. Fixes #543
Wow, I feel stupid that I didn't realize my mistake here. |
At first I was like "we were stupid", but after digging in, the problem was a pretty subtle thing about how variance is propagated from the |
Thanks! TBH, I myself wasn't sure it's actually broken until I wrote the POC code. :) I just had a hunch because I vaguely remembered LHS outlives RHS. I wouldn't be surprised if I got confused about the same thing at some point in the future. |
Fixes unsoundness in `preallocated_gen_new` which previously did not properly constrain the lifetime of the buffer used to back the context object. We introduce an unsafe marker trait, and impl it for our existing preallocated-context markers. Annoyingly the trait has to be public even though it should never be used directly, and is only used alongside the sealed `Context` trait, so it is de-facto sealed itself. Fixes rust-bitcoin#543
1e6eb6c shut clippy up (Andrew Poelstra) f961497 context: introduce unsafe `PreallocatedContext` trait (Andrew Poelstra) Pull request description: Stop this from being a generic function over all contexts, to only a function generic over contexts where we can bound the lifetime precisely. Introduces a new unsafe trait. I *believe* the only code this breaks was already unsound: * code that tried to use one of the `*Preallocated` context markers with an incorrect lifetime * code that tried to use `preallocated_gen_new` with a non-`*Preallocated` marker, which I believe we allowed before (I just noticed this now) and almost certainly would've led to UB on drop Fixes #543 ACKs for top commit: Kixunil: ACK 1e6eb6c tcharding: ACK 1e6eb6c Tree-SHA512: 44eb4637a2f86d5b16d40174cb9e27f37cf8eb4f29546159dbbdcd3326d01f9de2f500ba732376dd84e67ebc3528c709d2d4e2aceb8a329bcb9fb9d25c9b89cb
Reopening. TODO
|
Fixes unsoundness in `preallocated_gen_new` which previously did not properly constrain the lifetime of the buffer used to back the context object. We introduce an unsafe marker trait, and impl it for our existing preallocated-context markers. Annoyingly the trait has to be public even though it should never be used directly, and is only used alongside the sealed `Context` trait, so it is de-facto sealed itself. Fixes rust-bitcoin#543
Fixes unsoundness in `preallocated_gen_new` which previously did not properly constrain the lifetime of the buffer used to back the context object. We introduce an unsafe marker trait, and impl it for our existing preallocated-context markers. Annoyingly the trait has to be public even though it should never be used directly, and is only used alongside the sealed `Context` trait, so it is de-facto sealed itself. Fixes rust-bitcoin#543
I've create two branches on my tree |
Thanks @tcharding! I think the plan should be:
Does that sound good? |
FYI I've added the invalid type demo to the issue so that people coming from advisory can see it. |
Fixes unsoundness in `preallocated_gen_new` which previously did not properly constrain the lifetime of the buffer used to back the context object. We introduce an unsafe marker trait, and impl it for our existing preallocated-context markers. Annoyingly the trait has to be public even though it should never be used directly, and is only used alongside the sealed `Context` trait, so it is de-facto sealed itself. Fixes rust-bitcoin#543
Looks like we're missing backport for 0.24.x I don't even see the branch. Also maybe clean up some branches on GitHub? It's quite hard to find the right ones among so many. I'm going to sleep now so if you happen to release during that time I will file advisory PR when I wake up. (Also added missing 0.25 to it; the spec looks crazy but hopefully correct.) |
Fixes unsoundness in `preallocated_gen_new` which previously did not properly constrain the lifetime of the buffer used to back the context object. We introduce an unsafe marker trait, and impl it for our existing preallocated-context markers. Annoyingly the trait has to be public even though it should never be used directly, and is only used alongside the sealed `Context` trait, so it is de-facto sealed itself. Fixes rust-bitcoin#543
Pushed 0.24.x branch; deleted all the old PR branches that for some reason were pushed to |
Published the 0.22.x and 0.23.x backports. |
So unless I'm mistaken once #559 is merged and released I can file the advisory PR? |
Yep! |
Ok @Kixunil we should be good to go with the advisory. |
Filed rustsec/advisory-db#1480 |
FYI looks like yanking was a good idea. :) use-ink/ink#1528 I hope to remember this one in the future. |
rust-bitcoin/rust-secp256k1#543 Signed-off-by: Mark T. B. Carroll <[email protected]>
Behold, use-after-free!
Yep, no
unsafe
. Curiously, this seems to corrupt the heap on my machine or something because it crashes after printing the key. Anyway, it's still UB so I'm lucky I didn't get nasal daemons. 🤷♂️But, but, but there's
C: 'buf
bound!The problem is
C: 'buf
meansC
outlives'buf
, so in our case'static 'buf
which is trivially true for all lifetimes'buf
.In theory the bound should've been inverted:
'buf: C
. But sadly, that's not a valid Rust syntax.The bad news is I see no way of avoiding putting lifetimes everywhere with the current design.
Possibilities I can think of:
Secp256k1
unsized type (to preventmem::swap
which is curiously forbidden by secp). This will needlessly addsize
back as slice metadata but we can't do anything about it until extern types are stabilized and in our MSRV. I thinkBox<Secp256k1>
could be valid, if not we need to have our ownBoxedSecp256k1
.Context
,Signing
, andVerification
traits contaminating everything.Maybe there's some ridiculous hack I didn't think about (my mind is not fresh rn), so if anyone knows that'd be great.
@apoelstra found another problem with the API: it's possible to cause invalid deallocation. Demo for reference:
Notice that the array lives for the duration of the whole function so wrong lifetimes do not cause UB but invalid deallocation does.
The text was updated successfully, but these errors were encountered: