Skip to content
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
6 changes: 4 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# openkal Specification, version 0.6
# openkal Specification, version 0.7

## 1. Scope

Expand Down Expand Up @@ -38,13 +38,14 @@ provides an interface in whole or not at all.
| `openkal.memory` | a region of the address space | core |
| `openkal.env` | the parameters a program receives at inception | standard |
| `openkal.time` | a time source | standard |
| `openkal.random` | a source of unpredictable bytes | optional |
| `openkal.fs` | a directory, and an open file | standard |
| `openkal.process` | a program image that has been started | standard |
| `openkal.task` | an execution context, and a suspension primitive | standard |
| `openkal.exec` | a region of the address space a program may execute | optional |
| `openkal.event` | readiness of a set of resources | reserved |

Version 0.6 specifies the core, standard and optional interfaces. The reserved
Version 0.7 specifies the core, standard and optional interfaces. The reserved
row is not specified, and its name shall not be used for other purposes.

*Core* denotes an interface every implementation provides. *Standard* denotes
Expand Down Expand Up @@ -152,6 +153,7 @@ implementation provides neither.
| `openkal.fs` | `openkal.fs` | `openkal/fs.h` |
| `openkal.process` | `openkal.process` | `openkal/process.h` |
| `openkal.task` | `openkal.task` | `openkal/task.h` |
| `openkal.random` | `openkal.random` | `openkal/random.h` |
| `openkal.exec` | `openkal.exec` | `openkal/exec.h` |

`openkal.h` includes every header, for a consumer that uses several.
Expand Down
3 changes: 3 additions & 0 deletions SURFACE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ kal_time_monotonic_granularity
kal_time_props
kal_time_sleep
kal_time_wall
# openkal.random
kal_random_fill
kal_random_props
# openkal.fs
kal_fs_close_dir
kal_fs_close_file
Expand Down
21 changes: 19 additions & 2 deletions conformance/mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ repo = "https://github.com/mcpplibs/openkal"
# What it depends upon is therefore openkal and the language. The formatting in
# okc.report is the price of that, and it is sixty lines.
[dependencies]
openkal = "0.6.0"
openkal = "0.7.0"

# The implementation under examination is not named here.
#
Expand Down Expand Up @@ -56,6 +56,7 @@ default = ["core"]
core = []
env = []
time = []
random = []
fs = []
process = ["fs"] # a program is started relative to a directory
task = []
Expand All @@ -74,7 +75,23 @@ standard = ["hosted"]
# rather than an observation that did not hold. The caller names this set for an
# implementation that provides these interfaces, and omits it for one that does
# not --- which is the same choice the implementation itself made.
optional = ["exec"]
# ⚠️ `random` IS HERE AND NOT IN `hosted`, AND THE NOTE ABOVE ALREADY SAID WHY.
#
# It was put in `hosted` first, and the suite then failed to LINK against every
# backend that had not yet implemented it:
#
# lld-link: error: undefined symbol: kal_random_fill
#
# — which is exactly the outcome the paragraph above warns about: a report that
# is a link failure rather than an observation that did not hold. Clause 6.1
# makes an absent interface not a deviation, so a set that demands it turns a
# permitted choice into a build error.
#
# `random` has the same shape as `exec`: every hosted platform provides it, a
# bare-metal one provides it only if its board has a source, and neither is a
# deviation. The caller names this set for an implementation that provides
# these interfaces and omits it for one that does not.
optional = ["exec", "random"]

# The kinds of examination. Behaviour is always performed; the other three are
# selected, because each costs time that a reader running the suite to answer
Expand Down
2 changes: 2 additions & 0 deletions conformance/src/declarations.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ void okc_declarations_c(void)
(void)sizeof(&kal_process_spawn);
(void)sizeof(&kal_process_terminate);
(void)sizeof(&kal_process_wait);
(void)sizeof(&kal_random_fill);
(void)sizeof(&kal_random_props);
(void)sizeof(&kal_stderr);
(void)sizeof(&kal_stdin);
(void)sizeof(&kal_stdout);
Expand Down
70 changes: 70 additions & 0 deletions conformance/src/sections/random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module okc.random;

