From 347677c3330ece8496b9cd242fd7e4292c2260ae Mon Sep 17 00:00:00 2001 From: Joseph Hunkeler Date: Fri, 24 Apr 2026 15:55:21 -0400 Subject: NUL terminate after copy --- src/lib/core/artifactory.c | 11 +++++++++++ src/lib/core/conda.c | 13 ++++++++++++- src/lib/core/copy.c | 1 + src/lib/core/docker.c | 9 +++++++++ src/lib/core/environment.c | 1 + src/lib/core/ini.c | 12 ++++++++++++ src/lib/core/multiprocessing.c | 1 - src/lib/core/recipe.c | 1 + src/lib/core/relocation.c | 1 + src/lib/core/str.c | 14 ++++++++++++-- src/lib/core/strlist.c | 12 ++++++++++-- src/lib/core/template.c | 2 ++ src/lib/core/template_func_proto.c | 3 +++ src/lib/core/utils.c | 19 +++++++++++++++---- src/lib/core/wheelinfo.c | 4 ++++ 15 files changed, 94 insertions(+), 10 deletions(-) (limited to 'src/lib/core') diff --git a/src/lib/core/artifactory.c b/src/lib/core/artifactory.c index 2490346..54f4ba0 100644 --- a/src/lib/core/artifactory.c +++ b/src/lib/core/artifactory.c @@ -26,14 +26,17 @@ int artifactory_download_cli(char *dest, // convert platform string to lower-case SYSDEBUG("%s", "Set os_ident"); strncpy(os_ident, os, sizeof(os_ident) - 1); + os_ident[sizeof(os_ident) - 1] = '\0'; tolower_s(os_ident); SYSDEBUG("os_ident=%s", os_ident); // translate OS identifier if (!strcmp(os_ident, "darwin") || startswith(os_ident, "macos")) { strncpy(os_ident, "mac", sizeof(os_ident) - 1); + os_ident[sizeof(os_ident) - 1] = '\0'; } else if (!strcmp(os_ident, "linux")) { strncpy(os_ident, "linux", sizeof(os_ident) - 1); + os_ident[sizeof(os_ident) - 1] = '\0'; } else { fprintf(stderr, "%s: unknown operating system: %s\n", __FUNCTION__, os_ident); return -1; @@ -42,7 +45,9 @@ int artifactory_download_cli(char *dest, // translate ARCH identifier SYSDEBUG("%s", "Set arch_ident"); strncpy(arch_ident, arch, sizeof(arch_ident) - 1); + arch_ident[sizeof(arch_ident) - 1] = '\0'; SYSDEBUG("arch_ident=%s", arch_ident); + if (startswith(arch_ident, "i") && endswith(arch_ident, "86")) { strncpy(arch_ident, "386", sizeof(arch_ident) - 1); } else if (!strcmp(arch_ident, "amd64") || !strcmp(arch_ident, "x86_64") || !strcmp(arch_ident, "x64")) { @@ -57,6 +62,8 @@ int artifactory_download_cli(char *dest, fprintf(stderr, "%s: unknown architecture: %s\n", __FUNCTION__, arch_ident); return -1; } + arch_ident[sizeof(arch_ident) - 1] = '\0'; + SYSDEBUG("%s", "Construct URL"); snprintf(url, sizeof(url), "%s/%s/%s/%s/%s-%s-%s/%s", @@ -69,6 +76,7 @@ int artifactory_download_cli(char *dest, arch_ident, // jfrog-cli-linux-x86_64 remote_filename); // jf strncpy(path, dest, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; if (mkdirs(path, 0755)) { fprintf(stderr, "%s: %s: %s", __FUNCTION__, path, strerror(errno)); @@ -265,7 +273,10 @@ int jfrog_cli(struct JFRT_Auth *auth, const char *subsystem, const char *task, c if (!globals.verbose) { strncpy(proc.f_stdout, "/dev/null", sizeof(proc.f_stdout) - 1); + proc.f_stdout[sizeof(proc.f_stdout) - 1] = '\0'; + strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); + proc.f_stderr[sizeof(proc.f_stderr) - 1] = '\0'; } return shell(&proc, cmd); } diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c index 4cb7710..1da4d6a 100644 --- a/src/lib/core/conda.c +++ b/src/lib/core/conda.c @@ -11,10 +11,12 @@ int micromamba(const struct MicromambaInfo *info, char *command, ...) { tolower_s(sys.sysname); if (!strcmp(sys.sysname, "darwin")) { strncpy(sys.sysname, "osx", sizeof(sys.sysname) - 1); + sys.sysname[sizeof(sys.sysname) - 1] = '\0'; } if (!strcmp(sys.machine, "x86_64")) { strncpy(sys.machine, "64", sizeof(sys.machine) - 1); + sys.machine[sizeof(sys.machine) - 1] = '\0'; } char url[PATH_MAX] = {0}; @@ -146,6 +148,7 @@ int pkg_index_provides(int mode, const char *index, const char *spec) { // Normalize the local spec string strncpy(spec_local, spec, sizeof(spec_local) - 1); + spec_local[sizeof(spec_local) - 1] = '\0'; tolower_s(spec_local); lstrip(spec_local); strip(spec_local); @@ -167,12 +170,15 @@ int pkg_index_provides(int mode, const char *index, const char *spec) { // Do an installation in dry-run mode to see if the package exists in the given index. // The --force argument ignores local installation and cache, and actually polls the remote index(es) strncpy(cmd, "python -m pip install --force --dry-run --no-cache --no-deps ", sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = '\0'; if (index) { snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "--index-url='%s' ", index); } snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "'%s' ", spec_local); } else if (mode == PKG_USE_CONDA) { strncpy(cmd, "mamba search ", sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = '\0'; + if (index) { snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "--channel '%s' ", index); } @@ -247,6 +253,8 @@ int conda_exec(const char *args) { break; } } + conda_as[sizeof(conda_as) - 1] = '\0'; + const char *command_fmt = "%s %s"; const int len = snprintf(NULL, 0, command_fmt, conda_as, args); @@ -352,7 +360,8 @@ int conda_activate(const char *root, const char *env_name) { close(fd); // Configure our process for output to a log file - strncpy(proc.f_stdout, logfile, PATH_MAX - 1); + strncpy(proc.f_stdout, logfile, sizeof(proc.f_stdout) - 1); + proc.f_stdout[sizeof(proc.f_stdout) - 1] = '\0'; // Verify conda's init scripts are available if (access(path_conda, F_OK) < 0) { @@ -497,6 +506,7 @@ int conda_setup_headless() { if (globals.conda_packages && strlist_count(globals.conda_packages)) { memset(cmd, 0, sizeof(cmd)); strncpy(cmd, "install ", sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = '\0'; total = strlist_count(globals.conda_packages); for (size_t i = 0; i < total; i++) { @@ -520,6 +530,7 @@ int conda_setup_headless() { if (globals.pip_packages && strlist_count(globals.pip_packages)) { memset(cmd, 0, sizeof(cmd)); strncpy(cmd, "install ", sizeof(cmd) - 1); + cmd[sizeof(cmd) - 1] = '\0'; total = strlist_count(globals.pip_packages); for (size_t i = 0; i < total; i++) { diff --git a/src/lib/core/copy.c b/src/lib/core/copy.c index 5b4e468..6697f59 100644 --- a/src/lib/core/copy.c +++ b/src/lib/core/copy.c @@ -15,6 +15,7 @@ int copy2(const char *src, const char *dest, unsigned int op) { char dname[1024] = {0}; strncpy(dname, dest, sizeof(dname) - 1); + dname[sizeof(dname) - 1] = '\0'; char *dname_endptr = strrchr(dname, '/'); if (dname_endptr != NULL) { diff --git a/src/lib/core/docker.c b/src/lib/core/docker.c index b289e5a..484f476 100644 --- a/src/lib/core/docker.c +++ b/src/lib/core/docker.c @@ -19,9 +19,11 @@ int docker_exec(const char *args, const unsigned flags) { if (final_flags & STASIS_DOCKER_QUIET_STDOUT) { strncpy(proc.f_stdout, "/dev/null", sizeof(proc.f_stdout) - 1); + proc.f_stdout[sizeof(proc.f_stdout) - 1] = '\0'; } if (final_flags & STASIS_DOCKER_QUIET_STDERR) { strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); + proc.f_stderr[sizeof(proc.f_stderr) - 1] = '\0'; } if (!final_flags) { @@ -74,6 +76,8 @@ int docker_build(const char *dirpath, const char *args, int engine) { if (engine & STASIS_DOCKER_BUILD_X) { strncpy(build, "buildx build", sizeof(build) - 1); } + build[sizeof(build) - 1] = '\0'; + snprintf(cmd, sizeof(cmd), "%s %s %s", build, args, dirpath); return docker_exec(cmd, 0); } @@ -94,6 +98,7 @@ int docker_save(const char *image, const char *destdir, const char *compression_ } else { strncpy(ext, compression_program, sizeof(ext) - 1); } + ext[sizeof(ext) - 1] = '\0'; snprintf(cmd, sizeof(cmd), "save \"%s\" | %s > \"%s/%s.tar.%s\"", image, compression_program, destdir, image, ext); } else { snprintf(cmd, sizeof(cmd), "save \"%s\" -o \"%s/%s.tar\"", image, destdir, image); @@ -122,7 +127,11 @@ static char *docker_ident() { memset(&proc, 0, sizeof(proc)); strncpy(proc.f_stdout, tempfile, sizeof(proc.f_stdout) - 1); + proc.f_stdout[sizeof(proc.f_stdout) - 1] = '\0'; + strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); + proc.f_stderr[sizeof(proc.f_stderr) - 1] = '\0'; + shell(&proc, "docker --version"); if (!freopen(tempfile, "r", fp)) { diff --git a/src/lib/core/environment.c b/src/lib/core/environment.c index 3c94d33..1bd9d28 100644 --- a/src/lib/core/environment.c +++ b/src/lib/core/environment.c @@ -90,6 +90,7 @@ void runtime_export(RuntimeEnv *env, char **keys) { break; } } + export_command[sizeof(export_command) - 1] = '\0'; for (size_t i = 0; i < strlist_count(env); i++) { char output[STASIS_BUFSIZ] = {0}; diff --git a/src/lib/core/ini.c b/src/lib/core/ini.c index 6995eb2..16ffc20 100644 --- a/src/lib/core/ini.c +++ b/src/lib/core/ini.c @@ -181,7 +181,9 @@ int ini_getval(struct INIFILE *ini, char *section_name, char *key, int type, int break; case INIVAL_TYPE_STR_ARRAY: strncpy(tbufp, data_copy, sizeof(tbuf) - 1); + tbuf[sizeof(tbuf) - 1] = '\0'; guard_free(data_copy); + data_copy = calloc(STASIS_BUFSIZ, sizeof(*data_copy)); if (!data_copy) { return -1; @@ -526,6 +528,7 @@ struct INIFILE *ini_open(const char *filename) { // Create an implicit section. [default] does not need to be present in the INI config ini_section_create(&ini, "default"); strncpy(current_section, "default", sizeof(current_section) - 1); + current_section[sizeof(current_section) - 1] = '\0'; // Open the configuration file for reading FILE *fp = fopen(filename, "r"); @@ -600,6 +603,8 @@ struct INIFILE *ini_open(const char *filename) { // Record the name of the section. This is used until another section is found. memset(current_section, 0, sizeof(current_section)); strncpy(current_section, section_name, sizeof(current_section) - 1); + current_section[sizeof(current_section) - 1] = '\0'; + guard_free(section_name); memset(line, 0, sizeof(line)); continue; @@ -621,16 +626,21 @@ struct INIFILE *ini_open(const char *filename) { size_t key_len = operator - line; memset(key, 0, sizeof(inikey[0])); strncpy(key, line, key_len); + key[key_len] = '\0'; lstrip(key); strip(key); + memset(key_last, 0, sizeof(inikey[1])); strncpy(key_last, key, sizeof(inikey[1]) - 1); + key_last[sizeof(inikey[1]) - 1] = '\0'; + reading_value = 1; if (strlen(operator) > 1) { strncpy(value, &operator[1], sizeof(value) - 1); } else { strncpy(value, "", sizeof(value) - 1); } + value[sizeof(value) - 1] = '\0'; if (isempty(value)) { //printf("%s is probably long raw data\n", key); hint = INIVAL_TYPE_STR_ARRAY; @@ -644,7 +654,9 @@ struct INIFILE *ini_open(const char *filename) { strip(value); } else { strncpy(key, key_last, sizeof(inikey[0]) - 1); + key[sizeof(inikey[0]) - 1] = '\0'; strncpy(value, line, sizeof(value) - 1); + value[sizeof(value) - 1] = '\0'; } memset(line, 0, sizeof(line)); diff --git a/src/lib/core/multiprocessing.c b/src/lib/core/multiprocessing.c index 7b16af3..8fd8b93 100644 --- a/src/lib/core/multiprocessing.c +++ b/src/lib/core/multiprocessing.c @@ -261,7 +261,6 @@ void mp_pool_show_summary(struct MultiProcessingPool *pool) { char duration[255] = {0}; seconds_to_human_readable(task->time_data.duration, duration, sizeof(duration)); printf("%-4s %10d %10s %-10s\n", status_str, task->parent_pid, duration, task->ident) ; - //printf("%-4s %10d %7lds %-10s\n", status_str, task->parent_pid, task->elapsed, task->ident) ; } puts(""); } diff --git a/src/lib/core/recipe.c b/src/lib/core/recipe.c index cc96139..72aa6a3 100644 --- a/src/lib/core/recipe.c +++ b/src/lib/core/recipe.c @@ -17,6 +17,7 @@ int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result) { } } strncpy(*result, destdir, PATH_MAX - 1); + *result[PATH_MAX - 1] = '\0'; if (!access(destdir, F_OK)) { if (!strcmp(destdir, "/")) { diff --git a/src/lib/core/relocation.c b/src/lib/core/relocation.c index ea8b9c6..f7dafe8 100644 --- a/src/lib/core/relocation.c +++ b/src/lib/core/relocation.c @@ -85,6 +85,7 @@ int replace_text(char *original, const char *target, const char *replacement, un } // replace original with contents of buffer strncpy(original, buffer, buffer_len + 1); + original[buffer_len] = '\0'; return 0; } diff --git a/src/lib/core/str.c b/src/lib/core/str.c index 368ab49..b04dfed 100644 --- a/src/lib/core/str.c +++ b/src/lib/core/str.c @@ -63,6 +63,7 @@ void strchrdel(char *sptr, const char *chars) { for (size_t i = 0; i < strlen(chars); i++) { char ch[2] = {0}; strncpy(ch, &chars[i], 1); + ch[sizeof(ch) - 1] = '\0'; replace_text(sptr, ch, "", 0); } } @@ -105,7 +106,7 @@ char** split(char *_sptr, const char* delim, size_t max) // Separate the string into individual parts and store them in the result array char *token = NULL; char *sptr_tmp = sptr; - size_t pos = 0; + ptrdiff_t pos = 0; size_t i; for (i = 0; (token = strsep(&sptr_tmp, delim)) != NULL; i++) { // When max is zero, record all tokens @@ -120,6 +121,7 @@ char** split(char *_sptr, const char* delim, size_t max) return NULL; } strncpy(result[i], token, STASIS_BUFSIZ - 1); + result[i][STASIS_BUFSIZ - 1] = '\0'; } // pos is non-zero when maximum split is reached @@ -130,6 +132,7 @@ char** split(char *_sptr, const char* delim, size_t max) return NULL; } strncpy(result[i], &orig[pos], STASIS_BUFSIZ - 1); + result[i][STASIS_BUFSIZ - 1] = '\0'; } guard_free(sptr); @@ -562,7 +565,10 @@ char *normalize_space(char *s) { } // Rewrite the input string - strncpy(result, tmp_orig, strlen(result) + 1); + const size_t result_len = strlen(result) + 1; + strncpy(result, tmp_orig, result_len); + result[result_len] = '\0'; + guard_free(tmp_orig); return result; } @@ -583,6 +589,10 @@ char **strdup_array(char **array) { result = calloc(elems + 1, sizeof(*result)); for (size_t i = 0; i < elems; i++) { result[i] = strdup(array[i]); + if (!result[i]) { + guard_array_free(result); + break; + } } return result; diff --git a/src/lib/core/strlist.c b/src/lib/core/strlist.c index ff9c098..a9a95e7 100644 --- a/src/lib/core/strlist.c +++ b/src/lib/core/strlist.c @@ -4,6 +4,10 @@ */ #include "download.h" #include "strlist.h" + +#include +#include + #include "utils.h" /** @@ -85,6 +89,8 @@ int strlist_append_file(struct StrList *pStrList, char *_path, ReaderFn *readerF int fd; char tempfile[PATH_MAX] = {0}; strncpy(tempfile, "/tmp/.remote_file.XXXXXX", sizeof(tempfile) - 1); + tempfile[sizeof(tempfile) - 1] = '\0'; + if ((fd = mkstemp(tempfile)) < 0) { retval = -1; goto fatal; @@ -420,8 +426,10 @@ void strlist_set(struct StrList **pStrList, size_t index, char *value) { (*pStrList)->data[index] = tmp; } - memset((*pStrList)->data[index], '\0', strlen(value) + 1); - strncpy((*pStrList)->data[index], value, strlen(value)); + const size_t len = strlen(value) + 1; + memset((*pStrList)->data[index], '\0', len); + strncpy((*pStrList)->data[index], value, len); + (*pStrList)->data[index][len] = '\0'; } } diff --git a/src/lib/core/template.c b/src/lib/core/template.c index 3a1b759..c72c6e5 100644 --- a/src/lib/core/template.c +++ b/src/lib/core/template.c @@ -236,6 +236,8 @@ char *tpl_render(char *str) { } else if (do_func) { // {{ func:NAME(a, ...) }} char func_name_temp[STASIS_NAME_MAX] = {0}; strncpy(func_name_temp, type_stop + 1, sizeof(func_name_temp) - 1); + func_name_temp[sizeof(func_name_temp) - 1] = '\0'; + char *param_begin = strchr(func_name_temp, '('); if (!param_begin) { fprintf(stderr, "At position %zu in %s\nfunction name must be followed by a '('\n", off, key); diff --git a/src/lib/core/template_func_proto.c b/src/lib/core/template_func_proto.c index fc58e33..f28a1eb 100644 --- a/src/lib/core/template_func_proto.c +++ b/src/lib/core/template_func_proto.c @@ -81,6 +81,8 @@ int get_junitxml_file_entrypoint(void *frame, void *data_out) { } char nametmp[PATH_MAX] = {0}; strncpy(nametmp, cwd, sizeof(nametmp) - 1); + nametmp[sizeof(nametmp) - 1] = '\0'; + char *name = path_basename(nametmp); *output = calloc(PATH_MAX, sizeof(**output)); @@ -106,6 +108,7 @@ int get_basetemp_dir_entrypoint(void *frame, void *data_out) { } char nametmp[PATH_MAX] = {0}; strncpy(nametmp, cwd, sizeof(nametmp) - 1); + nametmp[sizeof(nametmp) - 1] = '\0'; char *name = path_basename(nametmp); *output = calloc(PATH_MAX, sizeof(**output)); diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c index 2b7f0ec..b528add 100644 --- a/src/lib/core/utils.c +++ b/src/lib/core/utils.c @@ -35,9 +35,11 @@ int popd() { int rmtree(char *_path) { int status = 0; char path[PATH_MAX] = {0}; - strncpy(path, _path, sizeof(path) - 1); struct dirent *d_entity; + strncpy(path, _path, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + DIR *dir = opendir(path); if (!dir) { return 1; @@ -106,6 +108,7 @@ char *expandpath(const char *_path) { char *tmphome; if ((tmphome = getenv(homes[i])) != NULL) { strncpy(home, tmphome, PATH_MAX - 1); + home[PATH_MAX - 1] = '\0'; break; } } @@ -285,6 +288,7 @@ char *find_program(const char *name) { continue; } strncpy(result, abspath, sizeof(result) - 1); + result[sizeof(result) - 1] = '\0'; break; } path = path_orig; @@ -451,6 +455,7 @@ void msg(unsigned type, char *fmt, ...) { fprintf(stream, "%s", STASIS_COLOR_GREEN); strncpy(status, " ", sizeof(status) - 1); } + status[sizeof(status) - 1] = '\0'; if (type & STASIS_MSG_L1) { snprintf(header, sizeof(header), "==>%s" STASIS_COLOR_RESET STASIS_COLOR_WHITE, status); @@ -498,6 +503,8 @@ char *xmkstemp(FILE **fp, const char *mode) { } else { strncpy(tmpdir, "/tmp", sizeof(tmpdir) - 1); } + tmpdir[sizeof(tmpdir) - 1] = '\0'; + memset(t_name, 0, sizeof(t_name)); snprintf(t_name, sizeof(t_name), "%s/%s", tmpdir, "STASIS.XXXXXX"); @@ -722,6 +729,7 @@ int fix_tox_conf(const char *filename, char **result, size_t maxlen) { // Store path to modified config strncpy(*result, tempfile, maxlen - 1); + *result[maxlen - 1] = '\0'; guard_free(tempfile); ini_free(&toxini); @@ -766,11 +774,12 @@ char *collapse_whitespace(char **s) { int redact_sensitive(const char **to_redact, size_t to_redact_size, char *src, char *dest, size_t maxlen) { const char *redacted = "***REDACTED***"; - char *tmp = calloc(strlen(redacted) + strlen(src) + 1, sizeof(*tmp)); + char *tmp = calloc(maxlen + 1, sizeof(*tmp)); if (!tmp) { return -1; } - strncpy(tmp, src, strlen(redacted) + strlen(src)); + strncpy(tmp, src, maxlen); + tmp[maxlen] = '\0'; for (size_t i = 0; i < to_redact_size; i++) { if (to_redact[i] && strstr(tmp, to_redact[i])) { @@ -780,7 +789,8 @@ int redact_sensitive(const char **to_redact, size_t to_redact_size, char *src, c } memset(dest, 0, maxlen); - strncpy(dest, tmp, maxlen - 1); + strncpy(dest, tmp, maxlen); + dest[maxlen] = '\0'; guard_free(tmp); return 0; @@ -834,6 +844,7 @@ int mkdirs(const char *_path, mode_t mode) { char *token; char pathbuf[PATH_MAX] = {0}; strncpy(pathbuf, _path, sizeof(pathbuf) - 1); + pathbuf[sizeof(pathbuf) - 1] = '\0'; char *path = pathbuf; errno = 0; diff --git a/src/lib/core/wheelinfo.c b/src/lib/core/wheelinfo.c index ce8ea74..9d8a6af 100644 --- a/src/lib/core/wheelinfo.c +++ b/src/lib/core/wheelinfo.c @@ -7,6 +7,7 @@ struct WheelInfo *wheelinfo_get(const char *basepath, const char *name, char *to char package_name[NAME_MAX]; strncpy(package_name, name, sizeof(package_name) - 1); + package_name[sizeof(package_name) - 1] = '\0'; tolower_s(package_name); snprintf(package_path, sizeof(package_path), "%s/%s", basepath, package_name); @@ -19,8 +20,11 @@ struct WheelInfo *wheelinfo_get(const char *basepath, const char *name, char *to if (!strcmp(rec->d_name, ".") || !strcmp(rec->d_name, "..")) { continue; } + char filename[NAME_MAX]; strncpy(filename, rec->d_name, sizeof(filename) - 1); + filename[sizeof(filename) - 1] = '\0'; + char *ext = strstr(filename, ".whl"); if (ext) { *ext = '\0'; -- cgit