diff options
| author | Joseph Hunkeler <jhunkeler@users.noreply.github.com> | 2026-04-21 12:15:11 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-21 12:15:11 -0400 |
| commit | e05702d1818088439fd017786a036103062db358 (patch) | |
| tree | 379773aaaae0193d1a53583646b48e23edd817a5 /src/lib/core | |
| parent | 2258cd05bcded0125136c17d51568831ac421bf7 (diff) | |
| parent | 577912ff0e1996b9846db00247648abd828a8f43 (diff) | |
| download | stasis-e05702d1818088439fd017786a036103062db358.tar.gz | |
Merge pull request #134 from jhunkeler/sprintf-to-snprintf
String safety
Diffstat (limited to 'src/lib/core')
| -rw-r--r-- | src/lib/core/artifactory.c | 50 | ||||
| -rw-r--r-- | src/lib/core/conda.c | 103 | ||||
| -rw-r--r-- | src/lib/core/copy.c | 2 | ||||
| -rw-r--r-- | src/lib/core/docker.c | 30 | ||||
| -rw-r--r-- | src/lib/core/environment.c | 6 | ||||
| -rw-r--r-- | src/lib/core/github.c | 8 | ||||
| -rw-r--r-- | src/lib/core/include/utils.h | 5 | ||||
| -rw-r--r-- | src/lib/core/ini.c | 30 | ||||
| -rw-r--r-- | src/lib/core/multiprocessing.c | 25 | ||||
| -rw-r--r-- | src/lib/core/recipe.c | 6 | ||||
| -rw-r--r-- | src/lib/core/relocation.c | 10 | ||||
| -rw-r--r-- | src/lib/core/str.c | 14 | ||||
| -rw-r--r-- | src/lib/core/strlist.c | 4 | ||||
| -rw-r--r-- | src/lib/core/system.c | 2 | ||||
| -rw-r--r-- | src/lib/core/template.c | 4 | ||||
| -rw-r--r-- | src/lib/core/template_func_proto.c | 12 | ||||
| -rw-r--r-- | src/lib/core/utils.c | 110 | ||||
| -rw-r--r-- | src/lib/core/wheelinfo.c | 6 |
18 files changed, 239 insertions, 188 deletions
diff --git a/src/lib/core/artifactory.c b/src/lib/core/artifactory.c index d5457e7..9e41046 100644 --- a/src/lib/core/artifactory.c +++ b/src/lib/core/artifactory.c @@ -14,37 +14,37 @@ int artifactory_download_cli(char *dest, char arch_ident[STASIS_NAME_MAX] = {0}; // convert platform string to lower-case - strcpy(os_ident, os); + strncpy(os_ident, os, sizeof(os_ident) - 1); tolower_s(os_ident); // translate OS identifier if (!strcmp(os_ident, "darwin") || startswith(os_ident, "macos")) { - strcpy(os_ident, "mac"); + strncpy(os_ident, "mac", sizeof(os_ident) - 1); } else if (!strcmp(os_ident, "linux")) { - strcpy(os_ident, "linux"); + strncpy(os_ident, "linux", sizeof(os_ident) - 1); } else { fprintf(stderr, "%s: unknown operating system: %s\n", __FUNCTION__, os_ident); return -1; } // translate ARCH identifier - strcpy(arch_ident, arch); + strncpy(arch_ident, arch, sizeof(arch_ident) - 1); if (startswith(arch_ident, "i") && endswith(arch_ident, "86")) { - strcpy(arch_ident, "386"); + strncpy(arch_ident, "386", sizeof(arch_ident) - 1); } else if (!strcmp(arch_ident, "amd64") || !strcmp(arch_ident, "x86_64") || !strcmp(arch_ident, "x64")) { if (!strcmp(os_ident, "mac")) { - strcpy(arch_ident, "386"); + strncpy(arch_ident, "386", sizeof(arch_ident) - 1); } else { - strcpy(arch_ident, "amd64"); + strncpy(arch_ident, "amd64", sizeof(arch_ident) - 1); } } else if (!strcmp(arch_ident, "arm64") || !strcmp(arch_ident, "aarch64")) { - strcpy(arch_ident, "arm64"); + strncpy(arch_ident, "arm64", sizeof(arch_ident) - 1); } else { fprintf(stderr, "%s: unknown architecture: %s\n", __FUNCTION__, arch_ident); return -1; } - snprintf(url, sizeof(url) - 1, "%s/%s/%s/%s/%s-%s-%s/%s", + snprintf(url, sizeof(url), "%s/%s/%s/%s/%s-%s-%s/%s", jfrog_artifactory_base_url, // https://releases.jfrog.io/artifactory jfrog_artifactory_product, // jfrog-cli cli_major_ver, // v\d+(-jf)? @@ -53,14 +53,16 @@ int artifactory_download_cli(char *dest, os_ident, // ... arch_ident, // jfrog-cli-linux-x86_64 remote_filename); // jf - strcpy(path, dest); + strncpy(path, dest, sizeof(path) - 1); if (mkdirs(path, 0755)) { fprintf(stderr, "%s: %s: %s", __FUNCTION__, path, strerror(errno)); return -1; } - sprintf(path + strlen(path), "/%s", remote_filename); + const char *remote_filename_fmt = "/%s"; + int remote_filename_fmt_len = snprintf(NULL, 0, remote_filename_fmt, remote_filename); + snprintf(path + strlen(path), sizeof(path) - remote_filename_fmt_len, remote_filename_fmt, remote_filename); char *errmsg = NULL; long fetch_status = download(url, path, &errmsg); if (HTTP_ERROR(fetch_status)) { @@ -78,7 +80,7 @@ void jfrt_register_opt_str(char *jfrt_val, const char *opt_name, struct StrList // no data return; } - snprintf(data, sizeof(data) - 1, "--%s=\"%s\"", opt_name, jfrt_val); + snprintf(data, sizeof(data), "--%s=\"%s\"", opt_name, jfrt_val); strlist_append(&*opt_map, data); } @@ -89,7 +91,7 @@ void jfrt_register_opt_bool(bool jfrt_val, const char *opt_name, struct StrList // option will not be used return; } - snprintf(data, sizeof(data) - 1, "--%s", opt_name); + snprintf(data, sizeof(data), "--%s", opt_name); strlist_append(&*opt_map, data); } @@ -100,7 +102,7 @@ void jfrt_register_opt_int(int jfrt_val, const char *opt_name, struct StrList ** // option will not be used return; } - snprintf(data, sizeof(data) - 1, "--%s=%d", opt_name, jfrt_val); + snprintf(data, sizeof(data), "--%s=%d", opt_name, jfrt_val); strlist_append(&*opt_map, data); } @@ -111,7 +113,7 @@ void jfrt_register_opt_long(long jfrt_val, const char *opt_name, struct StrList // option will not be used return; } - snprintf(data, sizeof(data) - 1, "--%s=%ld", opt_name, jfrt_val); + snprintf(data, sizeof(data), "--%s=%ld", opt_name, jfrt_val); strlist_append(&*opt_map, data); } @@ -230,7 +232,7 @@ int jfrog_cli(struct JFRT_Auth *auth, const char *subsystem, const char *task, c auth->client_cert_path, auth->password, }; - snprintf(cmd, sizeof(cmd) - 1, "jf %s %s %s %s", subsystem, task, auth_args, args ? args : ""); + snprintf(cmd, sizeof(cmd), "jf %s %s %s %s", subsystem, task, auth_args, args ? args : ""); redact_sensitive(redactable, sizeof(redactable) / sizeof (*redactable), cmd, cmd_redacted, sizeof(cmd_redacted) - 1); guard_free(auth_args); @@ -242,8 +244,8 @@ int jfrog_cli(struct JFRT_Auth *auth, const char *subsystem, const char *task, c } if (!globals.verbose) { - strcpy(proc.f_stdout, "/dev/null"); - strcpy(proc.f_stderr, "/dev/null"); + strncpy(proc.f_stdout, "/dev/null", sizeof(proc.f_stdout) - 1); + strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); } return shell(&proc, cmd); } @@ -254,13 +256,13 @@ static int jfrog_cli_rt(struct JFRT_Auth *auth, char *task, char *args) { int jfrog_cli_rt_build_collect_env(struct JFRT_Auth *auth, char *build_name, char *build_number) { char cmd[STASIS_BUFSIZ] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "\"%s\" \"%s\"", build_name, build_number); + snprintf(cmd, sizeof(cmd), "\"%s\" \"%s\"", build_name, build_number); return jfrog_cli(auth, "rt", "build-collect-env", cmd); } int jfrog_cli_rt_build_publish(struct JFRT_Auth *auth, char *build_name, char *build_number) { char cmd[STASIS_BUFSIZ] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "\"%s\" \"%s\"", build_name, build_number); + snprintf(cmd, sizeof(cmd), "\"%s\" \"%s\"", build_name, build_number); return jfrog_cli(auth, "rt", "build-publish", cmd); } @@ -326,7 +328,7 @@ int jfrog_cli_rt_download(struct JFRT_Auth *auth, struct JFRT_Download *ctx, cha return -1; } - snprintf(cmd, sizeof(cmd) - 1, "%s '%s' '%s'", args, repo_path, dest ? dest : ""); + snprintf(cmd, sizeof(cmd), "%s '%s' '%s'", args, repo_path, dest ? dest : ""); guard_free(args); guard_strlist_free(&arg_map); @@ -411,12 +413,12 @@ int jfrog_cli_rt_upload(struct JFRT_Auth *auth, struct JFRT_Upload *ctx, char *s if (base) { src = base; } else { - strcat(src, "/"); + strncat(src, "/", sizeof(src) - strlen(src) - 1); } pushd(new_src); } - snprintf(cmd, sizeof(cmd) - 1, "%s '%s' \"%s\"", args, src, repo_path); + snprintf(cmd, sizeof(cmd), "%s '%s' \"%s\"", args, src, repo_path); guard_free(args); guard_strlist_free(&arg_map); @@ -475,7 +477,7 @@ int jfrog_cli_rt_search(struct JFRT_Auth *auth, struct JFRT_Search *ctx, char *r return -1; } - snprintf(cmd, sizeof(cmd) - 1, "%s '%s/%s'", args, repo_path, pattern ? pattern: ""); + snprintf(cmd, sizeof(cmd), "%s '%s/%s'", args, repo_path, pattern ? pattern: ""); guard_free(args); guard_strlist_free(&arg_map); diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c index 16483ee..90c6ba1 100644 --- a/src/lib/core/conda.c +++ b/src/lib/core/conda.c @@ -10,18 +10,20 @@ int micromamba(const struct MicromambaInfo *info, char *command, ...) { tolower_s(sys.sysname); if (!strcmp(sys.sysname, "darwin")) { - strcpy(sys.sysname, "osx"); + strncpy(sys.sysname, "osx", sizeof(sys.sysname) - 1); } if (!strcmp(sys.machine, "x86_64")) { - strcpy(sys.machine, "64"); + strncpy(sys.machine, "64", sizeof(sys.machine) - 1); } char url[PATH_MAX]; - sprintf(url, "https://micro.mamba.pm/api/micromamba/%s-%s/latest", sys.sysname, sys.machine); + const char *url_fmt = "https://micro.mamba.pm/api/micromamba/%s-%s/latest"; + const int url_fmt_len = snprintf(NULL, 0, url_fmt, sys.sysname, sys.machine); + snprintf(url, sizeof(url) - url_fmt_len, url_fmt, sys.sysname, sys.machine); char installer_path[PATH_MAX]; - sprintf(installer_path, "%s/latest", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp"); + snprintf(installer_path, sizeof(installer_path), "%s/latest", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp"); if (access(installer_path, F_OK)) { char *errmsg = NULL; @@ -34,12 +36,12 @@ int micromamba(const struct MicromambaInfo *info, char *command, ...) { } char mmbin[PATH_MAX]; - sprintf(mmbin, "%s/micromamba", info->micromamba_prefix); + snprintf(mmbin, sizeof(mmbin), "%s/micromamba", info->micromamba_prefix); if (access(mmbin, F_OK)) { char untarcmd[PATH_MAX * 2]; mkdirs(info->micromamba_prefix, 0755); - sprintf(untarcmd, "tar -xvf %s -C %s --strip-components=1 bin/micromamba 1>/dev/null", installer_path, info->micromamba_prefix); + snprintf(untarcmd, sizeof(untarcmd), "tar -xvf %s -C %s --strip-components=1 bin/micromamba 1>/dev/null", installer_path, info->micromamba_prefix); int untarcmd_status = system(untarcmd); if (untarcmd_status) { return -1; @@ -47,16 +49,37 @@ int micromamba(const struct MicromambaInfo *info, char *command, ...) { } char cmd[STASIS_BUFSIZ] = {0}; - sprintf(cmd, "%s -r %s -p %s ", mmbin, info->conda_prefix, info->conda_prefix); va_list args; + int cmd_len = snprintf(cmd, sizeof(cmd), "%s -r %s -p %s ", mmbin, info->conda_prefix, info->conda_prefix); + if (cmd_len < 0) { + SYSERROR("%s", "Unable to build argument list for micromamba"); + va_end(args); + return -1; + } + if ((size_t) cmd_len > sizeof(cmd)) { + SYSERROR("%s", "micromamba command truncated"); + va_end(args); + return -1; + } + va_start(args, command); - vsprintf(cmd + strlen(cmd), command, args); + cmd_len = vsnprintf(cmd + strlen(cmd), sizeof(cmd) - cmd_len, command, args); + if (cmd_len < 0) { + SYSERROR("%s", "Unable to append arguments to micromamba command"); + va_end(args); + return -1; + } + if ((size_t) cmd_len > sizeof(cmd)) { + SYSERROR("%s", "micromamba command truncated while appending arguments"); + va_end(args); + return -1; + } va_end(args); mkdirs(info->conda_prefix, 0755); char rcpath[PATH_MAX]; - sprintf(rcpath, "%s/.condarc", info->conda_prefix); + snprintf(rcpath, sizeof(rcpath), "%s/.condarc", info->conda_prefix); touch(rcpath); if (errno == ENOENT) { errno = 0; @@ -147,22 +170,22 @@ int pkg_index_provides(int mode, const char *index, const char *spec) { int status = 0; struct Process proc = {0}; proc.redirect_stderr = 1; - strcpy(proc.f_stdout, logfile); + strncpy(proc.f_stdout, logfile, sizeof(proc.f_stdout) - 1); if (mode == PKG_USE_PIP) { // 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); if (index) { - snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "--index-url='%s' ", index); + snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "--index-url='%s' ", index); } - snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "'%s' ", spec_local); + snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "'%s' ", spec_local); } else if (mode == PKG_USE_CONDA) { strncpy(cmd, "mamba search ", sizeof(cmd) - 1); if (index) { - snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "--channel '%s' ", index); + snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "--channel '%s' ", index); } - snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "'%s' ", spec_local); + snprintf(cmd + strlen(cmd), sizeof(cmd) - strlen(cmd), "'%s' ", spec_local); } else { return PKG_INDEX_PROVIDES_E_INTERNAL_MODE_UNKNOWN; } @@ -224,12 +247,12 @@ int conda_exec(const char *args) { "deactivate", NULL }; - char conda_as[6] = {0}; + char conda_as[10] = {0}; - strcpy(conda_as, "conda"); + strncpy(conda_as, "conda", sizeof(conda_as) - 1); for (size_t i = 0; mamba_commands[i] != NULL; i++) { if (startswith(args, mamba_commands[i])) { - strcpy(conda_as, "mamba"); + strncpy(conda_as, "mamba", sizeof(conda_as) - 1); break; } } @@ -321,13 +344,13 @@ int conda_activate(const char *root, const char *env_name) { struct Process proc = {0}; // Where to find conda's init scripts - sprintf(path_conda, "%s%s", root, init_script_conda); - sprintf(path_mamba, "%s%s", root, init_script_mamba); + snprintf(path_conda, sizeof(path_conda), "%s%s", root, init_script_conda); + snprintf(path_mamba, sizeof(path_mamba), "%s%s", root, init_script_mamba); // Set the path to our stdout log // Emulate mktemp()'s behavior. Give us a unique file name, but don't use // the file handle at all. We'll open it as a FILE stream soon enough. - sprintf(logfile, "%s/%s", globals.tmpdir, "shell_XXXXXX"); + snprintf(logfile, sizeof(logfile), "%s/%s", globals.tmpdir, "shell_XXXXXX"); int fd = mkstemp(logfile); if (fd < 0) { @@ -337,7 +360,7 @@ int conda_activate(const char *root, const char *env_name) { close(fd); // Configure our process for output to a log file - strcpy(proc.f_stdout, logfile); + strncpy(proc.f_stdout, logfile, PATH_MAX - 1); // Verify conda's init scripts are available if (access(path_conda, F_OK) < 0) { @@ -417,15 +440,15 @@ int conda_check_required() { // Construct a "conda list" command that searches for all required packages // using conda's (python's) regex matching - strcat(cmd, "conda list '"); + strncat(cmd, "conda list '", sizeof(cmd) - strlen(cmd) - 1); for (size_t i = 0; conda_minimum_viable_tools[i] != NULL; i++) { - strcat(cmd, "^"); - strcat(cmd, conda_minimum_viable_tools[i]); + strncat(cmd, "^", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, conda_minimum_viable_tools[i], sizeof(cmd) - strlen(cmd) - 1); if (conda_minimum_viable_tools[i + 1] != NULL) { - strcat(cmd, "|"); + strncat(cmd, "|", sizeof(cmd) - strlen(cmd) - 1); } } - strcat(cmd, "' | cut -d ' ' -f 1"); + strncat(cmd, "' | cut -d ' ' -f 1", sizeof(cmd) - strlen(cmd) - 1); // Verify all required packages are installed char *cmd_out = shell_output(cmd, &status); @@ -478,9 +501,10 @@ int conda_setup_headless() { char cmd[PATH_MAX]; size_t total = 0; + const char *cmd_fmt = "'%s'"; if (globals.conda_packages && strlist_count(globals.conda_packages)) { memset(cmd, 0, sizeof(cmd)); - strcpy(cmd, "install "); + strncpy(cmd, "install ", sizeof(cmd) - 1); total = strlist_count(globals.conda_packages); for (size_t i = 0; i < total; i++) { @@ -488,9 +512,10 @@ int conda_setup_headless() { if (isempty(item)) { continue; } - sprintf(cmd + strlen(cmd), "'%s'", item); + const int cmd_fmt_len = snprintf(NULL, 0, cmd_fmt, item); + snprintf(cmd + strlen(cmd), sizeof(cmd) - cmd_fmt_len, cmd_fmt, item); if (i < total - 1) { - strcat(cmd, " "); + strncat(cmd, " ", sizeof(cmd) - strlen(cmd) - 1); } } @@ -502,7 +527,7 @@ int conda_setup_headless() { if (globals.pip_packages && strlist_count(globals.pip_packages)) { memset(cmd, 0, sizeof(cmd)); - strcpy(cmd, "install "); + strncpy(cmd, "install ", sizeof(cmd) - 1); total = strlist_count(globals.pip_packages); for (size_t i = 0; i < total; i++) { @@ -510,9 +535,10 @@ int conda_setup_headless() { if (isempty(item)) { continue; } - sprintf(cmd + strlen(cmd), "'%s'", item); + const int cmd_fmt_len = snprintf(NULL, 0, cmd_fmt, item); + snprintf(cmd + strlen(cmd), sizeof(cmd) - cmd_fmt_len, cmd_fmt, item); if (i < total - 1) { - strcat(cmd, " "); + strncat(cmd, " ", sizeof(cmd) - strlen(cmd) - 1); } } @@ -552,7 +578,7 @@ int conda_env_create_from_uri(char *name, char *uri, char *python_version) { } char tempfile[PATH_MAX] = {0}; - sprintf(tempfile, "%s/remote_XXXXXX", globals.tmpdir); + snprintf(tempfile, sizeof(tempfile), "%s/remote_XXXXXX", globals.tmpdir); // Touch a temporary file int fd = mkstemp(tempfile); @@ -560,7 +586,7 @@ int conda_env_create_from_uri(char *name, char *uri, char *python_version) { unlink(tempfile); // We'll create a new file with the same random bits, ending with .yml - strcat(tempfile, ".yml"); + strncat(tempfile, ".yml", sizeof(tempfile) - strlen(tempfile) - 1); char *errmsg = NULL; const long http_code = download(uri_fs ? uri_fs : uri, tempfile, &errmsg); if (HTTP_ERROR(http_code)) { @@ -610,13 +636,13 @@ int conda_env_create(char *name, char *python_version, char *packages) { int conda_env_remove(char *name) { char env_command[PATH_MAX]; - sprintf(env_command, "env remove -n %s", name); + snprintf(env_command, sizeof(env_command), "env remove -n %s", name); return conda_exec(env_command); } int conda_env_export(char *name, char *output_dir, char *output_filename) { char env_command[PATH_MAX]; - sprintf(env_command, "env export -n %s -f %s/%s.yml", name, output_dir, output_filename); + snprintf(env_command, sizeof(env_command), "env export -n %s -f %s/%s.yml", name, output_dir, output_filename); return conda_exec(env_command); } @@ -637,12 +663,13 @@ char *conda_get_active_environment() { int conda_index(const char *path) { char command[PATH_MAX]; - sprintf(command, "index %s", path); + snprintf(command, sizeof(command), "index %s", path); return conda_exec(command); } int conda_env_exists(const char *root, const char *name) { char path[PATH_MAX] = {0}; - snprintf(path, sizeof(path) - 1 - 6, "%s/envs/%s", root, name); + const int len = snprintf(NULL, 0, "%s/%s", root, name); + snprintf(path, sizeof(path) - len, "%s/envs/%s", root, name); return access(path, F_OK) == 0; } diff --git a/src/lib/core/copy.c b/src/lib/core/copy.c index 25eede3..ba52507 100644 --- a/src/lib/core/copy.c +++ b/src/lib/core/copy.c @@ -14,7 +14,7 @@ int copy2(const char *src, const char *dest, unsigned int op) { } char dname[1024] = {0}; - strcpy(dname, dest); + strncpy(dname, dest, sizeof(dname) - 1); char *dname_endptr = strrchr(dname, '/'); if (dname_endptr != NULL) { diff --git a/src/lib/core/docker.c b/src/lib/core/docker.c index 39357ad..52599d3 100644 --- a/src/lib/core/docker.c +++ b/src/lib/core/docker.c @@ -7,7 +7,7 @@ int docker_exec(const char *args, const unsigned flags) { memset(&proc, 0, sizeof(proc)); memset(cmd, 0, sizeof(cmd)); - snprintf(cmd, sizeof(cmd) - 1, "docker %s", args); + snprintf(cmd, sizeof(cmd), "docker %s", args); unsigned final_flags = 0; if (flags & STASIS_DOCKER_QUIET) { @@ -18,10 +18,10 @@ int docker_exec(const char *args, const unsigned flags) { } if (final_flags & STASIS_DOCKER_QUIET_STDOUT) { - strcpy(proc.f_stdout, "/dev/null"); + strncpy(proc.f_stdout, "/dev/null", sizeof(proc.f_stdout) - 1); } if (final_flags & STASIS_DOCKER_QUIET_STDERR) { - strcpy(proc.f_stderr, "/dev/null"); + strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); } if (!final_flags) { @@ -36,7 +36,7 @@ int docker_script(const char *image, char *args, char *data, const unsigned flag (void)flags; // TODO: placeholder char cmd[PATH_MAX] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "docker run -i %s \"%s\" /bin/sh -", args ? args : "", image); + snprintf(cmd, sizeof(cmd), "docker run -i %s \"%s\" /bin/sh -", args ? args : "", image); FILE *outfile = popen(cmd, "w"); if (!outfile) { @@ -68,12 +68,12 @@ int docker_build(const char *dirpath, const char *args, int engine) { memset(cmd, 0, sizeof(cmd)); if (engine & STASIS_DOCKER_BUILD) { - strcpy(build, "build"); + strncpy(build, "build", sizeof(build) - 1); } if (engine & STASIS_DOCKER_BUILD_X) { - strcpy(build, "buildx build"); + strncpy(build, "buildx build", sizeof(build) - 1); } - snprintf(cmd, sizeof(cmd) - 1, "%s %s %s", build, args, dirpath); + snprintf(cmd, sizeof(cmd), "%s %s %s", build, args, dirpath); return docker_exec(cmd, 0); } @@ -83,19 +83,19 @@ int docker_save(const char *image, const char *destdir, const char *compression_ if (compression_program && strlen(compression_program)) { char ext[255] = {0}; if (startswith(compression_program, "zstd")) { - strcpy(ext, "zst"); + strncpy(ext, "zst", sizeof(ext) - 1); } else if (startswith(compression_program, "xz")) { - strcpy(ext, "xz"); + strncpy(ext, "xz", sizeof(ext) - 1); } else if (startswith(compression_program, "gzip")) { - strcpy(ext, "gz"); + strncpy(ext, "gz", sizeof(ext) - 1); } else if (startswith(compression_program, "bzip2")) { - strcpy(ext, "bz2"); + strncpy(ext, "bz2", sizeof(ext) - 1); } else { strncpy(ext, compression_program, sizeof(ext) - 1); } - sprintf(cmd, "save \"%s\" | %s > \"%s/%s.tar.%s\"", image, compression_program, destdir, image, ext); + snprintf(cmd, sizeof(cmd), "save \"%s\" | %s > \"%s/%s.tar.%s\"", image, compression_program, destdir, image, ext); } else { - sprintf(cmd, "save \"%s\" -o \"%s/%s.tar\"", image, destdir, image); + snprintf(cmd, sizeof(cmd), "save \"%s\" -o \"%s/%s.tar\"", image, destdir, image); } return docker_exec(cmd, 0); @@ -120,8 +120,8 @@ static char *docker_ident() { } memset(&proc, 0, sizeof(proc)); - strcpy(proc.f_stdout, tempfile); - strcpy(proc.f_stderr, "/dev/null"); + strncpy(proc.f_stdout, tempfile, sizeof(proc.f_stdout) - 1); + strncpy(proc.f_stderr, "/dev/null", sizeof(proc.f_stderr) - 1); shell(&proc, "docker --version"); if (!freopen(tempfile, "r", fp)) { diff --git a/src/lib/core/environment.c b/src/lib/core/environment.c index 7ece5e6..3c94d33 100644 --- a/src/lib/core/environment.c +++ b/src/lib/core/environment.c @@ -70,7 +70,7 @@ void runtime_export(RuntimeEnv *env, char **keys) { NULL, }; - char export_command[7]; // export=6 and setenv=6... convenient + char export_command[10]; // export=6 and setenv=6... convenient char *_sh = getenv("SHELL"); char *sh = path_basename(_sh); if (sh == NULL) { @@ -80,13 +80,13 @@ void runtime_export(RuntimeEnv *env, char **keys) { for (size_t i = 0; borne[i] != NULL; i++) { if (strcmp(sh, borne[i]) == 0) { - strcpy(export_command, "export"); + strncpy(export_command, "export", sizeof(export_command) - 1); break; } } for (size_t i = 0; unborne[i] != NULL; i++) { if (strcmp(sh, unborne[i]) == 0) { - strcpy(export_command, "setenv"); + strncpy(export_command, "setenv", sizeof(export_command) - 1); break; } } diff --git a/src/lib/core/github.c b/src/lib/core/github.c index f0c5199..c5281c6 100644 --- a/src/lib/core/github.c +++ b/src/lib/core/github.c @@ -58,9 +58,9 @@ int get_github_release_notes(const char *api_token, const char *repo, const char } // Render the header data - sprintf(endpoint_header_auth, endpoint_header_auth_fmt, api_token); - sprintf(endpoint_post_fields, endpoint_post_fields_fmt, tag, target_commitish); - sprintf(endpoint_url, endpoint_url_fmt, repo); + snprintf(endpoint_header_auth, sizeof(endpoint_header_auth), endpoint_header_auth_fmt, api_token); + snprintf(endpoint_post_fields, sizeof(endpoint_post_fields), endpoint_post_fields_fmt, tag, target_commitish); + snprintf(endpoint_url, sizeof(endpoint_url), endpoint_url_fmt, repo); // Begin curl configuration curl_easy_setopt(curl, CURLOPT_URL, endpoint_url); @@ -77,7 +77,7 @@ int get_github_release_notes(const char *api_token, const char *repo, const char // Set the user-agent (github requires one) char user_agent[20] = {0}; - sprintf(user_agent, "stasis/%s", VERSION); + snprintf(user_agent, sizeof(user_agent), "stasis/%s", VERSION); curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent); // Execute curl request diff --git a/src/lib/core/include/utils.h b/src/lib/core/include/utils.h index 335a7e4..c1ee513 100644 --- a/src/lib/core/include/utils.h +++ b/src/lib/core/include/utils.h @@ -293,9 +293,10 @@ int xml_pretty_print_in_place(const char *filename, const char *pretty_print_pro * Applies STASIS fixups to a tox ini config * @param filename path to tox.ini * @param result path to processed configuration + * @param maxlen * @return 0 on success, -1 on error */ -int fix_tox_conf(const char *filename, char **result); +int fix_tox_conf(const char *filename, char **result, size_t maxlen); char *collapse_whitespace(char **s); @@ -420,7 +421,7 @@ int env_manipulate_pathstr(const char *key, char *path, int mode); /** * Append or replace a file extension */ -int gen_file_extension_str(char *filename, const char *extension); +int gen_file_extension_str(char *filename, size_t maxlen, const char *extension); /** * Remove [extra]s from a spec string diff --git a/src/lib/core/ini.c b/src/lib/core/ini.c index cf6f670..1ecff17 100644 --- a/src/lib/core/ini.c +++ b/src/lib/core/ini.c @@ -177,7 +177,7 @@ int ini_getval(struct INIFILE *ini, char *section_name, char *key, int type, int } break; case INIVAL_TYPE_STR_ARRAY: - strcpy(tbufp, data_copy); + strncpy(tbufp, data_copy, sizeof(tbuf) - 1); guard_free(data_copy); data_copy = calloc(STASIS_BUFSIZ, sizeof(*data_copy)); if (!data_copy) { @@ -186,8 +186,8 @@ int ini_getval(struct INIFILE *ini, char *section_name, char *key, int type, int while ((token = strsep(&tbufp, "\n")) != NULL) { //lstrip(token); if (!isempty(token)) { - strcat(data_copy, token); - strcat(data_copy, "\n"); + strncat(data_copy, token, BUFSIZ - strlen(data_copy) - 1); + strncat(data_copy, "\n", BUFSIZ - strlen(data_copy) - 1); } } strip(data_copy); @@ -353,7 +353,7 @@ int ini_data_append(struct INIFILE **ini, char *section_name, char *key, char *v } else { data->value = value_tmp; } - strcat(data->value, value); + strncat(data->value, value, value_len_new - strlen(data->value)); } return 0; } @@ -454,12 +454,12 @@ int ini_write(struct INIFILE *ini, FILE **stream, unsigned mode) { if (*hint == INIVAL_TYPE_STR_ARRAY) { int leading_space = isspace(*render); if (leading_space) { - sprintf(outvalue + strlen(outvalue), "%s" LINE_SEP, render); + snprintf(outvalue + strlen(outvalue), sizeof(outvalue) - strlen(outvalue), "%s" LINE_SEP, render); } else { - sprintf(outvalue + strlen(outvalue), " %s" LINE_SEP, render); + snprintf(outvalue + strlen(outvalue), sizeof(outvalue) - strlen(outvalue), " %s" LINE_SEP, render); } } else { - sprintf(outvalue + strlen(outvalue), "%s", render); + snprintf(outvalue + strlen(outvalue), sizeof(outvalue) - strlen(outvalue), "%s", render); } if (mode == INI_WRITE_PRESERVE) { guard_free(render); @@ -467,7 +467,7 @@ int ini_write(struct INIFILE *ini, FILE **stream, unsigned mode) { } guard_array_free(parts); strip(outvalue); - strcat(outvalue, LINE_SEP); + strncat(outvalue, LINE_SEP, sizeof(outvalue) - strlen(outvalue) - 1); fprintf(*stream, "%s = %s%s", ini->section[x]->data[y]->key, *hint == INIVAL_TYPE_STR_ARRAY ? LINE_SEP : "", outvalue); guard_free(value); } else { @@ -522,7 +522,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"); - strcpy(current_section, "default"); + strncpy(current_section, "default", sizeof(current_section) - 1); // Open the configuration file for reading FILE *fp = fopen(filename, "r"); @@ -596,7 +596,7 @@ 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)); - strcpy(current_section, section_name); + strncpy(current_section, section_name, sizeof(current_section) - 1); guard_free(section_name); memset(line, 0, sizeof(line)); continue; @@ -621,12 +621,12 @@ struct INIFILE *ini_open(const char *filename) { lstrip(key); strip(key); memset(key_last, 0, sizeof(inikey[1])); - strcpy(key_last, key); + strncpy(key_last, key, sizeof(inikey[1]) - 1); reading_value = 1; if (strlen(operator) > 1) { - strcpy(value, &operator[1]); + strncpy(value, &operator[1], sizeof(value) - 1); } else { - strcpy(value, ""); + strncpy(value, "", sizeof(value) - 1); } if (isempty(value)) { //printf("%s is probably long raw data\n", key); @@ -640,8 +640,8 @@ struct INIFILE *ini_open(const char *filename) { } strip(value); } else { - strcpy(key, key_last); - strcpy(value, line); + strncpy(key, key_last, sizeof(inikey[0]) - 1); + strncpy(value, line, sizeof(value) - 1); } memset(line, 0, sizeof(line)); diff --git a/src/lib/core/multiprocessing.c b/src/lib/core/multiprocessing.c index 7ae23c9..92970f0 100644 --- a/src/lib/core/multiprocessing.c +++ b/src/lib/core/multiprocessing.c @@ -74,7 +74,9 @@ int child(struct MultiProcessingPool *pool, struct MultiProcessingTask *task) { // Set log file name if (globals.enable_task_logging) { - sprintf(task->log_file + strlen(task->log_file), "task-%zu-%d.log", mp_global_task_count, task->parent_pid); + const char *log_file_fmt = "task-%zu-%d.log"; + const int log_file_len = snprintf(NULL, 0, log_file_fmt, mp_global_task_count, task->parent_pid); + snprintf(task->log_file + strlen(task->log_file), sizeof(task->log_file) - log_file_len, log_file_fmt, mp_global_task_count, task->parent_pid); } fp_log = freopen(task->log_file, "w+", stdout); if (!fp_log) { @@ -171,17 +173,17 @@ struct MultiProcessingTask *mp_pool_task(struct MultiProcessingPool *pool, const // Set log file path memset(slot->log_file, 0, sizeof(*slot->log_file)); if (globals.enable_task_logging) { - strcat(slot->log_file, pool->log_root); - strcat(slot->log_file, "/"); + strncat(slot->log_file, pool->log_root, sizeof(slot->log_file) - strlen(slot->log_file) - 1); + strncat(slot->log_file, "/", sizeof(slot->log_file) - strlen(slot->log_file) - 1); } else { - strcpy(slot->log_file, "/dev/stdout"); + strncpy(slot->log_file, "/dev/stdout", sizeof(slot->log_file) - 1); } // Set working directory if (isempty(working_dir)) { - strcpy(slot->working_dir, "."); + strncpy(slot->working_dir, ".", sizeof(slot->working_dir) - 1); } else { - strncpy(slot->working_dir, working_dir, PATH_MAX - 1); + strncpy(slot->working_dir, working_dir, sizeof(slot->working_dir) - 1); } // Create a temporary file to act as our intermediate command script @@ -234,14 +236,13 @@ void mp_pool_show_summary(struct MultiProcessingPool *pool) { if (task->status == MP_POOL_TASK_STATUS_INITIAL && task->pid == MP_POOL_PID_UNUSED) { // You will only see this label if the task pool is killed by // MP_POOL_FAIL_FAST and tasks are still queued for execution - strcpy(status_str, "HOLD"); + strncpy(status_str, "HOLD", sizeof(status_str) - 1); } else if (!task->status && !task->signaled_by) { - - strcpy(status_str, "DONE"); + strncpy(status_str, "DONE", sizeof(status_str) - 1); } else if (task->signaled_by) { - strcpy(status_str, "TERM"); + strncpy(status_str, "TERM", sizeof(status_str) - 1); } else { - strcpy(status_str, "FAIL"); + strncpy(status_str, "FAIL", sizeof(status_str) - 1); } char duration[255] = {0}; @@ -362,7 +363,7 @@ int mp_pool_join(struct MultiProcessingPool *pool, size_t jobs, size_t flags) { char progress[1024] = {0}; const double percent = ((double) (tasks_complete + 1) / (double) pool->num_used) * 100; - snprintf(progress, sizeof(progress) - 1, "[%s:%s] [%3.1f%%]", pool->ident, slot->ident, percent); + snprintf(progress, sizeof(progress), "[%s:%s] [%3.1f%%]", pool->ident, slot->ident, percent); int task_timed_out = false; if (slot->timeout) { diff --git a/src/lib/core/recipe.c b/src/lib/core/recipe.c index 99d3fe1..cc96139 100644 --- a/src/lib/core/recipe.c +++ b/src/lib/core/recipe.c @@ -9,14 +9,14 @@ int recipe_clone(char *recipe_dir, char *url, char *gitref, char **result) { memset(destdir, 0, sizeof(destdir)); reponame = path_basename(url); - sprintf(destdir, "%s/%s", recipe_dir, reponame); + snprintf(destdir, sizeof(destdir), "%s/%s", recipe_dir, reponame); if (!*result) { *result = calloc(PATH_MAX, sizeof(*result)); if (!*result) { return -1; } } - strncpy(*result, destdir, PATH_MAX); + strncpy(*result, destdir, PATH_MAX - 1); if (!access(destdir, F_OK)) { if (!strcmp(destdir, "/")) { @@ -52,7 +52,7 @@ int recipe_get_type(char *repopath) { for (size_t i = 0; marker[i] != NULL; i++) { char path[PATH_MAX] = {0}; - sprintf(path, "%s/%s", repopath, marker[i]); + snprintf(path, sizeof(path), "%s/%s", repopath, marker[i]); int result = access(path, F_OK); if (!result) { return type[i]; diff --git a/src/lib/core/relocation.c b/src/lib/core/relocation.c index 58b829d..bd5504b 100644 --- a/src/lib/core/relocation.c +++ b/src/lib/core/relocation.c @@ -50,18 +50,18 @@ int replace_text(char *original, const char *target, const char *replacement, un // replacement is shorter than the target if (rep_len < target_len) { // shrink the string - strcat(buffer, replacement); + strncat(buffer, replacement, sizeof(buffer) - strlen(buffer) - 1); memmove(pos, pos + target_len, strlen(pos) - target_len); memset(pos + (strlen(pos) - target_len), 0, target_len); } else { // replacement is longer than the target // write the replacement value to the buffer - strcat(buffer, replacement); + strncat(buffer, replacement, sizeof(buffer) - strlen(buffer) - 1); // target consumed. jump to the end of the substring. pos += target_len; } if (flags & REPLACE_TRUNCATE_AFTER_MATCH) { if (strstr(pos, LINE_SEP)) { - strcat(buffer, LINE_SEP); + strncat(buffer, LINE_SEP, sizeof(buffer) - strlen(buffer) - 1); } break; } @@ -69,7 +69,7 @@ int replace_text(char *original, const char *target, const char *replacement, un if (!((match = strstr(pos, target)))) { // no more matches // append whatever remains to the buffer - strcat(buffer, pos); + strncat(buffer, pos, sizeof(buffer) - strlen(buffer) - 1); // stop break; } @@ -84,7 +84,7 @@ int replace_text(char *original, const char *target, const char *replacement, un memset(original + buffer_len, 0, original_len - buffer_len); } // replace original with contents of buffer - strcpy(original, buffer); + strncpy(original, buffer, buffer_len + 1); return 0; } diff --git a/src/lib/core/str.c b/src/lib/core/str.c index 9524886..368ab49 100644 --- a/src/lib/core/str.c +++ b/src/lib/core/str.c @@ -119,7 +119,7 @@ char** split(char *_sptr, const char* delim, size_t max) if (!result[i]) { return NULL; } - strcpy(result[i], token); + strncpy(result[i], token, STASIS_BUFSIZ - 1); } // pos is non-zero when maximum split is reached @@ -129,7 +129,7 @@ char** split(char *_sptr, const char* delim, size_t max) if (!result[i]) { return NULL; } - strcpy(result[i], &orig[pos]); + strncpy(result[i], &orig[pos], STASIS_BUFSIZ - 1); } guard_free(sptr); @@ -153,9 +153,9 @@ char *join(char **arr, const char *separator) { result = (char *)calloc(total_bytes, sizeof(char)); for (int i = 0; i < records; i++) { - strcat(result, arr[i]); + strncat(result, arr[i], total_bytes - strlen(result) - 1); if (i < (records - 1)) { - strcat(result, separator); + strncat(result, separator, total_bytes - strlen(result) - 1); } } return result; @@ -207,11 +207,11 @@ char *join_ex(char *separator, ...) { result = calloc(size + 1, sizeof(char)); for (size_t i = 0; i < argc; i++) { // Append argument to string - strcat(result, argv[i]); + strncat(result, argv[i], size - strlen(result)); // no -1 because +1 above // Do not append a trailing separator when we reach the last argument if (i < (argc - 1)) { - strcat(result, separator); + strncat(result, separator, size - strlen(result)); // no -1 because +1 above } guard_free(argv[i]); } @@ -562,7 +562,7 @@ char *normalize_space(char *s) { } // Rewrite the input string - strcpy(result, tmp_orig); + strncpy(result, tmp_orig, strlen(result) + 1); guard_free(tmp_orig); return result; } diff --git a/src/lib/core/strlist.c b/src/lib/core/strlist.c index f3754c3..ff9c098 100644 --- a/src/lib/core/strlist.c +++ b/src/lib/core/strlist.c @@ -84,7 +84,7 @@ int strlist_append_file(struct StrList *pStrList, char *_path, ReaderFn *readerF if (is_url) { int fd; char tempfile[PATH_MAX] = {0}; - strcpy(tempfile, "/tmp/.remote_file.XXXXXX"); + strncpy(tempfile, "/tmp/.remote_file.XXXXXX", sizeof(tempfile) - 1); if ((fd = mkstemp(tempfile)) < 0) { retval = -1; goto fatal; @@ -421,7 +421,7 @@ void strlist_set(struct StrList **pStrList, size_t index, char *value) { } memset((*pStrList)->data[index], '\0', strlen(value) + 1); - strcpy((*pStrList)->data[index], value); + strncpy((*pStrList)->data[index], value, strlen(value)); } } diff --git a/src/lib/core/system.c b/src/lib/core/system.c index 9eff64a..6c18cc2 100644 --- a/src/lib/core/system.c +++ b/src/lib/core/system.c @@ -161,7 +161,7 @@ char *shell_output(const char *command, int *status) { result = tmp; } } - strcat(result, line); + strncat(result, line, current_size - strlen(result) - 1); memset(line, 0, sizeof(line)); } *status = pclose(pp); diff --git a/src/lib/core/template.c b/src/lib/core/template.c index dd3c7a2..623b811 100644 --- a/src/lib/core/template.c +++ b/src/lib/core/template.c @@ -218,7 +218,7 @@ char *tpl_render(char *str) { value = strdup(env_val ? env_val : ""); } else if (do_func) { // {{ func:NAME(a, ...) }} char func_name_temp[STASIS_NAME_MAX] = {0}; - strcpy(func_name_temp, type_stop + 1); + strncpy(func_name_temp, type_stop + 1, sizeof(func_name_temp) - 1); 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); @@ -272,7 +272,7 @@ char *tpl_render(char *str) { // Append replacement value grow(z, &output_bytes, &output); - strcat(output, value); + strncat(output, value, output_bytes - strlen(output) - 1); guard_free(value); output[z] = 0; } diff --git a/src/lib/core/template_func_proto.c b/src/lib/core/template_func_proto.c index 52a11b5..fc58e33 100644 --- a/src/lib/core/template_func_proto.c +++ b/src/lib/core/template_func_proto.c @@ -44,7 +44,7 @@ int get_github_release_notes_auto_tplfunc_entrypoint(void *frame, void *data_out // Using HEAD, GitHub returns the previous tag char *note = NULL; char h1_title[NAME_MAX] = {0}; - sprintf(h1_title, "# %s", test->name); + snprintf(h1_title, sizeof(h1_title), "# %s", test->name); strlist_append(¬es_list, h1_title); result += get_github_release_notes(api_token ? api_token : "anonymous", repository, @@ -80,7 +80,7 @@ int get_junitxml_file_entrypoint(void *frame, void *data_out) { return -1; } char nametmp[PATH_MAX] = {0}; - strcpy(nametmp, cwd); + strncpy(nametmp, cwd, sizeof(nametmp) - 1); char *name = path_basename(nametmp); *output = calloc(PATH_MAX, sizeof(**output)); @@ -88,7 +88,7 @@ int get_junitxml_file_entrypoint(void *frame, void *data_out) { SYSERROR("failed to allocate output string: %s", strerror(errno)); return -1; } - sprintf(*output, "%s/results-%s-%s.xml", ctx->storage.results_dir, name, ctx->info.release_name); + snprintf(*output, PATH_MAX, "%s/results-%s-%s.xml", ctx->storage.results_dir, name, ctx->info.release_name); return result; } @@ -105,7 +105,7 @@ int get_basetemp_dir_entrypoint(void *frame, void *data_out) { return -1; } char nametmp[PATH_MAX] = {0}; - strcpy(nametmp, cwd); + strncpy(nametmp, cwd, sizeof(nametmp) - 1); char *name = path_basename(nametmp); *output = calloc(PATH_MAX, sizeof(**output)); @@ -113,7 +113,7 @@ int get_basetemp_dir_entrypoint(void *frame, void *data_out) { SYSERROR("failed to allocate output string: %s", strerror(errno)); return -1; } - sprintf(*output, "%s/truth-%s-%s", ctx->storage.tmpdir, name, ctx->info.release_name); + snprintf(*output, PATH_MAX, "%s/truth-%s-%s", ctx->storage.tmpdir, name, ctx->info.release_name); return result; } @@ -126,7 +126,7 @@ int tox_run_entrypoint(void *frame, void *data_out) { // Apply workaround for tox positional arguments char *toxconf = NULL; if (!access("tox.ini", F_OK)) { - if (!fix_tox_conf("tox.ini", &toxconf)) { + if (!fix_tox_conf("tox.ini", &toxconf, PATH_MAX)) { msg(STASIS_MSG_L3, "Fixing tox positional arguments\n"); *output = calloc(STASIS_BUFSIZ, sizeof(**output)); if (!*output) { diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c index e106193..f65a1d8 100644 --- a/src/lib/core/utils.c +++ b/src/lib/core/utils.c @@ -45,9 +45,9 @@ int rmtree(char *_path) { while ((d_entity = readdir(dir)) != NULL) { char abspath[PATH_MAX] = {0}; - strcat(abspath, path); - strcat(abspath, DIR_SEP); - strcat(abspath, d_entity->d_name); + strncat(abspath, path, sizeof(abspath) - strlen(abspath) - 1); + strncat(abspath, DIR_SEP, sizeof(abspath) - strlen(abspath) - 1); + strncat(abspath, d_entity->d_name, sizeof(abspath) - strlen(abspath) - 1); if (!strcmp(d_entity->d_name, ".") || !strcmp(d_entity->d_name, "..") || !strcmp(abspath, path)) { continue; @@ -278,13 +278,13 @@ char *find_program(const char *name) { result[0] = '\0'; while ((path_elem = strsep(&path, PATH_SEP))) { char abspath[PATH_MAX] = {0}; - strcat(abspath, path_elem); - strcat(abspath, DIR_SEP); - strcat(abspath, name); + strncat(abspath, path_elem, sizeof(abspath) - strlen(abspath) - 1); + strncat(abspath, DIR_SEP, sizeof(abspath) - strlen(abspath) - 1); + strncat(abspath, name, sizeof(abspath) - strlen(abspath) - 1); if (access(abspath, F_OK) < 0) { continue; } - strncpy(result, abspath, sizeof(result)); + strncpy(result, abspath, sizeof(result) - 1); break; } path = path_orig; @@ -316,11 +316,13 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { } static char command[PATH_MAX] = {0}; - sprintf(command, "%s clone -c advice.detachedHead=false --recursive %s", program, url); + snprintf(command, sizeof(command), "%s clone -c advice.detachedHead=false --recursive %s", program, url); if (destdir && access(destdir, F_OK) < 0) { // Destination directory does not exist - sprintf(command + strlen(command), " %s", destdir); + const char *command_fmt = " %s"; + const int command_fmt_len = snprintf(NULL, 0, command_fmt, destdir); + snprintf(command + strlen(command), sizeof(command) - command_fmt_len, command_fmt, destdir); // Clone the repo result = shell(proc, command); if (result) { @@ -338,7 +340,7 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { if (!pushd(chdir_to)) { memset(command, 0, sizeof(command)); - sprintf(command, "%s fetch --all", program); + snprintf(command, sizeof(command), "%s fetch --all", program); result = shell(proc, command); if (result) { goto die_pop; @@ -346,7 +348,7 @@ int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { if (gitref != NULL) { memset(command, 0, sizeof(command)); - sprintf(command, "%s checkout %s", program, gitref); + snprintf(command, sizeof(command), "%s checkout %s", program, gitref); result = shell(proc, command); if (result) { @@ -403,7 +405,7 @@ char *git_rev_parse(const char *path, char *args) { } // TODO: Use `-C [path]` if the version of git installed supports it - sprintf(cmd, "git rev-parse %s", args); + snprintf(cmd, sizeof(cmd), "git rev-parse %s", args); FILE *pp = popen(cmd, "r"); if (!pp) { return NULL; @@ -442,27 +444,39 @@ void msg(unsigned type, char *fmt, ...) { // for error output stream = stderr; fprintf(stream, "%s", STASIS_COLOR_RED); - strcpy(status, " ERROR: "); + strncpy(status, " ERROR: ", sizeof(status) - 1); } else if (type & STASIS_MSG_WARN) { stream = stderr; fprintf(stream, "%s", STASIS_COLOR_YELLOW); - strcpy(status, " WARNING: "); + strncpy(status, " WARNING: ", sizeof(status) - 1); } else { fprintf(stream, "%s", STASIS_COLOR_GREEN); - strcpy(status, " "); + strncpy(status, " ", sizeof(status) - 1); } if (type & STASIS_MSG_L1) { - sprintf(header, "==>%s" STASIS_COLOR_RESET STASIS_COLOR_WHITE, status); + snprintf(header, sizeof(header), "==>%s" STASIS_COLOR_RESET STASIS_COLOR_WHITE, status); } else if (type & STASIS_MSG_L2) { - sprintf(header, " ->%s" STASIS_COLOR_RESET, status); + snprintf(header, sizeof(header), " ->%s" STASIS_COLOR_RESET, status); } else if (type & STASIS_MSG_L3) { - sprintf(header, STASIS_COLOR_BLUE " ->%s" STASIS_COLOR_RESET, status); + snprintf(header, sizeof(header), STASIS_COLOR_BLUE " ->%s" STASIS_COLOR_RESET, status); } - fprintf(stream, "%s", header); - vfprintf(stream, fmt, args); - fprintf(stream, "%s", STASIS_COLOR_RESET); + if (fprintf(stream, "%s", header) < 0) { + SYSERROR("%s", "unable to write message header to stream"); + return; + } + + const int len = vfprintf(stream, fmt, args); + if (len < 0) { + SYSERROR("%s", "unable to write message to stream"); + return; + } + + if (fprintf(stream, "%s", STASIS_COLOR_RESET) < 0) { + SYSERROR("%s", "unable to write message footer to stream"); + return; + } va_end(args); } @@ -482,12 +496,12 @@ char *xmkstemp(FILE **fp, const char *mode) { char t_name[PATH_MAX * 2]; if (globals.tmpdir) { - strcpy(tmpdir, globals.tmpdir); + strncpy(tmpdir, globals.tmpdir, sizeof(tmpdir) - 1); } else { - strcpy(tmpdir, "/tmp"); + strncpy(tmpdir, "/tmp", sizeof(tmpdir) - 1); } memset(t_name, 0, sizeof(t_name)); - sprintf(t_name, "%s/%s", tmpdir, "STASIS.XXXXXX"); + snprintf(t_name, sizeof(t_name), "%s/%s", tmpdir, "STASIS.XXXXXX"); fd = mkstemp(t_name); *fp = fdopen(fd, mode); @@ -587,7 +601,7 @@ int xml_pretty_print_in_place(const char *filename, const char *pretty_print_pro return 0; } memset(cmd, 0, sizeof(cmd)); - snprintf(cmd, sizeof(cmd) - 1, "%s %s %s", pretty_print_prog, pretty_print_args, filename); + snprintf(cmd, sizeof(cmd), "%s %s %s", pretty_print_prog, pretty_print_args, filename); result = shell_output(cmd, &status); if (status || !result) { goto pretty_print_failed; @@ -636,9 +650,10 @@ int xml_pretty_print_in_place(const char *filename, const char *pretty_print_pro * * @param filename /path/to/tox.ini * @param result path of replacement tox.ini configuration + * @param maxlen * @return 0 on success, -1 on error */ -int fix_tox_conf(const char *filename, char **result) { +int fix_tox_conf(const char *filename, char **result, size_t maxlen) { struct INIFILE *toxini; FILE *fptemp; @@ -650,7 +665,7 @@ int fix_tox_conf(const char *filename, char **result) { // If the result pointer is NULL, allocate enough to store a filesystem path if (!*result) { - *result = calloc(PATH_MAX, sizeof(**result)); + *result = calloc(maxlen, sizeof(**result)); if (!*result) { guard_free(tempfile); return -1; @@ -692,7 +707,7 @@ int fix_tox_conf(const char *filename, char **result) { return -1; } value = tmp; - strcat(value, with_posargs); + strncat(value, with_posargs, (strlen(value) + strlen(with_posargs)) - strlen(value) - 1); ini_setval(&toxini, INI_SETVAL_REPLACE, section_name, key, value); } } @@ -707,7 +722,7 @@ int fix_tox_conf(const char *filename, char **result) { fclose(fptemp); // Store path to modified config - strcpy(*result, tempfile); + strncpy(*result, tempfile, maxlen - 1); guard_free(tempfile); ini_free(&toxini); @@ -756,7 +771,7 @@ int redact_sensitive(const char **to_redact, size_t to_redact_size, char *src, c if (!tmp) { return -1; } - strcpy(tmp, src); + strncpy(tmp, src, strlen(redacted) + strlen(src)); for (size_t i = 0; i < to_redact_size; i++) { if (to_redact[i] && strstr(tmp, to_redact[i])) { @@ -819,16 +834,15 @@ long get_cpu_count() { int mkdirs(const char *_path, mode_t mode) { char *token; char pathbuf[PATH_MAX] = {0}; - char *path; - path = pathbuf; - strcpy(path, _path); + strncpy(pathbuf, _path, sizeof(pathbuf) - 1); + char *path = pathbuf; errno = 0; char result[PATH_MAX] = {0}; int status = 0; while ((token = strsep(&path, "/")) != NULL && !status) { - strcat(result, token); - strcat(result, "/"); + strncat(result, token, sizeof result - strlen(result) - 1); + strncat(result, "/", sizeof result - strlen(result) - 1); status = mkdir(result, mode); if (status && errno == EEXIST) { status = 0; @@ -884,10 +898,10 @@ int env_manipulate_pathstr(const char *key, char *path, int mode) { return 0; } -int gen_file_extension_str(char *filename, const char *extension) { +int gen_file_extension_str(char *filename, const size_t maxlen, const char *extension) { char *ext_orig = strrchr(filename, '.'); if (!ext_orig) { - strcat(filename, extension); + strncat(filename, extension, maxlen - strlen(filename) - 1); return 0; } @@ -912,13 +926,15 @@ void debug_hexdump(char *data, int len) { char *pos = start; while (pos != end) { if (count == 0) { - sprintf(addr + strlen(addr), "%p", pos); + const char *pos_fmt = "%p"; + const int pos_fmt_len = snprintf(NULL, 0, pos_fmt, pos); + snprintf(addr + strlen(addr), sizeof(addr) - pos_fmt_len, pos_fmt, pos); } if (count == 8) { - strcat(bytes, " "); + strncat(bytes, " ", sizeof(bytes) - strlen(bytes) - 1); } if (count > 15) { - sprintf(output, "%s | %s | %s", addr, bytes, ascii); + snprintf(output, sizeof(output), "%s | %s | %s", addr, bytes, ascii); puts(output); memset(output, 0, sizeof(output)); memset(addr, 0, sizeof(addr)); @@ -928,8 +944,13 @@ void debug_hexdump(char *data, int len) { continue; } - sprintf(bytes + strlen(bytes), "%02X ", (unsigned char) *pos); - sprintf(ascii + strlen(ascii), "%c", isprint(*pos) ? *pos : '.'); + const char *bytes_fmt = "%02X "; + const int bytes_fmt_len = snprintf(NULL, 0, bytes_fmt, (unsigned char) *pos); + snprintf(bytes + strlen(bytes), sizeof(bytes) - bytes_fmt_len, bytes_fmt, (unsigned char) *pos); + + const char *ascii_fmt = "%c"; + // no need to calculate length for a single character + snprintf(ascii + strlen(ascii), sizeof(ascii) - strlen(ascii), ascii_fmt, isprint(*pos) ? *pos : '.'); pos++; count++; @@ -937,11 +958,11 @@ void debug_hexdump(char *data, int len) { if (count <= 8) { // Add group padding - strcat(bytes, " "); + strncat(bytes, " ", sizeof(bytes) - strlen(bytes) - 1); } const int padding = 16 - count; for (int i = 0; i < padding; i++) { - strcat(bytes, " "); + strncat(bytes, " ", sizeof(bytes) - strlen(bytes) - 1); } snprintf(output, DEBUG_HEXDUMP_FMT_BYTES + sizeof(addr) + sizeof(bytes) + sizeof(ascii), "%s | %s | %s", addr, bytes, ascii); puts(output); @@ -1171,4 +1192,3 @@ int get_random_bytes(char *result, size_t maxlen) { result[bytes ? bytes - 1 : 0] = '\0'; return 0; } - diff --git a/src/lib/core/wheelinfo.c b/src/lib/core/wheelinfo.c index 1a93a82..ce8ea74 100644 --- a/src/lib/core/wheelinfo.c +++ b/src/lib/core/wheelinfo.c @@ -6,9 +6,9 @@ struct WheelInfo *wheelinfo_get(const char *basepath, const char *name, char *to char package_path[PATH_MAX]; char package_name[NAME_MAX]; - strcpy(package_name, name); + strncpy(package_name, name, sizeof(package_name) - 1); tolower_s(package_name); - sprintf(package_path, "%s/%s", basepath, package_name); + snprintf(package_path, sizeof(package_path), "%s/%s", basepath, package_name); DIR *dp = opendir(package_path); if (!dp) { @@ -20,7 +20,7 @@ struct WheelInfo *wheelinfo_get(const char *basepath, const char *name, char *to continue; } char filename[NAME_MAX]; - strcpy(filename, rec->d_name); + strncpy(filename, rec->d_name, sizeof(filename) - 1); char *ext = strstr(filename, ".whl"); if (ext) { *ext = '\0'; |
