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.
This commit is contained in:
Paul Eggert
2024-08-07 17:03:22 -07:00
parent a80f364662
commit 4642cd04ed
8 changed files with 59 additions and 39 deletions

View File

@@ -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

View File

@@ -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':

View File

@@ -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,

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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 '\\':

View File

@@ -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;
}