import openkal.types;
import openkal.random;
import okc.report;
import okc.spec;

namespace okc::random {

void run() {
heading("openkal.random");
#ifndef MCPP_FEATURE_RANDOM
unobserved(kind::behaviour, "openkal.random", "the interface was not selected");
return;
#else
claim("kal_random_props", kal_random_props);

// A fill either succeeds completely or changes nothing. The buffer is
// pre-set to a value the source is unlikely to produce for every byte, so
// that a partial fill --- the state this interface does not have --- would
// be visible rather than being mistaken for entropy.
{
unsigned char buf[32];
for (auto& b : buf) b = 0xA5;
const int rc = kal_random_fill(buf, sizeof buf);
observe(kind::behaviour, rc == kal_ok || rc == kal_err_again,
"a fill reports success or a momentarily empty source");
if (rc == kal_ok) {
bool all_untouched = true;
for (auto b : buf) if (b != 0xA5) { all_untouched = false; break; }
observe(kind::behaviour, !all_untouched,
"a successful fill wrote the buffer");
}
}

// ⚠️ TWO FILLS DIFFER, AND THE CHANCE OF A FALSE REPORT IS STATED RATHER
// THAN LEFT FOR A READER TO WONDER ABOUT.
//
// A source that returned a constant would satisfy every check above. Two
// fills of 32 bytes agreeing by chance has probability 2^-256, which is not
// reachable; a source that agrees is returning a constant, and that is what
// this observes.
{
unsigned char a[32], b[32];
const int ra = kal_random_fill(a, sizeof a);
const int rb = kal_random_fill(b, sizeof b);
if (ra == kal_ok && rb == kal_ok) {
bool identical = true;
for (unsigned i = 0; i < sizeof a; ++i)
if (a[i] != b[i]) { identical = false; break; }
observe(kind::behaviour, !identical,
"two fills do not return the same bytes");
} else {
unobserved(kind::behaviour, "two fills differ",
"a fill reported no entropy");
}
}

// A zero-length fill is not an error: a caller computing a length may
// legitimately arrive at zero, and refusing would oblige every caller to
// branch before calling.
{
const int rc = kal_random_fill(nullptr, 0);
observe(kind::behaviour, rc == kal_ok,
"a fill of zero bytes succeeds");
}
#endif
}

}
6 changes: 6 additions & 0 deletions conformance/src/sections/random.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The section that examines openkal.random.
export module okc.random;

