fix: inherit process environment and untrack libc objects

This commit is contained in:
2026-08-11 13:08:40 +02:00
parent 0a91131cb0
commit 3ef98a9fd5
26 changed files with 254 additions and 36 deletions
+134 -20
View File
@@ -123,6 +123,9 @@ static inline long _zos_syscall4(long nr, long a1, long a2, long a3, long a4) {
#define SYS_MMAP_ANON 168
#define SYS_MUNMAP 169
#define SYS_MPROTECT 170
#define SYS_GETENVIRON 171
#define SYS_SETENVIRON 172
#define SYS_SPAWN_ENV 173
/* ========================================================================
errno
@@ -164,9 +167,15 @@ struct _DIR {
struct _env_entry {
char *name;
char *value;
char *string;
};
static struct _env_entry _env_entries[64];
static char *_environ_vector[65];
char **environ = _environ_vector;
static int _environ_initialized;
static int _environ_loading;
void __libc_init_environ(const char *blob, size_t len);
static sighandler_t _signal_handlers[32];
static const char *_weekday_short[] = {
@@ -354,6 +363,39 @@ static int _alloc_env_slot(void) {
return -1;
}
static void _rebuild_environ(void) {
int out = 0;
for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) {
if (_env_entries[i].string != NULL)
_environ_vector[out++] = _env_entries[i].string;
}
_environ_vector[out] = NULL;
}
static int _sync_environ_to_kernel(void) {
static char blob[4096];
size_t out = 0;
for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) {
if (_env_entries[i].string == NULL) continue;
size_t len = strlen(_env_entries[i].string) + 1;
if (out + len >= sizeof(blob)) return -1;
memcpy(blob + out, _env_entries[i].string, len);
out += len;
}
blob[out++] = '\0';
return _zos_syscall2(SYS_SETENVIRON, (long)blob, (long)out) < 0 ? -1 : 0;
}
static void _ensure_environ_initialized(void) {
if (_environ_initialized) return;
static char blob[4096];
int len = (int)_zos_syscall2(SYS_GETENVIRON, (long)blob, (long)sizeof(blob));
if (len > 0)
__libc_init_environ(blob, (size_t)len);
else
_environ_initialized = 1;
}
/* ========================================================================
string.h functions
======================================================================== */
@@ -1053,6 +1095,7 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) {
char *getenv(const char *name) {
if (name == NULL || name[0] == '\0') return NULL;
_ensure_environ_initialized();
int slot = _find_env_slot(name);
if (slot < 0) return NULL;
@@ -1064,36 +1107,58 @@ int setenv(const char *name, const char *value, int overwrite) {
errno = EINVAL;
return -1;
}
_ensure_environ_initialized();
int slot = _find_env_slot(name);
if (slot >= 0 && !overwrite) {
return 0;
}
size_t required = 1 + strlen(name) + strlen(value) + 2; /* final NUL, '=', entry NUL */
for (int i = 0; i < (int)(sizeof(_env_entries) / sizeof(_env_entries[0])); i++) {
if (i != slot && _env_entries[i].string != NULL)
required += strlen(_env_entries[i].string) + 1;
}
if (required >= 4096) {
errno = E2BIG;
return -1;
}
if (slot < 0) {
slot = _alloc_env_slot();
if (slot < 0) {
errno = ENOMEM;
return -1;
}
} else {
free(_env_entries[slot].name);
free(_env_entries[slot].value);
_env_entries[slot].name = NULL;
_env_entries[slot].value = NULL;
}
_env_entries[slot].name = strdup(name);
_env_entries[slot].value = strdup(value);
if (_env_entries[slot].name == NULL || _env_entries[slot].value == NULL) {
free(_env_entries[slot].name);
free(_env_entries[slot].value);
_env_entries[slot].name = NULL;
_env_entries[slot].value = NULL;
char *new_name = strdup(name);
char *new_value = strdup(value);
char *new_string = (char *)malloc(strlen(name) + strlen(value) + 2);
if (new_name == NULL || new_value == NULL || new_string == NULL) {
free(new_name);
free(new_value);
free(new_string);
errno = ENOMEM;
return -1;
}
strcpy(new_string, name);
strcat(new_string, "=");
strcat(new_string, value);
free(_env_entries[slot].name);
free(_env_entries[slot].value);
free(_env_entries[slot].string);
_env_entries[slot].name = new_name;
_env_entries[slot].value = new_value;
_env_entries[slot].string = new_string;
_rebuild_environ();
if (!_environ_loading && _sync_environ_to_kernel() < 0) {
errno = EIO;
return -1;
}
return 0;
}
@@ -1102,14 +1167,22 @@ int unsetenv(const char *name) {
errno = EINVAL;
return -1;
}
_ensure_environ_initialized();
int slot = _find_env_slot(name);
if (slot < 0) return 0;
free(_env_entries[slot].name);
free(_env_entries[slot].value);
free(_env_entries[slot].string);
_env_entries[slot].name = NULL;
_env_entries[slot].value = NULL;
_env_entries[slot].string = NULL;
_rebuild_environ();
if (_sync_environ_to_kernel() < 0) {
errno = EIO;
return -1;
}
return 0;
}
@@ -4114,7 +4187,6 @@ int posix_spawn(pid_t *pid, const char *path,
const posix_spawnattr_t *attr,
char *const argv[], char *const envp[]) {
(void)attr;
(void)envp; /* no environment transfer on Montauk spawn */
if (path == NULL) {
return EINVAL;
@@ -4148,7 +4220,24 @@ int posix_spawn(pid_t *pid, const char *path,
}
argsbuf[o] = '\0';
long child = _zos_syscall2(SYS_SPAWN, (long)path, (long)argsbuf);
long child;
if (envp == NULL) {
child = _zos_syscall2(SYS_SPAWN, (long)path, (long)argsbuf);
} else {
static char envbuf[4096];
size_t envlen = 0;
for (int i = 0; envp[i] != NULL; i++) {
const char *eq = strchr(envp[i], '=');
if (eq == NULL || eq == envp[i]) return EINVAL;
size_t len = strlen(envp[i]) + 1;
if (envlen + len >= sizeof(envbuf)) return E2BIG;
memcpy(envbuf + envlen, envp[i], len);
envlen += len;
}
envbuf[envlen++] = '\0';
child = _zos_syscall4(SYS_SPAWN_ENV, (long)path, (long)argsbuf,
(long)envbuf, (long)envlen);
}
if (child < 0) {
return ENOENT;
}
@@ -4192,12 +4281,37 @@ int posix_spawnp(pid_t *pid, const char *file,
return ENOENT;
}
/* The libc environment lives in name/value slots (getenv/setenv), not
NAME=VALUE strings, and exec* is unimplemented, so nothing can
consume a populated environ yet. An empty, valid vector satisfies
POSIX-shaped code until the posix_spawn work builds the real one. */
static char *_environ_empty[] = { NULL };
char **environ = _environ_empty;
/* Called by crt1 before constructors. The kernel stores the inherited
environment as consecutive NAME=VALUE strings followed by an extra NUL. */
void __libc_init_environ(const char *blob, size_t len) {
if (_environ_initialized || blob == NULL || len == 0) return;
_environ_initialized = 1;
_environ_loading = 1;
size_t off = 0;
while (off < len && blob[off] != '\0') {
size_t end = off;
while (end < len && blob[end] != '\0') end++;
if (end == len) break;
size_t eq = off;
while (eq < end && blob[eq] != '=') eq++;
if (eq > off && eq < end) {
size_t name_len = eq - off;
char *name = (char *)malloc(name_len + 1);
if (name == NULL) break;
memcpy(name, blob + off, name_len);
name[name_len] = '\0';
if (setenv(name, blob + eq + 1, 1) < 0) {
free(name);
break;
}
free(name);
}
off = end + 1;
}
_environ_loading = 0;
}
/* No fork/exec process model on Montauk (spawn-style only, see
SYS_SPAWN). These exist so POSIX code links; they fail cleanly at