cpio/cpio-2.13-CVE-2021-38185.patch

622 lines
19 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From a458d64ad1e47c0912c2ba0702a148c396984105 Mon Sep 17 00:00:00 2001
From: Ondrej Dubaj <odubaj@redhat.com>
Date: Mon, 13 Sep 2021 08:13:08 +0200
Subject: [PATCH] * src/dstring.c (ds_init): Take a single argument.
(ds_free): New function. (ds_resize): Take a single argument. Use
x2nrealloc to expand the storage.
(ds_reset,ds_append,ds_concat,ds_endswith): New function. (ds_fgetstr):
Rewrite. In particular, this fixes integer overflow. (ds_resize): Take
additional argument: number of bytes to leave available after ds_idx. All
uses changed. * src/dstring.h (dynamic_string): Keep both the allocated
length (ds_size) and index of the next free byte in the string (ds_idx).
(ds_init,ds_resize): Change signature. (ds_len): New macro.
(ds_free,ds_reset,ds_append,ds_concat,ds_endswith): New protos. *
src/copyin.c: Use new ds_ functions. (read_name_from_file): Handle len == 0.
(read_name_from_file): Print error message and skip file if its name is not
nul-terminated. (long_format): Cast rdev numbers to unsigned long *
src/copyout.c: Likewise. * src/copypass.c: Likewise. * src/util.c: Likewise.
(tape_empty_output_buffer): Fix condition. * src/idcache.c
(getuser,getgroup): Use umaxtostr instead of sprintf. * src/userspec.c
(parse_user_spec): Likewise. * configure.ac: Raise version number to 2.13.90.
---
configure.ac | 6 ++--
src/copyin.c | 69 ++++++++++++++++++++++------------------
src/copyout.c | 16 ++++------
src/copypass.c | 32 +++++++++----------
src/dstring.c | 85 ++++++++++++++++++++++++++++++++++++--------------
src/dstring.h | 30 +++++++++---------
src/idcache.c | 11 +++----
src/userspec.c | 9 ++----
src/util.c | 9 ++----
9 files changed, 150 insertions(+), 117 deletions(-)
diff --git a/configure.ac b/configure.ac
index 2132256..875b44f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -15,13 +15,13 @@ dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program. If not, see <http://www.gnu.org/licenses/>.
-AC_INIT([GNU cpio], [2.13], [bug-cpio@gnu.org],,
+AC_INIT([GNU cpio], [2.13.90], [bug-cpio@gnu.org],,
[http://www.gnu.org/software/cpio])
AC_CONFIG_SRCDIR(src/cpio.h)
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
-AC_PREREQ([2.63])
-AM_INIT_AUTOMAKE([1.11.1 gnits tar-ustar dist-bzip2 std-options silent-rules])
+AC_PREREQ([2.64])
+AM_INIT_AUTOMAKE([1.15 gnits tar-ustar dist-bzip2 std-options silent-rules])
# Enable silent rules by default:
AM_SILENT_RULES([yes])
diff --git a/src/copyin.c b/src/copyin.c
index 93b006a..df5da9c 100644
--- a/src/copyin.c
+++ b/src/copyin.c
@@ -56,10 +56,10 @@ query_rename(struct cpio_file_stat* file_hdr, FILE *tty_in, FILE *tty_out,
static dynamic_string new_name; /* New file name for rename option. */
static int initialized_new_name = false;
if (!initialized_new_name)
- {
- ds_init (&new_name, 128);
- initialized_new_name = true;
- }
+ {
+ ds_init (&new_name);
+ initialized_new_name = true;
+ }
if (rename_flag)
{
@@ -756,8 +756,9 @@ long_format (struct cpio_file_stat *file_hdr, char const *link_name)
if ((file_hdr->c_mode & CP_IFMT) == CP_IFCHR
|| (file_hdr->c_mode & CP_IFMT) == CP_IFBLK)
- printf ("%3lu, %3lu ", file_hdr->c_rdev_maj,
- file_hdr->c_rdev_min);
+ printf ("%3lu, %3lu ",
+ (unsigned long) file_hdr->c_rdev_maj,
+ (unsigned long) file_hdr->c_rdev_min);
else
printf ("%8"PRIuMAX" ", (uintmax_t) file_hdr->c_filesize);
@@ -777,21 +778,20 @@ long_format (struct cpio_file_stat *file_hdr, char const *link_name)
already in `save_patterns' (from the command line) are preserved. */
static void
-read_pattern_file ()
+read_pattern_file (void)
{
- int max_new_patterns;
- char **new_save_patterns;
- int new_num_patterns;
+ char **new_save_patterns = NULL;
+ size_t max_new_patterns;
+ size_t new_num_patterns;
int i;
- dynamic_string pattern_name;
+ dynamic_string pattern_name = DYNAMIC_STRING_INITIALIZER;
FILE *pattern_fp;
if (num_patterns < 0)
num_patterns = 0;
- max_new_patterns = 1 + num_patterns;
- new_save_patterns = (char **) xmalloc (max_new_patterns * sizeof (char *));
new_num_patterns = num_patterns;
- ds_init (&pattern_name, 128);
+ max_new_patterns = num_patterns;
+ new_save_patterns = xcalloc (max_new_patterns, sizeof (new_save_patterns[0]));
pattern_fp = fopen (pattern_file_name, "r");
if (pattern_fp == NULL)
@@ -800,16 +800,16 @@ read_pattern_file ()
{
while (ds_fgetstr (pattern_fp, &pattern_name, '\n') != NULL)
{
- if (new_num_patterns >= max_new_patterns)
- {
- max_new_patterns += 1;
- new_save_patterns = (char **)
- xrealloc ((char *) new_save_patterns,
- max_new_patterns * sizeof (char *));
- }
+ if (new_num_patterns == max_new_patterns)
+ new_save_patterns = x2nrealloc (new_save_patterns,
+ &max_new_patterns,
+ sizeof (new_save_patterns[0]));
new_save_patterns[new_num_patterns] = xstrdup (pattern_name.ds_string);
++new_num_patterns;
}
+
+ ds_free (&pattern_name);
+
if (ferror (pattern_fp) || fclose (pattern_fp) == EOF)
close_error (pattern_file_name);
}
@@ -999,8 +999,21 @@ read_in_header (struct cpio_file_stat *file_hdr, int in_des)
static void
read_name_from_file (struct cpio_file_stat *file_hdr, int fd, uintmax_t len)
{
- cpio_realloc_c_name (file_hdr, len);
- tape_buffered_read (file_hdr->c_name, fd, len);
+ if (len == 0)
+ {
+ error (0, 0, _("malformed header: file name of zero length"));
+ }
+ else
+ {
+ cpio_realloc_c_name (file_hdr, len);
+ tape_buffered_read (file_hdr->c_name, fd, len);
+ if (file_hdr->c_name[len-1] != 0)
+ {
+ error (0, 0, _("malformed header: file name is not nul-terminated"));
+ /* Skip this file */
+ len = 0;
+ }
+ }
file_hdr->c_namesize = len;
}
@@ -1197,9 +1210,8 @@ swab_array (char *ptr, int count)
in the file system. */
void
-process_copy_in ()
+process_copy_in (void)
{
- char done = false; /* True if trailer reached. */
FILE *tty_in = NULL; /* Interactive file for rename option. */
FILE *tty_out = NULL; /* Interactive file for rename option. */
FILE *rename_in = NULL; /* Batch file for rename option. */
@@ -1271,7 +1283,7 @@ process_copy_in ()
change_dir ();
/* While there is more input in the collection, process the input. */
- while (!done)
+ while (1)
{
swapping_halfwords = swapping_bytes = false;
@@ -1305,10 +1317,7 @@ process_copy_in ()
{
/* Is this the header for the TRAILER file? */
if (strcmp (CPIO_TRAILER_NAME, file_hdr.c_name) == 0)
- {
- done = true;
- break;
- }
+ break;
cpio_safer_name_suffix (file_hdr.c_name, false, !no_abs_paths_flag,
false);
diff --git a/src/copyout.c b/src/copyout.c
index 4b7336b..421d36d 100644
--- a/src/copyout.c
+++ b/src/copyout.c
@@ -594,9 +594,10 @@ assign_string (char **pvar, char *value)
The format of the header depends on the compatibility (-c) flag. */
void
-process_copy_out ()
+process_copy_out (void)
{
- dynamic_string input_name; /* Name of file read from stdin. */
+ dynamic_string input_name = DYNAMIC_STRING_INITIALIZER;
+ /* Name of file read from stdin. */
struct stat file_stat; /* Stat record for file. */
struct cpio_file_stat file_hdr = CPIO_FILE_STAT_INITIALIZER;
/* Output header information. */
@@ -605,7 +606,6 @@ process_copy_out ()
char *orig_file_name = NULL;
/* Initialize the copy out. */
- ds_init (&input_name, 128);
file_hdr.c_magic = 070707;
/* Check whether the output file might be a tape. */
@@ -657,14 +657,9 @@ process_copy_out ()
{
if (file_hdr.c_mode & CP_IFDIR)
{
- int len = strlen (input_name.ds_string);
/* Make sure the name ends with a slash */
- if (input_name.ds_string[len-1] != '/')
- {
- ds_resize (&input_name, len + 2);
- input_name.ds_string[len] = '/';
- input_name.ds_string[len+1] = 0;
- }
+ if (!ds_endswith (&input_name, '/'))
+ ds_append (&input_name, '/');
}
}
@@ -875,6 +870,7 @@ process_copy_out ()
(unsigned long) blocks), (unsigned long) blocks);
}
cpio_file_stat_free (&file_hdr);
+ ds_free (&input_name);
}
diff --git a/src/copypass.c b/src/copypass.c
index a5f9b7b..43bde7e 100644
--- a/src/copypass.c
+++ b/src/copypass.c
@@ -48,10 +48,12 @@ set_copypass_perms (int fd, const char *name, struct stat *st)
If `link_flag', link instead of copying. */
void
-process_copy_pass ()
+process_copy_pass (void)
{
- dynamic_string input_name; /* Name of file from stdin. */
- dynamic_string output_name; /* Name of new file. */
+ dynamic_string input_name = DYNAMIC_STRING_INITIALIZER;
+ /* Name of file from stdin. */
+ dynamic_string output_name = DYNAMIC_STRING_INITIALIZER;
+ /* Name of new file. */
size_t dirname_len; /* Length of `directory_name'. */
int res; /* Result of functions. */
char *slash; /* For moving past slashes in input name. */
@@ -65,25 +67,18 @@ process_copy_pass ()
created files */
/* Initialize the copy pass. */
- ds_init (&input_name, 128);
dirname_len = strlen (directory_name);
if (change_directory_option && !ISSLASH (directory_name[0]))
{
char *pwd = xgetcwd ();
- dirname_len += strlen (pwd) + 1;
- ds_init (&output_name, dirname_len + 2);
- strcpy (output_name.ds_string, pwd);
- strcat (output_name.ds_string, "/");
- strcat (output_name.ds_string, directory_name);
+ ds_concat (&output_name, pwd);
+ ds_append (&output_name, '/');
}
- else
- {
- ds_init (&output_name, dirname_len + 2);
- strcpy (output_name.ds_string, directory_name);
- }
- output_name.ds_string[dirname_len] = '/';
+ ds_concat (&output_name, directory_name);
+ ds_append (&output_name, '/');
+ dirname_len = ds_len (&output_name);
output_is_seekable = true;
change_dir ();
@@ -116,8 +111,8 @@ process_copy_pass ()
/* Make the name of the new file. */
for (slash = input_name.ds_string; *slash == '/'; ++slash)
;
- ds_resize (&output_name, dirname_len + strlen (slash) + 2);
- strcpy (output_name.ds_string + dirname_len + 1, slash);
+ ds_reset (&output_name, dirname_len);
+ ds_concat (&output_name, slash);
existing_dir = false;
if (lstat (output_name.ds_string, &out_file_stat) == 0)
@@ -335,6 +330,9 @@ process_copy_pass ()
(unsigned long) blocks),
(unsigned long) blocks);
}
+
+ ds_free (&input_name);
+ ds_free (&output_name);
}
/* Try and create a hard link from FILE_NAME to another file
diff --git a/src/dstring.c b/src/dstring.c
index e9c063f..c788057 100644
--- a/src/dstring.c
+++ b/src/dstring.c
@@ -22,6 +22,7 @@
#endif
#include <stdio.h>
+#include <stdlib.h>
#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
#include <string.h>
#else
@@ -33,24 +34,40 @@
/* Initialiaze dynamic string STRING with space for SIZE characters. */
void
-ds_init (dynamic_string *string, int size)
+ds_init (dynamic_string *string)
{
- string->ds_length = size;
- string->ds_string = (char *) xmalloc (size);
+ memset (string, 0, sizeof *string);
}
-/* Expand dynamic string STRING, if necessary, to hold SIZE characters. */
+/* Free the dynamic string storage. */
void
-ds_resize (dynamic_string *string, int size)
+ds_free (dynamic_string *string)
{
- if (size > string->ds_length)
+ free (string->ds_string);
+}
+
+/* Expand dynamic string STRING, if necessary. */
+
+void
+ds_resize (dynamic_string *string, size_t len)
+{
+ while (len + string->ds_idx >= string->ds_size)
{
- string->ds_length = size;
- string->ds_string = (char *) xrealloc ((char *) string->ds_string, size);
+ string->ds_string = x2nrealloc (string->ds_string, &string->ds_size,
+ 1);
}
}
+/* Reset the index of the dynamic string S to LEN. */
+
+void
+ds_reset (dynamic_string *s, size_t len)
+{
+ ds_resize (s, len);
+ s->ds_idx = len;
+}
+
/* Dynamic string S gets a string terminated by the EOS character
(which is removed) from file F. S will increase
in size during the function if the string from F is longer than
@@ -61,34 +78,49 @@ ds_resize (dynamic_string *string, int size)
char *
ds_fgetstr (FILE *f, dynamic_string *s, char eos)
{
- int insize; /* Amount needed for line. */
- int strsize; /* Amount allocated for S. */
int next_ch;
/* Initialize. */
- insize = 0;
- strsize = s->ds_length;
+ s->ds_idx = 0;
/* Read the input string. */
- next_ch = getc (f);
- while (next_ch != eos && next_ch != EOF)
+ while ((next_ch = getc (f)) != eos && next_ch != EOF)
{
- if (insize >= strsize - 1)
- {
- ds_resize (s, strsize * 2 + 2);
- strsize = s->ds_length;
- }
- s->ds_string[insize++] = next_ch;
- next_ch = getc (f);
+ ds_resize (s, 0);
+ s->ds_string[s->ds_idx++] = next_ch;
}
- s->ds_string[insize++] = '\0';
+ ds_resize (s, 0);
+ s->ds_string[s->ds_idx] = '\0';
- if (insize == 1 && next_ch == EOF)
+ if (s->ds_idx == 0 && next_ch == EOF)
return NULL;
else
return s->ds_string;
}
+void
+ds_append (dynamic_string *s, int c)
+{
+ ds_resize (s, 0);
+ s->ds_string[s->ds_idx] = c;
+ if (c)
+ {
+ s->ds_idx++;
+ ds_resize (s, 0);
+ s->ds_string[s->ds_idx] = 0;
+ }
+}
+
+void
+ds_concat (dynamic_string *s, char const *str)
+{
+ size_t len = strlen (str);
+ ds_resize (s, len);
+ memcpy (s->ds_string + s->ds_idx, str, len);
+ s->ds_idx += len;
+ s->ds_string[s->ds_idx] = 0;
+}
+
char *
ds_fgets (FILE *f, dynamic_string *s)
{
@@ -100,3 +132,10 @@ ds_fgetname (FILE *f, dynamic_string *s)
{
return ds_fgetstr (f, s, '\0');
}
+
+/* Return true if the dynamic string S ends with character C. */
+int
+ds_endswith (dynamic_string *s, int c)
+{
+ return (s->ds_idx > 0 && s->ds_string[s->ds_idx - 1] == c);
+}
diff --git a/src/dstring.h b/src/dstring.h
index b5135fe..756cc1f 100644
--- a/src/dstring.h
+++ b/src/dstring.h
@@ -17,10 +17,6 @@
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA. */
-#ifndef NULL
-#define NULL 0
-#endif
-
/* A dynamic string consists of record that records the size of an
allocated string and the pointer to that string. The actual string
is a normal zero byte terminated string that can be used with the
@@ -30,22 +26,24 @@
typedef struct
{
- int ds_length; /* Actual amount of storage allocated. */
- char *ds_string; /* String. */
+ size_t ds_size; /* Actual amount of storage allocated. */
+ size_t ds_idx; /* Index of the next free byte in the string. */
+ char *ds_string; /* String storage. */
} dynamic_string;
+#define DYNAMIC_STRING_INITIALIZER { 0, 0, NULL }
-/* Macros that look similar to the original string functions.
- WARNING: These macros work only on pointers to dynamic string records.
- If used with a real record, an "&" must be used to get the pointer. */
-#define ds_strlen(s) strlen ((s)->ds_string)
-#define ds_strcmp(s1, s2) strcmp ((s1)->ds_string, (s2)->ds_string)
-#define ds_strncmp(s1, s2, n) strncmp ((s1)->ds_string, (s2)->ds_string, n)
-#define ds_index(s, c) index ((s)->ds_string, c)
-#define ds_rindex(s, c) rindex ((s)->ds_string, c)
+void ds_init (dynamic_string *string);
+void ds_free (dynamic_string *string);
+void ds_reset (dynamic_string *s, size_t len);
-void ds_init (dynamic_string *string, int size);
-void ds_resize (dynamic_string *string, int size);
+/* All functions below guarantee that s->ds_string[s->ds_idx] == '\0' */
char *ds_fgetname (FILE *f, dynamic_string *s);
char *ds_fgets (FILE *f, dynamic_string *s);
char *ds_fgetstr (FILE *f, dynamic_string *s, char eos);
+void ds_append (dynamic_string *s, int c);
+void ds_concat (dynamic_string *s, char const *str);
+
+#define ds_len(s) ((s)->ds_idx)
+
+int ds_endswith (dynamic_string *s, int c);
diff --git a/src/idcache.c b/src/idcache.c
index 33b0d3f..6bd1f3e 100644
--- a/src/idcache.c
+++ b/src/idcache.c
@@ -34,6 +34,7 @@
#endif
#include <unistd.h>
+#include <inttostr.h>
struct userid
{
@@ -59,7 +60,6 @@ getuser (uid_t uid)
{
register struct userid *tail;
struct passwd *pwent;
- char usernum_string[20];
for (tail = user_alist; tail; tail = tail->next)
if (tail->id.u == uid)
@@ -70,8 +70,8 @@ getuser (uid_t uid)
tail->id.u = uid;
if (pwent == 0)
{
- sprintf (usernum_string, "%u", (unsigned) uid);
- tail->name = xstrdup (usernum_string);
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ tail->name = xstrdup (umaxtostr (uid, nbuf));
}
else
tail->name = xstrdup (pwent->pw_name);
@@ -134,7 +134,6 @@ getgroup (gid_t gid)
{
register struct userid *tail;
struct group *grent;
- char groupnum_string[20];
for (tail = group_alist; tail; tail = tail->next)
if (tail->id.g == gid)
@@ -145,8 +144,8 @@ getgroup (gid_t gid)
tail->id.g = gid;
if (grent == 0)
{
- sprintf (groupnum_string, "%u", (unsigned int) gid);
- tail->name = xstrdup (groupnum_string);
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ tail->name = xstrdup (umaxtostr (gid, nbuf));
}
else
tail->name = xstrdup (grent->gr_name);
diff --git a/src/userspec.c b/src/userspec.c
index eb3640e..b03234e 100644
--- a/src/userspec.c
+++ b/src/userspec.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
+#include <inttostr.h>
#ifndef HAVE_ENDPWENT
# define endpwent()
@@ -141,12 +142,8 @@ parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
grp = getgrgid (pwd->pw_gid);
if (grp == NULL)
{
- /* This is enough room to hold the unsigned decimal
- representation of any 32-bit quantity and the trailing
- zero byte. */
- char uint_buf[21];
- sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
- V_STRDUP (groupname, uint_buf);
+ char nbuf[UINTMAX_STRSIZE_BOUND];
+ V_STRDUP (groupname, umaxtostr (pwd->pw_gid, nbuf));
}
else
{
diff --git a/src/util.c b/src/util.c
index 0e8d88c..b721f37 100644
--- a/src/util.c
+++ b/src/util.c
@@ -79,8 +79,7 @@ tape_empty_output_buffer (int out_des)
if (output_is_special
&& (bytes_written >= 0
- || (bytes_written < 0
- && (errno == ENOSPC || errno == EIO || errno == ENXIO))))
+ || (errno == ENOSPC || errno == EIO || errno == ENXIO)))
{
get_next_reel (out_des);
if (bytes_written > 0)
@@ -846,11 +845,9 @@ get_next_reel (int tape_des)
FILE *tty_out; /* File for interacting with user. */
int old_tape_des;
char *next_archive_name;
- dynamic_string new_name;
+ dynamic_string new_name = DYNAMIC_STRING_INITIALIZER;
char *str_res;
- ds_init (&new_name, 128);
-
/* Open files for interactive communication. */
tty_in = fopen (TTY_NAME, "r");
if (tty_in == NULL)
@@ -925,7 +922,7 @@ get_next_reel (int tape_des)
error (PAXEXIT_FAILURE, 0, _("internal error: tape descriptor changed from %d to %d"),
old_tape_des, tape_des);
- free (new_name.ds_string);
+ ds_free (&new_name);
fclose (tty_in);
fclose (tty_out);
}
--
2.31.1