Repository navigation
Use /dev/urandom on macOS #38
Description
Activity
Miri would be sad about this, as we can hook
SecRandomCopyBytesmuch easier than hooking a file. But you probably shouldn't let that affect your decision. This might temporarily regress random testing on macOS on Miri, though.Cc @oli-obk
Note that
ringusesSecRandomCopyBytes, see this issue: briansmith/ring#149As is noted in the second comment from briansmith/ring#149
SecRandomCopyBytesis a CSPRNG which periodically reseeds from/dev/random. This CSPRNG is run on its own thread with access synchronised via Grand Central Dispatch. UsingSecRandomCopyBytesinvolves linking withSecurity.frameworkand by extension Core Foundation which does a lot of life before main initialisation and is likely the cause of rust-random/rand#733.Interestingly MacOS supports a
getentropysyscall since 10.12 which reads from the same source as/dev/random. Note that/dev/randomand/dev/urandomappear to be exactly the same on MacOS.My suggestion would be to take the same approach on MacOS as Linux. Try the
getentropysyscall first and fall back to reading/dev/(u)randomonENOSYS.For iOS we would try
getentropyfirst and fall back toSecRandomCopyBytesonENOSYSas/dev/urandomis not directly accessible from the sandbox. Or, if we are not supporting versions before iOS 10, we can just usegetentropywith no fallback.If we agree with this approach I am happy to implement.
@RalfJung I think this would also make Miri happy.
At first glance looks good to me and it will be great if you'll implement it! But before doing it I think we should wait for #13 to be closed first.
I think this would also make Miri happy.
The dedicated syscall for Linux works great for Miri, so I suppose the same would be true for macOS. :)
Let me see if I'm understanding this correctly. We don't want to use
SecRandomCopyBytesbecause:- It makes linking more complex (Use getrandom crate rust-lang/rust#62082 (comment))
- It incurs a notable startup cost (It makes startup time significantly longer on Mac rand#733)
First, iOS:
For iOS we would try
getentropyfirst and fall back toSecRandomCopyBytesonENOSYSas/dev/urandomis not directly accessible from the sandbox.Both of the downsides to using
SecRandomCopyBytesoccur regardless of if we actually use it. So it would seem best to just only useSecRandomCopyBytesuntil the min Rust supported iOS version is iOS 10. When that happens,getentropy(2)should be added to thelibccrate, and we should just calllibc::getentropy. I've opened rust-lang/libc#1406 to figure out what the min iOS version should even be.Now, macOS:
My suggestion would be to take the same approach on MacOS as Linux. Try the
getentropysyscall first and fall back to reading/dev/(u)randomonENOSYS.The main problem with this approach is that it's not supported on macOS. Unlike Linux, which has a stable syscall API, the stable API for macOS is the libc and other system libraries/frameworks. This means that there's not a way to "check" if
getentropyis a supported syscall. It's either in the libc or it is not. For example, thelibccrate has all theSYS_*numbers on Linux, but does not expose them on macOS, as they are unstable and an implementation detail.Given this, it seems like the implementation options for macOS are:
- Stick with using
SecRandomCopyBytes
- Upsides: simple, same implementation as iOS
- Downsides: hard to link, and a (small) startup cost - Do what the solaris implementation does to dynamically see if
getentropyis in the libc, and then fall back to/dev/randomif it isn't there.
- Upsides: mitigates file descriptor depletion attack
- Downsides: very hacky, doesn't work well with miri (@RalfJung is this true?) - Just use
/dev/randomalways:
- Upsides: very simple
- Downsides: file descriptor depletion attack - Just use
libc::getentropyunconditionally
- Infeasible until min macOS version supported by Rust is 10.12
Despite it's weirdness, I think approch (2) is the best, as we already have similar implementations, and it seems to solve most of the problems raised in rust-lang/rust#62082
Grand Central Dispatch is not initialised until the first call to
SecRandomCopyBytesat which point at least two threads are spawned so there is an advantage to using it only as a fallback. Also, on iOS it is almost guaranteed that you are linking against Core Foundation. I believe it is also possible to dynamically loadSecRandomCopyBytesif you really want to guarantee no overhead.By syscall I mean calling a function provided by
libSystemon MacOS which are can be called from thelibccrate, and byENOSYSI mean that dynamically loading that symbol failed. I felt that gave a simpler explanation. Sorry if that caused any confusion.So yes I agree with your option 2. This pattern is used fairly regularly in Rust's libstd and I think is compatible with miri. There's even a macro for it!
- Downsides: very hacky, doesn't work well with miri (@RalfJung is this true?)
dlsymshould be easier to support in Miri than file descriptors. It's a hack in Miri as well, but not much worse than some of the things we already do. ;)@ebarnard that's awesome info (especially the macro), it's good to know that weak linkage is more reliable than I thought!
So I think we're agreed with what to do here:
- Use weak linkage to
getentropy, and if we have it, call withchunks(256). - Fallback to a suboptimal method:
- iOS:
SecRandomCopyBytes - macOS: read from
/dev/random
- Use weak linkage to
I've been looking into the iOS situation a bit more and it seems the header
sys/random.hisn't included in the iOS 12 SDK although oddly the syscall number does appear insys/syscall.hand the symbol is exported fromlibsystem_kernel.dylib.Therefore presumably it is a private API so we should probably just stick to using only
SecRandomCopyByteson iOS.- Use weak linkage to
getentropy, and if we have it, call withchunks(256).
Yes, it behaves similarly to the OpenBSD syscall so we would need
chunks(256).- Use weak linkage to
@ebarnard
The TLS removal PR got merged, so you can start macOS/iOS rework.Therefore presumably it is a private API so we should probably just stick to using only
SecRandomCopyByteson iOS.Sounds very reasonable to me.
See this comment by @RalfJung. It also should help with rust-random/rand#733.
Open question: do we need to read one byte from
/dev/randomto ensure that entropy pool has initialized?cc @ebarnard