export namespace okc::random {
void run();
}
2 changes: 2 additions & 0 deletions conformance/src/suite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import okc.stream;
import okc.memory;
import okc.env;
import okc.time;
import okc.random;
import okc.fs;
import okc.process;
import okc.task;
Expand All @@ -26,6 +27,7 @@ int run_all() {
memory::run();
env::run();
time::run();
random::run();
fs::run();
process::run();
task::run();
Expand Down
1 change: 1 addition & 0 deletions include/openkal.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "openkal/memory.h"
#include "openkal/env.h"
#include "openkal/time.h"
#include "openkal/random.h"
#include "openkal/fs.h"
#include "openkal/process.h"
#include "openkal/task.h"
Expand Down
57 changes: 57 additions & 0 deletions include/openkal/random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* openkal.random --- a source of unpredictable bytes.
*
* ⚠️ NOT A GENERATOR. This interface answers "give me bytes this environment
* considers unpredictable". It does not define a pseudo-random algorithm, hold
* state between calls, or promise a distribution. A program wanting a
* reproducible sequence seeds its own generator from these bytes once and does
* not come back; a program wanting unpredictability comes back.
*
* ⭐ THE INTERFACE EXISTS BECAUSE NOTHING ELSE HERE CAN SUPPLY IT. Entropy is
* not derivable from the other eight: a clock is not a source (a reading is
* unpredictable to a reader of the source and not to an adversary), and
* `openkal.fs` deliberately cannot open a platform-named object such as
* `/dev/urandom` --- a capability-oriented filesystem hands over roots, not
* absolute paths, and that refusal is the model working.
*
* ⚠️ AND IT IS NOT UNIVERSAL, WHICH IS WHY IT IS ITS OWN INTERFACE. Every
* hosted platform has one; a bare-metal machine has one only if its board does.
* Clause 6.1 already expresses that: an implementation providing no source
* omits these names, and a program that asks fails to link rather than
* receiving zeros.
*/
#ifndef OPENKAL_RANDOM_H
#define OPENKAL_RANDOM_H
#include "types.h"

/* Positions in kal_random_props.
*
* ⚠️ THERE IS NO `AVAILABLE` POSITION, AND ITS ABSENCE IS THE DESIGN. Whether
* an environment has a source is answered by whether this interface is present
* at all (clause 6.1), not by a word a program reads after linking. A backend
* that defined `kal_random_props = 0` while providing no operation would let a
* program past the point the linker exists to stop it at. */
#define KAL_RANDOM_PROP_BLOCKING ((kal_uintptr)1u << 0)
#define KAL_RANDOM_PROP_HARDWARE ((kal_uintptr)1u << 1)

#ifdef __cplusplus
extern "C" {
#endif

/* Fills `len` bytes at `out`.
*
* ⚠️ NO PARTIAL SUCCESS. Either every byte is filled or none is. A caller that
* had to loop would have to distinguish "short read" from "no more entropy",
* and the second is not a state this interface has: an environment either has a
* source or does not provide the interface.
*
* `kal_err_again` reports a source that is momentarily empty --- a machine
* early in boot whose pool has not been seeded. It is not "this environment has
* no source", which is answered by the interface's absence. */
int kal_random_fill(void* out, kal_uintptr len);

extern const kal_uintptr kal_random_props;

#ifdef __cplusplus
}
#endif
#endif /* OPENKAL_RANDOM_H */
2 changes: 1 addition & 1 deletion mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal"
version = "0.6.0"
version = "0.7.0"
description = "openkal: a portable kernel ABI specification. This package carries the normative declarations; implementations are separate packages."
license = "Apache-2.0"
authors = ["mcpplibs"]
Expand Down
53 changes: 53 additions & 0 deletions src/random.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// openkal.random --- a source of unpredictable bytes.
//
// ⭐ THE INTERFACE EXISTS BECAUSE NOTHING ELSE HERE CAN SUPPLY IT, WHICH IS THE
// ONLY REASON ANY INTERFACE HERE EXISTS.
//
// Entropy is not derivable from the other eight. A clock reading is
// unpredictable to a reader of the source and not to an adversary — a
// distinction a port that tried it has already had to write down. And
// `openkal.fs` deliberately cannot open a platform-named object such as
// `/dev/urandom`: a capability-oriented filesystem hands a program its roots
// rather than the whole namespace, so that refusal is the model working rather
// than a gap in it.
//
// ⚠️ NOT UNIVERSAL, AND THAT IS WHY IT IS ITS OWN INTERFACE RATHER THAN AN
// OPERATION ON AN EXISTING ONE. Every hosted platform has a source; a
// bare-metal machine has one only if its board does. Clause 6.1 already
// expresses exactly that shape: an implementation with no source omits these
// names, and a program that asks fails to LINK rather than receiving zeros at
// run time.
module;
#include <openkal/random.h>

export module openkal.random;
export import openkal.types;

export using ::kal_random_fill;
export using ::kal_random_props;

export namespace kal::random {

struct props_tag;
using props = kal::props<props_tag>;

// Positions in kal_random_props. A position, once assigned, retains its
// meaning; an unassigned position reads as zero, so that a program compiled
// against a later specification behaves correctly against an earlier
// implementation.
//
// ⚠️ THERE IS NO `available` POSITION. Whether an environment has a source is
// answered by whether this module can be imported and its names linked, not by
// a word read after linking — see the note on the header.
inline constexpr props blocking{KAL_RANDOM_PROP_BLOCKING};
inline constexpr props hardware{KAL_RANDOM_PROP_HARDWARE};

// Fills `out` with `len` unpredictable bytes, or fills none of it.
inline int fill(void* out, kal_uintptr len) {
return kal_random_fill(out, len);
}

inline props properties() { return props{kal_random_props}; }
inline bool has(props p) { return properties().has(p); }

}
Loading