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 | |
| parent | 2258cd05bcded0125136c17d51568831ac421bf7 (diff) | |
| parent | 577912ff0e1996b9846db00247648abd828a8f43 (diff) | |
| download | stasis-e05702d1818088439fd017786a036103062db358.tar.gz | |
Merge pull request #134 from jhunkeler/sprintf-to-snprintf
String safety
52 files changed, 546 insertions, 422 deletions
diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index e3f0ce7..e2315f7 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -64,6 +64,7 @@ jobs: cmake -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DASAN=ON -DTESTS=ON -DTESTS_VERBOSE=ON -DTESTS_RT=ON @@ -77,6 +78,13 @@ jobs: - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} - run: ctest --build-config ${{ matrix.build_type }} --output-on-failure --output-junit results.xml --test-output-size-passed 65536 --test-output-size-failed 65536 + run: | + export ASAN_OPTIONS="verify_asan_link_order=0 strict_string_checks=1 detect_stack_use_after_return=1" + if [[ "$RUNNER_OS" == "macOS" ]]; then + ASAN_OPTIONS="$ASAN_OPTIONS detect_leaks=0" + else + ASAN_OPTIONS="$ASAN_OPTIONS detect_leaks=1" + fi + ctest --build-config ${{ matrix.build_type }} --output-on-failure --output-junit results.xml --test-output-size-passed 65536 --test-output-size-failed 65536 env: STASIS_SYSCONFDIR: ../../.. diff --git a/CMakeLists.txt b/CMakeLists.txt index 71e310b..fc403ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,13 @@ find_package(CURL) option(ASAN "Address Analyzer" OFF) set(ASAN_OPTIONS "-fsanitize=address,null,undefined") +message(CHECK_START "Enable Address Analyzer (ASAN)") if (ASAN) + message(CHECK_PASS "Yes") add_compile_options(${ASAN_OPTIONS} -fno-omit-frame-pointer -g -O0) add_link_options(${ASAN_OPTIONS}) +else() + message(CHECK_FAIL "No") endif() pkg_check_modules(ZIP libzip) diff --git a/src/cli/stasis/args.c b/src/cli/stasis/args.c index dbc9c2f..98b4479 100644 --- a/src/cli/stasis/args.c +++ b/src/cli/stasis/args.c @@ -85,28 +85,30 @@ void usage(char *progname) { int width = get_option_max_width(long_options); for (int x = 0; long_options[x].name != 0; x++) { char tmp[STASIS_NAME_MAX] = {0}; - char output[sizeof(tmp)] = {0}; + char output[STASIS_NAME_MAX] = {0}; char opt_long[50] = {0}; // --? [ARG]? char opt_short[50] = {0}; // -? [ARG]? - strcat(opt_long, "--"); - strcat(opt_long, long_options[x].name); + strncat(opt_long, "--", sizeof(opt_long) - strlen(opt_long) - 1); + strncat(opt_long, long_options[x].name, sizeof(opt_long) - strlen(opt_long) - 1); if (long_options[x].has_arg) { - strcat(opt_long, " ARG"); + strncat(opt_long, " ARG", sizeof(opt_long) - strlen(opt_long) - 1); } if (long_options[x].val <= 'z') { - strcat(opt_short, "-"); + strncat(opt_short, "-", sizeof(opt_short) - strlen(opt_short) - 1); opt_short[1] = (char) long_options[x].val; if (long_options[x].has_arg) { - strcat(opt_short, " ARG"); + strncat(opt_short, " ARG", sizeof(opt_short) - strlen(opt_short) - 1); } } else { - strcat(opt_short, " "); + strncat(opt_short, " ", sizeof(opt_short) - strlen(opt_short) - 1); } - sprintf(tmp, " %%-%ds\t%%s\t\t%%s", width + 4); - sprintf(output, tmp, opt_long, opt_short, long_options_help[x]); + const char *opt_fmt = " %%-%ds\t%%s\t\t%%s"; + size_t opt_fmt_len = snprintf(NULL, 0, opt_fmt, width); + snprintf(tmp, sizeof(tmp) - opt_fmt_len, opt_fmt, width + 4); + snprintf(output, sizeof(output), tmp, opt_long, opt_short, long_options_help[x]); puts(output); } } diff --git a/src/cli/stasis/stasis_main.c b/src/cli/stasis/stasis_main.c index 44efc4a..328d825 100644 --- a/src/cli/stasis/stasis_main.c +++ b/src/cli/stasis/stasis_main.c @@ -45,7 +45,7 @@ static void configure_stasis_ini(struct Delivery *ctx, char **config_input) { if (!*config_input) { SYSDEBUG("%s", "No configuration passed by argument. Using basic config."); char cfgfile[PATH_MAX * 2]; - sprintf(cfgfile, "%s/%s", globals.sysconfdir, "stasis.ini"); + snprintf(cfgfile, sizeof(cfgfile), "%s/%s", globals.sysconfdir, "stasis.ini"); SYSDEBUG("cfgfile: %s", cfgfile); if (!access(cfgfile, F_OK | R_OK)) { *config_input = strdup(cfgfile); @@ -161,9 +161,9 @@ static void check_conda_prefix_length(const struct Delivery *ctx) { } } -static void setup_conda(struct Delivery *ctx, char *installer_url) { +static void setup_conda(struct Delivery *ctx, char *installer_url, const size_t maxlen) { msg(STASIS_MSG_L1, "Conda setup\n"); - delivery_get_conda_installer_url(ctx, installer_url); + delivery_get_conda_installer_url(ctx, installer_url, maxlen); msg(STASIS_MSG_L2, "Downloading: %s\n", installer_url); if (delivery_get_conda_installer(ctx, installer_url)) { msg(STASIS_MSG_ERROR, "download failed: %s\n", installer_url); @@ -429,7 +429,7 @@ static void build_docker(struct Delivery *ctx, const int disabled) { msg(STASIS_MSG_L1 | STASIS_MSG_WARN, "Docker image building is disabled by CLI argument\n"); } else { char dockerfile[PATH_MAX] = {0}; - sprintf(dockerfile, "%s/%s", ctx->storage.build_docker_dir, "Dockerfile"); + snprintf(dockerfile, sizeof(dockerfile), "%s/%s", ctx->storage.build_docker_dir, "Dockerfile"); if (globals.enable_docker) { if (!access(dockerfile, F_OK)) { msg(STASIS_MSG_L1, "Building Docker image\n"); @@ -461,7 +461,7 @@ static void generate_release(struct Delivery *ctx, char *env_name, char *env_nam delivery_export(ctx, (char *[]) {env_name, env_name_testing, NULL}); char specfile[PATH_MAX]; - sprintf(specfile, "%s/%s.yml", ctx->storage.delivery_dir, env_name); + snprintf(specfile, sizeof(specfile), "%s/%s.yml", ctx->storage.delivery_dir, env_name); delivery_rewrite_stage1(ctx, specfile); build_docker(ctx, disable_docker); @@ -532,7 +532,7 @@ int main(int argc, char *argv[]) { globals.continue_on_error = true; break; case 'p': - strcpy(python_override_version, optarg); + strncpy(python_override_version, optarg, sizeof(python_override_version) - 1); break; case 'l': globals.cpu_limit = strtol(optarg, NULL, 10); @@ -652,9 +652,9 @@ int main(int argc, char *argv[]) { configure_jfrog_cli(&ctx); runtime_apply(ctx.runtime.environ); - strcpy(env_name, ctx.info.release_name); - strcpy(env_name_testing, env_name); - strcat(env_name_testing, "-test"); + strncpy(env_name, ctx.info.release_name, sizeof(env_name) - 1); + strncpy(env_name_testing, env_name, sizeof(env_name_testing) - 1); + strncat(env_name_testing, "-test", sizeof(env_name_testing) - strlen(env_name_testing) - 1); char *envs[] = { "release", env_name, "testing", env_name_testing, @@ -666,7 +666,7 @@ int main(int argc, char *argv[]) { check_conda_install_prefix(&ctx); check_conda_prefix_length(&ctx); - setup_conda(&ctx, installer_url); + setup_conda(&ctx, installer_url, sizeof(installer_url)); configure_conda_base(&ctx, envs); configure_conda_purge(&ctx, envs); setup_activate_test_env(&ctx, env_name_testing); diff --git a/src/cli/stasis_indexer/args.c b/src/cli/stasis_indexer/args.c index 2d92ab0..8c9d3fe 100644 --- a/src/cli/stasis_indexer/args.c +++ b/src/cli/stasis_indexer/args.c @@ -30,7 +30,7 @@ void usage(char *name) { for (int i = 0; i < maxopts - 1; i++) { char line[255] = {0}; - sprintf(line, " --%s -%c %-20s", long_options[i].name, long_options[i].val, long_options_help[i]); + snprintf(line, sizeof(line), " --%s -%c %-20s", long_options[i].name, long_options[i].val, long_options_help[i]); puts(line); } diff --git a/src/cli/stasis_indexer/helpers.c b/src/cli/stasis_indexer/helpers.c index 6dc653d..0debfe4 100644 --- a/src/cli/stasis_indexer/helpers.c +++ b/src/cli/stasis_indexer/helpers.c @@ -96,44 +96,44 @@ int pandoc_exec(const char *in_file, const char *out_file, const char *css_file, if (!get_pandoc_version(&pandoc_version)) { // < 2.19 if (pandoc_version < 0x02130000) { - strcat(pandoc_versioned_args, "--self-contained "); + strncat(pandoc_versioned_args, "--self-contained ", sizeof(pandoc_versioned_args) - strlen(pandoc_versioned_args) - 1); } else { // >= 2.19 - strcat(pandoc_versioned_args, "--embed-resources "); + strncat(pandoc_versioned_args, "--embed-resources ", sizeof(pandoc_versioned_args) - strlen(pandoc_versioned_args) - 1); } // >= 1.15.0.4 if (pandoc_version >= 0x010f0004) { - strcat(pandoc_versioned_args, "--standalone "); + strncat(pandoc_versioned_args, "--standalone ", sizeof(pandoc_versioned_args) - strlen(pandoc_versioned_args) - 1); } // >= 1.10.0.1 if (pandoc_version >= 0x010a0001) { - strcat(pandoc_versioned_args, "-f gfm+autolink_bare_uris "); + strncat(pandoc_versioned_args, "-f gfm+autolink_bare_uris ", sizeof(pandoc_versioned_args) - strlen(pandoc_versioned_args) - 1); } // > 3.1.9 if (pandoc_version > 0x03010900) { - strcat(pandoc_versioned_args, "-f gfm+alerts "); + strncat(pandoc_versioned_args, "-f gfm+alerts ", sizeof(pandoc_versioned_args) - strlen(pandoc_versioned_args) - 1); } } // Converts a markdown file to html char cmd[STASIS_BUFSIZ] = {0}; - strcpy(cmd, "pandoc "); - strcat(cmd, pandoc_versioned_args); + strncpy(cmd, "pandoc ", sizeof(cmd) - 1); + strncat(cmd, pandoc_versioned_args, sizeof(cmd) - strlen(cmd) - 1); if (css_file && strlen(css_file)) { - strcat(cmd, "--css "); - strcat(cmd, css_file); + strncat(cmd, "--css ", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, css_file, sizeof(cmd) - strlen(cmd) - 1); } - strcat(cmd, " "); - strcat(cmd, "--metadata title=\""); - strcat(cmd, title); - strcat(cmd, "\" "); - strcat(cmd, "-o "); - strcat(cmd, out_file); - strcat(cmd, " "); - strcat(cmd, in_file); + strncat(cmd, " ", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, "--metadata title=\"", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, title, sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, "\" ", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, "-o ", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, out_file, sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, " ", sizeof(cmd) - strlen(cmd) - 1); + strncat(cmd, in_file, sizeof(cmd) - strlen(cmd) - 1); if (globals.verbose) { puts(cmd); @@ -243,7 +243,15 @@ int get_files(struct StrList **out, const char *path, const char *pattern, ...) va_list args; va_start(args, pattern); char userpattern[PATH_MAX] = {0}; - vsprintf(userpattern, pattern, args); + const int len = vsnprintf(userpattern, sizeof(userpattern), pattern, args); + if (len < 0) { + SYSERROR("%s", "vsnprintf failed\n"); + va_end(args); + return -1; + } + if ((size_t) len > sizeof(userpattern)) { + fprintf(stderr, "WARNING: %s: userpattern truncated!\n", __FUNCTION__); + } va_end(args); if (!strlen(userpattern)) { userpattern[0] = '*'; @@ -377,8 +385,8 @@ int write_manifest(const char *path, char **exclude_path, FILE *fp) { } char filepath[PATH_MAX] = {0}; strncpy(filepath, path, PATH_MAX - 1); - strcat(filepath, "/"); - strcat(filepath, rec->d_name); + strncat(filepath, "/", sizeof(filepath) - strlen(filepath) - 1); + strncat(filepath, rec->d_name, sizeof(filepath) - strlen(filepath) - 1); if (rec->d_type == DT_DIR) { write_manifest(filepath, exclude_path, fp); continue; diff --git a/src/cli/stasis_indexer/junitxml_report.c b/src/cli/stasis_indexer/junitxml_report.c index 21cf729..d30ee09 100644 --- a/src/cli/stasis_indexer/junitxml_report.c +++ b/src/cli/stasis_indexer/junitxml_report.c @@ -98,7 +98,7 @@ static int write_report_output(struct Delivery *ctx, FILE *destfp, const char *x int indexer_junitxml_report(struct Delivery **ctx, const size_t nelem) { char indexfile[PATH_MAX] = {0}; - sprintf(indexfile, "%s/README.md", (*ctx)->storage.results_dir); + snprintf(indexfile, sizeof(indexfile), "%s/README.md", (*ctx)->storage.results_dir); struct StrList *file_listing = listdir((*ctx)->storage.results_dir); if (!file_listing) { @@ -117,7 +117,7 @@ int indexer_junitxml_report(struct Delivery **ctx, const size_t nelem) { int current_rc = (*ctx)->meta.rc; for (size_t d = 0; d < nelem; d++) { char pattern[PATH_MAX] = {0}; - snprintf(pattern, sizeof(pattern) - 1, "*%s*", ctx[d]->info.release_name); + snprintf(pattern, sizeof(pattern), "*%s*", ctx[d]->info.release_name); // if the result directory contains tests for this release name, print them if (!is_file_in_listing(file_listing, pattern)) { diff --git a/src/cli/stasis_indexer/readmes.c b/src/cli/stasis_indexer/readmes.c index edc6312..91c936f 100644 --- a/src/cli/stasis_indexer/readmes.c +++ b/src/cli/stasis_indexer/readmes.c @@ -12,7 +12,7 @@ int indexer_readmes(struct Delivery **ctx, const size_t nelem) { } char indexfile[PATH_MAX] = {0}; - sprintf(indexfile, "%s/README.md", (*ctx)->storage.delivery_dir); + snprintf(indexfile, sizeof(indexfile), "%s/README.md", (*ctx)->storage.delivery_dir); FILE *indexfp = fopen(indexfile, "w+"); if (!indexfp) { @@ -52,10 +52,10 @@ int indexer_readmes(struct Delivery **ctx, const size_t nelem) { if (!latest_deliveries[i]->meta.name) { continue; } - sprintf(link_name, "latest-py%s-%s-%s.yml", latest_deliveries[i]->meta.python_compact, latest_deliveries[i]->system.platform[DELIVERY_PLATFORM_RELEASE], latest_deliveries[i]->system.arch); - sprintf(readme_name, "README-py%s-%s-%s.md", latest_deliveries[i]->meta.python_compact, latest_deliveries[i]->system.platform[DELIVERY_PLATFORM_RELEASE], latest_deliveries[i]->system.arch); - sprintf(conf_name, "%s.ini", latest_deliveries[i]->info.release_name); - sprintf(conf_name_relative, "../config/%s.ini", latest_deliveries[i]->info.release_name); + snprintf(link_name, sizeof(link_name), "latest-py%s-%s-%s.yml", latest_deliveries[i]->meta.python_compact, latest_deliveries[i]->system.platform[DELIVERY_PLATFORM_RELEASE], latest_deliveries[i]->system.arch); + snprintf(readme_name, sizeof(readme_name), "README-py%s-%s-%s.md", latest_deliveries[i]->meta.python_compact, latest_deliveries[i]->system.platform[DELIVERY_PLATFORM_RELEASE], latest_deliveries[i]->system.arch); + snprintf(conf_name, sizeof(conf_name), "%s.ini", latest_deliveries[i]->info.release_name); + snprintf(conf_name_relative, sizeof(conf_name_relative), "../config/%s.ini", latest_deliveries[i]->info.release_name); if (strstr(link_name, platform) && strstr(link_name, arch)) { fprintf(indexfp, "- Python %s\n", latest_deliveries[i]->meta.python); fprintf(indexfp, " - Info: [README](%s)\n", readme_name); diff --git a/src/cli/stasis_indexer/stasis_indexer_main.c b/src/cli/stasis_indexer/stasis_indexer_main.c index 840e897..d475c15 100644 --- a/src/cli/stasis_indexer/stasis_indexer_main.c +++ b/src/cli/stasis_indexer/stasis_indexer_main.c @@ -13,9 +13,9 @@ int indexer_combine_rootdirs(const char *dest, char **rootdirs, const size_t roo char destdir_with_output[PATH_MAX] = {0}; char *destdir = destdir_bare; - strcpy(destdir_bare, dest); - strcpy(destdir_with_output, dest); - strcat(destdir_with_output, "/output"); + strncpy(destdir_bare, dest, sizeof(destdir_bare) - 1); + strncpy(destdir_with_output, dest, sizeof(destdir_with_output) - 1); + strncat(destdir_with_output, "/output", sizeof(destdir_with_output) - strlen(destdir_with_output) - 1); if (!access(destdir_with_output, F_OK)) { destdir = destdir_with_output; @@ -26,9 +26,9 @@ int indexer_combine_rootdirs(const char *dest, char **rootdirs, const size_t roo char srcdir_bare[PATH_MAX] = {0}; char srcdir_with_output[PATH_MAX] = {0}; char *srcdir = srcdir_bare; - strcpy(srcdir_bare, rootdirs[i]); - strcpy(srcdir_with_output, rootdirs[i]); - strcat(srcdir_with_output, "/output"); + strncpy(srcdir_bare, rootdirs[i], sizeof(srcdir_bare) - 1); + strncpy(srcdir_with_output, rootdirs[i], sizeof(srcdir_with_output) - 1); + strncat(srcdir_with_output, "/output", sizeof(srcdir_with_output) - strlen(srcdir_with_output) - 1); if (access(srcdir_bare, F_OK)) { fprintf(stderr, "%s does not exist\n", srcdir_bare); @@ -80,11 +80,11 @@ int indexer_symlinks(struct Delivery **ctx, const size_t nelem) { if (!data[i]->meta.name) { continue; } - sprintf(link_name_spec, "latest-py%s-%s-%s.yml", data[i]->meta.python_compact, data[i]->system.platform[DELIVERY_PLATFORM_RELEASE], data[i]->system.arch); - sprintf(file_name_spec, "%s.yml", data[i]->info.release_name); + snprintf(link_name_spec, sizeof(link_name_spec), "latest-py%s-%s-%s.yml", data[i]->meta.python_compact, data[i]->system.platform[DELIVERY_PLATFORM_RELEASE], data[i]->system.arch); + snprintf(file_name_spec, sizeof(file_name_spec), "%s.yml", data[i]->info.release_name); - sprintf(link_name_readme, "README-py%s-%s-%s.md", data[i]->meta.python_compact, data[i]->system.platform[DELIVERY_PLATFORM_RELEASE], data[i]->system.arch); - sprintf(file_name_readme, "README-%s.md", data[i]->info.release_name); + snprintf(link_name_readme, sizeof(link_name_readme), "README-py%s-%s-%s.md", data[i]->meta.python_compact, data[i]->system.platform[DELIVERY_PLATFORM_RELEASE], data[i]->system.arch); + snprintf(file_name_readme, sizeof(file_name_readme), "README-%s.md", data[i]->info.release_name); if (!access(link_name_spec, F_OK)) { if (unlink(link_name_spec)) { @@ -151,7 +151,7 @@ void indexer_init_dirs(struct Delivery *ctx, const char *workdir) { char newpath[PATH_MAX] = {0}; if (getenv("PATH")) { - sprintf(newpath, "%s/bin:%s", ctx->storage.tools_dir, getenv("PATH")); + snprintf(newpath, sizeof(newpath), "%s/bin:%s", ctx->storage.tools_dir, getenv("PATH")); setenv("PATH", newpath, 1); } else { SYSERROR("%s", "environment variable PATH is undefined. Unable to continue."); @@ -261,11 +261,11 @@ int main(const int argc, char *argv[]) { char workdir_template[PATH_MAX] = {0}; const char *system_tmp = getenv("TMPDIR"); if (system_tmp) { - strcat(workdir_template, system_tmp); + strncat(workdir_template, system_tmp, sizeof(workdir_template) - strlen(workdir_template) - 1); } else { - strcat(workdir_template, "/tmp"); + strncat(workdir_template, "/tmp", sizeof(workdir_template) - strlen(workdir_template) - 1); } - strcat(workdir_template, "/stasis-combine.XXXXXX"); + strncat(workdir_template, "/stasis-combine.XXXXXX", sizeof(workdir_template) - strlen(workdir_template) - 1); char *workdir = mkdtemp(workdir_template); if (!workdir) { SYSERROR("Unable to create temporary directory: %s", workdir_template); @@ -320,6 +320,11 @@ int main(const int argc, char *argv[]) { msg(STASIS_MSG_L1, "Loading metadata\n"); struct StrList *metafiles = NULL; get_files(&metafiles, ctx.storage.meta_dir, "*.stasis"); + if (!metafiles || !strlist_count(metafiles)) { + SYSERROR("%s: No metadata!", ctx.storage.meta_dir); + delivery_free(&ctx); + exit(1); + } strlist_sort(metafiles, STASIS_SORT_LEN_ASCENDING); struct Delivery **local = calloc(strlist_count(metafiles) + 1, sizeof(*local)); @@ -411,7 +416,7 @@ int main(const int argc, char *argv[]) { msg(STASIS_MSG_L1, "Copying indexed delivery to '%s'\n", destdir); char cmd[PATH_MAX] = {0}; - sprintf(cmd, "rsync -ah%s --delete --exclude 'tmp/' --exclude 'tools/' '%s/' '%s/'", globals.verbose ? "v" : "q", workdir, destdir); + snprintf(cmd, sizeof(cmd), "rsync -ah%s --delete --exclude 'tmp/' --exclude 'tools/' '%s/' '%s/'", globals.verbose ? "v" : "q", workdir, destdir); guard_free(destdir); if (globals.verbose) { diff --git a/src/cli/stasis_indexer/website.c b/src/cli/stasis_indexer/website.c index 966391e..8a5126d 100644 --- a/src/cli/stasis_indexer/website.c +++ b/src/cli/stasis_indexer/website.c @@ -8,7 +8,7 @@ int indexer_make_website(struct Delivery **ctx) { return -1; } - sprintf(css_filename, "%s/%s", globals.sysconfdir, "stasis_pandoc.css"); + snprintf(css_filename, PATH_MAX, "%s/%s", globals.sysconfdir, "stasis_pandoc.css"); const int have_css = access(css_filename, F_OK | R_OK) == 0; struct StrList *dirs = strlist_init(); @@ -29,14 +29,14 @@ int indexer_make_website(struct Delivery **ctx) { char *filename = path_basename(strlist_item(inputs, x)); char fullpath_src[PATH_MAX] = {0}; char fullpath_dest[PATH_MAX] = {0}; - sprintf(fullpath_src, "%s/%s", root, filename); + snprintf(fullpath_src, sizeof(fullpath_src), "%s/%s", root, filename); if (access(fullpath_src, F_OK)) { continue; } // Replace *.md extension with *.html. - strcpy(fullpath_dest, fullpath_src); - gen_file_extension_str(fullpath_dest, ".html"); + strncpy(fullpath_dest, fullpath_src, sizeof(fullpath_dest) - 1); + gen_file_extension_str(fullpath_dest, sizeof(fullpath_dest), ".html"); // Convert markdown to html if (pandoc_exec(fullpath_src, fullpath_dest, have_css ? css_filename : NULL, "STASIS")) { @@ -52,8 +52,8 @@ int indexer_make_website(struct Delivery **ctx) { if (!strcmp(filename, "README.md")) { char link_from[PATH_MAX] = {0}; char link_dest[PATH_MAX] = {0}; - strcpy(link_from, "README.html"); - sprintf(link_dest, "%s/%s", root, "index.html"); + strncpy(link_from, "README.html", sizeof(link_from) - 1); + snprintf(link_dest, sizeof(link_dest), "%s/%s", root, "index.html"); if (symlink(link_from, link_dest)) { SYSERROR("Warning: symlink(%s, %s) failed: %s", link_from, link_dest, strerror(errno)); } 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'; diff --git a/src/lib/delivery/delivery.c b/src/lib/delivery/delivery.c index be6e8ff..7d78878 100644 --- a/src/lib/delivery/delivery.c +++ b/src/lib/delivery/delivery.c @@ -265,8 +265,11 @@ void delivery_free(struct Delivery *ctx) { guard_free(ctx->_stasis_ini_fp.mission_path); } -int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt) { - size_t fmt_len = strlen(fmt); +int delivery_format_str(struct Delivery *ctx, char **dest, size_t maxlen, const char *fmt) { + const size_t fmt_len = strlen(fmt); + if (maxlen < 1) { + maxlen = 1; + } if (!*dest) { *dest = calloc(STASIS_NAME_MAX, sizeof(**dest)); @@ -280,47 +283,47 @@ int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt) { i++; switch (fmt[i]) { case 'n': // name - strcat(*dest, ctx->meta.name); + strncat(*dest, ctx->meta.name, maxlen - 1); break; case 'c': // codename - strcat(*dest, ctx->meta.codename); + strncat(*dest, ctx->meta.codename, maxlen - 1); break; case 'm': // mission - strcat(*dest, ctx->meta.mission); + strncat(*dest, ctx->meta.mission, maxlen - 1); break; case 'r': // revision - sprintf(*dest + strlen(*dest), "%d", ctx->meta.rc); + snprintf(*dest + strlen(*dest), maxlen, "%d", ctx->meta.rc); break; case 'R': // "final"-aware revision if (ctx->meta.final) - strcat(*dest, "final"); + strncat(*dest, "final", maxlen); else - sprintf(*dest + strlen(*dest), "%d", ctx->meta.rc); + snprintf(*dest + strlen(*dest), maxlen, "%d", ctx->meta.rc); break; case 'v': // version - strcat(*dest, ctx->meta.version); + strncat(*dest, ctx->meta.version, maxlen - 1); break; case 'P': // python version - strcat(*dest, ctx->meta.python); + strncat(*dest, ctx->meta.python, maxlen - 1); break; case 'p': // python version major/minor - strcat(*dest, ctx->meta.python_compact); + strncat(*dest, ctx->meta.python_compact, maxlen - 1); break; case 'a': // system architecture name - strcat(*dest, ctx->system.arch); + strncat(*dest, ctx->system.arch, maxlen - 1); break; case 'o': // system platform (OS) name - strcat(*dest, ctx->system.platform[DELIVERY_PLATFORM_RELEASE]); + strncat(*dest, ctx->system.platform[DELIVERY_PLATFORM_RELEASE], maxlen - 1); break; case 't': // unix epoch - sprintf(*dest + strlen(*dest), "%ld", ctx->info.time_now); + snprintf(*dest + strlen(*dest), maxlen, "%ld", ctx->info.time_now); break; default: // unknown formatter, write as-is - sprintf(*dest + strlen(*dest), "%c%c", fmt[i - 1], fmt[i]); + snprintf(*dest + strlen(*dest), maxlen, "%c%c", fmt[i - 1], fmt[i]); break; } } else { // write non-format text - sprintf(*dest + strlen(*dest), "%c", fmt[i]); + snprintf(*dest + strlen(*dest), maxlen, "%c", fmt[i]); } } return 0; @@ -335,11 +338,11 @@ void delivery_defer_packages(struct Delivery *ctx, int type) { if (DEFER_CONDA == type) { dataptr = ctx->conda.conda_packages; deferred = ctx->conda.conda_packages_defer; - strcpy(mode, "conda"); + strncpy(mode, "conda", sizeof(mode) - 1); } else if (DEFER_PIP == type) { dataptr = ctx->conda.pip_packages; deferred = ctx->conda.pip_packages_defer; - strcpy(mode, "pip"); + strncpy(mode, "pip", sizeof(mode) - 1); } else { SYSERROR("BUG: type %d does not map to a supported package manager!\n", type); exit(1); diff --git a/src/lib/delivery/delivery_artifactory.c b/src/lib/delivery/delivery_artifactory.c index 97db752..0a74241 100644 --- a/src/lib/delivery/delivery_artifactory.c +++ b/src/lib/delivery/delivery_artifactory.c @@ -4,8 +4,8 @@ int delivery_init_artifactory(struct Delivery *ctx) { int status = 0; char dest[PATH_MAX] = {0}; char filepath[PATH_MAX] = {0}; - snprintf(dest, sizeof(dest) - 1, "%s/bin", ctx->storage.tools_dir); - snprintf(filepath, sizeof(dest) - 1, "%s/bin/jf", ctx->storage.tools_dir); + snprintf(dest, sizeof(dest), "%s/bin", ctx->storage.tools_dir); + snprintf(filepath, sizeof(dest), "%s/bin/jf", ctx->storage.tools_dir); if (!access(filepath, F_OK)) { // already have it @@ -32,7 +32,7 @@ int delivery_init_artifactory(struct Delivery *ctx) { // JFROG_CLI_HOME_DIR is where .jfrog is stored char path[PATH_MAX] = {0}; - snprintf(path, sizeof(path) - 1, "%s/.jfrog", ctx->storage.build_dir); + snprintf(path, sizeof(path), "%s/.jfrog", ctx->storage.build_dir); setenv("JFROG_CLI_HOME_DIR", path, 1); // JFROG_CLI_TEMP_DIR is where the obvious is stored @@ -84,8 +84,8 @@ int delivery_artifact_upload(struct Delivery *ctx) { for (size_t f = 0; f < strlist_count(ctx->deploy.jfrog[i].files); f++) { char dest[PATH_MAX] = {0}; char files[PATH_MAX] = {0}; - snprintf(dest, sizeof(dest) - 1, "%s/%s", ctx->deploy.jfrog[i].repo, ctx->deploy.jfrog[i].dest); - snprintf(files, sizeof(files) - 1, "%s", strlist_item(ctx->deploy.jfrog[i].files, f)); + snprintf(dest, sizeof(dest), "%s/%s", ctx->deploy.jfrog[i].repo, ctx->deploy.jfrog[i].dest); + snprintf(files, sizeof(files), "%s", strlist_item(ctx->deploy.jfrog[i].files, f)); status += jfrog_cli_rt_upload(&ctx->deploy.jfrog_auth, &ctx->deploy.jfrog[i].upload_ctx, files, dest); } } @@ -134,7 +134,7 @@ int delivery_mission_render_files(struct Delivery *ctx) { guard_free(data.src); return 1; } - sprintf(data.src, "%s/%s/%s", ctx->storage.mission_dir, ctx->meta.mission, val.as_char_p); + snprintf(data.src, PATH_MAX, "%s/%s/%s", ctx->storage.mission_dir, ctx->meta.mission, val.as_char_p); msg(STASIS_MSG_L2, "%s\n", data.src); int err = 0; @@ -210,7 +210,7 @@ int delivery_series_sync(struct Delivery *ctx) { } char *release_pattern = NULL; - if (delivery_format_str(ctx, &release_pattern, r_fmt) < 0) { + if (delivery_format_str(ctx, &release_pattern, STASIS_NAME_MAX, r_fmt) < 0) { SYSERROR("Unable to render delivery format string: %s", r_fmt); guard_free(r_fmt); return -1; diff --git a/src/lib/delivery/delivery_build.c b/src/lib/delivery/delivery_build.c index 0a86ec4..3ff5df7 100644 --- a/src/lib/delivery/delivery_build.c +++ b/src/lib/delivery/delivery_build.c @@ -37,7 +37,7 @@ int delivery_build_recipes(struct Delivery *ctx) { tag[strlen(ctx->tests->test[i]->repository_info_tag)] = '\0'; } } else { - strcpy(tag, ctx->tests->test[i]->version); + strncpy(tag, ctx->tests->test[i]->version, sizeof(tag) - 1); } //sprintf(recipe_version, "{%% set version = GIT_DESCRIBE_TAG ~ \".dev\" ~ GIT_DESCRIBE_NUMBER ~ \"+\" ~ GIT_DESCRIBE_HASH %%}"); @@ -48,15 +48,15 @@ int delivery_build_recipes(struct Delivery *ctx) { // Perhaps we can key it to the recipe type, because the archive is a requirement imposed // by conda-forge. Hmm. - sprintf(recipe_version, "{%% set version = \"%s\" %%}", tag); - sprintf(recipe_git_url, " url: %s/archive/refs/tags/{{ version }}.tar.gz", ctx->tests->test[i]->repository); - strcpy(recipe_git_rev, ""); - sprintf(recipe_buildno, " number: 0"); + snprintf(recipe_version, sizeof(recipe_version), "{%% set version = \"%s\" %%}", tag); + snprintf(recipe_git_url, sizeof(recipe_git_url), " url: %s/archive/refs/tags/{{ version }}.tar.gz", ctx->tests->test[i]->repository); + strncpy(recipe_git_rev, "", sizeof(recipe_git_rev) - 1); + snprintf(recipe_buildno, sizeof(recipe_buildno), " number: 0"); unsigned flags = REPLACE_TRUNCATE_AFTER_MATCH; //file_replace_text("meta.yaml", "{% set version = ", recipe_version); if (ctx->meta.final) { // remove this. i.e. statis cannot deploy a release to conda-forge - sprintf(recipe_version, "{%% set version = \"%s\" %%}", ctx->tests->test[i]->version); + snprintf(recipe_version, sizeof(recipe_version), "{%% set version = \"%s\" %%}", ctx->tests->test[i]->version); // TODO: replace sha256 of tagged archive // TODO: leave the recipe unchanged otherwise. in theory this should produce the same conda package hash as conda forge. // For now, remove the sha256 requirement @@ -74,25 +74,25 @@ int delivery_build_recipes(struct Delivery *ctx) { char arch[STASIS_NAME_MAX] = {0}; char platform[STASIS_NAME_MAX] = {0}; - strcpy(platform, ctx->system.platform[DELIVERY_PLATFORM]); + strncpy(platform, ctx->system.platform[DELIVERY_PLATFORM], sizeof(platform) - 1); if (strstr(platform, "Darwin")) { memset(platform, 0, sizeof(platform)); - strcpy(platform, "osx"); + strncpy(platform, "osx", sizeof(platform) - 1); } tolower_s(platform); if (strstr(ctx->system.arch, "arm64")) { - strcpy(arch, "arm64"); + strncpy(arch, "arm64", sizeof(arch) - 1); } else if (strstr(ctx->system.arch, "64")) { - strcpy(arch, "64"); + strncpy(arch, "64", sizeof(arch) - 1); } else { - strcat(arch, "32"); // blind guess + strncat(arch, "32", sizeof(arch) - 1); // blind guess } tolower_s(arch); - sprintf(command, "mambabuild --python=%s -m ../.ci_support/%s_%s_.yaml .", + snprintf(command, sizeof(command), "mambabuild --python=%s -m ../.ci_support/%s_%s_.yaml .", ctx->meta.python, platform, arch); } else { - sprintf(command, "mambabuild --python=%s .", ctx->meta.python); + snprintf(command, sizeof(command), "mambabuild --python=%s .", ctx->meta.python); } int status = conda_exec(command); if (status) { @@ -131,7 +131,7 @@ int filter_repo_tags(char *repo, struct StrList *patterns) { int match = fnmatch(pattern, tag, 0); if (!match) { char cmd[PATH_MAX] = {0}; - sprintf(cmd, "git tag -d %s", tag); + snprintf(cmd, sizeof(cmd), "git tag -d %s", tag); result += system(cmd); break; } @@ -398,7 +398,7 @@ struct StrList *delivery_build_wheels(struct Delivery *ctx) { memset(srcdir, 0, sizeof(srcdir)); memset(wheeldir, 0, sizeof(wheeldir)); - sprintf(srcdir, "%s/%s", ctx->storage.build_sources_dir, ctx->tests->test[i]->name); + snprintf(srcdir, sizeof(srcdir), "%s/%s", ctx->storage.build_sources_dir, ctx->tests->test[i]->name); if (git_clone(&proc, ctx->tests->test[i]->repository, srcdir, ctx->tests->test[i]->version)) { SYSERROR("Unable to checkout tag '%s' for package '%s' from repository '%s'\n", ctx->tests->test[i]->version, ctx->tests->test[i]->name, ctx->tests->test[i]->repository); @@ -434,9 +434,9 @@ struct StrList *delivery_build_wheels(struct Delivery *ctx) { COE_CHECK_ABORT(dep_status, "Unreproducible delivery"); } - strcpy(dname, ctx->tests->test[i]->name); + strncpy(dname, ctx->tests->test[i]->name, sizeof(dname) - 1); tolower_s(dname); - sprintf(outdir, "%s/%s", ctx->storage.wheel_artifact_dir, dname); + snprintf(outdir, sizeof(outdir), "%s/%s", ctx->storage.wheel_artifact_dir, dname); if (mkdirs(outdir, 0755)) { fprintf(stderr, "failed to create output directory: %s\n", outdir); guard_strlist_free(&result); diff --git a/src/lib/delivery/delivery_conda.c b/src/lib/delivery/delivery_conda.c index 191d93f..d6898a4 100644 --- a/src/lib/delivery/delivery_conda.c +++ b/src/lib/delivery/delivery_conda.c @@ -1,21 +1,32 @@ #include "delivery.h" -void delivery_get_conda_installer_url(struct Delivery *ctx, char *result) { +void delivery_get_conda_installer_url(struct Delivery *ctx, char *result, size_t maxlen) { + int len = 0; if (ctx->conda.installer_version) { // Use version specified by configuration file - sprintf(result, "%s/%s-%s-%s-%s.sh", ctx->conda.installer_baseurl, + len = snprintf(NULL, 0, ctx->conda.installer_baseurl, + ctx->conda.installer_name, + ctx->conda.installer_version, + ctx->conda.installer_platform, + ctx->conda.installer_arch); + + snprintf(result, maxlen - len, "%s/%s-%s-%s-%s.sh", ctx->conda.installer_baseurl, ctx->conda.installer_name, ctx->conda.installer_version, ctx->conda.installer_platform, ctx->conda.installer_arch); } else { // Use latest installer - sprintf(result, "%s/%s-%s-%s.sh", ctx->conda.installer_baseurl, + len = snprintf(NULL, 0, "%s/%s-%s-%s.sh", ctx->conda.installer_baseurl, ctx->conda.installer_name, ctx->conda.installer_platform, ctx->conda.installer_arch); - } + snprintf(result, maxlen - len, "%s/%s-%s-%s.sh", ctx->conda.installer_baseurl, + ctx->conda.installer_name, + ctx->conda.installer_platform, + ctx->conda.installer_arch); + } } int delivery_get_conda_installer(struct Delivery *ctx, char *installer_url) { @@ -23,7 +34,7 @@ int delivery_get_conda_installer(struct Delivery *ctx, char *installer_url) { char *installer = path_basename(installer_url); memset(script_path, 0, sizeof(script_path)); - sprintf(script_path, "%s/%s", ctx->storage.tmpdir, installer); + snprintf(script_path, sizeof(script_path), "%s/%s", ctx->storage.tmpdir, installer); if (access(script_path, F_OK)) { // Script doesn't exist char *errmsg = NULL; @@ -61,7 +72,7 @@ void delivery_install_conda(char *install_script, char *conda_install_dir) { // Proceed with the installation // -b = batch mode (non-interactive) char cmd[PATH_MAX] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "%s %s -b -p %s", + snprintf(cmd, sizeof(cmd), "%s %s -b -p %s", find_program("bash"), install_script, conda_install_dir); @@ -73,7 +84,7 @@ void delivery_install_conda(char *install_script, char *conda_install_dir) { // Proceed with the installation // -b = batch mode (non-interactive) char cmd[PATH_MAX] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "%s %s -b -p %s", + snprintf(cmd, sizeof(cmd), "%s %s -b -p %s", find_program("bash"), install_script, conda_install_dir); @@ -97,7 +108,7 @@ void delivery_conda_enable(struct Delivery *ctx, char *conda_install_dir) { // way to make sure the file is used. Not setting this variable leads to strange // behavior, especially if a conda environment is already active when STASIS is loaded. char rcpath[PATH_MAX]; - sprintf(rcpath, "%s/%s", conda_install_dir, ".condarc"); + snprintf(rcpath, sizeof(rcpath), "%s/%s", conda_install_dir, ".condarc"); setenv("CONDARC", rcpath, 1); if (runtime_replace(&ctx->runtime.environ, __environ)) { perror("unable to replace runtime environment after activating conda"); diff --git a/src/lib/delivery/delivery_docker.c b/src/lib/delivery/delivery_docker.c index 2c43caf..e5f1c2f 100644 --- a/src/lib/delivery/delivery_docker.c +++ b/src/lib/delivery/delivery_docker.c @@ -19,7 +19,7 @@ int delivery_docker(struct Delivery *ctx) { msg(STASIS_MSG_WARN | STASIS_MSG_L2, "No docker tags defined by configuration. Generating default tag(s).\n"); // generate local tag memset(default_tag, 0, sizeof(default_tag)); - sprintf(default_tag, "%s:%s-py%s", ctx->meta.name, ctx->info.build_name, ctx->meta.python_compact); + snprintf(default_tag, sizeof(default_tag), "%s:%s-py%s", ctx->meta.name, ctx->info.build_name, ctx->meta.python_compact); tolower_s(default_tag); // Add tag @@ -29,7 +29,7 @@ int delivery_docker(struct Delivery *ctx) { if (has_registry) { // generate tag for target registry memset(default_tag, 0, sizeof(default_tag)); - sprintf(default_tag, "%s/%s:%s-py%s", ctx->deploy.docker.registry, ctx->meta.name, ctx->info.build_number, ctx->meta.python_compact); + snprintf(default_tag, sizeof(default_tag), "%s/%s:%s-py%s", ctx->deploy.docker.registry, ctx->meta.name, ctx->info.build_number, ctx->meta.python_compact); tolower_s(default_tag); // Add tag @@ -44,9 +44,12 @@ int delivery_docker(struct Delivery *ctx) { // Append image tags to command for (size_t i = 0; i < total_tags; i++) { char *tag_orig = strlist_item(ctx->deploy.docker.tags, i); - strcpy(tag, tag_orig); + strncpy(tag, tag_orig, sizeof(tag) - 1); docker_sanitize_tag(tag); - sprintf(args + strlen(args), " -t \"%s\" ", tag); + + const char *tag_fmt = " -t \"%s\" "; + const int tag_fmt_len = snprintf(NULL, 0, tag_fmt, tag); + snprintf(args + strlen(args), tag_fmt_len, tag_fmt, tag); } // Append build arguments to command (i.e. --build-arg "key=value" @@ -55,7 +58,10 @@ int delivery_docker(struct Delivery *ctx) { if (!build_arg) { break; } - sprintf(args + strlen(args), " --build-arg \"%s\" ", build_arg); + + const char *build_arg_fmt = " --build-arg \"%s\" "; + const int build_arg_fmt_len = snprintf(NULL, 0, build_arg_fmt, build_arg); + snprintf(args + strlen(args), sizeof(args) - build_arg_fmt_len, build_arg_fmt, build_arg); } // Build the image @@ -65,24 +71,24 @@ int delivery_docker(struct Delivery *ctx) { memset(delivery_file, 0, sizeof(delivery_file)); memset(dest, 0, sizeof(dest)); - sprintf(delivery_file, "%s/%s.yml", ctx->storage.delivery_dir, ctx->info.release_name); + snprintf(delivery_file, sizeof(delivery_file), "%s/%s.yml", ctx->storage.delivery_dir, ctx->info.release_name); if (access(delivery_file, F_OK) < 0) { fprintf(stderr, "docker build cannot proceed without delivery file: %s\n", delivery_file); return -1; } - sprintf(dest, "%s/%s.yml", ctx->storage.build_docker_dir, ctx->info.release_name); + snprintf(dest, sizeof(dest), "%s/%s.yml", ctx->storage.build_docker_dir, ctx->info.release_name); if (copy2(delivery_file, dest, CT_PERM)) { fprintf(stderr, "Failed to copy delivery file to %s: %s\n", dest, strerror(errno)); return -1; } memset(dest, 0, sizeof(dest)); - sprintf(dest, "%s/packages", ctx->storage.build_docker_dir); + snprintf(dest, sizeof(dest), "%s/packages", ctx->storage.build_docker_dir); msg(STASIS_MSG_L2, "Copying conda packages\n"); memset(rsync_cmd, 0, sizeof(rsync_cmd)); - sprintf(rsync_cmd, "rsync -avi --progress '%s' '%s'", ctx->storage.conda_artifact_dir, dest); + snprintf(rsync_cmd, sizeof(rsync_cmd), "rsync -avi --progress '%s' '%s'", ctx->storage.conda_artifact_dir, dest); if (system(rsync_cmd)) { fprintf(stderr, "Failed to copy conda artifacts to docker build directory\n"); return -1; @@ -90,7 +96,7 @@ int delivery_docker(struct Delivery *ctx) { msg(STASIS_MSG_L2, "Copying wheel packages\n"); memset(rsync_cmd, 0, sizeof(rsync_cmd)); - sprintf(rsync_cmd, "rsync -avi --progress '%s' '%s'", ctx->storage.wheel_artifact_dir, dest); + snprintf(rsync_cmd, sizeof(rsync_cmd), "rsync -avi --progress '%s' '%s'", ctx->storage.wheel_artifact_dir, dest); if (system(rsync_cmd)) { fprintf(stderr, "Failed to copy wheel artifacts to docker build directory\n"); } @@ -102,7 +108,7 @@ int delivery_docker(struct Delivery *ctx) { // Test the image // All tags point back to the same image so test the first one we see // regardless of how many are defined - strcpy(tag, strlist_item(ctx->deploy.docker.tags, 0)); + strncpy(tag, strlist_item(ctx->deploy.docker.tags, 0), sizeof(tag) - 1); docker_sanitize_tag(tag); msg(STASIS_MSG_L2, "Executing image test script for %s\n", tag); diff --git a/src/lib/delivery/delivery_export.c b/src/lib/delivery/delivery_export.c index d982ad5..c12a365 100644 --- a/src/lib/delivery/delivery_export.c +++ b/src/lib/delivery/delivery_export.c @@ -4,7 +4,7 @@ static void delivery_export_configuration(const struct Delivery *ctx) { msg(STASIS_MSG_L2, "Exporting delivery configuration\n"); if (!pushd(ctx->storage.cfgdump_dir)) { char filename[PATH_MAX] = {0}; - sprintf(filename, "%s.ini", ctx->info.release_name); + snprintf(filename, sizeof(filename), "%s.ini", ctx->info.release_name); FILE *spec = fopen(filename, "w+"); if (!spec) { msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed %s\n", filename); @@ -14,7 +14,7 @@ static void delivery_export_configuration(const struct Delivery *ctx) { fclose(spec); memset(filename, 0, sizeof(filename)); - sprintf(filename, "%s-rendered.ini", ctx->info.release_name); + snprintf(filename, sizeof(filename), "%s-rendered.ini", ctx->info.release_name); spec = fopen(filename, "w+"); if (!spec) { msg(STASIS_MSG_ERROR | STASIS_MSG_L2, "failed %s\n", filename); diff --git a/src/lib/delivery/delivery_init.c b/src/lib/delivery/delivery_init.c index 1666f0a..ff877f0 100644 --- a/src/lib/delivery/delivery_init.c +++ b/src/lib/delivery/delivery_init.c @@ -174,26 +174,26 @@ int delivery_init_platform(struct Delivery *ctx) { } if (!strcmp(ctx->system.arch, "x86_64")) { - strcpy(archsuffix, "64"); + strncpy(archsuffix, "64", sizeof(archsuffix) - 1); } else { - strcpy(archsuffix, ctx->system.arch); + strncpy(archsuffix, ctx->system.arch, sizeof(archsuffix) - 1); } SYSDEBUG("%s", "Setting platform"); - strcpy(ctx->system.platform[DELIVERY_PLATFORM], uts.sysname); + strncpy(ctx->system.platform[DELIVERY_PLATFORM], uts.sysname, DELIVERY_PLATFORM_MAXLEN - 1); if (!strcmp(ctx->system.platform[DELIVERY_PLATFORM], "Darwin")) { - sprintf(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], "osx-%s", archsuffix); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], "MacOSX"); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], "macos"); + snprintf(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], DELIVERY_PLATFORM_MAXLEN, "osx-%s", archsuffix); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], "MacOSX", DELIVERY_PLATFORM_MAXLEN - 1); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], "macos", DELIVERY_PLATFORM_MAXLEN - 1); } else if (!strcmp(ctx->system.platform[DELIVERY_PLATFORM], "Linux")) { - sprintf(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], "linux-%s", archsuffix); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], "Linux"); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], "linux"); + snprintf(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], DELIVERY_PLATFORM_MAXLEN, "linux-%s", archsuffix); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], "Linux", DELIVERY_PLATFORM_MAXLEN - 1); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], "linux", DELIVERY_PLATFORM_MAXLEN - 1); } else { // Not explicitly supported systems - strcpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], ctx->system.platform[DELIVERY_PLATFORM]); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], ctx->system.platform[DELIVERY_PLATFORM]); - strcpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], ctx->system.platform[DELIVERY_PLATFORM]); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], ctx->system.platform[DELIVERY_PLATFORM], DELIVERY_PLATFORM_MAXLEN - 1); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_CONDA_INSTALLER], ctx->system.platform[DELIVERY_PLATFORM], DELIVERY_PLATFORM_MAXLEN - 1); + strncpy(ctx->system.platform[DELIVERY_PLATFORM_RELEASE], ctx->system.platform[DELIVERY_PLATFORM], DELIVERY_PLATFORM_MAXLEN - 1); tolower_s(ctx->system.platform[DELIVERY_PLATFORM_RELEASE]); } @@ -203,7 +203,7 @@ int delivery_init_platform(struct Delivery *ctx) { cpu_count = 1; } char ncpus[100] = {0}; - sprintf(ncpus, "%ld", cpu_count); + snprintf(ncpus, sizeof(ncpus), "%ld", cpu_count); // Declare some important bits as environment variables setenv("CPU_COUNT", ncpus, 1); @@ -252,16 +252,16 @@ int delivery_init(struct Delivery *ctx, int render_mode) { delivery_init_dirs_stage1(ctx); char config_local[PATH_MAX]; - sprintf(config_local, "%s/%s", ctx->storage.tmpdir, "config"); + snprintf(config_local, sizeof(config_local), "%s/%s", ctx->storage.tmpdir, "config"); setenv("XDG_CONFIG_HOME", config_local, 1); char cache_local[PATH_MAX]; - sprintf(cache_local, "%s/%s", ctx->storage.tmpdir, "cache"); + snprintf(cache_local, sizeof(cache_local), "%s/%s", ctx->storage.tmpdir, "cache"); setenv("XDG_CACHE_HOME", cache_local, 1); // add tools to PATH char pathvar_tmp[STASIS_BUFSIZ]; - sprintf(pathvar_tmp, "%s/bin:%s", ctx->storage.tools_dir, getenv("PATH")); + snprintf(pathvar_tmp, sizeof(pathvar_tmp), "%s/bin:%s", ctx->storage.tools_dir, getenv("PATH")); setenv("PATH", pathvar_tmp, 1); // Prevent git from paginating output @@ -336,7 +336,7 @@ int bootstrap_build_info(struct Delivery *ctx) { int delivery_exists(struct Delivery *ctx) { int release_exists = DELIVERY_NOT_FOUND; char release_pattern[PATH_MAX] = {0}; - sprintf(release_pattern, "*%s*", ctx->info.release_name); + snprintf(release_pattern, sizeof(release_pattern), "*%s*", ctx->info.release_name); if (globals.enable_artifactory) { if (jfrt_auth_init(&ctx->deploy.jfrog_auth)) { diff --git a/src/lib/delivery/delivery_install.c b/src/lib/delivery/delivery_install.c index 2de80cf..1e2b82c 100644 --- a/src/lib/delivery/delivery_install.c +++ b/src/lib/delivery/delivery_install.c @@ -145,16 +145,16 @@ int delivery_purge_packages(struct Delivery *ctx, const char *env_name, int use_ case PKG_USE_CONDA: fn = conda_exec; list = ctx->conda.conda_packages_purge; - strcpy(package_manager, "conda"); + strncpy(package_manager, "conda", sizeof(package_manager) - 1); // conda is already configured for "always_yes" - strcpy(subcommand, "remove"); + strncpy(subcommand, "remove", sizeof(subcommand) - 1); break; case PKG_USE_PIP: fn = pip_exec; list = ctx->conda.pip_packages_purge; - strcpy(package_manager, "pip"); + strncpy(package_manager, "pip", sizeof(package_manager) - 1); // avoid user prompt to remove packages - strcpy(subcommand, "uninstall -y"); + strncpy(subcommand, "uninstall -y", sizeof(subcommand) - 1); break; default: SYSERROR("Unknown package manager: %d", use_pkg_manager); @@ -203,7 +203,7 @@ int delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, cha } memset(command_base, 0, sizeof(command_base)); - strcat(command_base, "install"); + strncat(command_base, "install", sizeof(command_base) - strlen(command_base) - 1); typedef int (*Runner)(const char *); Runner runner = NULL; @@ -214,15 +214,17 @@ int delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, cha } if (INSTALL_PKG_CONDA_DEFERRED & type) { - strcat(command_base, " --use-local"); + strncat(command_base, " --use-local", sizeof(command_base) - strlen(command_base) - 1); } else if (INSTALL_PKG_PIP_DEFERRED & type) { // Don't change the baseline package set unless we're working with a // new build. Release candidates will need to keep packages as stable // as possible between releases. if (!ctx->meta.based_on) { - strcat(command_base, " --upgrade"); + strncat(command_base, " --upgrade", sizeof(command_base) - strlen(command_base) - 1); } - sprintf(command_base + strlen(command_base), " --extra-index-url 'file://%s'", ctx->storage.wheel_artifact_dir); + const char *command_base_fmt = " --extra-index-url 'file://%s'"; + const int len = snprintf(NULL, 0, command_base_fmt, ctx->storage.wheel_artifact_dir); + snprintf(command_base + strlen(command_base), sizeof(command_base) - len, command_base_fmt, ctx->storage.wheel_artifact_dir); } size_t args_alloc_len = STASIS_BUFSIZ; @@ -287,9 +289,9 @@ int delivery_install_packages(struct Delivery *ctx, char *conda_install_dir, cha char req[255] = {0}; if (!strcmp(name, info->name)) { - strcpy(req, info->name); + strncpy(req, info->name, sizeof(req) - 1); } else { - strcpy(req, name); + strncpy(req, name, sizeof(req) - 1); char *spec = find_version_spec(req); if (spec) { *spec = 0; diff --git a/src/lib/delivery/delivery_populate.c b/src/lib/delivery/delivery_populate.c index d41e3a4..3ce29e9 100644 --- a/src/lib/delivery/delivery_populate.c +++ b/src/lib/delivery/delivery_populate.c @@ -256,16 +256,16 @@ int populate_delivery_ini(struct Delivery *ctx, int render_mode) { return -1; } - if (delivery_format_str(ctx, &ctx->info.release_name, ctx->rules.release_fmt)) { + if (delivery_format_str(ctx, &ctx->info.release_name, STASIS_NAME_MAX, ctx->rules.release_fmt)) { fprintf(stderr, "Failed to generate release name. Format used: %s\n", ctx->rules.release_fmt); return -1; } if (!ctx->info.build_name) { - delivery_format_str(ctx, &ctx->info.build_name, ctx->rules.build_name_fmt); + delivery_format_str(ctx, &ctx->info.build_name, STASIS_NAME_MAX, ctx->rules.build_name_fmt); } if (!ctx->info.build_number) { - delivery_format_str(ctx, &ctx->info.build_number, ctx->rules.build_number_fmt); + delivery_format_str(ctx, &ctx->info.build_number, STASIS_NAME_MAX, ctx->rules.build_number_fmt); } // Best I can do to make output directories unique. Annoying. @@ -376,10 +376,10 @@ int populate_mission_ini(struct Delivery **ctx, int render_mode) { // Now populate the rules char missionfile[PATH_MAX] = {0}; if (getenv("STASIS_SYSCONFDIR")) { - sprintf(missionfile, "%s/%s/%s/%s.ini", + snprintf(missionfile, sizeof(missionfile), "%s/%s/%s/%s.ini", getenv("STASIS_SYSCONFDIR"), "mission", (*ctx)->meta.mission, (*ctx)->meta.mission); } else { - sprintf(missionfile, "%s/%s/%s/%s.ini", + snprintf(missionfile, sizeof(missionfile), "%s/%s/%s/%s.ini", globals.sysconfdir, "mission", (*ctx)->meta.mission, (*ctx)->meta.mission); } diff --git a/src/lib/delivery/delivery_postprocess.c b/src/lib/delivery/delivery_postprocess.c index a7bb2b4..8cb4e65 100644 --- a/src/lib/delivery/delivery_postprocess.c +++ b/src/lib/delivery/delivery_postprocess.c @@ -11,7 +11,7 @@ char *delivery_get_release_header(struct Delivery *ctx) { char output[STASIS_BUFSIZ]; char stamp[100]; strftime(stamp, sizeof(stamp) - 1, "%c", ctx->info.time_info); - sprintf(output, release_header, + snprintf(output, sizeof(output), release_header, ctx->info.release_name, ctx->rules.release_fmt, stamp, @@ -22,7 +22,7 @@ char *delivery_get_release_header(struct Delivery *ctx) { int delivery_dump_metadata(struct Delivery *ctx) { char filename[PATH_MAX]; - sprintf(filename, "%s/meta-%s.stasis", ctx->storage.meta_dir, ctx->info.release_name); + snprintf(filename, sizeof(filename), "%s/meta-%s.stasis", ctx->storage.meta_dir, ctx->info.release_name); FILE *fp = fopen(filename, "w+"); if (!fp) { return -1; @@ -143,7 +143,7 @@ void delivery_rewrite_spec(struct Delivery *ctx, char *filename, unsigned stage) file_replace_text(filename, "@CONDA_CHANNEL@", ctx->storage.conda_staging_url, 0); } else if (globals.jfrog.repo) { SYSDEBUG("%s", "Will replace conda channel with artifactory repo packages/conda url"); - sprintf(output, "%s/%s/%s/%s/packages/conda", globals.jfrog.url, globals.jfrog.repo, ctx->meta.mission, ctx->info.build_name); + snprintf(output, sizeof(output), "%s/%s/%s/%s/packages/conda", globals.jfrog.url, globals.jfrog.repo, ctx->meta.mission, ctx->info.build_name); file_replace_text(filename, "@CONDA_CHANNEL@", output, 0); } else { SYSDEBUG("%s", "Will replace conda channel with local conda artifact directory"); @@ -153,16 +153,16 @@ void delivery_rewrite_spec(struct Delivery *ctx, char *filename, unsigned stage) if (ctx->storage.wheel_staging_url) { SYSDEBUG("%s", "Will replace pip arguments with wheel staging url"); - sprintf(output, "--extra-index-url %s/%s/%s/packages/wheels", ctx->storage.wheel_staging_url, ctx->meta.mission, ctx->info.build_name); + snprintf(output, sizeof(output), "--extra-index-url %s/%s/%s/packages/wheels", ctx->storage.wheel_staging_url, ctx->meta.mission, ctx->info.build_name); file_replace_text(filename, "@PIP_ARGUMENTS@", ctx->storage.wheel_staging_url, 0); } else if (globals.enable_artifactory && globals.jfrog.url && globals.jfrog.repo) { SYSDEBUG("%s", "Will replace pip arguments with artifactory repo packages/wheel url"); - sprintf(output, "--extra-index-url %s/%s/%s/%s/packages/wheels", globals.jfrog.url, globals.jfrog.repo, ctx->meta.mission, ctx->info.build_name); + snprintf(output, sizeof(output), "--extra-index-url %s/%s/%s/%s/packages/wheels", globals.jfrog.url, globals.jfrog.repo, ctx->meta.mission, ctx->info.build_name); file_replace_text(filename, "@PIP_ARGUMENTS@", output, 0); } else { SYSDEBUG("%s", "Will replace pip arguments with local wheel artifact directory"); msg(STASIS_MSG_WARN, "wheel_staging_dir is not configured. Using fallback: '%s'\n", ctx->storage.wheel_artifact_dir); - sprintf(output, "--extra-index-url file://%s", ctx->storage.wheel_artifact_dir); + snprintf(output, sizeof(output), "--extra-index-url file://%s", ctx->storage.wheel_artifact_dir); file_replace_text(filename, "@PIP_ARGUMENTS@", output, 0); } } @@ -177,7 +177,7 @@ int delivery_copy_conda_artifacts(struct Delivery *ctx) { memset(conda_build_dir, 0, sizeof(conda_build_dir)); memset(subdir, 0, sizeof(subdir)); - sprintf(conda_build_dir, "%s/%s", ctx->storage.conda_install_prefix, "conda-bld"); + snprintf(conda_build_dir, sizeof(conda_build_dir), "%s/%s", ctx->storage.conda_install_prefix, "conda-bld"); // One must run conda build at least once to create the "conda-bld" directory. // When this directory is missing there can be no build artifacts. if (access(conda_build_dir, F_OK) < 0) { @@ -186,7 +186,7 @@ int delivery_copy_conda_artifacts(struct Delivery *ctx) { return 0; } - snprintf(cmd, sizeof(cmd) - 1, "rsync -avi --progress %s/%s %s", + snprintf(cmd, sizeof(cmd), "rsync -avi --progress %s/%s %s", conda_build_dir, ctx->system.platform[DELIVERY_PLATFORM_CONDA_SUBDIR], ctx->storage.conda_artifact_dir); @@ -200,7 +200,7 @@ int delivery_index_conda_artifacts(struct Delivery *ctx) { int delivery_copy_wheel_artifacts(struct Delivery *ctx) { char cmd[PATH_MAX] = {0}; - snprintf(cmd, sizeof(cmd) - 1, "rsync -avi --progress %s/*/dist/*.whl %s", + snprintf(cmd, sizeof(cmd), "rsync -avi --progress %s/*/dist/*.whl %s", ctx->storage.build_sources_dir, ctx->storage.wheel_artifact_dir); return system(cmd); @@ -217,7 +217,7 @@ int delivery_index_wheel_artifacts(struct Delivery *ctx) { // Generate a "dumb" local pypi index that is compatible with: // pip install --extra-index-url char top_index[PATH_MAX] = {0}; - sprintf(top_index, "%s/index.html", ctx->storage.wheel_artifact_dir); + snprintf(top_index, sizeof(top_index),"%s/index.html", ctx->storage.wheel_artifact_dir); SYSDEBUG("Opening top-level index for writing: %s", top_index); FILE *top_fp = fopen(top_index, "w+"); if (!top_fp) { @@ -232,7 +232,7 @@ int delivery_index_wheel_artifacts(struct Delivery *ctx) { } char bottom_index[PATH_MAX * 2] = {0}; - sprintf(bottom_index, "%s/%s/index.html", ctx->storage.wheel_artifact_dir, rec->d_name); + snprintf(bottom_index, sizeof(bottom_index), "%s/%s/index.html", ctx->storage.wheel_artifact_dir, rec->d_name); SYSDEBUG("Opening bottom-level for writing: %s", bottom_index); FILE *bottom_fp = fopen(bottom_index, "w+"); if (!bottom_fp) { @@ -248,7 +248,7 @@ int delivery_index_wheel_artifacts(struct Delivery *ctx) { fprintf(top_fp, "<a href=\"%s/\">%s</a><br/>\n", rec->d_name, rec->d_name); char dpath[PATH_MAX * 2] = {0}; - sprintf(dpath, "%s/%s", ctx->storage.wheel_artifact_dir, rec->d_name); + snprintf(dpath, sizeof(dpath), "%s/%s", ctx->storage.wheel_artifact_dir, rec->d_name); struct StrList *packages = listdir(dpath); if (!packages) { closedir(dp); diff --git a/src/lib/delivery/delivery_test.c b/src/lib/delivery/delivery_test.c index 96dbb10..a088cd7 100644 --- a/src/lib/delivery/delivery_test.c +++ b/src/lib/delivery/delivery_test.c @@ -156,7 +156,7 @@ void delivery_tests_run(struct Delivery *ctx) { } char destdir[PATH_MAX]; - sprintf(destdir, "%s/%s", ctx->storage.build_sources_dir, path_basename(test->repository)); + snprintf(destdir, sizeof(destdir), "%s/%s", ctx->storage.build_sources_dir, path_basename(test->repository)); if (!access(destdir, F_OK)) { msg(STASIS_MSG_L3, "Purging repository %s\n", destdir); @@ -200,11 +200,11 @@ void delivery_tests_run(struct Delivery *ctx) { msg(STASIS_MSG_L3, "Queuing task for %s\n", test->name); memset(&proc, 0, sizeof(proc)); - strcpy(cmd, test->script); + strncpy(cmd, test->script, strlen(test->script) + STASIS_BUFSIZ - 1); char *cmd_rendered = tpl_render(cmd); if (cmd_rendered) { if (strcmp(cmd_rendered, cmd) != 0) { - strcpy(cmd, cmd_rendered); + strncpy(cmd, cmd_rendered, strlen(test->script) + STASIS_BUFSIZ - 1); cmd[strlen(cmd_rendered) ? strlen(cmd_rendered) - 1 : 0] = 0; } guard_free(cmd_rendered); @@ -229,7 +229,7 @@ void delivery_tests_run(struct Delivery *ctx) { if (!globals.enable_parallel || !test->parallel) { selected = SERIAL; memset(pool_name, 0, sizeof(pool_name)); - strcpy(pool_name, "serial"); + strncpy(pool_name, "serial", sizeof(pool_name) - 1); } if (asprintf(&runner_cmd, runner_cmd_fmt, cmd) < 0) { @@ -267,7 +267,7 @@ void delivery_tests_run(struct Delivery *ctx) { const struct Test *test = ctx->tests->test[i]; if (test->script_setup) { char destdir[PATH_MAX]; - sprintf(destdir, "%s/%s", ctx->storage.build_sources_dir, path_basename(test->repository)); + snprintf(destdir, sizeof(destdir), "%s/%s", ctx->storage.build_sources_dir, path_basename(test->repository)); if (access(destdir, F_OK)) { SYSERROR("%s: %s", destdir, strerror(errno)); exit(1); @@ -382,7 +382,7 @@ int delivery_fixup_test_results(struct Delivery *ctx) { continue; } - sprintf(path, "%s/%s", ctx->storage.results_dir, rec->d_name); + snprintf(path, sizeof(path), "%s/%s", ctx->storage.results_dir, rec->d_name); msg(STASIS_MSG_L3, "%s\n", rec->d_name); if (xml_pretty_print_in_place(path, STASIS_XML_PRETTY_PRINT_PROG, STASIS_XML_PRETTY_PRINT_ARGS)) { msg(STASIS_MSG_L3 | STASIS_MSG_WARN, "Failed to rewrite file '%s'\n", rec->d_name); diff --git a/src/lib/delivery/include/delivery.h b/src/lib/delivery/include/delivery.h index 68f4b14..e524f4d 100644 --- a/src/lib/delivery/include/delivery.h +++ b/src/lib/delivery/include/delivery.h @@ -321,9 +321,10 @@ int delivery_get_conda_installer(struct Delivery *ctx, char *installer_url); * Generate URL based on Delivery context * @param ctx pointer to Delivery context * @param result pointer to char + * @param maxlen * @return in result */ -void delivery_get_conda_installer_url(struct Delivery *ctx, char *result); +void delivery_get_conda_installer_url(struct Delivery *ctx, char *result, size_t maxlen); /** * Install packages based on Delivery context @@ -394,10 +395,11 @@ void delivery_install_conda(char *install_script, char *conda_install_dir); * * @param ctx pointer to Delivery context * @param dest NULL pointer to string, or initialized string + * @param maxlen * @param fmt release format string * @return 0 on success, -1 on error */ -int delivery_format_str(struct Delivery *ctx, char **dest, const char *fmt); +int delivery_format_str(struct Delivery *ctx, char **dest, size_t maxlen, const char *fmt); // helper function int delivery_gather_tool_versions(struct Delivery *ctx); diff --git a/tests/test_artifactory.c b/tests/test_artifactory.c index 202a67c..fadc9f1 100644 --- a/tests/test_artifactory.c +++ b/tests/test_artifactory.c @@ -15,14 +15,14 @@ const char *gbuild_num = "1"; static int jfrog_cli_rt_build_delete(struct JFRT_Auth *auth, char *build_name, char *build_num) { char cmd[STASIS_BUFSIZ]; memset(cmd, 0, sizeof(cmd)); - snprintf(cmd, sizeof(cmd) - 1, "--build \"%s/%s\"", build_name, build_num); + snprintf(cmd, sizeof(cmd), "--build \"%s/%s\"", build_name, build_num); return jfrog_cli(auth, "rt", "delete", cmd); } static int jfrog_cli_rt_delete(struct JFRT_Auth *auth, char *pattern) { char cmd[STASIS_BUFSIZ]; memset(cmd, 0, sizeof(cmd)); - snprintf(cmd, sizeof(cmd) - 1, "\"%s\"", pattern); + snprintf(cmd, sizeof(cmd), "\"%s\"", pattern); return jfrog_cli(auth, "rt", "delete", cmd); } @@ -60,7 +60,7 @@ void test_jfrog_cli_rt_download() { char *filename = "empty_file_upload.txt"; char path[PATH_MAX] = {0}; - sprintf(path, "%s/%s", getenv("STASIS_JF_REPO"), filename); + snprintf(path, sizeof(path), "%s/%s", getenv("STASIS_JF_REPO"), filename); STASIS_ASSERT(jfrog_cli_rt_download(&gauth, &dl, filename, ".") == 0, "jf download failed"); STASIS_ASSERT(jfrog_cli_rt_delete(&gauth, path) == 0, "jf delete test artifact failed"); } @@ -93,15 +93,15 @@ int main(int argc, char *argv[]) { } char path[PATH_MAX] = {0}; - sprintf(path, "%s/bin:%s", ctx.storage.tools_dir, getenv("PATH")); + snprintf(path, sizeof(path), "%s/bin:%s", ctx.storage.tools_dir, getenv("PATH")); setenv("PATH", path, 1); // The default config contains the URL information to download jfrog-cli char cfg_path[PATH_MAX] = {0}; if (strstr(sysconfdir, "..")) { - sprintf(cfg_path, "%s/%s/stasis.ini", basedir, sysconfdir); + snprintf(cfg_path, sizeof(cfg_path), "%s/%s/stasis.ini", basedir, sysconfdir); } else { - sprintf(cfg_path, "%s/stasis.ini", sysconfdir); + snprintf(cfg_path, sizeof(cfg_path), "%s/stasis.ini", sysconfdir); } ctx._stasis_ini_fp.cfg = ini_open(cfg_path); if (!ctx._stasis_ini_fp.cfg) { diff --git a/tests/test_conda.c b/tests/test_conda.c index 1d05e7e..9f0e718 100644 --- a/tests/test_conda.c +++ b/tests/test_conda.c @@ -40,7 +40,7 @@ struct Delivery ctx; void test_conda_installation() { char *install_url = calloc(255, sizeof(install_url)); - delivery_get_conda_installer_url(&ctx, install_url); + delivery_get_conda_installer_url(&ctx, install_url, PATH_MAX); delivery_get_conda_installer(&ctx, install_url); delivery_install_conda(ctx.conda.installer_path, ctx.storage.conda_install_prefix); @@ -92,7 +92,7 @@ void test_conda_exec() { void test_python_exec() { const char *python_system_path = find_program("python3"); char python_path[PATH_MAX]; - sprintf(python_path, "%s/bin/python3", ctx.storage.conda_install_prefix); + snprintf(python_path, sizeof(python_path), "%s/bin/python3", ctx.storage.conda_install_prefix); STASIS_ASSERT(strcmp(python_path, python_system_path ? python_system_path : "/not/found") == 0, "conda is not configured correctly."); STASIS_ASSERT(python_exec("-V") == 0, "python is broken"); diff --git a/tests/test_environment.c b/tests/test_environment.c index 4f36883..9a503c0 100644 --- a/tests/test_environment.c +++ b/tests/test_environment.c @@ -58,7 +58,7 @@ void test_runtime() { char output_truth[BUFSIZ] = {0}; char *your_path = runtime_get(env, "PATH"); - sprintf(output_truth, "Your PATH is '%s'.", your_path); + snprintf(output_truth, sizeof(output_truth), "Your PATH is '%s'.", your_path); guard_free(your_path); char *output_expanded = runtime_expand_var(env, "Your PATH is '${PATH}'."); diff --git a/tests/test_junitxml.c b/tests/test_junitxml.c index 362cb32..0bbbefb 100644 --- a/tests/test_junitxml.c +++ b/tests/test_junitxml.c @@ -4,7 +4,7 @@ void test_junitxml_testsuite_read() { struct JUNIT_Testsuite *testsuite; char datafile[PATH_MAX] = {0}; - snprintf(datafile, sizeof(datafile) - 1, "%s/result.xml", TEST_DATA_DIR); + snprintf(datafile, sizeof(datafile), "%s/result.xml", TEST_DATA_DIR); STASIS_ASSERT_FATAL((testsuite = junitxml_testsuite_read(datafile)) != NULL, "failed to load testsuite data"); STASIS_ASSERT(testsuite->name != NULL, "Test suite must be named"); @@ -48,7 +48,7 @@ void test_junitxml_testsuite_read() { void test_junitxml_testsuite_read_error() { struct JUNIT_Testsuite *testsuite; char datafile[PATH_MAX] = {0}; - snprintf(datafile, sizeof(datafile) - 1, "%s/result_error.xml", TEST_DATA_DIR); + snprintf(datafile, sizeof(datafile), "%s/result_error.xml", TEST_DATA_DIR); STASIS_ASSERT_FATAL((testsuite = junitxml_testsuite_read(datafile)) != NULL, "failed to load testsuite data"); STASIS_ASSERT(testsuite->name != NULL, "test suite must be named"); diff --git a/tests/test_multiprocessing.c b/tests/test_multiprocessing.c index 3a462f1..767a9e0 100644 --- a/tests/test_multiprocessing.c +++ b/tests/test_multiprocessing.c @@ -65,7 +65,7 @@ void test_mp_task() { for (size_t i = 0; i < sizeof(commands) / sizeof(*commands); i++) { struct MultiProcessingTask *task; char task_name[100] = {0}; - sprintf(task_name, "mytask%zu", i); + snprintf(task_name, sizeof(task_name), "mytask%zu", i); STASIS_ASSERT_FATAL((task = mp_pool_task(pool, task_name, NULL, commands[i])) != NULL, "Task should not be NULL"); STASIS_ASSERT(task->pid == MP_POOL_PID_UNUSED, "PID should be non-zero at this point"); STASIS_ASSERT(task->parent_pid == MP_POOL_PID_UNUSED, "Parent PID should be non-zero"); @@ -137,7 +137,7 @@ void test_mp_fail_fast() { for (size_t i = 0; i < sizeof(commands_ff) / sizeof(*commands_ff); i++) { char *command = commands_ff[i]; char taskname[100] = {0}; - snprintf(taskname, sizeof(taskname) - 1, "task_%03zu", i); + snprintf(taskname, sizeof(taskname), "task_%03zu", i); STASIS_ASSERT(mp_pool_task(p, taskname, NULL, (char *) command) != NULL, "Failed to queue task"); } diff --git a/tests/test_recipe.c b/tests/test_recipe.c index fc7cc78..3ea21ce 100644 --- a/tests/test_recipe.c +++ b/tests/test_recipe.c @@ -4,7 +4,7 @@ static void make_local_recipe(const char *localdir) { char path[PATH_MAX] = {0}; - sprintf(path, "./%s", localdir); + snprintf(path, sizeof(path), "./%s", localdir); mkdir(path, 0755); if (!pushd(path)) { touch("meta.yaml"); diff --git a/tests/test_relocation.c b/tests/test_relocation.c index a6c33f2..69142dc 100644 --- a/tests/test_relocation.c +++ b/tests/test_relocation.c @@ -14,9 +14,12 @@ void test_replace_text() { const char *target = targets[i]; const char *expected = targets[i + 1]; char input[BUFSIZ] = {0}; - strcpy(input, test_string); + strncpy(input, test_string, sizeof(input) - 1); + printf("input: %s\n", input); + printf("target: %s\n", target); STASIS_ASSERT(replace_text(input, target, "^^^", 0) == 0, "string replacement failed"); + printf("result: %s\n\n", input); STASIS_ASSERT(strcmp(input, expected) == 0, "unexpected replacement"); } diff --git a/tests/test_str.c b/tests/test_str.c index a98a34d..aac5d71 100644 --- a/tests/test_str.c +++ b/tests/test_str.c @@ -37,7 +37,7 @@ void test_tolower_s() { for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) { char input[100] = {0}; - strcpy(input, tc[i].data); + strncpy(input, tc[i].data, sizeof(input) - 1); tolower_s(input); STASIS_ASSERT(strcmp(input, tc[i].expected) == 0, "unexpected result"); } @@ -317,9 +317,8 @@ void test_lstrip() { STASIS_ASSERT(lstrip(NULL) == NULL, "incorrect return type"); for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) { char *buf = calloc(255, sizeof(*buf)); - char *result; - strcpy(buf, tc[i].data); - result = lstrip(buf); + strncpy(buf, tc[i].data, 254); + char *result = lstrip(buf); STASIS_ASSERT(strcmp(result ? result : "", tc[i].expected) == 0, "incorrect strip-from-left"); guard_free(buf); } @@ -342,9 +341,8 @@ void test_strip() { STASIS_ASSERT(strip(NULL) == NULL, "incorrect return type"); for (size_t i = 0; i < sizeof(tc) / sizeof(*tc); i++) { char *buf = calloc(255, sizeof(*buf)); - char *result; - strcpy(buf, tc[i].data); - result = strip(buf); + strncpy(buf, tc[i].data, 254); + char *result = strip(buf); STASIS_ASSERT(strcmp(result ? result : "", tc[i].expected) == 0, "incorrect strip-from-right"); guard_free(buf); } diff --git a/tests/test_system.c b/tests/test_system.c index 2271e13..9e4a862 100644 --- a/tests/test_system.c +++ b/tests/test_system.c @@ -54,7 +54,7 @@ void test_shell_safe_verify_restrictions() { char cmd[PATH_MAX] = {0}; memset(&proc, 0, sizeof(proc)); - sprintf(cmd, "true%c false", invalid_chars[i]); + snprintf(cmd, sizeof(cmd), "true%c false", invalid_chars[i]); shell_safe(&proc, cmd); STASIS_ASSERT(proc.returncode == -1, "expected a negative result due to intentional error"); } diff --git a/tests/test_template.c b/tests/test_template.c index 596c2b7..e8f0c1d 100644 --- a/tests/test_template.c +++ b/tests/test_template.c @@ -10,8 +10,9 @@ static int adder(struct tplfunc_frame *frame, void *result) { int a = (int) strtol(frame->argv[0].t_char_ptr, NULL, 10); int b = (int) strtol(frame->argv[1].t_char_ptr, NULL, 10); char **ptr = (char **) result; - *ptr = calloc(100, sizeof(*ptr)); - sprintf(*ptr, "%d", a + b); + const size_t sz = 100; + *ptr = calloc(sz, sizeof(*ptr)); + snprintf(*ptr, sz, "%d", a + b); return 0; } @@ -19,8 +20,9 @@ static int subtractor(struct tplfunc_frame *frame, void *result) { int a = (int) strtol(frame->argv[0].t_char_ptr, NULL, 10); int b = (int) strtol(frame->argv[1].t_char_ptr, NULL, 10); char **ptr = (char **) result; - *ptr = calloc(100, sizeof(*ptr)); - sprintf(*ptr, "%d", a - b); + const size_t sz = 100; + *ptr = calloc(sz, sizeof(*ptr)); + snprintf(*ptr, sz, "%d", a - b); return 0; } @@ -28,8 +30,9 @@ static int multiplier(struct tplfunc_frame *frame, void *result) { int a = (int) strtol(frame->argv[0].t_char_ptr, NULL, 10); int b = (int) strtol(frame->argv[1].t_char_ptr, NULL, 10); char **ptr = (char **) result; - *ptr = calloc(100, sizeof(*ptr)); - sprintf(*ptr, "%d", a * b); + const size_t sz = 100; + *ptr = calloc(sz, sizeof(*ptr)); + snprintf(*ptr, sz, "%d", a * b); return 0; } @@ -37,8 +40,9 @@ static int divider(struct tplfunc_frame *frame, void *result) { int a = (int) strtol(frame->argv[0].t_char_ptr, NULL, 10); int b = (int) strtol(frame->argv[1].t_char_ptr, NULL, 10); char **ptr = (char **) result; - *ptr = calloc(100, sizeof(*ptr)); - sprintf(*ptr, "%d", a / b); + size_t sz = 100; + *ptr = calloc(sz, sizeof(*ptr)); + snprintf(*ptr, sz, "%d", a / b); return 0; } @@ -57,6 +61,19 @@ void test_tpl_workflow() { STASIS_ASSERT(strcmp(result, "Hello environment!") == 0, "environment variable content mismatch"); guard_free(result); unsetenv("HELLO"); + + const char *message_file = "message.txt"; + char message_fmt[] = "They wanted a {{ hello_message }} " + "So we gave them a {{ hello_message }}"; + const char *message_expected = "They wanted a Hello world! " + "So we gave them a Hello world!"; + const int state = tpl_render_to_file(message_fmt, message_file); + STASIS_ASSERT_FATAL(state == 0, "failed to write rendered string to file"); + char *message_contents = stasis_testing_read_ascii(message_file); + STASIS_ASSERT(strcmp(message_contents, message_expected) == 0, "message in file does not match original message"); + guard_free(message_contents); + remove(message_file); + guard_free(data); } @@ -68,6 +85,8 @@ void test_tpl_register() { STASIS_ASSERT(tpl_pool_used == (used_before_register + 1), "tpl_register did not increment allocation counter"); STASIS_ASSERT(tpl_pool[used_before_register] != NULL, "register did not allocate a tpl_item record in the pool"); + const char *message = tpl_getval("hello_message"); + STASIS_ASSERT(strcmp(message, "Hello world!") == 0, "stored message corrupt"); free(data); } @@ -92,25 +111,25 @@ void test_tpl_register_func() { char *result = NULL; result = tpl_render("{{ func:add(0,3) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "add: Answer was not 3"); guard_free(result); result = tpl_render("{{ func:add(1,2) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "add: Answer was not 3"); guard_free(result); result = tpl_render("{{ func:sub(6,3) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "sub: was not 3"); guard_free(result); result = tpl_render("{{ func:sub(4,1) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "sub: Answer was not 3"); guard_free(result); result = tpl_render("{{ func:mul(1, 3) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "mul: Answer was not 3"); guard_free(result); result = tpl_render("{{ func:div(6,2) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "div: Answer was not 3"); guard_free(result); result = tpl_render("{{ func:div(3,1) }}"); - STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "Answer was not 3"); + STASIS_ASSERT(result != NULL && strcmp(result, "3") == 0, "div: Answer was not 3"); guard_free(result); } diff --git a/tests/test_utils.c b/tests/test_utils.c index cfe79e0..7361139 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -65,7 +65,7 @@ void test_fix_tox_conf() { if (fp) { fprintf(fp, "%s", data); fclose(fp); - STASIS_ASSERT(fix_tox_conf(filename, &result) == 0, "fix_tox_conf failed"); + STASIS_ASSERT(fix_tox_conf(filename, &result, PATH_MAX) == 0, "fix_tox_conf failed"); } else { STASIS_ASSERT(false, "writing mock tox.ini failed"); } @@ -129,7 +129,7 @@ void test_isempty_dir() { STASIS_ASSERT(isempty_dir(dname) > 0, "empty directory went undetected"); char path[PATH_MAX]; - sprintf(path, "%s/file.txt", dname); + snprintf(path, sizeof(path), "%s/file.txt", dname); touch(path); STASIS_ASSERT(isempty_dir(dname) == 0, "populated directory went undetected"); @@ -147,7 +147,9 @@ void test_xmkstemp() { char buf[100] = {0}; tempfp = fopen(tempfile, "r"); - fgets(buf, sizeof(buf) - 1, tempfp); + const char *line = fgets(buf, sizeof(buf) - 1, tempfp); + STASIS_ASSERT_FATAL(line != NULL, "file should contain data written earlier"); + STASIS_ASSERT(strcmp(line, buf) == 0, "file should contain the correct data"); fclose(tempfp); STASIS_ASSERT(strcmp(buf, data) == 0, "data written to temp file is incorrect"); @@ -193,7 +195,7 @@ void test_git_clone_and_describe() { // initialize a bare repo so we can clone it char init_cmd[PATH_MAX]; - sprintf(init_cmd, "git init --bare %s", repo_git); + snprintf(init_cmd, sizeof(init_cmd), "git init --bare %s", repo_git); system(init_cmd); // clone the bare repo @@ -308,7 +310,7 @@ void test_path_dirname() { const char *input = data[i]; const char *expected = data[i + 1]; char tmp[PATH_MAX] = {0}; - strcpy(tmp, input); + strncpy(tmp, input, sizeof(tmp) - 1); char *result = path_dirname(tmp); STASIS_ASSERT(strcmp(expected, result) == 0, NULL); @@ -329,8 +331,7 @@ void test_path_basename() { } void test_expandpath() { - char *home; - + char *home = NULL; const char *homes[] = { "HOME", "USERPROFILE", @@ -341,10 +342,11 @@ void test_expandpath() { break; } } + STASIS_ASSERT_FATAL(home != NULL, "cannot expand without knowing the user's home directory path"); char path[PATH_MAX] = {0}; - strcat(path, "~"); - strcat(path, DIR_SEP); + strncat(path, "~", sizeof(path) - strlen(path) - 1); + strncat(path, DIR_SEP, sizeof(path) - strlen(path) - 1); char *expanded = expandpath(path); STASIS_ASSERT(startswith(expanded, home) > 0, expanded); @@ -366,8 +368,8 @@ void test_rmtree() { for (size_t i = 0; i < sizeof(tree) / sizeof(*tree); i++) { char path[PATH_MAX]; char mockfile[PATH_MAX + 10]; - sprintf(path, "%s/%s", root, tree[i]); - sprintf(mockfile, "%s/%zu.txt", path, i); + snprintf(path, sizeof(path), "%s/%s", root, tree[i]); + snprintf(mockfile, sizeof(mockfile), "%s/%zu.txt", path, i); if (mkdir(path, 0755)) { perror(path); STASIS_ASSERT(false, NULL); diff --git a/tests/test_wheel.c b/tests/test_wheel.c index 1eabb1b..531e4b7 100644 --- a/tests/test_wheel.c +++ b/tests/test_wheel.c @@ -171,7 +171,7 @@ int main(int argc, char *argv[]) { } char *install_url = calloc(255, sizeof(install_url)); - delivery_get_conda_installer_url(&ctx, install_url); + delivery_get_conda_installer_url(&ctx, install_url, PATH_MAX); delivery_get_conda_installer(&ctx, install_url); delivery_install_conda(ctx.conda.installer_path, ctx.storage.conda_install_prefix); guard_free(install_url); |
