Skip to content

Use getrandom syscall for all Linux and Android targets. #45896

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

Merged
merged 2 commits into from
Nov 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 5 additions & 50 deletions src/libstd/sys/unix/rand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,45 +32,14 @@ mod imp {
use libc;
use sys::os::errno;

#[cfg(all(target_os = "linux",
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x")))]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn getrandom(buf: &mut [u8]) -> libc::c_long {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
const NR_GETRANDOM: libc::c_long = 0x40000000 + 318;
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
const NR_GETRANDOM: libc::c_long = 318;
#[cfg(target_arch = "x86")]
const NR_GETRANDOM: libc::c_long = 355;
#[cfg(target_arch = "arm")]
const NR_GETRANDOM: libc::c_long = 384;
#[cfg(target_arch = "s390x")]
const NR_GETRANDOM: libc::c_long = 349;
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
const NR_GETRANDOM: libc::c_long = 359;
#[cfg(target_arch = "aarch64")]
const NR_GETRANDOM: libc::c_long = 278;

const GRND_NONBLOCK: libc::c_uint = 0x0001;

unsafe {
libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why libc::GRND_NONBLOCK? Presumably users want this call to be blocking?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GRND_NONBLOCK causes getrandom to return an error immediately rather than blocking indefinitely if the kernel randomness source isn't yet initialized. It only matters for things running around init time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right behavior is to block indefinitely until the kernel randomness source is initialized. See, e.g., this paper. The vast majority of applications that need cryptographic randomness need it to actually be secure. The tiny minority of applications that don't can call getrandom directly if they really want to. This is a secure-by-default vs insecure-by-default thing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API is not publicly exposed. It's only used by RandomState.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, Python 3.5 had some controversy about this: https://lwn.net/Articles/693189/

But Python 3.6 does default to blocking now: https://docs.python.org/3.6/library/os.html#os.urandom

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API is not publicly exposed. It's only used by RandomState.

Sure, but that implies that RandomState would then be insecure. To be concrete on the problem here: if getrandom with GRND_NONBLOCK returns immediately because the entropy system isn't yet initialized, and then you fall back on reading from /dev/urandom, it means that (barring the system getting initialized between the call to getrandom and the read from /dev/urandom), you'll be reading insecure entropy.

In fact, it won't just be that single read that will return insecure entropy, it could be all future reads. As section 3.1 of this paper (see the "Corollary" section under "Reality 1") describes, reading while the entropy system isn't yet initialized can actually allow an attacker to execute attacks that compromise the entropy system on an ongoing basis into the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is quite a bit of discussion about this here: #32953.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, fair enough. The TLDR of that discussion seems to be that using RandomState for HashMaps right after boot is problematic on embedded devices because of the blocking problem.

In keeping with the general trend of default-secure, perhaps we could have blocking be the default, but add a constructor that produces a RandomState that doesn't block? Users that need it explicitly could call that constructor, but most users would get the secure option without having to do anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't solve the underlying problem, which is that you don't have control of the construction of every HashMap in your dependency graph.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point. I hadn't thought of that.

}
}

#[cfg(not(all(target_os = "linux",
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x"))))]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
Expand All @@ -94,14 +63,7 @@ mod imp {
return true
}

#[cfg(all(target_os = "linux",
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x")))]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn is_getrandom_available() -> bool {
use io;
use sync::atomic::{AtomicBool, Ordering};
Expand All @@ -125,14 +87,7 @@ mod imp {
AVAILABLE.load(Ordering::Relaxed)
}

#[cfg(not(all(target_os = "linux",
any(target_arch = "x86_64",
target_arch = "x86",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "s390x"))))]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn is_getrandom_available() -> bool { false }

pub fn fill_bytes(v: &mut [u8]) {
Expand Down