@@ -11,6 +11,7 @@ SRC_URI = "${GNU_MIRROR}/cpio/cpio-${PV}.tar.gz \
file://test.sh \
file://CVE-2026-66485.patch \
file://CVE-2026-66484.patch \
+ file://CVE-2026-66486.patch \
"
SRC_URI[sha256sum] = "efa50ef983137eefc0a02fdb51509d624b5e3295c980aa127ceee4183455499e"
new file mode 100644
@@ -0,0 +1,497 @@
+From 2ff9600c9ef32e88759843cdbde74c8db5ae9b30 Mon Sep 17 00:00:00 2001
+From: Sergey Poznyakoff <gray@gnu.org>
+Date: Thu, 23 Jul 2026 17:26:05 +0300
+Subject: [PATCH] Quote file names in error messages and in listings.
+
+* NEWS: Document changes.
+* doc/cpio.texi: Likewise.
+* src/copyin.c: Quote file and member names.
+* src/copyout.c: Likewise.
+* src/copypass.c: Likewise.
+* src/main.c: New options: --quoting-style and --quote-chars.
+(process_args): Set default quoting style.
+* tests/CVE-2019-14866.at: Fix expected output.
+
+CVE: CVE-2026-66485
+Upstream-Status: Backport [https://git.savannah.gnu.org/cgit/cpio.git/commit/?id=2ff9600c9ef32e88759843cdbde74c8db5ae9b30]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ NEWS | 16 ++++++++++++-
+ doc/cpio.texi | 13 ++++++++++
+ src/copyin.c | 39 +++++++++++++++++-------------
+ src/copyout.c | 26 ++++++++++----------
+ src/copypass.c | 11 +++++----
+ src/main.c | 53 +++++++++++++++++++++++++++++++++++++++--
+ tests/CVE-2019-14866.at | 2 +-
+ 7 files changed, 123 insertions(+), 37 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index d036665..24d85b5 100644
+--- a/NEWS
++++ b/NEWS
+@@ -1,8 +1,22 @@
+-GNU cpio NEWS -- history of user-visible changes. 2024-01-14
++GNU cpio NEWS -- history of user-visible changes. 2026-07-23
+ Copyright (C) 2003-2024 Free Software Foundation, Inc.
+ See the end of file for copying conditions.
+
+ Please send cpio bug reports to <bug-cpio@gnu.org>.
++
++Version 2.15.? (git)
++
++* New options
++
++ --quoting-style=STYLE
++ Set name quoting style used when printing file names. Valid styles
++ are: c, c-maybe, clocale, escape, help (displays available styles
++ and exits), literal (default), locale, shell, shell-always,
++ shell-escape, and shell-escape-always.
++
++ --quote-chars=STRING
++ Additionally quote characters from STRING when printing file names.
++
+
+ Version 2.15 - Sergey Poznyakoff, 2024-01-14
+
+diff --git a/doc/cpio.texi b/doc/cpio.texi
+index 8d596fb..dc69a4d 100644
+--- a/doc/cpio.texi
++++ b/doc/cpio.texi
+@@ -814,6 +814,19 @@ Run in copy-pass mode.
+ [@ref{copy-in},@ref{copy-out},@ref{copy-pass}]
+ @*Do not print the number of blocks copied.
+
++@item --quote-chars=@var{string}
++Always quote characters from @var{string}, even if the selected
++quoting style would not quote them (@pxref{quoting styles,,,tar,GNU
++tar}).
++
++@item --quoting-style=@var{style}
++Set quoting style to use when printing member and file names
++(@pxref{quoting styles,,,tar,GNU tar}). Valid @var{style} values are:
++@code{literal}, @code{shell}, @code{shell-always}, @code{c},
++@code{escape}, @code{locale}, and @code{clocale}. Default quoting
++style is @code{literal}, unless overridden while configuring the
++package.
++
+ @item -r
+ @itemx --rename
+ [@ref{copy-in}]
+diff --git a/src/copyin.c b/src/copyin.c
+index 59ce98b..2afc9d4 100644
+--- a/src/copyin.c
++++ b/src/copyin.c
+@@ -114,7 +114,7 @@ get_link_name (struct cpio_file_stat *file_hdr, int in_file_des)
+ if (file_hdr->c_filesize < 0 || file_hdr->c_filesize > SIZE_MAX-1)
+ {
+ error (0, 0, _("%s: stored filename length is out of range"),
+- file_hdr->c_name);
++ quote (file_hdr->c_name));
+ link_name = NULL;
+ }
+ else
+@@ -150,7 +150,11 @@ list_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ }
+ else
+ #endif
+- long_format (file_hdr, (char *) 0);
++ long_format (file_hdr, NULL);
++ }
++ else if (name_end == '\n' && isatty (fileno (stdout)))
++ {
++ printf ("%s%c", quotearg (file_hdr->c_name), name_end);
+ }
+ else
+ {
+@@ -173,7 +177,7 @@ list_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ if (crc != file_hdr->c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+- file_hdr->c_name, crc, file_hdr->c_chksum);
++ quote (file_hdr->c_name), crc, file_hdr->c_chksum);
+ }
+ }
+ }
+@@ -200,7 +204,7 @@ try_existing_file (struct cpio_file_stat* file_hdr, int in_file_des,
+ && file_hdr->c_mtime <= file_stat.st_mtime)
+ {
+ error (0, 0, _("%s not created: newer or same age version exists"),
+- file_hdr->c_name);
++ quote (file_hdr->c_name));
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+@@ -210,7 +214,7 @@ try_existing_file (struct cpio_file_stat* file_hdr, int in_file_des,
+ : unlink (file_hdr->c_name))
+ {
+ error (0, errno, _("cannot remove current %s"),
+- file_hdr->c_name);
++ quote (file_hdr->c_name));
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+@@ -271,7 +275,8 @@ create_defered_links (struct cpio_file_stat *file_hdr)
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+- d->header.c_name, file_hdr->c_name);
++ quote_n (0, d->header.c_name),
++ quote_n (1, file_hdr->c_name));
+ }
+ if (d_prev != NULL)
+ d_prev->next = d->next;
+@@ -467,7 +472,8 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+- file_hdr->c_tar_linkname, file_hdr->c_name);
++ quote_n (0, file_hdr->c_tar_linkname),
++ quote_n (1, file_hdr->c_name));
+ }
+ return;
+ }
+@@ -500,7 +506,7 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ swapping_halfwords = true;
+ else
+ error (0, 0, _("cannot swap halfwords of %s: odd number of halfwords"),
+- file_hdr->c_name);
++ quote (file_hdr->c_name));
+ }
+ if (swap_bytes_flag)
+ {
+@@ -508,7 +514,7 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ swapping_bytes = true;
+ else
+ error (0, 0, _("cannot swap bytes of %s: odd number of bytes"),
+- file_hdr->c_name);
++ quote (file_hdr->c_name));
+ }
+ copy_files_tape_to_disk (in_file_des, out_file_des, file_hdr->c_filesize);
+ disk_empty_output_buffer (out_file_des, true);
+@@ -519,7 +525,7 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ {
+ if (crc != file_hdr->c_chksum)
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+- file_hdr->c_name, crc, file_hdr->c_chksum);
++ quote (file_hdr->c_name), crc, file_hdr->c_chksum);
+ }
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+@@ -534,7 +540,7 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
+ {
+ if (crc != file_hdr->c_chksum)
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+- file_hdr->c_name, crc, file_hdr->c_chksum);
++ quote (file_hdr->c_name), crc, file_hdr->c_chksum);
+ }
+
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+@@ -582,7 +588,8 @@ copyin_device (struct cpio_file_stat* file_hdr)
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+- file_hdr->c_tar_linkname, file_hdr->c_name);
++ quote_n (0, file_hdr->c_tar_linkname),
++ quote_n (1, file_hdr->c_name));
+ /* Something must be wrong, because we couldn't
+ find the file to link to. But can we assume
+ that the device maj/min numbers are correct
+@@ -855,7 +862,7 @@ copyin_file (struct cpio_file_stat *file_hdr, int in_file_des)
+ #endif
+
+ default:
+- error (0, 0, _("%s: unknown file type"), file_hdr->c_name);
++ error (0, 0, _("%s: unknown file type"), quote (file_hdr->c_name));
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ }
+@@ -1550,13 +1557,13 @@ process_copy_in (void)
+ if (crc != file_hdr.c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+- file_hdr.c_name, crc, file_hdr.c_chksum);
++ quote (file_hdr.c_name), crc, file_hdr.c_chksum);
+ }
+ /* Debian hack: -v and -V now work with --only-verify-crc.
+ (99/11/10) -BEM */
+ if (verbose_flag)
+ {
+- fprintf (stderr, "%s\n", file_hdr.c_name);
++ fprintf (stderr, "%s\n", quotearg (file_hdr.c_name));
+ }
+ if (dot_flag)
+ {
+@@ -1581,7 +1588,7 @@ process_copy_in (void)
+ copyin_file(&file_hdr, in_file_des);
+
+ if (verbose_flag)
+- fprintf (stderr, "%s\n", file_hdr.c_name);
++ fprintf (stderr, "%s\n", quotearg (file_hdr.c_name));
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+diff --git a/src/copyout.c b/src/copyout.c
+index fd88080..b9fb676 100644
+--- a/src/copyout.c
++++ b/src/copyout.c
+@@ -46,7 +46,8 @@ read_for_checksum (int in_file_des, off_t file_size, char *file_name)
+ {
+ bytes_read = read (in_file_des, buf, BUFSIZ);
+ if (bytes_read < 0)
+- error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"), file_name);
++ error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"),
++ quote (file_name));
+ if (bytes_read == 0)
+ break;
+ for (i = 0; i < bytes_read; i++)
+@@ -54,7 +55,8 @@ read_for_checksum (int in_file_des, off_t file_size, char *file_name)
+ file_size -= bytes_read;
+ }
+ if (lseek (in_file_des, 0L, SEEK_SET))
+- error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"), file_name);
++ error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"),
++ quote (file_name));
+
+ return crc;
+ }
+@@ -288,7 +290,7 @@ field_width_error (const char *filename, const char *fieldname,
+ char valbuf[UINTMAX_STRSIZE_BOUND + 1];
+ char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+ error (0, 0, _("%s: value %s %s out of allowed range 0..%s"),
+- filename, fieldname,
++ quote (filename), fieldname,
+ STRINGIFY_BIGINT (value, valbuf),
+ STRINGIFY_BIGINT (MAX_VAL_WITH_DIGITS (width - nul, LG_8),
+ maxbuf));
+@@ -298,7 +300,7 @@ static void
+ field_width_warning (const char *filename, const char *fieldname)
+ {
+ if (warn_option & CPIO_WARN_TRUNCATE)
+- error (0, 0, _("%s: truncating %s"), filename, fieldname);
++ error (0, 0, _("%s: truncating %s"), quote (filename), fieldname);
+ }
+
+ void
+@@ -466,7 +468,7 @@ write_out_binary_header (dev_t rdev,
+ short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min);
+
+ if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0)
+- error (0, 0, _("%s: truncating inode number"), file_hdr->c_name);
++ error (0, 0, _("%s: truncating inode number"), quote (file_hdr->c_name));
+
+ short_hdr.c_ino = file_hdr->c_ino & 0xFFFF;
+ if (short_hdr.c_ino != file_hdr->c_ino)
+@@ -497,7 +499,7 @@ write_out_binary_header (dev_t rdev,
+ {
+ char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+ error (0, 0, _("%s: value %s %s out of allowed range 0..%u"),
+- file_hdr->c_name, _("name size"),
++ quote (file_hdr->c_name), _("name size"),
+ STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFu);
+ return 1;
+ }
+@@ -510,7 +512,7 @@ write_out_binary_header (dev_t rdev,
+ {
+ char maxbuf[UINTMAX_STRSIZE_BOUND + 1];
+ error (0, 0, _("%s: value %s %s out of allowed range 0..%lu"),
+- file_hdr->c_name, _("file size"),
++ quote (file_hdr->c_name), _("file size"),
+ STRINGIFY_BIGINT (file_hdr->c_namesize, maxbuf), 0xFFFFFFFFlu);
+ return 1;
+ }
+@@ -558,7 +560,7 @@ write_out_header (struct cpio_file_stat *file_hdr, int out_des)
+ case arf_ustar:
+ if (is_tar_filename_too_long (file_hdr->c_name))
+ {
+- error (0, 0, _("%s: file name too long"), file_hdr->c_name);
++ error (0, 0, _("%s: file name too long"), quote (file_hdr->c_name));
+ return 1;
+ }
+ return write_out_tar_header (file_hdr, out_des);
+@@ -749,7 +751,7 @@ process_copy_out (void)
+ if (archive_format == arf_tar)
+ {
+ error (0, 0, _("%s not dumped: not a regular file"),
+- orig_file_name);
++ quote (orig_file_name));
+ continue;
+ }
+ else if (archive_format == arf_ustar)
+@@ -800,7 +802,7 @@ process_copy_out (void)
+ if (link_size + 1 > 100)
+ {
+ error (0, 0, _("%s: symbolic link too long"),
+- file_hdr.c_name);
++ quote (file_hdr.c_name));
+ }
+ else
+ {
+@@ -823,11 +825,11 @@ process_copy_out (void)
+ #endif
+
+ default:
+- error (0, 0, _("%s: unknown file type"), orig_file_name);
++ error (0, 0, _("%s: unknown file type"), quote (orig_file_name));
+ }
+
+ if (verbose_flag)
+- fprintf (stderr, "%s\n", orig_file_name);
++ fprintf (stderr, "%s\n", quote (orig_file_name));
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+diff --git a/src/copypass.c b/src/copypass.c
+index 7d7e970..928990f 100644
+--- a/src/copypass.c
++++ b/src/copypass.c
+@@ -127,7 +127,7 @@ process_copy_pass (void)
+ && in_file_stat.st_mtime <= out_file_stat.st_mtime)
+ {
+ error (0, 0, _("%s not created: newer or same age version exists"),
+- output_name.ds_string);
++ quote (output_name.ds_string));
+ continue; /* Go to the next file. */
+ }
+ else if (S_ISDIR (out_file_stat.st_mode)
+@@ -135,7 +135,7 @@ process_copy_pass (void)
+ : unlink (output_name.ds_string))
+ {
+ error (0, errno, _("cannot remove current %s"),
+- output_name.ds_string);
++ quote (output_name.ds_string));
+ continue; /* Go to the next file. */
+ }
+ }
+@@ -312,7 +312,8 @@ process_copy_pass (void)
+ #endif
+ else
+ {
+- error (0, 0, _("%s: unknown file type"), input_name.ds_string);
++ error (0, 0, _("%s: unknown file type"),
++ quote (input_name.ds_string));
+ }
+
+ if (verbose_flag)
+@@ -388,12 +389,12 @@ link_to_name (char const *link_name, char const *link_target)
+ {
+ if (verbose_flag)
+ error (0, 0, _("%s linked to %s"),
+- link_target, link_name);
++ quote_n (0, link_target), quote_n (1, link_name));
+ }
+ else if (link_flag)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+- link_target, link_name);
++ quote_n (0, link_target), quote_n (1, link_name));
+ }
+ return res;
+ }
+diff --git a/src/main.c b/src/main.c
+index 978dfff..dcd40b8 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -61,7 +61,9 @@ enum cpio_options {
+ RENUMBER_INODES_OPTION,
+ IGNORE_DEVNO_OPTION,
+ IGNORE_DIRNLINK_OPTION,
+- DEVICE_INDEPENDENT_OPTION
++ DEVICE_INDEPENDENT_OPTION,
++ QUOTING_STYLE_OPTION,
++ QUOTE_CHARS_OPTION
+ };
+
+ const char *program_authors[] =
+@@ -141,6 +143,12 @@ static struct argp_option options[] = {
+ N_("Control warning display. Currently FLAG is one of 'none', 'truncate', 'all'. Multiple options accumulate."), GRID+1 },
+ {"owner", 'R', N_("[USER][:.][GROUP]"), 0,
+ N_("Set the ownership of all files created to the specified USER and/or GROUP"), GRID+1 },
++ {"quoting-style", QUOTING_STYLE_OPTION, N_("STYLE"), 0,
++ N_("set name quoting style; use --quoting-style=help for a list of valid STYLE values"),
++ GRID+1 },
++ {"quote-chars", QUOTE_CHARS_OPTION, N_("STRING"), 0,
++ N_("additionally quote characters from STRING"),
++ GRID+1 },
+ #undef GRID
+
+ #define GRID 110
+@@ -295,6 +303,36 @@ warn_control (char *arg)
+ return 1;
+ }
+
++static void
++cpio_list_quoting_styles (int indent)
++{
++ int i;
++
++ for (i = 0; quoting_style_args[i]; i++)
++ printf ("%*.*s%s\n", indent, indent, "", quoting_style_args[i]);
++}
++
++static void
++cpio_set_quoting_style (char *arg)
++{
++ if (strcmp (arg, "help") == 0)
++ {
++ cpio_list_quoting_styles (0);
++ exit (EXIT_SUCCESS);
++ }
++
++ for (idx_t i = 0; quoting_style_args[i]; i++)
++ if (strcmp (arg, quoting_style_args[i]) == 0)
++ {
++ set_quoting_style (NULL, i);
++ return;
++ }
++ USAGE_ERROR ((0, 0,
++ _("Unknown quoting style '%s'."
++ " Try '%s --quoting-style=help' to get a list."),
++ arg, program_name));
++}
++
+ static error_t
+ parse_opt (int key, char *arg, struct argp_state *state)
+ {
+@@ -454,10 +492,19 @@ crc newc odc bin ustar tar (all-caps also recognized)"), arg));
+ copy_function = process_copy_pass;
+ break;
+
++ case QUOTE_CHARS_OPTION:
++ for (;*arg; arg++)
++ set_char_quoting (NULL, *arg, 1);
++ break;
++
++ case QUOTING_STYLE_OPTION:
++ cpio_set_quoting_style (arg);
++ break;
++
+ case IGNORE_DEVNO_OPTION:
+ ignore_devno_option = 1;
+ break;
+-
++
+ case RENUMBER_INODES_OPTION:
+ renumber_inodes_option = 1;
+ break;
+@@ -593,6 +640,8 @@ process_args (int argc, char *argv[])
+
+ xstat = lstat;
+
++ set_quoting_style (NULL, DEFAULT_QUOTING_STYLE);
++
+ if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &index, NULL))
+ exit (PAXEXIT_FAILURE);
+
+diff --git a/tests/CVE-2019-14866.at b/tests/CVE-2019-14866.at
+index 2bfdabc..bdf6a04 100644
+--- a/tests/CVE-2019-14866.at
++++ b/tests/CVE-2019-14866.at
+@@ -29,7 +29,7 @@ fi
+ ],
+ [0],
+ [],
+-[cpio: file: value size 17179869184 out of allowed range 0..8589934591
++[cpio: 'file': value size 17179869184 out of allowed range 0..8589934591
+ 2 blocks
+ ])
+ AT_CLEANUP