mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-08-27 02:34:53 +00:00
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.
65 lines
1.9 KiB
C
65 lines
1.9 KiB
C
#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) {
|
|
set_env_prefix("PATH", ":", "/usr/bin/");
|
|
set_env_prefix("PATH", ":", "/usr/local/bin/");
|
|
argv[0] = "/send/me/flags";
|
|
return execv("/send/me/flags", argv);
|
|
}
|