Skip to content

Commit 8d35678

Browse files
0.24.0 --- openkal 0.23: placement, sealing, private and prefetched parts; attenuation and Landlock; faults, fault reports, samples, touches; resources (#48)
Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent e146e78 commit 8d35678

14 files changed

Lines changed: 2125 additions & 35 deletions

File tree

‎README.md‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.
55

66
```toml
77
[dependencies]
8-
openkal = "0.22.0"
8+
openkal = "0.23.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.23.0"
11+
openkal-linux = "0.24.0"
1212
```
1313

1414
## Why it does not use a C library
@@ -102,6 +102,43 @@ A program that carries a runtime of its own answers from that runtime instead
102102
(`pthread_getattr_np`), because in that arrangement the runtime is what owns the
103103
stacks and is what a program above openkal would ask for itself.
104104

105+
## What 0.24 adds (openkal 0.23)
106+
107+
**A region at an address.** `kal_exec_alloc_at` maps the region with
108+
`MAP_FIXED_NOREPLACE`, which refuses rather than replaces what is there; a kernel
109+
before 4.17 reads the flag as a hint, so a mapping placed elsewhere is released
110+
and the request refused. `KAL_EXEC_PROP_PLACE` is claimed.
111+
112+
**A part sealed, a part read in.** `kal_exec_seal_part` is `mprotect` to read
113+
only, and `mseal` for `KAL_EXEC_SEAL_FOREVER` where the kernel has it (6.10;
114+
`KAL_EXEC_PROP_SEAL_FOREVER` is claimed by asking it once). `KAL_EXEC_MAP_PREFETCH`
115+
is `MAP_POPULATE`; `KAL_EXEC_MAP_BTI` is `PROT_BTI` on arm64, claimed where the
116+
kernel accepts it.
117+
118+
**A handle with less** (`kal_fs_dir_attenuate`): a table indexed by descriptor,
119+
consulted by every operation of `openkal.fs` that takes a directory and empty in
120+
a program that attenuates nothing. A read-only handle refuses every change; one
121+
that states nothing changes answers absence from a listing it has read, within
122+
a budget the resource preference sets; a recorded one appends `<kind><name>\0`
123+
to its record in batches, and `e\0` at `kal_exit` or the C library's
124+
destructors. A grant carries its rights in `KAL_GRANT_RIGHTS`, and its record
125+
as a descriptor after the grants.
126+
127+
**Confinement** (`KAL_SPAWN_CONFINE`, `KAL_PROCESS_PROP_CONFINE`): Landlock, at
128+
the ABI version the kernel reports, set in the started image before it is
129+
replaced. The grants are the rules, by their rights; the program's own file,
130+
`/proc` and `/dev/null` are added, and `PR_SET_NO_NEW_PRIVS` is set.
131+
132+
**Observation.** A fault is reported on an alternate stack from
133+
`/proc/self/maps` and the ELF symbol tables, and the program is then ended as
134+
the kernel ends it. Samples are `ITIMER_PROF` and `SIGPROF` into a ring a
135+
handler writes without a lock. Touches: a part recorded is made readable and
136+
not executable, and the handler makes each page executable again at its first
137+
execution and records it; a fault that is not a touch is reported, or passed
138+
to the program's own handler. `kal_process_info` reports minor and major
139+
faults (`getrusage`, `/proc/<pid>/stat`). `kal_memory_info` is `MemAvailable`
140+
within the cgroup's `memory.max`; economy sets `PR_SET_THP_DISABLE`.
141+
105142
## What 0.23 adds (openkal 0.22)
106143

107144
**The null device at a position.** `KAL_SPAWN_NULL_IN`, `KAL_SPAWN_NULL_OUT` and

‎mcpp.toml‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.23.0"
4+
version = "0.24.0"
55
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
66
license = "Apache-2.0"
77

@@ -62,10 +62,12 @@ provides-interfaces = [
6262
"openkal.timeout",
6363
"openkal.map",
6464
"openkal.region",
65+
"openkal.prof",
66+
"openkal.debug",
6567
]
6668

6769
[dependencies]
68-
openkal = "0.22.0"
70+
openkal = "0.23.0"
6971

7072
# The package contributes definitions and no modules. The interface it
7173
# implements is declared by the specification package, which this package

‎src/abort.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "sys.h"
2+
#include "rights.h"
23
#include <openkal/abort.h>
34

45
extern "C" {
@@ -20,6 +21,9 @@ extern "C" {
2021
// destructors first --- is not observable in a small program and is observable
2122
// in a large one, which is why it is stated rather than left to judgement.
2223
[[noreturn]] void kal_exit(int code) {
24+
// What the program's attenuated handles recorded is written, and ended
25+
// (0.23, fs.h): a record without its end is of a program that did not end.
26+
okl::rights::end_of_program();
2327
for (;;) okl::sys(okl::nr_exit_group, code);
2428
}
2529

‎src/env.cpp‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "sys.h"
22
#include <openkal/env.h>
33

4+
namespace okl { int resource_preference(); }
45
namespace okl {
56

67
// The vectors the kernel places on the stack at inception. They are recorded
@@ -252,4 +253,45 @@ kal_intptr kal_env_system(char* out, kal_uintptr cap) {
252253
return give("linux", 5, out, cap);
253254
}
254255

256+
// THE RESOURCE PREFERENCE (0.23), which a start conveys in KAL_RESOURCES
257+
// (process.cpp) and a person sets the same way.
258+
int kal_env_resources(void) { return okl::resource_preference(); }
259+
260+
}
261+
262+
namespace okl {
263+
int resource_preference() {
264+
static int known = -1;
265+
int v = __atomic_load_n(&known, __ATOMIC_RELAXED);
266+
if (v >= 0) return v;
267+
v = 0;
268+
constexpr char key[] = "KAL_RESOURCES=";
269+
constexpr okl_uptr key_len = sizeof key - 1;
270+
for (char** e = okl::g_envp; e && *e; ++e) {
271+
okl_uptr i = 0;
272+
while (i < key_len && (*e)[i] == key[i]) ++i;
273+
if (i != key_len) continue;
274+
const char* val = *e + key_len;
275+
auto is = [&](const char* w) {
276+
okl_uptr k = 0;
277+
while (w[k] && val[k] == w[k]) ++k;
278+
return w[k] == '\0' && val[k] == '\0';
279+
};
280+
v = is("performance") ? 1 : is("economy") ? 2 : 0;
281+
break;
282+
}
283+
__atomic_store_n(&known, v, __ATOMIC_RELAXED);
284+
return v;
255285
}
286+
} // namespace okl
287+
288+
namespace {
289+
// THE ENVIRONMENT'S OWN POLICY FOR MEMORY, AS THE PREFERENCE ASKS (0.23): a
290+
// program started for economy has no transparent huge pages, which hold a whole
291+
// two mebibytes resident for the first byte touched. After `capture', which
292+
// reads the environment.
293+
[[gnu::constructor(102)]] void apply_resource_preference() {
294+
if (okl::resource_preference() == 2)
295+
okl::sys(okl::nr_prctl, 41 /* PR_SET_THP_DISABLE */, 1, 0, 0, 0);
296+
}
297+
} // namespace

‎src/exec.cpp‎

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ okl_uptr round_up(okl_uptr n, okl_uptr to) { return (n + to - 1) & ~(to - 1); }
3939

4040
} // namespace
4141

42+
namespace {
43+
// Whether this kernel has `mseal': it answers a range of no length with zero,
44+
// and a kernel without it ENOSYS.
45+
bool sealing() {
46+
static int known = -1;
47+
if (known < 0) known = okl::failed(okl::sys(okl::nr_mseal, 0, 0, 0)) ? 0 : 1;
48+
return known == 1;
49+
}
50+
// Whether instructions mapped PROT_BTI are held to branch targets: the
51+
// processor states it in AT_HWCAP2 (HWCAP2_BTI), and only on aarch64.
52+
bool branch_targets() {
53+
#if defined(__aarch64__)
54+
return (okl::auxval(26 /* AT_HWCAP2 */) & (okl_ulong{1} << 17)) != 0;
55+
#else
56+
return false;
57+
#endif
58+
}
59+
} // namespace
60+
4261
extern "C" {
4362

4463
void* kal_exec_alloc(kal_uintptr size) {
@@ -51,6 +70,26 @@ void* kal_exec_alloc(kal_uintptr size) {
5170
return reinterpret_cast<void*>(r);
5271
}
5372

73+
// AT AN ADDRESS (0.23): MAP_FIXED_NOREPLACE, which refuses rather than replaces
74+
// what is there. A kernel before 4.17 does not know the flag and reads the
75+
// address as a hint, so the answer is compared and a mapping placed elsewhere
76+
// released: the address is either given or refused, never approximated.
77+
void* kal_exec_alloc_at(void* at, kal_uintptr size) {
78+
const okl_uptr where = reinterpret_cast<okl_uptr>(at);
79+
if (size == 0 || where == 0 || (where & (page() - 1)) != 0) return nullptr;
80+
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), page());
81+
const okl_long r = okl::sys(okl::nr_mmap, static_cast<okl_long>(where), static_cast<okl_long>(bytes),
82+
okl::prot_read | okl::prot_write,
83+
okl::map_private | okl::map_anonymous | 0x100000 /* MAP_FIXED_NOREPLACE */,
84+
-1, 0);
85+
if (okl::failed(r)) return nullptr;
86+
if (static_cast<okl_uptr>(r) != where) {
87+
okl::sys(okl::nr_munmap, r, static_cast<okl_long>(bytes));
88+
return nullptr;
89+
}
90+
return at;
91+
}
92+
5493
int kal_exec_publish(void* p, kal_uintptr size) {
5594
if (p == nullptr || size == 0) return kal_err_invalid;
5695
const okl_uptr bytes = round_up(static_cast<okl_uptr>(size), page());
@@ -114,6 +153,25 @@ int kal_exec_publish_part(void* region, kal_uintptr offset, kal_uintptr size) {
114153
return okl::failed(r) ? okl::translate(r) : kal_ok;
115154
}
116155

156+
// SEALED READ-ONLY (0.23): the protection, and, for ever, `mseal' (Linux 6.10)
157+
// over it, after which the kernel refuses to change the part's protection or to
158+
// unmap it --- `mprotect' and `munmap' of it report EPERM, which this
159+
// implementation reports as kal_err_permission.
160+
int kal_exec_seal_part(void* region, kal_uintptr offset, kal_uintptr size, kal_uintptr flags) {
161+
const okl_uptr g = page();
162+
if (region == nullptr || size == 0 || (offset & (g - 1)) != 0 || (size & (g - 1)) != 0
163+
|| (reinterpret_cast<okl_uptr>(region) & (g - 1)) != 0 || (flags & ~KAL_EXEC_SEAL_FOREVER) != 0)
164+
return kal_err_invalid;
165+
const okl_long at = reinterpret_cast<okl_long>(static_cast<char*>(region) + offset);
166+
const okl_long r = okl::sys(okl::nr_mprotect, at, static_cast<okl_long>(size), okl::prot_read);
167+
if (okl::failed(r)) return okl::translate(r);
168+
if ((flags & KAL_EXEC_SEAL_FOREVER) && sealing()) {
169+
const okl_long m = okl::sys(okl::nr_mseal, at, static_cast<okl_long>(size), 0);
170+
if (okl::failed(m)) return okl::translate(m);
171+
}
172+
return kal_ok;
173+
}
174+
117175
kal_uintptr kal_exec_granularity(void) { return page(); }
118176

119177
// A PART THAT IS A FILE'S (0.21): the file's pages mapped over the part,
@@ -127,18 +185,29 @@ int kal_exec_map_part(void* region, kal_uintptr offset, kal_uintptr size, kal_fi
127185
const int fd = okl::unpack(f.h);
128186
if (region == nullptr || size == 0 || fd < 0 || (offset & (g - 1)) != 0 || (size & (g - 1)) != 0
129187
|| (reinterpret_cast<okl_uptr>(region) & (g - 1)) != 0 || at % KAL_MAP_ALIGNMENT != 0
130-
|| (how & ~KAL_EXEC_MAP_EXECUTE) != 0)
188+
|| (how & ~(KAL_EXEC_MAP_EXECUTE | KAL_EXEC_MAP_PREFETCH | KAL_EXEC_MAP_BTI | KAL_EXEC_MAP_WRITE)) != 0
189+
|| ((how & KAL_EXEC_MAP_WRITE) && (how & KAL_EXEC_MAP_EXECUTE)))
131190
return kal_err_invalid;
132191
okl::kstat st{};
133192
const okl_long s = okl::sys(okl::nr_fstat, fd, reinterpret_cast<okl_long>(&st));
134193
if (okl::failed(s)) return okl::translate(s);
135194
if ((st.mode & okl::s_ifmt) != okl::s_ifreg) return kal_err_not_supported;
136195
const kal_u64 length = st.size > 0 ? static_cast<kal_u64>(st.size) : 0;
137196
if (at > length || static_cast<kal_u64>(size) > length - at) return kal_err_invalid;
138-
const okl_long prot = okl::prot_read | ((how & KAL_EXEC_MAP_EXECUTE) ? okl::prot_exec : 0);
197+
// READ IN NOW (0.23): MAP_POPULATE reads the part and fills its page
198+
// tables in the one call, where each page would otherwise be a fault of its
199+
// own on its first touch. BRANCH TARGETS: PROT_BTI, where the processor
200+
// holds them.
201+
const bool bti = (how & KAL_EXEC_MAP_BTI) && (how & KAL_EXEC_MAP_EXECUTE) && branch_targets();
202+
// WRITTEN PRIVATELY (0.23): the mapping is private already, so a page the
203+
// program writes is its own copy and the file is not changed.
204+
const okl_long prot = okl::prot_read | ((how & KAL_EXEC_MAP_EXECUTE) ? okl::prot_exec : 0)
205+
| ((how & KAL_EXEC_MAP_WRITE) ? okl::prot_write : 0)
206+
| (bti ? 0x10 /* PROT_BTI */ : 0);
207+
const okl_long populate = (how & KAL_EXEC_MAP_PREFETCH) ? 0x8000 /* MAP_POPULATE */ : 0;
139208
const okl_long r = okl::sys(okl::nr_mmap,
140209
reinterpret_cast<okl_long>(static_cast<char*>(region) + offset),
141-
static_cast<okl_long>(size), prot, okl::map_private | okl::map_fixed, fd,
210+
static_cast<okl_long>(size), prot, okl::map_private | okl::map_fixed | populate, fd,
142211
static_cast<okl_long>(at));
143212
if (okl::failed(r)) {
144213
// ENODEV: the file system does not map. EPERM: its files are not
@@ -163,7 +232,8 @@ void kal_exec_free(void* p, kal_uintptr size) {
163232
kal_uintptr kal_exec_props(void) {
164233
// Executable memory is available to every artifact on this kernel: nothing
165234
// here is granted only to a program produced in a particular way.
166-
return KAL_EXEC_PROP_REPUBLISH | KAL_EXEC_PROP_AVAILABLE | KAL_EXEC_PROP_FILE;
235+
return KAL_EXEC_PROP_REPUBLISH | KAL_EXEC_PROP_AVAILABLE | KAL_EXEC_PROP_FILE | KAL_EXEC_PROP_PLACE
236+
| (sealing() ? KAL_EXEC_PROP_SEAL_FOREVER : 0u) | (branch_targets() ? KAL_EXEC_PROP_BTI : 0u);
167237
}
168238

169239
} // extern "C"

0 commit comments

Comments
 (0)