xsparse cleanup, including integer overflow
* scripts/xsparse.c: Include inttypes.h, for strtoimax. Don’t include stdint.h, since inttypes.h includes it. Sort include directives. Make all extern functions and vars static, except for ‘main’. (string_to_off): Use strtoimax instead of doing overflow checking by hand, incorrectly (it relied on undefined behavior). (string_to_size): New arg MAXSIZE. All callers changed. (get_var): Return bool not int. Fix unlikely integer overflow. Use strncmp instead of memcmp, to avoid unlikely pointer overflow. (read_xheader, read_map, main): Avoid unlikely integer overflow. Check for I/O errors more consistently. (main): Prefer bool to int, and put vars near use.
This commit is contained in:
@@ -20,16 +20,17 @@
|
|||||||
|
|
||||||
Written by Sergey Poznyakoff */
|
Written by Sergey Poznyakoff */
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Bound on length of the string representing an off_t.
|
/* Bound on length of the string representing an off_t.
|
||||||
See INT_STRLEN_BOUND in intprops.h for explanation */
|
See INT_STRLEN_BOUND in intprops.h for explanation */
|
||||||
@@ -44,10 +45,10 @@ struct sp_array
|
|||||||
off_t numbytes;
|
off_t numbytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *progname;
|
static char *progname;
|
||||||
int verbose;
|
static bool verbose;
|
||||||
|
|
||||||
void
|
static void
|
||||||
die (int code, char *fmt, ...)
|
die (int code, char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -60,7 +61,7 @@ die (int code, char *fmt, ...)
|
|||||||
exit (code);
|
exit (code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
static void *
|
||||||
emalloc (size_t size)
|
emalloc (size_t size)
|
||||||
{
|
{
|
||||||
char *p = malloc (size);
|
char *p = malloc (size);
|
||||||
@@ -69,49 +70,33 @@ emalloc (size_t size)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t
|
static off_t
|
||||||
string_to_off (char *p, char **endp)
|
string_to_off (char *p, char **endp)
|
||||||
{
|
{
|
||||||
off_t v = 0;
|
errno = 0;
|
||||||
|
intmax_t i = strtoimax (p, endp, 10);
|
||||||
for (; *p; p++)
|
off_t v = i;
|
||||||
{
|
if (i < 0 || v != i || errno == ERANGE)
|
||||||
int digit = *p - '0';
|
die (1, "number out of allowed range, near %s", p);
|
||||||
off_t x = v * 10;
|
if (errno || p == *endp)
|
||||||
if (9 < (unsigned) digit)
|
die (1, "number parse error near %s", p);
|
||||||
{
|
|
||||||
if (endp)
|
|
||||||
{
|
|
||||||
*endp = p;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
die (1, "number parse error near %s", p);
|
|
||||||
}
|
|
||||||
else if (x / 10 != v)
|
|
||||||
die (1, "number out of allowed range, near %s", p);
|
|
||||||
v = x + digit;
|
|
||||||
if (v < 0)
|
|
||||||
die (1, "negative number");
|
|
||||||
}
|
|
||||||
if (endp)
|
|
||||||
*endp = p;
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
static size_t
|
||||||
string_to_size (char *p, char **endp)
|
string_to_size (char *p, char **endp, size_t maxsize)
|
||||||
{
|
{
|
||||||
off_t v = string_to_off (p, endp);
|
off_t v = string_to_off (p, endp);
|
||||||
size_t ret = v;
|
size_t ret = v;
|
||||||
if (ret != v)
|
if (! (ret == v && ret <= maxsize))
|
||||||
die (1, "number too big");
|
die (1, "number too big");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sparse_map_size;
|
static size_t sparse_map_size;
|
||||||
struct sp_array *sparse_map;
|
static struct sp_array *sparse_map;
|
||||||
|
|
||||||
void
|
static void
|
||||||
get_line (char *s, int size, FILE *stream)
|
get_line (char *s, int size, FILE *stream)
|
||||||
{
|
{
|
||||||
char *p = fgets (s, size, stream);
|
char *p = fgets (s, size, stream);
|
||||||
@@ -122,10 +107,10 @@ get_line (char *s, int size, FILE *stream)
|
|||||||
len = strlen (p);
|
len = strlen (p);
|
||||||
if (s[len - 1] != '\n')
|
if (s[len - 1] != '\n')
|
||||||
die (1, "buffer overflow");
|
die (1, "buffer overflow");
|
||||||
s[len - 1] = 0;
|
s[len - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static bool
|
||||||
get_var (FILE *fp, char **name, char **value)
|
get_var (FILE *fp, char **name, char **value)
|
||||||
{
|
{
|
||||||
static char *buffer;
|
static char *buffer;
|
||||||
@@ -138,12 +123,12 @@ get_var (FILE *fp, char **name, char **value)
|
|||||||
size_t len, s;
|
size_t len, s;
|
||||||
|
|
||||||
if (!fgets (buffer, bufsize, fp))
|
if (!fgets (buffer, bufsize, fp))
|
||||||
return 0;
|
return false;
|
||||||
len = strlen (buffer);
|
len = strlen (buffer);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
s = string_to_size (buffer, &p);
|
s = string_to_size (buffer, &p, SIZE_MAX - 1);
|
||||||
if (*p != ' ')
|
if (*p != ' ')
|
||||||
die (1, "malformed header: expected space but found %s", p);
|
die (1, "malformed header: expected space but found %s", p);
|
||||||
if (buffer[len-1] != '\n')
|
if (buffer[len-1] != '\n')
|
||||||
@@ -160,7 +145,7 @@ get_var (FILE *fp, char **name, char **value)
|
|||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
while (memcmp (p, "GNU.sparse.", 11));
|
while (strncmp (p, "GNU.sparse.", 11) != 0);
|
||||||
|
|
||||||
p += 11;
|
p += 11;
|
||||||
q = strchr (p, '=');
|
q = strchr (p, '=');
|
||||||
@@ -170,15 +155,15 @@ get_var (FILE *fp, char **name, char **value)
|
|||||||
q[strlen (q) - 1] = 0;
|
q[strlen (q) - 1] = 0;
|
||||||
*name = p;
|
*name = p;
|
||||||
*value = q;
|
*value = q;
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *outname;
|
static char *outname;
|
||||||
off_t outsize;
|
static off_t outsize;
|
||||||
unsigned version_major;
|
static unsigned int version_major;
|
||||||
unsigned version_minor;
|
static unsigned int version_minor;
|
||||||
|
|
||||||
void
|
static void
|
||||||
read_xheader (char *name)
|
read_xheader (char *name)
|
||||||
{
|
{
|
||||||
char *kw, *val;
|
char *kw, *val;
|
||||||
@@ -205,11 +190,11 @@ read_xheader (char *name)
|
|||||||
}
|
}
|
||||||
else if (strcmp (kw, "major") == 0)
|
else if (strcmp (kw, "major") == 0)
|
||||||
{
|
{
|
||||||
version_major = string_to_size (val, NULL);
|
version_major = string_to_size (val, NULL, SIZE_MAX);
|
||||||
}
|
}
|
||||||
else if (strcmp (kw, "minor") == 0)
|
else if (strcmp (kw, "minor") == 0)
|
||||||
{
|
{
|
||||||
version_minor = string_to_size (val, NULL);
|
version_minor = string_to_size (val, NULL, SIZE_MAX);
|
||||||
}
|
}
|
||||||
else if (strcmp (kw, "realsize") == 0
|
else if (strcmp (kw, "realsize") == 0
|
||||||
|| strcmp (kw, "size") == 0)
|
|| strcmp (kw, "size") == 0)
|
||||||
@@ -218,7 +203,8 @@ read_xheader (char *name)
|
|||||||
}
|
}
|
||||||
else if (strcmp (kw, "numblocks") == 0)
|
else if (strcmp (kw, "numblocks") == 0)
|
||||||
{
|
{
|
||||||
sparse_map_size = string_to_size (val, NULL);
|
sparse_map_size = string_to_size (val, NULL,
|
||||||
|
SIZE_MAX / sizeof *sparse_map);
|
||||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||||
}
|
}
|
||||||
else if (strcmp (kw, "offset") == 0)
|
else if (strcmp (kw, "offset") == 0)
|
||||||
@@ -258,10 +244,11 @@ read_xheader (char *name)
|
|||||||
die (1, "size of the sparse map unknown");
|
die (1, "size of the sparse map unknown");
|
||||||
if (i != sparse_map_size)
|
if (i != sparse_map_size)
|
||||||
die (1, "not all sparse entries supplied");
|
die (1, "not all sparse entries supplied");
|
||||||
fclose (fp);
|
if (ferror (fp) || fclose (fp) < 0)
|
||||||
|
die (1, "read error: %s", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
read_map (FILE *ifp)
|
read_map (FILE *ifp)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -271,7 +258,7 @@ read_map (FILE *ifp)
|
|||||||
printf ("Reading v.1.0 sparse map\n");
|
printf ("Reading v.1.0 sparse map\n");
|
||||||
|
|
||||||
get_line (nbuf, sizeof nbuf, ifp);
|
get_line (nbuf, sizeof nbuf, ifp);
|
||||||
sparse_map_size = string_to_size (nbuf, NULL);
|
sparse_map_size = string_to_size (nbuf, NULL, SIZE_MAX / sizeof *sparse_map);
|
||||||
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
sparse_map = emalloc (sparse_map_size * sizeof *sparse_map);
|
||||||
|
|
||||||
for (i = 0; i < sparse_map_size; i++)
|
for (i = 0; i < sparse_map_size; i++)
|
||||||
@@ -282,11 +269,15 @@ read_map (FILE *ifp)
|
|||||||
sparse_map[i].numbytes = string_to_off (nbuf, NULL);
|
sparse_map[i].numbytes = string_to_off (nbuf, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE,
|
off_t ifp_offset = ftello (ifp);
|
||||||
SEEK_SET);
|
if (ifp_offset < 0)
|
||||||
|
die (1, "ftello");
|
||||||
|
if (ifp_offset % BLOCKSIZE != 0
|
||||||
|
&& fseeko (ifp, BLOCKSIZE - ifp_offset % BLOCKSIZE, SEEK_CUR) < 0)
|
||||||
|
die (1, "fseeko");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
expand_sparse (FILE *sfp, int ofd)
|
expand_sparse (FILE *sfp, int ofd)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -327,7 +318,7 @@ expand_sparse (FILE *sfp, int ofd)
|
|||||||
free (buffer);
|
free (buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
usage (int code)
|
usage (int code)
|
||||||
{
|
{
|
||||||
printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
|
printf ("Usage: %s [OPTIONS] infile [outfile]\n", progname);
|
||||||
@@ -342,7 +333,7 @@ usage (int code)
|
|||||||
exit (code);
|
exit (code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
guess_outname (char *name)
|
guess_outname (char *name)
|
||||||
{
|
{
|
||||||
char *p;
|
char *p;
|
||||||
@@ -388,12 +379,8 @@ int
|
|||||||
main (int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
int dry_run = 0;
|
bool dry_run = false;
|
||||||
char *xheader_file = NULL;
|
char *xheader_file = NULL;
|
||||||
char *inname;
|
|
||||||
FILE *ifp;
|
|
||||||
struct stat st;
|
|
||||||
int ofd;
|
|
||||||
|
|
||||||
progname = argv[0];
|
progname = argv[0];
|
||||||
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
|
while ((c = getopt (argc, argv, "hnvx:")) != EOF)
|
||||||
@@ -409,9 +396,9 @@ main (int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
dry_run = 1;
|
dry_run = true;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -428,15 +415,16 @@ main (int argc, char **argv)
|
|||||||
if (xheader_file)
|
if (xheader_file)
|
||||||
read_xheader (xheader_file);
|
read_xheader (xheader_file);
|
||||||
|
|
||||||
inname = argv[0];
|
char *inname = argv[0];
|
||||||
if (argv[1])
|
if (argv[1])
|
||||||
outname = argv[1];
|
outname = argv[1];
|
||||||
|
|
||||||
if (stat (inname, &st))
|
struct stat st;
|
||||||
|
if (stat (inname, &st) < 0)
|
||||||
die (1, "cannot stat %s (%d)", inname, errno);
|
die (1, "cannot stat %s (%d)", inname, errno);
|
||||||
|
|
||||||
ifp = fopen (inname, "r");
|
FILE *ifp = fopen (inname, "r");
|
||||||
if (ifp == NULL)
|
if (!ifp)
|
||||||
die (1, "cannot open file %s (%d)", inname, errno);
|
die (1, "cannot open file %s (%d)", inname, errno);
|
||||||
|
|
||||||
if (!xheader_file || version_major == 1)
|
if (!xheader_file || version_major == 1)
|
||||||
@@ -445,8 +433,8 @@ main (int argc, char **argv)
|
|||||||
if (!outname)
|
if (!outname)
|
||||||
guess_outname (inname);
|
guess_outname (inname);
|
||||||
|
|
||||||
ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
|
int ofd = open (outname, O_RDWR|O_CREAT|O_TRUNC, st.st_mode);
|
||||||
if (ofd == -1)
|
if (ofd < 0)
|
||||||
die (1, "cannot open file %s (%d)", outname, errno);
|
die (1, "cannot open file %s (%d)", outname, errno);
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
@@ -460,15 +448,17 @@ main (int argc, char **argv)
|
|||||||
|
|
||||||
expand_sparse (ifp, ofd);
|
expand_sparse (ifp, ofd);
|
||||||
|
|
||||||
fclose (ifp);
|
if (ferror (ifp) || fclose (ifp) < 0)
|
||||||
close (ofd);
|
die (1, "input error: %s", inname);
|
||||||
|
if (close (ofd) < 0)
|
||||||
|
die (1, "output error: %s", outname);
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf ("Done\n");
|
printf ("Done\n");
|
||||||
|
|
||||||
if (outsize)
|
if (outsize)
|
||||||
{
|
{
|
||||||
if (stat (outname, &st))
|
if (stat (outname, &st) < 0)
|
||||||
die (1, "cannot stat output file %s (%d)", outname, errno);
|
die (1, "cannot stat output file %s (%d)", outname, errno);
|
||||||
if (st.st_size != outsize)
|
if (st.st_size != outsize)
|
||||||
die (1, "expanded file has wrong size");
|
die (1, "expanded file has wrong size");
|
||||||
|
|||||||
Reference in New Issue
Block a user