Skip to content

Commit d5a3208

Browse files
A started program receives the three streams and its grants, and nothing else
Builds on the previous commit, which left a started program a descriptor for its own file instead of one for its base directory. Three further departures from clause 7.13 of the specification are removed. The descriptor for the program is opened close-on-exec, and only a program that needs an interpreter keeps it: the kernel refuses such a program with ENOENT before the point of no return, and the start is repeated with the flag cleared. An ordinary executable now starts with nothing above the three streams. The descriptor is moved above the placed positions, so that a caller without a standard input does not hand a script its own descriptor as one. What the caller itself inherited without the close-on-exec flag is no longer passed on. Everything above the grants is marked in the started image with close_range, or from /proc/self/fd or up to the descriptor limit on a kernel older than 5.11. Grants arrive. Every source is moved above the positions being filled before any is placed, so one placement no longer overwrites another grant, a stream, or the base the program's name is resolved under; a grant already at its own position no longer keeps the flag that closed it. The names travel in KAL_PREOPENS=<pid>{;<fd>,<len>,<name>}, bound to the started process by its pid in the way systemd's LISTEN_FDS is, and kal_fs_preopen in the started program enumerates exactly the grants, in order and by name. A count of zero leaves none; not asking leaves the working directory and / as before. tests/conformance_spawn.cpp observes each of these from the started program. Against 0.15.0 seven of its observations fail. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 5104f8d commit d5a3208

4 files changed

Lines changed: 534 additions & 40 deletions

File tree

‎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)