devices: Update rng devices to be platform agnostic

On Unix, instead of getting random data from `/dev/urandom`, it will get
it from the `rand` platform agnostic crate instead.

OsRng.fill_bytes on unix will make a syscall to getrandom(2) if
available, otherwise it will read from `dev/urandom` after a succesful
poll to `dev/random`. Regardless of which way a random data is
retrieved, if the entropy pool is not intialized, `fill_bytes` will
block until it is intialized. This shouldn't be a problem because it is
a one time cost.

This CL will also upstream the Windows implementation of the rng device.

BUG=b:213149162
TEST=built and presubmits

Change-Id: Ic017f11795f8006e0bf2a04eb0478b3a3d336507
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3657812
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Richard Zhang <rizhang@google.com>
This commit is contained in:
Richard 2022-05-19 12:07:44 -07:00 committed by Chromeos LUCI
parent 69a55d5540
commit a19d6ce89c
5 changed files with 52 additions and 55 deletions

View file

@ -53,6 +53,7 @@ p9 = "*"
power_monitor = { path = "../power_monitor" }
protobuf = { version = "2.3", optional = true }
protos = { path = "../protos", optional = true }
rand = "0.7.2"
remain = "*"
resources = { path = "../resources" }
serde = { version = "1", features = [ "derive" ] }

View file

@ -2,34 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fs::File;
use std::io;
use std::io::{self, Write};
use std::thread;
use base::{error, warn, AsRawDescriptor, Event, PollToken, RawDescriptor, WaitContext};
use base::{error, warn, Event, PollToken, RawDescriptor, WaitContext};
use rand::rngs::OsRng;
use rand::RngCore;
use remain::sorted;
use thiserror::Error;
use vm_memory::GuestMemory;
use super::{DeviceType, Interrupt, Queue, SignalableInterrupt, VirtioDevice, Writer};
use super::{DeviceType, Interrupt, Queue, SignalableInterrupt, VirtioDevice};
use crate::virtio::Writer;
const QUEUE_SIZE: u16 = 256;
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
#[sorted]
#[derive(Error, Debug)]
pub enum RngError {
/// Can't access /dev/urandom
#[error("failed to access /dev/urandom: {0}")]
AccessingRandomDev(io::Error),
}
pub enum RngError {}
pub type Result<T> = std::result::Result<T, RngError>;
struct Worker {
interrupt: Interrupt,
queue: Queue,
mem: GuestMemory,
random_file: File,
}
impl Worker {
@ -39,19 +37,30 @@ impl Worker {
let mut needs_interrupt = false;
while let Some(avail_desc) = queue.pop(&self.mem) {
let index = avail_desc.index;
let random_file = &mut self.random_file;
let written = match Writer::new(self.mem.clone(), avail_desc)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
.and_then(|mut writer| writer.write_from(random_file, std::usize::MAX))
{
Ok(n) => n,
let writer_or_err = Writer::new(self.mem.clone(), avail_desc)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e));
let written_size = match writer_or_err {
Ok(mut writer) => {
let avail_bytes = writer.available_bytes();
let mut rand_bytes = vec![0u8; avail_bytes];
OsRng.fill_bytes(&mut rand_bytes);
match writer.write_all(&rand_bytes) {
Ok(_) => rand_bytes.len(),
Err(e) => {
warn!("Failed to write random data to the guest: {}", e);
0usize
}
}
}
Err(e) => {
warn!("Failed to write random data to the guest: {}", e);
0
0usize
}
};
queue.add_used(&self.mem, index, written as u32);
queue.add_used(&self.mem, index, written_size as u32);
needs_interrupt = true;
}
@ -122,18 +131,15 @@ impl Worker {
pub struct Rng {
kill_evt: Option<Event>,
worker_thread: Option<thread::JoinHandle<Worker>>,
random_file: Option<File>,
virtio_features: u64,
}
impl Rng {
/// Create a new virtio rng device that gets random data from /dev/urandom.
pub fn new(virtio_features: u64) -> Result<Rng> {
let random_file = File::open("/dev/urandom").map_err(RngError::AccessingRandomDev)?;
Ok(Rng {
kill_evt: None,
worker_thread: None,
random_file: Some(random_file),
virtio_features,
})
}
@ -154,13 +160,7 @@ impl Drop for Rng {
impl VirtioDevice for Rng {
fn keep_rds(&self) -> Vec<RawDescriptor> {
let mut keep_rds = Vec::new();
if let Some(random_file) = &self.random_file {
keep_rds.push(random_file.as_raw_descriptor());
}
keep_rds
Vec::new()
}
fn device_type(&self) -> DeviceType {
@ -197,29 +197,25 @@ impl VirtioDevice for Rng {
let queue = queues.remove(0);
if let Some(random_file) = self.random_file.take() {
let worker_result =
thread::Builder::new()
.name("virtio_rng".to_string())
.spawn(move || {
let mut worker = Worker {
interrupt,
queue,
mem,
random_file,
};
worker.run(queue_evts.remove(0), kill_evt);
worker
});
let worker_result =
thread::Builder::new()
.name("virtio_rng".to_string())
.spawn(move || {
let mut worker = Worker {
interrupt,
queue,
mem,
};
worker.run(queue_evts.remove(0), kill_evt);
worker
});
match worker_result {
Err(e) => {
error!("failed to spawn virtio_rng worker: {}", e);
return;
}
Ok(join_handle) => {
self.worker_thread = Some(join_handle);
}
match worker_result {
Err(e) => {
error!("failed to spawn virtio_rng worker: {}", e);
}
Ok(join_handle) => {
self.worker_thread = Some(join_handle);
}
}
}
@ -238,10 +234,7 @@ impl VirtioDevice for Rng {
error!("{}: failed to get back resources", self.debug_label());
return false;
}
Ok(worker) => {
self.random_file = Some(worker.random_file);
return true;
}
Ok(_) => return true,
}
}
false

View file

@ -4,5 +4,6 @@
@include /usr/share/policy/crosvm/common_device.policy
getrandom: 1
openat: return ENOENT
prctl: arg0 == PR_SET_NAME

View file

@ -4,6 +4,7 @@
@include /usr/share/policy/crosvm/common_device.policy
getrandom: 1
open: return ENOENT
openat: return ENOENT
prctl: arg0 == PR_SET_NAME

View file

@ -4,6 +4,7 @@
@include /usr/share/policy/crosvm/common_device.policy
getrandom: 1
open: return ENOENT
openat: return ENOENT
prctl: arg0 == PR_SET_NAME