makeBinaryWrapper: fix read past NUL

When setting a prefix for a path-like environment variable, the
deduplication code in set_env_prefix reads past the NUL byte at the end
of the env val and into the next entry. This corrupts the resultant env
value with data from the next env var, or other data sitting after it.
This commit is contained in:
Archit Gupta
2026-06-09 00:55:01 -07:00
parent ee0c2b1652
commit d75cae80fa
8 changed files with 78 additions and 8 deletions

View File

@@ -366,10 +366,10 @@ void set_env_prefix(char *env, char *sep, char *prefix) {
return;
}
unsigned long sep_len = strlen(sep);
int n_before = existing_prefix - existing_env;
int n_before = existing_prefix - existing_env - sep_len;
assert_success(asprintf(&val, \"%s%s%.*s%s\", prefix, sep,
n_before, existing_env,
existing_prefix + prefix_len + sep_len));
existing_prefix + prefix_len));
} else {
assert_success(asprintf(&val, \"%s%s%s\", prefix, sep, existing_env));
}

View File

@@ -42,10 +42,10 @@ void set_env_prefix(char *env, char *sep, char *prefix) {
return;
}
unsigned long sep_len = strlen(sep);
int n_before = existing_prefix - existing_env;
int n_before = existing_prefix - existing_env - sep_len;
assert_success(asprintf(&val, "%s%s%.*s%s", prefix, sep,
n_before, existing_env,
existing_prefix + prefix_len + sep_len));
existing_prefix + prefix_len));
} else {
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing_env));
}

View File

@@ -59,6 +59,7 @@ let
"overlength-strings"
"prefix"
"suffix"
"prefix-dedup-last"
] makeGoldenTest
// lib.optionalAttrs (!stdenv.hostPlatform.isDarwin) {
cross =

View File

@@ -42,10 +42,10 @@ void set_env_prefix(char *env, char *sep, char *prefix) {
return;
}
unsigned long sep_len = strlen(sep);
int n_before = existing_prefix - existing_env;
int n_before = existing_prefix - existing_env - sep_len;
assert_success(asprintf(&val, "%s%s%.*s%s", prefix, sep,
n_before, existing_env,
existing_prefix + prefix_len + sep_len));
existing_prefix + prefix_len));
} else {
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing_env));
}

View File

@@ -0,0 +1,64 @@
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
int is_surrounded_by_sep(char *env, char *ptr, unsigned long len, char *sep) {
unsigned long sep_len = strlen(sep);
// Check left side (if not at start)
if (env != ptr) {
if (ptr - env < sep_len)
return 0;
if (strncmp(sep, ptr - sep_len, sep_len) != 0) {
return 0;
}
}
// Check right side (if not at end)
char *end_ptr = ptr + len;
if (*end_ptr != '\0') {
if (strncmp(sep, ptr + len, sep_len) != 0) {
return 0;
}
}
return 1;
}
void set_env_prefix(char *env, char *sep, char *prefix) {
char *existing_env = getenv(env);
if (existing_env) {
char *val;
char *existing_prefix = strstr(existing_env, prefix);
unsigned long prefix_len = strlen(prefix);
// If the prefix already exists, remove the original
if (existing_prefix && is_surrounded_by_sep(existing_env, existing_prefix, prefix_len, sep)) {
if (existing_env == existing_prefix) {
return;
}
unsigned long sep_len = strlen(sep);
int n_before = existing_prefix - existing_env - sep_len;
assert_success(asprintf(&val, "%s%s%.*s%s", prefix, sep,
n_before, existing_env,
existing_prefix + prefix_len));
} else {
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing_env));
}
assert_success(setenv(env, val, 1));
free(val);
} else {
assert_success(setenv(env, prefix, 1));
}
}
int main(int argc, char **argv) {
putenv("PATH=/usr/bin:/usr/local/bin");
set_env_prefix("PATH", ":", "/usr/local/bin");
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@@ -0,0 +1,2 @@
--set PATH /usr/bin:/usr/local/bin \
--prefix PATH : /usr/local/bin

View File

@@ -0,0 +1,3 @@
PATH=/usr/local/bin:/usr/bin
CWD=SUBST_CWD
SUBST_ARGV0

View File

@@ -42,10 +42,10 @@ void set_env_prefix(char *env, char *sep, char *prefix) {
return;
}
unsigned long sep_len = strlen(sep);
int n_before = existing_prefix - existing_env;
int n_before = existing_prefix - existing_env - sep_len;
assert_success(asprintf(&val, "%s%s%.*s%s", prefix, sep,
n_before, existing_env,
existing_prefix + prefix_len + sep_len));
existing_prefix + prefix_len));
} else {
assert_success(asprintf(&val, "%s%s%s", prefix, sep, existing_env));
}