Fix some `getrandom` behavior

This commit is contained in:
Ruihan Li 2025-09-30 09:18:55 +08:00 committed by Tate, Hongliang Tian
parent 36a38c3f04
commit d43fbd8f89
12 changed files with 128 additions and 56 deletions

View File

@ -18,7 +18,7 @@ impl Device for Full {
}
fn id(&self) -> DeviceId {
// Same value with Linux
// The same value as Linux
DeviceId::new(1, 7)
}

View File

@ -20,7 +20,7 @@ pub use urandom::Urandom;
use crate::{
fs::{
device::{add_node, Device, DeviceId, DeviceType},
device::{add_node, Device, DeviceId},
fs_resolver::FsPath,
ramfs::RamFs,
},

View File

@ -1,11 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
use super::*;
use crate::{
events::IoEvents,
fs::inode_handle::FileIo,
fs::{
device::{Device, DeviceId, DeviceType},
inode_handle::FileIo,
},
prelude::*,
process::signal::{PollHandle, Pollable},
};
@ -18,7 +18,7 @@ impl Device for Null {
}
fn id(&self) -> DeviceId {
// Same value with Linux
// The same value as Linux
DeviceId::new(1, 3)
}
@ -28,7 +28,7 @@ impl Device for Null {
}
impl Pollable for Null {
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}
@ -40,6 +40,8 @@ impl FileIo for Null {
}
fn write(&self, reader: &mut VmReader) -> Result<usize> {
Ok(reader.remain())
let len = reader.remain();
reader.skip(len);
Ok(len)
}
}

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
use super::Urandom;
use crate::{
events::IoEvents,
fs::{
@ -10,15 +9,14 @@ use crate::{
},
prelude::*,
process::signal::{PollHandle, Pollable},
util::random::getrandom,
};
pub struct Random;
impl Random {
pub fn getrandom(buf: &mut [u8]) -> Result<usize> {
getrandom(buf);
Ok(buf.len())
pub fn getrandom(writer: &mut VmWriter) -> Result<usize> {
// TODO: Support true randomness by collecting environment noise.
Urandom::getrandom(writer)
}
}
@ -38,7 +36,7 @@ impl Device for Random {
}
impl Pollable for Random {
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}
@ -46,13 +44,12 @@ impl Pollable for Random {
impl FileIo for Random {
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
let mut buf = vec![0; writer.avail()];
let size = Self::getrandom(buf.as_mut_slice());
writer.write_fallible(&mut buf.as_slice().into())?;
size
Self::getrandom(writer)
}
fn write(&self, reader: &mut VmReader) -> Result<usize> {
Ok(reader.remain())
let len = reader.remain();
reader.skip(len);
Ok(len)
}
}

View File

@ -3,11 +3,14 @@
use ostd::mm::{DmaCoherent, FrameAllocOptions, HasPaddr, VmIo};
use tdx_guest::tdcall::{get_report, TdCallError};
use super::*;
use crate::{
error::Error,
events::IoEvents,
fs::{inode_handle::FileIo, utils::IoctlCmd},
fs::{
device::{Device, DeviceId, DeviceType},
inode_handle::FileIo,
utils::IoctlCmd,
},
prelude::*,
process::signal::{PollHandle, Pollable},
};

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
use crate::{
events::IoEvents,
fs::{
@ -16,9 +14,26 @@ use crate::{
pub struct Urandom;
impl Urandom {
pub fn getrandom(buf: &mut [u8]) -> Result<usize> {
getrandom(buf);
Ok(buf.len())
pub fn getrandom(writer: &mut VmWriter) -> Result<usize> {
const IO_CAPABILITY: usize = 4096;
if !writer.has_avail() {
return Ok(0);
}
let mut buffer = vec![0; writer.avail().min(IO_CAPABILITY)];
let mut written_bytes = 0;
while writer.has_avail() {
getrandom(&mut buffer[..writer.avail().min(IO_CAPABILITY)]);
match writer.write_fallible(&mut VmReader::from(buffer.as_slice())) {
Ok(len) => written_bytes += len,
Err((err, 0)) if written_bytes == 0 => return Err(err.into()),
Err((_, len)) => return Ok(written_bytes + len),
}
}
Ok(written_bytes)
}
}
@ -38,7 +53,7 @@ impl Device for Urandom {
}
impl Pollable for Urandom {
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}
@ -46,13 +61,12 @@ impl Pollable for Urandom {
impl FileIo for Urandom {
fn read(&self, writer: &mut VmWriter) -> Result<usize> {
let mut buf = vec![0; writer.avail()];
let size = Self::getrandom(buf.as_mut_slice());
writer.write_fallible(&mut buf.as_slice().into())?;
size
Self::getrandom(writer)
}
fn write(&self, reader: &mut VmReader) -> Result<usize> {
Ok(reader.remain())
let len = reader.remain();
reader.skip(len);
Ok(len)
}
}

View File

@ -1,11 +1,11 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
use super::*;
use crate::{
events::IoEvents,
fs::inode_handle::FileIo,
fs::{
device::{Device, DeviceId, DeviceType},
inode_handle::FileIo,
},
prelude::*,
process::signal::{PollHandle, Pollable},
};
@ -18,7 +18,7 @@ impl Device for Zero {
}
fn id(&self) -> DeviceId {
// Same value with Linux
// The same value as Linux
DeviceId::new(1, 5)
}
@ -28,7 +28,7 @@ impl Device for Zero {
}
impl Pollable for Zero {
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
fn poll(&self, mask: IoEvents, _poller: Option<&mut PollHandle>) -> IoEvents {
let events = IoEvents::IN | IoEvents::OUT;
events & mask
}

View File

@ -4,28 +4,39 @@ use super::SyscallReturn;
use crate::{device, prelude::*};
pub fn sys_getrandom(buf: Vaddr, count: usize, flags: u32, ctx: &Context) -> Result<SyscallReturn> {
let flags = GetRandomFlags::from_bits_truncate(flags);
let flags = GetRandomFlags::from_bits(flags)
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid flags"))?;
debug!(
"buf = 0x{:x}, count = 0x{:x}, flags = {:?}",
buf, count, flags
);
// TODO: support nonblock flag.
// Currently our getrandom implementation relies on x86-specific `rdrand` instruction, so it will never block.
let mut buffer = vec![0u8; count];
if flags.contains(GetRandomFlags::GRND_INSECURE | GetRandomFlags::GRND_RANDOM) {
return_errno_with_message!(
Errno::EINVAL,
"requesting insecure and blocking randomness makes no sense"
);
}
// Currently we don't really generate true randomness by collecting environment noise, so we
// will never block.
// TODO: Support `GRND_NONBLOCK` and `GRND_INSECURE`.
let user_space = ctx.user_space();
let mut writer = user_space.writer(buf, count)?;
let read_len = if flags.contains(GetRandomFlags::GRND_RANDOM) {
device::Random::getrandom(&mut buffer)?
device::Random::getrandom(&mut writer)?
} else {
device::Urandom::getrandom(&mut buffer)?
device::Urandom::getrandom(&mut writer)?
};
ctx.user_space()
.write_bytes(buf, &mut VmReader::from(buffer.as_slice()))?;
Ok(SyscallReturn::Return(read_len as isize))
}
bitflags::bitflags! {
#[derive(Pod)]
#[repr(C)]
pub struct GetRandomFlags: u32 {
/// Flags for `getrandom`.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.16.9/source/include/uapi/linux/random.h#L56>.
struct GetRandomFlags: u32 {
const GRND_NONBLOCK = 0x0001;
const GRND_RANDOM = 0x0002;
const GRND_INSECURE = 0x0004;

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
@ -64,4 +62,4 @@ FN_SETUP(close)
{
CHECK(close(fd));
}
END_SETUP()
END_SETUP()

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MPL-2.0
#include <unistd.h>
#include <sys/mman.h>
#include <sys/fcntl.h>
#include "../test.h"
#define PAGE_SIZE 4096
FN_TEST(short_rw)
{
int fd;
char *buf;
fd = TEST_SUCC(open("/dev/random", O_RDONLY));
buf = TEST_SUCC(mmap(NULL, PAGE_SIZE * 3, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
TEST_SUCC(munmap(buf + PAGE_SIZE * 2, PAGE_SIZE));
// Invalid address
TEST_ERRNO(read(fd, buf + PAGE_SIZE * 2, PAGE_SIZE), EFAULT);
TEST_RES(read(fd, buf + PAGE_SIZE * 2, 0), _ret == 0);
// Valid address, insufficient space
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - 1, PAGE_SIZE), _ret == 1);
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - (PAGE_SIZE - 1), PAGE_SIZE + 2),
_ret == (PAGE_SIZE - 1));
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - PAGE_SIZE, PAGE_SIZE + 2),
_ret == PAGE_SIZE);
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - (PAGE_SIZE + 1), PAGE_SIZE + 2),
_ret == (PAGE_SIZE + 1));
// Valid address, sufficient space
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - 1, 1), _ret == 1);
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - (PAGE_SIZE - 1), PAGE_SIZE - 2),
_ret == (PAGE_SIZE - 2));
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - PAGE_SIZE, PAGE_SIZE - 1),
_ret == (PAGE_SIZE - 1));
TEST_RES(read(fd, buf + PAGE_SIZE * 2 - (PAGE_SIZE + 1), PAGE_SIZE),
_ret == PAGE_SIZE);
TEST_SUCC(munmap(buf, PAGE_SIZE * 2));
TEST_SUCC(close(fd));
}
END_TEST()

View File

@ -67,3 +67,4 @@ epoll/epoll_err
epoll/poll_err
file_io/iovec_err
devfs/full
devfs/random

View File

@ -513,7 +513,7 @@ getrandom01
getrandom02
getrandom03
getrandom04
# getrandom05
getrandom05
getresgid01
# getresgid01_16