Skip to content

Commit bdf2975

Browse files
committed
feat: openkal.random on getrandom(2)
⭐ **内核自己的调用,而不是 `/dev/urandom`。** 那个设备要一个描述符, 描述符要一条路径,而能力型文件系统刻意不发绝对路径 —— 那是模型在工作。 并且一个还没有文件系统的程序仍然有这个调用。 两张系统调用表各加一条:x86_64 = 318,aarch64 = 278。 ⚠️ **不设 `GRND_NONBLOCK`,而这正是 `BLOCKING` 那一位所报的。** 不设它, 调用会等到熵池播种完成 —— 在一台刚启动几秒的机器上是真的等待。设它则把 等待变成短读,而本接口没有「部分成功」这个状态,所以等待被保留并在能力字里 被命名。 ⚠️ 失败时缓冲区不恢复,而契约就是这么说的:失败的填充留下未指定内容而非 原内容。恢复它会迫使本函数保留一份入参副本 —— 每次成功的调用都为那一次 失败付这个代价。 短返回是内核的(`getrandom` 单次上限 32 MiB),循环是把它变成本接口承诺的 「全有或全无」。 `props = BLOCKING`,不含 `HARDWARE`:读的是内核的池,而池是否由硬件源播种 不是这个后端能观察的。 实测:两次 fill 返回不同字节,props = 1。
1 parent a0a92ea commit bdf2975

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/random.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// openkal.random on Linux --- getrandom(2).
2+
//
3+
// ⭐ THE KERNEL'S OWN CALL AND NOT `/dev/urandom`. The device would need a
4+
// descriptor, which needs a path, which a capability-oriented filesystem
5+
// deliberately does not hand out; and a program early enough in its life not to
6+
// have a filesystem yet still has this call. `getrandom` is the interface the
7+
// kernel offers for exactly this question.
8+
#include "sys.h"
9+
#include <openkal/random.h>
10+
11+
namespace {
12+
13+
// ⚠️ `GRND_NONBLOCK` IS NOT SET, AND THAT IS WHAT `BLOCKING` REPORTS.
14+
//
15+
// Without it the call waits until the pool has been initialised, which on a
16+
// machine seconds into its first boot can be a real wait. Setting it instead
17+
// would turn that wait into a short read — a partial success this interface
18+
// does not have — so the wait is kept and named in the capability word.
19+
constexpr okl_long flags_blocking = 0;
20+
21+
} // namespace
22+
23+
extern "C" int kal_random_fill(void* out, kal_uintptr len) {
24+
if (len == 0) return kal_ok;
25+
if (out == nullptr) return kal_err_invalid;
26+
27+
auto* p = static_cast<unsigned char*>(out);
28+
kal_uintptr filled = 0;
29+
while (filled < len) {
30+
const okl_long r = okl::sys(okl::nr_getrandom,
31+
reinterpret_cast<okl_long>(p + filled),
32+
static_cast<okl_long>(len - filled),
33+
flags_blocking);
34+
if (r < 0) {
35+
// ⚠️ THE BUFFER IS NOT RESTORED, AND THE CONTRACT SAYS IT NEED NOT
36+
// BE: a failed fill leaves the buffer unspecified rather than
37+
// unchanged. Restoring it would oblige this function to keep a copy
38+
// of what it was handed, which is a cost every successful call
39+
// would pay for the benefit of the failing one.
40+
if (r == -4 /* EINTR */) continue;
41+
if (r == -11 /* EAGAIN */) return kal_err_again;
42+
return kal_err_io;
43+
}
44+
// A short return is the kernel's, not this interface's: `getrandom`
45+
// caps a single call at 32 MiB. Looping is what turns it into the
46+
// all-or-nothing this interface promises.
47+
filled += static_cast<kal_uintptr>(r);
48+
}
49+
return kal_ok;
50+
}
51+
52+
// Blocking, because GRND_NONBLOCK is not set above. Not hardware: the kernel's
53+
// pool is what this reads, and whether the pool was seeded from a hardware
54+
// source is not something this backend can observe.
55+
extern "C" const kal_uintptr kal_random_props = KAL_RANDOM_PROP_BLOCKING;

src/sys.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ enum : okl_long {
9797
nr_openat = 257, nr_mkdirat = 258, nr_newfstatat = 262, nr_unlinkat = 263,
9898
nr_renameat = 264, nr_readlinkat = 267, nr_dup3 = 292, nr_execveat = 322,
9999
nr_dup2 = 33, nr_utimensat = 280,
100+
nr_getrandom = 318,
100101
};
101102

102103
#elif defined(__aarch64__)
@@ -170,6 +171,7 @@ enum : okl_long {
170171
nr_clone = 220, nr_execve = 221, nr_wait4 = 260, nr_renameat = 38,
171172
nr_dup3 = 24, nr_execveat = 281, nr_dup2 = -1,
172173
nr_arch_prctl = -1, nr_utimensat = 88,
174+
nr_getrandom = 278,
173175
};
174176

175177
#else

0 commit comments

Comments
 (0)