From: Paul Eggert Date: Thu, 8 Aug 2024 00:03:22 +0000 (-0700) Subject: Avoid strtoul X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4642cd04edbd57414e004920fa4976d9f3be6206;p=thirdparty%2Ftar.git Avoid strtoul This is part of the general trend to prefer signed integer types, to allow better runtime checking with -fsanitize=undefined etc. * gnulib.modules: Remove strtoul. Add xstrtoimax. * src/checkpoint.c (checkpoint, format_checkpoint_string): * src/system.c (sys_exec_checkpoint_script): * src/tar.c (checkpoint_option): Use intmax_t, not unsigned, for checkpoint numbers. All uses changed. * src/checkpoint.c (checkpoint_compile_action): Don’t assume time_t == unsigned long. Treat overflows as TYPE_MAXIMUM (time_t), essentially infinity. * src/tar.c (tar_sparse_major, tar_sparse_minor): * src/tar.h (struct tar_stat_info): Use intmax_t, not unsigned, for sparse major and minor. All uses changed. * src/tar.c (parse_opt): Don’t mishandle multiple specifications of sparse major and minor. * src/transform.c (struct transform): Use idx_t, not unsigned, for match_number. All uses changed. (parse_transform_expr): Don’t mishandle large match numbers by wrapping them around. --- diff --git a/gnulib.modules b/gnulib.modules index cbcde2fb..dc826b43 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -108,7 +108,6 @@ strerror strnlen strtoimax strtol -strtoul strtoumax symlinkat sys_stat @@ -123,6 +122,7 @@ xalignalloc xalloc xalloc-die xgetcwd +xstrtoimax xstrtoumax xvasprintf year2038-recommended diff --git a/src/checkpoint.c b/src/checkpoint.c index 00079ec2..28e4c22a 100644 --- a/src/checkpoint.c +++ b/src/checkpoint.c @@ -50,7 +50,7 @@ struct checkpoint_action }; /* Checkpointing counter */ -static unsigned checkpoint; +static intmax_t checkpoint; /* List of checkpoint actions */ static struct checkpoint_action *checkpoint_action, *checkpoint_action_tail; @@ -128,9 +128,10 @@ checkpoint_compile_action (const char *str) else if (strncmp (str, "sleep=", 6) == 0) { char *p; - time_t n = strtoul (str+6, &p, 10); - if (*p) + intmax_t sleepsec = strtoimax (str + 6, &p, 10); + if (*p || sleepsec < 0) FATAL_ERROR ((0, 0, _("%s: not a valid timeout"), str)); + time_t n = ckd_add (&n, sleepsec, 0) ? TYPE_MAXIMUM (time_t) : n; act = alloc_action (cop_sleep); act->v.time = n; } @@ -231,7 +232,7 @@ static const char *def_format = static int format_checkpoint_string (FILE *fp, size_t len, const char *input, bool do_write, - unsigned cpn) + intmax_t cpn) { const char *opstr = do_write ? gettext ("write") : gettext ("read"); const char *ip; @@ -279,7 +280,7 @@ format_checkpoint_string (FILE *fp, size_t len, break; case 'u': - len += fprintf (fp, "%u", cpn); + len += fprintf (fp, "%jd", cpn); break; case 's': diff --git a/src/common.h b/src/common.h index e81a4631..135e6f3e 100644 --- a/src/common.h +++ b/src/common.h @@ -129,7 +129,7 @@ extern enum backup_type backup_type; extern bool block_number_option; -extern unsigned checkpoint_option; +extern intmax_t checkpoint_option; #define DEFAULT_CHECKPOINT 10 /* Specified name of compression program, or "gzip" as implied by -z. */ @@ -288,8 +288,7 @@ extern size_t strip_name_components; extern bool show_omitted_dirs_option; extern bool sparse_option; -extern unsigned tar_sparse_major; -extern unsigned tar_sparse_minor; +extern intmax_t tar_sparse_major, tar_sparse_minor; enum hole_detection_method { @@ -941,7 +940,7 @@ void sys_wait_command (void); int sys_exec_info_script (const char **archive_name, int volume_number); void sys_exec_checkpoint_script (const char *script_name, const char *archive_name, - int checkpoint_number); + intmax_t checkpoint_number); bool mtioseek (bool count_files, off_t count); int sys_exec_setmtime_script (const char *script_name, int dirfd, diff --git a/src/system.c b/src/system.c index 2b73afbf..3ec4e993 100644 --- a/src/system.c +++ b/src/system.c @@ -896,7 +896,7 @@ sys_exec_info_script (const char **archive_name, int volume_number) void sys_exec_checkpoint_script (const char *script_name, const char *archive_name, - int checkpoint_number) + intmax_t checkpoint_number) { pid_t pid = xfork (); @@ -919,8 +919,8 @@ sys_exec_checkpoint_script (const char *script_name, /* Child */ setenv ("TAR_VERSION", PACKAGE_VERSION, 1); setenv ("TAR_ARCHIVE", archive_name, 1); - char intbuf[INT_BUFSIZE_BOUND (int)]; - sprintf (intbuf, "%d", checkpoint_number); + char intbuf[INT_BUFSIZE_BOUND (intmax_t)]; + sprintf (intbuf, "%jd", checkpoint_number); setenv ("TAR_CHECKPOINT", intbuf, 1); sprintf (intbuf, "%d", blocking_factor); setenv ("TAR_BLOCKING_FACTOR", intbuf, 1); diff --git a/src/tar.c b/src/tar.c index 4cb07468..595ec8b7 100644 --- a/src/tar.c +++ b/src/tar.c @@ -44,7 +44,7 @@ enum atime_preserve atime_preserve_option; bool backup_option; enum backup_type backup_type; bool block_number_option; -unsigned checkpoint_option; +intmax_t checkpoint_option; const char *use_compress_program_option; bool dereference_option; bool hard_dereference_option; @@ -90,8 +90,8 @@ int xattrs_option; size_t strip_name_components; bool show_omitted_dirs_option; bool sparse_option; -unsigned tar_sparse_major; -unsigned tar_sparse_minor; +intmax_t tar_sparse_major; +intmax_t tar_sparse_minor; enum hole_detection_method hole_detection; bool starting_file_option; tarlong tape_length_option; @@ -1831,15 +1831,27 @@ parse_opt (int key, char *arg, struct argp_state *state) sparse_option = true; { char *p; - tar_sparse_major = strtoul (arg, &p, 10); - if (*p) + bool ok; + switch (xstrtoimax (arg, &p, 10, &tar_sparse_major, "")) { - if (*p != '.') - USAGE_ERROR ((0, 0, _("Invalid sparse version value"))); - tar_sparse_minor = strtoul (p + 1, &p, 10); - if (*p) - USAGE_ERROR ((0, 0, _("Invalid sparse version value"))); + case LONGINT_OK: + tar_sparse_minor = 0; + ok = 0 <= tar_sparse_major; + break; + + case LONGINT_INVALID_SUFFIX_CHAR: + ok = (*p == '.' + && (xstrtoimax (p + 1, nullptr, 10, &tar_sparse_minor, "") + == LONGINT_OK) + && 0 <= tar_sparse_minor && 0 <= tar_sparse_major); + break; + + default: + ok = false; + break; } + if (!ok) + USAGE_ERROR ((0, 0, _("Invalid sparse version value"))); } break; @@ -1936,10 +1948,10 @@ parse_opt (int key, char *arg, struct argp_state *state) checkpoint_compile_action ("."); arg++; } - checkpoint_option = strtoul (arg, &p, 0); - if (*p) + checkpoint_option = strtoimax (arg, &p, 0); + if (*p || checkpoint_option <= 0) FATAL_ERROR ((0, 0, - _("--checkpoint value is not an integer"))); + _("invalid --checkpoint value"))); } else checkpoint_option = DEFAULT_CHECKPOINT; diff --git a/src/tar.h b/src/tar.h index c5b39547..f53ef3e7 100644 --- a/src/tar.h +++ b/src/tar.h @@ -325,8 +325,8 @@ struct tar_stat_info bool is_sparse; /* Is the file sparse */ /* For sparse files: */ - unsigned sparse_major; - unsigned sparse_minor; + intmax_t sparse_major; + intmax_t sparse_minor; size_t sparse_map_avail; /* Index to the first unused element in sparse_map array. Zero if the file is not sparse */ diff --git a/src/transform.c b/src/transform.c index f9947193..2fc97b31 100644 --- a/src/transform.c +++ b/src/transform.c @@ -62,7 +62,7 @@ struct transform struct transform *next; enum transform_type transform_type; int flags; - unsigned match_number; + idx_t match_number; regex_t regex; /* Compiled replacement expression */ struct replace_segm *repl_head, *repl_tail; @@ -248,8 +248,14 @@ parse_transform_expr (const char *expr) case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - tf->match_number = strtoul (p, (char**) &p, 0); - p--; + { + char *endp; + intmax_t match_number = strtoimax (p, &endp, 10); + assume (0 <= match_number); + if (ckd_add (&tf->match_number, match_number, 0)) + tf->match_number = IDX_MAX; + p = endp - 1; + } break; default: @@ -290,17 +296,19 @@ parse_transform_expr (const char *expr) { if (*cur == '\\') { - size_t n; - add_literal_segment (tf, beg, cur); switch (*++cur) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - n = strtoul (cur, &cur, 10); - if (n > tf->regex.re_nsub) - USAGE_ERROR ((0, 0, _("Invalid transform replacement: back reference out of range"))); - add_backref_segment (tf, n); + { + intmax_t n = strtoimax (cur, &cur, 10); + assume (0 <= n); + if (tf->regex.re_nsub < n) + USAGE_ERROR ((0, 0, _("Invalid transform replacement:" + " back reference out of range"))); + add_backref_segment (tf, n); + } break; case '\\': diff --git a/src/xheader.c b/src/xheader.c index cf37e2d9..b306accd 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -1673,7 +1673,7 @@ sparse_major_decoder (struct tar_stat_info *st, MAYBE_UNUSED size_t size) { uintmax_t u; - if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + if (decode_num (&u, arg, INTMAX_MAX, keyword)) st->sparse_major = u; } @@ -1691,7 +1691,7 @@ sparse_minor_decoder (struct tar_stat_info *st, MAYBE_UNUSED size_t size) { uintmax_t u; - if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + if (decode_num (&u, arg, INTMAX_MAX, keyword)) st->sparse_minor = u; }