Skip to content

Commit 3493ed4

Browse files
0.15.1 --- a started program receives the three streams and its grants, and nothing else (#31)
Fixes #30. The first commit is the contributor's (Cloud_Yun): a started program is left a descriptor for its own file instead of one for its base directory. The maintainer commits that follow open that descriptor close-on-exec so that only a program needing an interpreter keeps it, stop passing on what the caller itself inherited, make grants arrive by name (openkal 0.14.1, clause 7.13), and release 0.15.1. Co-authored-by: SPeak <speakshen@163.com> Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
2 parents 9ef9720 + 293aeaa commit 3493ed4

7 files changed

Lines changed: 624 additions & 54 deletions

File tree

‎README.md‎

Lines changed: 25 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.14.0"
8+
openkal = "0.14.1"
99

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

1414
## Why it does not use a C library
@@ -101,6 +101,29 @@ resolve an absolute path and report one, and a name of `"."` leaves it able to
101101
do only the first — which version 0.4 did, and which is why `getcwd` could not
102102
have worked above it.
103103

104+
**A started program receives the three streams and its grants, and nothing
105+
else.** Clause 7.13. Every source is first moved above the positions being
106+
filled, so that placing one cannot overwrite another; everything above the
107+
grants is then marked to close on replacement, including what the calling
108+
program itself inherited (`close_range`, from Linux 5.11; `/proc/self/fd` or the
109+
descriptor limit before it). The program is started through a descriptor for its
110+
own file with `O_CLOEXEC`, and only a program that needs an interpreter --- a
111+
`#!` script, or a `binfmt_misc` binary --- keeps that descriptor: the kernel
112+
refuses such a program with `ENOENT` before the point of no return, and the start
113+
is repeated with the flag cleared. Such a script observes `$0` as `/dev/fd/<n>`.
114+
115+
**Granted directories are named in the environment.** A grant arrives as a
116+
descriptor at 3 and upward, and its name in the variable
117+
`KAL_PREOPENS=<pid>{;<fd>,<len>,<name>}`, the arrangement of systemd's
118+
`LISTEN_FDS` and `LISTEN_FDNAMES`. `<pid>` is written by the started process
119+
itself, so a value inherited through a program that does not read it names no
120+
one. A program started with grants enumerates exactly those directories, the
121+
first of which is the directory it regards as the one it was started in; a
122+
program started without them enumerates the working directory and `/`, as
123+
before. A grant names directories to a program that confines itself to its
124+
preopens; it does not confine a program that opens `/` on its own, which is the
125+
environment's responsibility (clause 11, entry 6).
126+
104127
**Interruption is retried, not reported.** A caller cannot distinguish an
105128
interrupted call from a genuine failure without knowledge of the platform, and
106129
an implementation that reports it produces short transfers on any system that

‎mcpp.toml‎

Lines changed: 2 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.15.0"
4+
version = "0.15.1"
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

@@ -63,7 +63,7 @@ provides-interfaces = [
6363
]
6464

6565
[dependencies]
66-
openkal = "0.14.0"
66+
openkal = "0.14.1"
6767

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

‎src/fs.cpp‎

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <openkal/fs.h>
44
#include <openkal/memory.h>
55

6+
namespace okl { extern char** g_envp; }
7+
68
namespace {
79

810

@@ -20,11 +22,74 @@ struct preopen { const char* name; kal_uintptr len; okl_uptr handle; };
2022

2123
char g_cwd[4096];
2224

25+
constexpr kal_uintptr kMaxGrants = 16;
26+
27+
// THE DIRECTORIES A STARTER GRANTED, WHEN THE PROGRAM WAS STARTED WITH GRANTS.
28+
//
29+
// They arrive as descriptors at 3 and upward, and their names in the variable
30+
// `kal_process_spawn' writes (see `vector::build_env' in process.cpp):
31+
//
32+
// KAL_PREOPENS=<pid>{;<fd>,<len>,<name>}
33+
//
34+
// The value is read only when `<pid>' is this process, so one inherited through
35+
// a program that does not read it is not mistaken for a grant. A descriptor that
36+
// is not a directory is reported as a preopen this program may not use, which is
37+
// how an entry that could not be opened is reported anyway. Each descriptor is
38+
// marked to close on replacement as it is taken over, so that a grant reaches
39+
// the program it was made to and not that program's own children (SPEC.md
40+
// clause 7.13). The names point into the environment, which lives as long as
41+
// the program does.
42+
//
43+
// Answers false when there is no such value, which leaves the directories this
44+
// implementation supplies by default.
45+
bool granted(preopen* t, kal_uintptr* n) {
46+
constexpr char key[] = "KAL_PREOPENS=";
47+
constexpr okl_uptr key_len = sizeof key - 1;
48+
const char* v = nullptr;
49+
for (char** e = okl::g_envp; e && *e; ++e) {
50+
okl_uptr i = 0;
51+
while (i < key_len && (*e)[i] == key[i]) ++i;
52+
if (i == key_len) { v = *e + key_len; break; }
53+
}
54+
if (v == nullptr) return false;
55+
56+
auto number = [&](okl_uptr& out) {
57+
if (*v < '0' || *v > '9') return false;
58+
out = 0;
59+
while (*v >= '0' && *v <= '9') out = out * 10 + static_cast<okl_uptr>(*v++ - '0');
60+
return true;
61+
};
62+
okl_uptr pid = 0;
63+
if (!number(pid) || static_cast<okl_long>(pid) != okl::sys(okl::nr_getpid)) return false;
64+
65+
kal_uintptr k = 0;
66+
while (*v == ';' && k < kMaxGrants) {
67+
++v;
68+
okl_uptr fd = 0, len = 0;
69+
if (!number(fd) || *v++ != ',' || !number(len) || *v++ != ',') return false;
70+
for (okl_uptr i = 0; i < len; ++i) if (v[i] == '\0') return false;
71+
72+
okl::kstat st{};
73+
const bool dir = !okl::failed(okl::sys(okl::nr_fstat, static_cast<okl_long>(fd),
74+
reinterpret_cast<okl_long>(&st)))
75+
&& (st.mode & okl::s_ifmt) == okl::s_ifdir;
76+
if (dir) okl::sys(okl::nr_fcntl, static_cast<okl_long>(fd), okl::f_setfd, okl::fd_cloexec);
77+
t[k++] = { v, len, dir ? okl::pack(static_cast<int>(fd)) : 0u };
78+
v += len;
79+
}
80+
if (*v != '\0') return false;
81+
*n = k;
82+
return true;
83+
}
84+
2385
preopen* table(kal_uintptr* count) {
24-
static preopen t[2];
86+
static preopen t[kMaxGrants];
87+
static kal_uintptr n = 2;
2588
static bool opened = false;
2689
if (!opened) {
2790
opened = true;
91+
if (granted(t, &n)) { if (count) *count = n; return t; }
92+
n = 2;
2893

2994
const okl_long n = okl::sys(okl::nr_getcwd, reinterpret_cast<okl_long>(g_cwd),
3095
static_cast<okl_long>(sizeof g_cwd));
@@ -45,7 +110,7 @@ preopen* table(kal_uintptr* count) {
45110
t[0] = { g_cwd, cwd_len, okl::failed(fd0) ? 0u : okl::pack(static_cast<int>(fd0)) };
46111
t[1] = { "/", 1, okl::failed(fd1) ? 0u : okl::pack(static_cast<int>(fd1)) };
47112
}
48-
if (count) *count = 2;
113+
if (count) *count = n;
49114
return t;
50115
}
51116

0 commit comments

Comments
 (0)