From 1d521775bd9b54bd3c3e685d5ab88d89b606a998 Mon Sep 17 00:00:00 2001 From: Peter Vrabec Date: Nov 23 2005 15:24:40 +0000 Subject: write_out_header rewritten to fix buffer overflow(#172669) --- diff --git a/cpio-2.6-writeOutHeaderBufferOverflow.patch b/cpio-2.6-writeOutHeaderBufferOverflow.patch new file mode 100644 index 0000000..91e8cd0 --- /dev/null +++ b/cpio-2.6-writeOutHeaderBufferOverflow.patch @@ -0,0 +1,556 @@ +--- cpio-2.6/src/extern.h.backup 2005-11-15 13:30:46.000000000 -0500 ++++ cpio-2.6/src/extern.h 2005-11-15 13:31:12.000000000 -0500 +@@ -112,7 +112,7 @@ + void print_name_with_quoting P_((char *p)); + + /* copyout.c */ +-void write_out_header P_((struct new_cpio_header *file_hdr, int out_des)); ++int write_out_header P_((struct new_cpio_header *file_hdr, int out_des)); + void process_copy_out P_((void)); + + /* copypass.c */ +--- cpio-2.6/src/copyout.c.backup 2005-11-15 11:26:24.000000000 -0500 ++++ cpio-2.6/src/copyout.c 2005-11-15 13:24:36.000000000 -0500 +@@ -159,7 +159,7 @@ + } + + /* We are about to put a file into a newc or crc archive that is +- multiply linked. We have already seen and defered all of the ++ multiply linked. We have already seen and deferred all of the + other links to the file but haven't written them into the archive. + Write the other links into the archive, and remove them from the + deferouts list. */ +@@ -231,8 +231,10 @@ + file_hdr.c_filesize, + header->c_name); + +- write_out_header (&file_hdr, out_file_des); +- copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, header->c_name); ++ if (write_out_header (&file_hdr, out_file_des)) ++ return; ++ copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, ++ header->c_name); + warn_if_file_changed(header->c_name, file_hdr.c_filesize, file_hdr.c_mtime); + + if (archive_format == arf_tar || archive_format == arf_ustar) +@@ -288,153 +290,311 @@ + } + } + +- +-/* Write out header FILE_HDR, including the file name, to file +- descriptor OUT_DES. */ ++/* FIXME: These two defines should be defined in paxutils */ ++#define LG_8 3 ++#define LG_16 4 ++ ++/* FIXME: to_ascii could be used instead of to_oct() and to_octal() from tar, ++ so it should be moved to paxutils too. ++ Allowed values for logbase are: 1 (binary), 2, 3 (octal), 4 (hex) */ ++int ++to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase) ++{ ++ static char codetab[] = "0123456789ABCDEF"; ++ int i = digits; ++ ++ do ++ { ++ where[--i] = codetab[(v & ((1 << logbase) - 1))]; ++ v >>= logbase; ++ } ++ while (i); ++ ++ return v != 0; ++} ++ ++static void ++field_width_error (const char *filename, const char *fieldname) ++{ ++ error (0, 0, _("%s: field width not sufficient for storing %s"), ++ filename, fieldname); ++} ++ ++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); ++} + + void +-write_out_header (struct new_cpio_header *file_hdr, int out_des) ++to_ascii_or_warn (char *where, uintmax_t n, size_t digits, ++ unsigned logbase, ++ const char *filename, const char *fieldname) + { +- if (archive_format == arf_newascii || archive_format == arf_crcascii) ++ if (to_ascii (where, n, digits, logbase)) ++ field_width_warning (filename, fieldname); ++} ++ ++int ++to_ascii_or_error (char *where, uintmax_t n, size_t digits, ++ unsigned logbase, ++ const char *filename, const char *fieldname) ++{ ++ if (to_ascii (where, n, digits, logbase)) + { +- char ascii_header[112]; +- char *magic_string; ++ field_width_error (filename, fieldname); ++ return 1; ++ } ++ return 0; ++} + +- if (archive_format == arf_crcascii) +- magic_string = "070702"; +- else +- magic_string = "070701"; +- sprintf (ascii_header, +- "%6s%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx", +- magic_string, +- file_hdr->c_ino, file_hdr->c_mode, file_hdr->c_uid, +- file_hdr->c_gid, file_hdr->c_nlink, file_hdr->c_mtime, +- file_hdr->c_filesize, file_hdr->c_dev_maj, file_hdr->c_dev_min, +- file_hdr->c_rdev_maj, file_hdr->c_rdev_min, file_hdr->c_namesize, +- file_hdr->c_chksum & 0xffffffff); +- tape_buffered_write (ascii_header, out_des, 110L); +- +- /* Write file name to output. */ +- tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); +- tape_pad_output (out_des, file_hdr->c_namesize + 110); +- } +- else if (archive_format == arf_oldascii || archive_format == arf_hpoldascii) +- { +- char ascii_header[78]; +- dev_t dev; +- dev_t rdev; ++int ++write_out_new_ascii_header (const char *magic_string, ++ struct new_cpio_header *file_hdr, int out_des) ++{ ++ char ascii_header[110]; ++ char *p; + +- if (archive_format == arf_oldascii) +- { +- dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); +- rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); +- } +- else +- { +- /* HP/UX cpio creates archives that look just like ordinary archives, +- but for devices it sets major = 0, minor = 1, and puts the +- actual major/minor number in the filesize field. */ +- switch (file_hdr->c_mode & CP_IFMT) +- { +- case CP_IFCHR: +- case CP_IFBLK: ++ p = stpcpy (ascii_header, magic_string); ++ to_ascii_or_warn (p, file_hdr->c_ino, 8, LG_16, ++ file_hdr->c_name, _("inode number")); ++ p += 8; ++ to_ascii_or_warn (p, file_hdr->c_mode, 8, LG_16, file_hdr->c_name, ++ _("file mode")); ++ p += 8; ++ to_ascii_or_warn (p, file_hdr->c_uid, 8, LG_16, file_hdr->c_name, ++ _("uid")); ++ p += 8; ++ to_ascii_or_warn (p, file_hdr->c_gid, 8, LG_16, file_hdr->c_name, ++ _("gid")); ++ p += 8; ++ to_ascii_or_warn (p, file_hdr->c_nlink, 8, LG_16, file_hdr->c_name, ++ _("number of links")); ++ p += 8; ++ to_ascii_or_warn (p, file_hdr->c_mtime, 8, LG_16, file_hdr->c_name, ++ _("modification time")); ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_filesize, 8, LG_16, file_hdr->c_name, ++ _("file size"))) ++ return 1; ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_dev_maj, 8, LG_16, file_hdr->c_name, ++ _("device major number"))) ++ return 1; ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_dev_min, 8, LG_16, file_hdr->c_name, ++ _("device minor number"))) ++ return 1; ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_rdev_maj, 8, LG_16, file_hdr->c_name, ++ _("rdev major"))) ++ return 1; ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_rdev_min, 8, LG_16, file_hdr->c_name, ++ _("rdev minor"))) ++ return 1; ++ p += 8; ++ if (to_ascii_or_error (p, file_hdr->c_namesize, 8, LG_16, file_hdr->c_name, ++ _("name size"))) ++ return 1; ++ p += 8; ++ to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16); ++ ++ tape_buffered_write (ascii_header, out_des, sizeof ascii_header); ++ ++ /* Write file name to output. */ ++ tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); ++ tape_pad_output (out_des, file_hdr->c_namesize + sizeof ascii_header); ++ return 0; ++} ++ ++int ++write_out_old_ascii_header (dev_t dev, dev_t rdev, ++ struct new_cpio_header *file_hdr, int out_des) ++{ ++ char ascii_header[76]; ++ char *p = ascii_header; ++ ++ to_ascii (p, file_hdr->c_magic, 6, LG_8); ++ p += 6; ++ to_ascii_or_warn (p, dev, 6, LG_8, file_hdr->c_name, _("device number")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_ino, 6, LG_8, file_hdr->c_name, ++ _("inode number")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_mode, 6, LG_8, file_hdr->c_name, ++ _("file mode")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_uid, 6, LG_8, file_hdr->c_name, _("uid")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_gid, 6, LG_8, file_hdr->c_name, _("gid")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_nlink, 6, LG_8, file_hdr->c_name, ++ _("number of links")); ++ p += 6; ++ to_ascii_or_warn (p, rdev, 6, LG_8, file_hdr->c_name, _("rdev")); ++ p += 6; ++ to_ascii_or_warn (p, file_hdr->c_mtime, 11, LG_8, file_hdr->c_name, ++ _("modification time")); ++ p += 11; ++ if (to_ascii_or_error (p, file_hdr->c_namesize, 6, LG_8, file_hdr->c_name, ++ _("name size"))) ++ return 1; ++ p += 6; ++ if (to_ascii_or_error (p, file_hdr->c_filesize, 11, LG_8, file_hdr->c_name, ++ _("file size"))) ++ return 1; ++ ++ tape_buffered_write (ascii_header, out_des, sizeof ascii_header); ++ ++ /* Write file name to output. */ ++ tape_buffered_write (file_hdr->c_name, out_des, file_hdr->c_namesize); ++ return 0; ++} ++ ++void ++hp_compute_dev (struct new_cpio_header *file_hdr, dev_t *pdev, dev_t *prdev) ++{ ++ /* HP/UX cpio creates archives that look just like ordinary archives, ++ but for devices it sets major = 0, minor = 1, and puts the ++ actual major/minor number in the filesize field. */ ++ switch (file_hdr->c_mode & CP_IFMT) ++ { ++ case CP_IFCHR: ++ case CP_IFBLK: + #ifdef CP_IFSOCK +- case CP_IFSOCK: ++ case CP_IFSOCK: + #endif + #ifdef CP_IFIFO +- case CP_IFIFO: ++ case CP_IFIFO: + #endif +- file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, +- file_hdr->c_rdev_min); +- dev = rdev = 1; +- break; +- default: +- dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); +- rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); +- break; +- } +- } ++ file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, ++ file_hdr->c_rdev_min); ++ *pdev = *prdev = makedev (0, 1); ++ break; ++ ++ default: ++ *pdev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); ++ *prdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); ++ break; ++ } ++} + +- if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0) +- error (0, 0, _("%s: truncating inode number"), file_hdr->c_name); ++int ++write_out_binary_header (dev_t rdev, ++ struct new_cpio_header *file_hdr, int out_des) ++{ ++ struct old_cpio_header short_hdr; + +- /* Debian hack: The type of dev_t has changed in glibc. Fixed output +- to ensure that a long int is passed to sprintf. This has been +- reported to "bug-gnu-utils@prep.ai.mit.edu". (1998/5/26) -BEM */ +- sprintf (ascii_header, +- "%06ho%06lo%06lo%06lo%06lo%06lo%06lo%06lo%011lo%06lo%011lo", +- file_hdr->c_magic & 0xFFFF, (long) dev & 0xFFFF, +- file_hdr->c_ino & 0xFFFF, file_hdr->c_mode & 0xFFFF, +- file_hdr->c_uid & 0xFFFF, file_hdr->c_gid & 0xFFFF, +- file_hdr->c_nlink & 0xFFFF, (long) rdev & 0xFFFF, +- file_hdr->c_mtime, file_hdr->c_namesize & 0xFFFF, +- file_hdr->c_filesize); +- tape_buffered_write (ascii_header, out_des, 76L); ++ short_hdr.c_magic = 070707; ++ short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + +- /* Write file name to output. */ +- tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); +- } +- else if (archive_format == arf_tar || archive_format == arf_ustar) +- { +- write_out_tar_header (file_hdr, out_des); +- } +- else +- { +- struct old_cpio_header short_hdr; ++ if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0) ++ error (0, 0, _("%s: truncating inode number"), file_hdr->c_name); + +- short_hdr.c_magic = 070707; +- short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); ++ short_hdr.c_ino = file_hdr->c_ino & 0xFFFF; ++ if (short_hdr.c_ino != file_hdr->c_ino) ++ field_width_warning (file_hdr->c_name, _("inode number")); ++ ++ short_hdr.c_mode = file_hdr->c_mode & 0xFFFF; ++ if (short_hdr.c_mode != file_hdr->c_mode) ++ field_width_warning (file_hdr->c_name, _("file mode")); ++ ++ short_hdr.c_uid = file_hdr->c_uid & 0xFFFF; ++ if (short_hdr.c_uid != file_hdr->c_uid) ++ field_width_warning (file_hdr->c_name, _("uid")); ++ ++ short_hdr.c_gid = file_hdr->c_gid & 0xFFFF; ++ if (short_hdr.c_gid != file_hdr->c_gid) ++ field_width_warning (file_hdr->c_name, _("gid")); ++ ++ short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF; ++ if (short_hdr.c_nlink != file_hdr->c_nlink) ++ field_width_warning (file_hdr->c_name, _("number of links")); ++ ++ short_hdr.c_rdev = rdev; ++ short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16; ++ short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF; ++ ++ short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF; ++ if (short_hdr.c_namesize != file_hdr->c_namesize) ++ { ++ field_width_error (file_hdr->c_name, _("name size")); ++ return 1; ++ } ++ ++ short_hdr.c_filesize = file_hdr->c_filesize; ++ if (short_hdr.c_filesize != file_hdr->c_filesize) ++ { ++ field_width_error (file_hdr->c_name, _("file size")); ++ return 1; ++ } ++ ++ short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16; ++ short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF; + +- if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0) +- error (0, 0, _("%s: truncating inode number"), file_hdr->c_name); ++ /* Output the file header. */ ++ tape_buffered_write ((char *) &short_hdr, out_des, 26); + +- short_hdr.c_ino = file_hdr->c_ino & 0xFFFF; +- short_hdr.c_mode = file_hdr->c_mode & 0xFFFF; +- short_hdr.c_uid = file_hdr->c_uid & 0xFFFF; +- short_hdr.c_gid = file_hdr->c_gid & 0xFFFF; +- short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF; +- if (archive_format != arf_hpbinary) +- short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); +- else +- { +- switch (file_hdr->c_mode & CP_IFMT) +- { +- /* HP/UX cpio creates archives that look just like ordinary +- archives, but for devices it sets major = 0, minor = 1, and +- puts the actual major/minor number in the filesize field. */ +- case CP_IFCHR: +- case CP_IFBLK: +-#ifdef CP_IFSOCK +- case CP_IFSOCK: +-#endif +-#ifdef CP_IFIFO +- case CP_IFIFO: +-#endif +- file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, +- file_hdr->c_rdev_min); +- short_hdr.c_rdev = makedev (0, 1); +- break; +- default: +- short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj, +- file_hdr->c_rdev_min); +- break; +- } +- } +- short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16; +- short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF; ++ /* Write file name to output. */ ++ tape_buffered_write (file_hdr->c_name, out_des, file_hdr->c_namesize); + +- short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF; ++ tape_pad_output (out_des, file_hdr->c_namesize + 26); ++ return 0; ++} + +- short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16; +- short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF; ++ ++/* Write out header FILE_HDR, including the file name, to file ++ descriptor OUT_DES. */ + +- /* Output the file header. */ +- tape_buffered_write ((char *) &short_hdr, out_des, 26L); ++int ++write_out_header (struct new_cpio_header *file_hdr, int out_des) ++{ ++ dev_t dev; ++ dev_t rdev; ++ ++ switch (archive_format) ++ { ++ case arf_newascii: ++ return write_out_new_ascii_header ("070701", file_hdr, out_des); ++ ++ case arf_crcascii: ++ return write_out_new_ascii_header ("070702", file_hdr, out_des); ++ ++ case arf_oldascii: ++ return write_out_old_ascii_header (makedev (file_hdr->c_dev_maj, ++ file_hdr->c_dev_min), ++ makedev (file_hdr->c_rdev_maj, ++ file_hdr->c_rdev_min), ++ file_hdr, out_des); ++ ++ case arf_hpoldascii: ++ hp_compute_dev (file_hdr, &dev, &rdev); ++ return write_out_old_ascii_header (dev, rdev, file_hdr, out_des); ++ ++ case arf_tar: ++ 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); ++ return 1; ++ } ++ write_out_tar_header (file_hdr, out_des); /* FIXME: No error checking */ ++ return 0; + +- /* Write file name to output. */ +- tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); ++ case arf_binary: ++ return write_out_binary_header (makedev (file_hdr->c_rdev_maj, ++ file_hdr->c_rdev_min), ++ file_hdr, out_des); ++ ++ case arf_hpbinary: ++ hp_compute_dev (file_hdr, &dev, &rdev); ++ /* FIXME: dev ignored. Should it be? */ ++ return write_out_binary_header (rdev, file_hdr, out_des); + +- tape_pad_output (out_des, file_hdr->c_namesize + 26); ++ default: ++ abort (); + } + } + +@@ -593,14 +753,7 @@ + file_hdr.c_namesize = strlen (p) + 1; + } + #endif +- if ((archive_format == arf_tar || archive_format == arf_ustar) +- && is_tar_filename_too_long (file_hdr.c_name)) +- { +- error (0, 0, _("%s: file name too long"), +- file_hdr.c_name); +- continue; +- } +- ++ + /* Copy the named file to the output. */ + switch (file_hdr.c_mode & CP_IFMT) + { +@@ -613,8 +766,8 @@ + file_hdr.c_dev_min))) + { + file_hdr.c_tar_linkname = otherfile; +- write_out_header (&file_hdr, out_file_des); +- break; ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + } + } + if ( (archive_format == arf_newascii || archive_format == arf_crcascii) +@@ -643,7 +796,8 @@ + file_hdr.c_filesize, + input_name.ds_string); + +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, input_name.ds_string); + warn_if_file_changed(input_name.ds_string, file_hdr.c_filesize, + file_hdr.c_mtime); +@@ -673,7 +827,8 @@ + + case CP_IFDIR: + file_hdr.c_filesize = 0; +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + break; + + case CP_IFCHR: +@@ -702,14 +857,16 @@ + file_hdr.c_mode = (file_stat.st_mode & 07777); + file_hdr.c_mode |= CP_IFREG; + file_hdr.c_tar_linkname = otherfile; +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + break; + } + add_inode (file_hdr.c_ino, file_hdr.c_name, + file_hdr.c_dev_maj, file_hdr.c_dev_min); + } + file_hdr.c_filesize = 0; +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + break; + + #ifdef CP_IFLNK +@@ -738,12 +895,14 @@ + { + link_name[link_size] = '\0'; + file_hdr.c_tar_linkname = link_name; +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + } + } + else + { +- write_out_header (&file_hdr, out_file_des); ++ if (write_out_header (&file_hdr, out_file_des)) ++ continue; + tape_buffered_write (link_name, out_file_des, link_size); + tape_pad_output (out_file_des, link_size); + } diff --git a/cpio.spec b/cpio.spec index 8cb5a6d..23ea196 100644 --- a/cpio.spec +++ b/cpio.spec @@ -6,7 +6,7 @@ Summary: A GNU archiving program. Name: cpio Version: 2.6 -Release: 9 +Release: 10 License: GPL Group: Applications/Archiving URL: http://www.gnu.org/software/cpio/ @@ -20,6 +20,7 @@ Patch18: cpio-2.6-chmodRaceC.patch Patch19: cpio-2.6-dirTraversal.patch Patch20: cpio-2.6-warnings.patch Patch21: cpio-2.6-checksum.patch +Patch22: cpio-2.6-writeOutHeaderBufferOverflow.patch %ifnos linux Prereq: /sbin/rmt @@ -53,6 +54,7 @@ Install cpio if you need a program to manage file archives. %patch19 -p1 -b .dirTraversal %patch20 -p1 -b .warnings %patch21 -p1 -b .checksum +%patch22 -p1 -b .bufferOverflow autoheader @@ -103,6 +105,9 @@ fi %{_infodir}/*.info* %changelog +* Wed Nov 23 2005 Peter Vrabec 2.6-10 +- write_out_header rewritten to fix buffer overflow(#172669) + * Mon Oct 31 2005 Peter Vrabec 2.6-9 - fix checksum error on 64-bit machines (#171649)