diff --git a/backport-0001-CVE-2021-20197.patch b/backport-0001-CVE-2021-20197.patch new file mode 100644 index 0000000000000000000000000000000000000000..e3a9dd4a165ddcc2ffc9376936bccea406621c83 --- /dev/null +++ b/backport-0001-CVE-2021-20197.patch @@ -0,0 +1,287 @@ +From 365f5fb6d0f0da83817431a275e99e6f6babbe04 Mon Sep 17 00:00:00 2001 +From: Siddhesh Poyarekar +Date: Mon, 7 Dec 2020 20:48:23 +0530 +Subject: [PATCH] binutils: Use file descriptors from make_tempname + +The purpose of creating a temporary file securely using mkstemp is +defeated if it is closed in make_tempname and reopened later for use; +it is as good as using mktemp. Get the file descriptor instead and +then use it to create the BFD object. + +bfd/ + + * opncls.c (bfd_fdopenw): New function. + * bfd-in2.h: Regenerate. + +binutils/ + + * bucomm.c (make_tempname): Add argument to return file + descriptor. + * bucomm.h (make_tempname): Likewise. + * ar.c: Include libbfd.h. + (write_archive): Adjust for change in make_tempname. Call + bfd_fdopenw instead of bfd_openw. + * objcopy.c: Include libbfd.h. + (copy_file): New argument OFD. Use bfd_fdopenw instead of + bfd_openw. + (strip_main): Adjust for change in make_tempname and + copy_file. + (copy_main): Likewise. +--- + bfd/ChangeLog | 6 ++++++ + bfd/bfd-in2.h | 2 ++ + bfd/opncls.c | 33 +++++++++++++++++++++++++++++++++ + binutils/ChangeLog | 17 +++++++++++++++++ + binutils/ar.c | 11 ++++++++--- + binutils/bucomm.c | 4 ++-- + binutils/bucomm.h | 2 +- + binutils/objcopy.c | 30 ++++++++++++++++++++++-------- + 8 files changed, 91 insertions(+), 14 deletions(-) + +diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h +index 935ba53..48e3d9b 100644 +--- a/bfd/bfd-in2.h ++++ b/bfd/bfd-in2.h +@@ -588,6 +588,8 @@ bfd *bfd_openr (const char *filename, const char *target); + + bfd *bfd_fdopenr (const char *filename, const char *target, int fd); + ++bfd *bfd_fdopenw (const char *filename, const char *target, int fd); ++ + bfd *bfd_openstreamr (const char * filename, const char * target, + void * stream); + +diff --git a/bfd/opncls.c b/bfd/opncls.c +index c2a1d2f..f7696b6 100644 +--- a/bfd/opncls.c ++++ b/bfd/opncls.c +@@ -395,6 +395,39 @@ bfd_fdopenr (const char *filename, const char *target, int fd) + + /* + FUNCTION ++ bfd_fdopenw ++ ++SYNOPSIS ++ bfd *bfd_fdopenw (const char *filename, const char *target, int fd); ++ ++DESCRIPTION ++ <> is exactly like <> with the exception that ++ the resulting BFD is suitable for output. ++*/ ++ ++bfd * ++bfd_fdopenw (const char *filename, const char *target, int fd) ++{ ++ bfd *out = bfd_fdopenr (filename, target, fd); ++ ++ if (out != NULL) ++ { ++ if (!bfd_write_p (out)) ++ { ++ close (fd); ++ _bfd_delete_bfd (out); ++ out = NULL; ++ bfd_set_error (bfd_error_invalid_operation); ++ } ++ else ++ out->direction = write_direction; ++ } ++ ++ return out; ++} ++ ++/* ++FUNCTION + bfd_openstreamr + + SYNOPSIS +diff --git a/binutils/ar.c b/binutils/ar.c +index 7d279d6..2253242 100644 +--- a/binutils/ar.c ++++ b/binutils/ar.c +@@ -25,6 +25,7 @@ + + #include "sysdep.h" + #include "bfd.h" ++#include "libbfd.h" + #include "libiberty.h" + #include "progress.h" + #include "getopt.h" +@@ -1252,20 +1253,24 @@ write_archive (bfd *iarch) + bfd *obfd; + char *old_name, *new_name; + bfd *contents_head = iarch->archive_next; ++ int ofd = -1; + + old_name = (char *) xmalloc (strlen (bfd_get_filename (iarch)) + 1); + strcpy (old_name, bfd_get_filename (iarch)); +- new_name = make_tempname (old_name); ++ new_name = make_tempname (old_name, &ofd); + + if (new_name == NULL) + bfd_fatal (_("could not create temporary file whilst writing archive")); + + output_filename = new_name; + +- obfd = bfd_openw (new_name, bfd_get_target (iarch)); ++ obfd = bfd_fdopenw (new_name, bfd_get_target (iarch), ofd); + + if (obfd == NULL) +- bfd_fatal (old_name); ++ { ++ close (ofd); ++ bfd_fatal (old_name); ++ } + + output_bfd = obfd; + +diff --git a/binutils/bucomm.c b/binutils/bucomm.c +index 9e6a028..5324420 100644 +--- a/binutils/bucomm.c ++++ b/binutils/bucomm.c +@@ -532,7 +532,7 @@ template_in_dir (const char *path) + as FILENAME. */ + + char * +-make_tempname (const char *filename) ++make_tempname (const char *filename, int *ofd) + { + char *tmpname = template_in_dir (filename); + int fd; +@@ -550,7 +550,7 @@ make_tempname (const char *filename) + free (tmpname); + return NULL; + } +- close (fd); ++ *ofd = fd; + return tmpname; + } + +diff --git a/binutils/bucomm.h b/binutils/bucomm.h +index d831834..afb8e09 100644 +--- a/binutils/bucomm.h ++++ b/binutils/bucomm.h +@@ -51,7 +51,7 @@ int display_info (void); + + void print_arelt_descr (FILE *, bfd *, bfd_boolean, bfd_boolean); + +-char *make_tempname (const char *); ++char *make_tempname (const char *, int *); + char *make_tempdir (const char *); + + bfd_vma parse_vma (const char *, const char *); +diff --git a/binutils/objcopy.c b/binutils/objcopy.c +index ca35df0..2eb083c 100644 +--- a/binutils/objcopy.c ++++ b/binutils/objcopy.c +@@ -20,6 +20,7 @@ + + #include "sysdep.h" + #include "bfd.h" ++#include "libbfd.h" + #include "progress.h" + #include "getopt.h" + #include "libiberty.h" +@@ -3727,7 +3728,7 @@ set_long_section_mode (bfd *output_bfd, bfd *input_bfd, enum long_section_name_h + /* The top-level control. */ + + static void +-copy_file (const char *input_filename, const char *output_filename, ++copy_file (const char *input_filename, const char *output_filename, int ofd, + const char *input_target, const char *output_target, + const bfd_arch_info_type *input_arch) + { +@@ -3802,9 +3803,14 @@ copy_file (const char *input_filename, const char *output_filename, + else + force_output_target = TRUE; + +- obfd = bfd_openw (output_filename, output_target); ++ if (ofd >= 0) ++ obfd = bfd_fdopenw (output_filename, output_target, ofd); ++ else ++ obfd = bfd_openw (output_filename, output_target); ++ + if (obfd == NULL) + { ++ close (ofd); + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; +@@ -3832,13 +3838,19 @@ copy_file (const char *input_filename, const char *output_filename, + if (output_target == NULL) + output_target = bfd_get_target (ibfd); + +- obfd = bfd_openw (output_filename, output_target); ++ if (ofd >= 0) ++ obfd = bfd_fdopenw (output_filename, output_target, ofd); ++ else ++ obfd = bfd_openw (output_filename, output_target); ++ + if (obfd == NULL) + { ++ close (ofd); + bfd_nonfatal_message (output_filename, NULL, NULL, NULL); + status = 1; + return; + } ++ + /* This is a no-op on non-Coff targets. */ + set_long_section_mode (obfd, ibfd, long_section_names); + +@@ -4802,6 +4814,7 @@ strip_main (int argc, char *argv[]) + int hold_status = status; + struct stat statbuf; + char *tmpname; ++ int tmpfd = -1; + + if (get_file_size (argv[i]) < 1) + { +@@ -4816,7 +4829,7 @@ strip_main (int argc, char *argv[]) + + if (output_file == NULL + || filename_cmp (argv[i], output_file) == 0) +- tmpname = make_tempname (argv[i]); ++ tmpname = make_tempname (argv[i], &tmpfd); + else + tmpname = output_file; + +@@ -4829,7 +4842,7 @@ strip_main (int argc, char *argv[]) + } + + status = 0; +- copy_file (argv[i], tmpname, input_target, output_target, NULL); ++ copy_file (argv[i], tmpname, tmpfd, input_target, output_target, NULL); + if (status == 0) + { + if (preserve_dates) +@@ -5049,7 +5062,7 @@ copy_main (int argc, char *argv[]) + bfd_boolean formats_info = FALSE; + bfd_boolean use_globalize = FALSE; + bfd_boolean use_keep_global = FALSE; +- int c; ++ int c, tmpfd = -1; + struct stat statbuf; + const bfd_arch_info_type *input_arch = NULL; + +@@ -5895,7 +5908,7 @@ copy_main (int argc, char *argv[]) + are the same, then create a temp and rename the result into the input. */ + if (output_filename == NULL + || filename_cmp (input_filename, output_filename) == 0) +- tmpname = make_tempname (input_filename); ++ tmpname = make_tempname (input_filename, &tmpfd); + else + tmpname = output_filename; + +@@ -5903,7 +5916,8 @@ copy_main (int argc, char *argv[]) + fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), + input_filename, strerror (errno)); + +- copy_file (input_filename, tmpname, input_target, output_target, input_arch); ++ copy_file (input_filename, tmpname, tmpfd, input_target, output_target, ++ input_arch); + if (status == 0) + { + if (preserve_dates) +-- +1.8.3.1 + diff --git a/backport-0002-CVE-2021-20197.patch b/backport-0002-CVE-2021-20197.patch new file mode 100644 index 0000000000000000000000000000000000000000..abea8f4028c2d590c4a5bbefa3f906bb7af52ebe --- /dev/null +++ b/backport-0002-CVE-2021-20197.patch @@ -0,0 +1,96 @@ +From 1a1c3b4cc17687091cff5a368bd6f13742bcfdf8 Mon Sep 17 00:00:00 2001 +From: Siddhesh Poyarekar +Date: Mon, 7 Dec 2020 20:48:28 +0530 +Subject: [PATCH] objcopy: Get input file stat after BFD open + +Get file state from the descriptor opened by copy_file for the input +BFD. This ensures continuity in the view of the input file through +the descriptor. At the moment it is only to preserve timestamps +recorded at the point that we opened the file for input but in the +next patch this state will also be used to preserve ownership and +permissions wherever applicable. + +binutils/ + + * objcopy.c (copy_file): New argument IN_STAT. Return stat of + ibfd through it. + (strip_main): Remove redundant stat calls. adjust copy_file + calls. + (copy_main): Likewise. +--- + binutils/ChangeLog | 9 +++++++++ + binutils/objcopy.c | 23 +++++++---------------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/binutils/objcopy.c b/binutils/objcopy.c +index 2eb083c..b6cf6ea 100644 +--- a/binutils/objcopy.c ++++ b/binutils/objcopy.c +@@ -3729,8 +3729,8 @@ set_long_section_mode (bfd *output_bfd, bfd *input_bfd, enum long_section_name_h + + static void + copy_file (const char *input_filename, const char *output_filename, int ofd, +- const char *input_target, const char *output_target, +- const bfd_arch_info_type *input_arch) ++ struct stat *in_stat, const char *input_target, ++ const char *output_target, const bfd_arch_info_type *input_arch) + { + bfd *ibfd; + char **obj_matching; +@@ -3749,7 +3749,7 @@ copy_file (const char *input_filename, const char *output_filename, int ofd, + /* To allow us to do "strip *" without dying on the first + non-object file, failures are nonfatal. */ + ibfd = bfd_openr (input_filename, input_target); +- if (ibfd == NULL) ++ if (ibfd == NULL || fstat (fileno (ibfd->iostream), in_stat) != 0) + { + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + status = 1; +@@ -4822,11 +4822,6 @@ strip_main (int argc, char *argv[]) + continue; + } + +- if (preserve_dates) +- /* No need to check the return value of stat(). +- It has already been checked in get_file_size(). */ +- stat (argv[i], &statbuf); +- + if (output_file == NULL + || filename_cmp (argv[i], output_file) == 0) + tmpname = make_tempname (argv[i], &tmpfd); +@@ -4842,7 +4837,8 @@ strip_main (int argc, char *argv[]) + } + + status = 0; +- copy_file (argv[i], tmpname, tmpfd, input_target, output_target, NULL); ++ copy_file (argv[i], tmpname, tmpfd, &statbuf, input_target, ++ output_target, NULL); + if (status == 0) + { + if (preserve_dates) +@@ -5899,11 +5895,6 @@ copy_main (int argc, char *argv[]) + convert_efi_target (efi); + } + +- if (preserve_dates) +- if (stat (input_filename, & statbuf) < 0) +- fatal (_("warning: could not locate '%s'. System error message: %s"), +- input_filename, strerror (errno)); +- + /* If there is no destination file, or the source and destination files + are the same, then create a temp and rename the result into the input. */ + if (output_filename == NULL +@@ -5916,8 +5907,8 @@ copy_main (int argc, char *argv[]) + fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), + input_filename, strerror (errno)); + +- copy_file (input_filename, tmpname, tmpfd, input_target, output_target, +- input_arch); ++ copy_file (input_filename, tmpname, tmpfd, &statbuf, input_target, ++ output_target, input_arch); + if (status == 0) + { + if (preserve_dates) +-- +1.8.3.1 + diff --git a/backport-0003-CVE-2021-20197.patch b/backport-0003-CVE-2021-20197.patch new file mode 100644 index 0000000000000000000000000000000000000000..4e2e2c72d4eb875d3142e341341a6df337b054c7 --- /dev/null +++ b/backport-0003-CVE-2021-20197.patch @@ -0,0 +1,368 @@ +From 014cc7f849e8209623fc99264814bce7b3b6faf2 Mon Sep 17 00:00:00 2001 +From: Siddhesh Poyarekar +Date: Mon, 7 Dec 2020 20:48:33 +0530 +Subject: [PATCH] binutils: Make smart_rename safe too + +smart_rename is capable of handling symlinks by copying and it also +tries to preserve ownership and permissions of files when they're +overwritten during the rename. This is useful in objcopy where the +file properties need to be preserved. + +However because smart_rename does this using file names, it leaves a +race window between renames and permission fixes. This change removes +this race window by using file descriptors from the original BFDs that +were used to manipulate these files wherever possible. + +The file that is to be renamed is also passed as a file descriptor so +that we use fchown/fchmod on the file descriptor, thus making sure +that we only modify the file we have opened to write. Further, in +case the file is to be overwritten (as is the case in ar or objcopy), +the permissions that need to be restored are taken from the file +descriptor that was opened for input so that integrity of the file +status is maintained all the way through to the rename. + +binutils/ + + * rename.c + * ar.c + (write_archive) [!defined (_WIN32) || defined (__CYGWIN32__)]: + Initialize TARGET_STAT and OFD to pass to SMART_RENAME. + * arsup.c + (ar_save) [defined (_WIN32) || defined (__CYGWIN32__)]: + Likewise. + * bucomm.h (smart_rename): Add new arguments to declaration. + * objcopy.c + (strip_main)[defined (_WIN32) || defined (__CYGWIN32__)]: + Initialize COPYFD and pass to SMART_RENAME. + (copy_main) [defined (_WIN32) || defined (__CYGWIN32__)]: + Likewise. + * rename.c (try_preserve_permissions): New function. + (smart_rename): Use it and add new arguments. +--- + binutils/ChangeLog | 18 ++++++++++ + binutils/ar.c | 12 ++++++- + binutils/arsup.c | 14 +++++++- + binutils/bucomm.h | 3 +- + binutils/objcopy.c | 42 +++++++++++++++++----- + binutils/rename.c | 101 +++++++++++++++++++++++++++++++++++++---------------- + 6 files changed, 148 insertions(+), 42 deletions(-) + +diff --git a/binutils/ar.c b/binutils/ar.c +index 2253242..6598dd9 100644 +--- a/binutils/ar.c ++++ b/binutils/ar.c +@@ -1254,6 +1254,8 @@ write_archive (bfd *iarch) + char *old_name, *new_name; + bfd *contents_head = iarch->archive_next; + int ofd = -1; ++ struct stat target_stat; ++ bfd_boolean skip_stat = FALSE; + + old_name = (char *) xmalloc (strlen (bfd_get_filename (iarch)) + 1); + strcpy (old_name, bfd_get_filename (iarch)); +@@ -1299,6 +1301,14 @@ write_archive (bfd *iarch) + if (!bfd_set_archive_head (obfd, contents_head)) + bfd_fatal (old_name); + ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ ofd = dup (ofd); ++ if (iarch == NULL || iarch->iostream == NULL) ++ skip_stat = TRUE; ++ else if (ofd == -1 || fstat (fileno (iarch->iostream), &target_stat) != 0) ++ bfd_fatal (old_name); ++#endif ++ + if (!bfd_close (obfd)) + bfd_fatal (old_name); + +@@ -1308,7 +1318,7 @@ write_archive (bfd *iarch) + /* We don't care if this fails; we might be creating the archive. */ + bfd_close (iarch); + +- if (smart_rename (new_name, old_name, 0) != 0) ++ if (smart_rename (new_name, old_name, ofd, skip_stat ? NULL : &target_stat, 0) != 0) + xexit (1); + free (old_name); + free (new_name); +diff --git a/binutils/arsup.c b/binutils/arsup.c +index a668f27..8b4437f 100644 +--- a/binutils/arsup.c ++++ b/binutils/arsup.c +@@ -345,13 +345,25 @@ ar_save (void) + else + { + char *ofilename = xstrdup (bfd_get_filename (obfd)); ++ bfd_boolean skip_stat = FALSE; ++ struct stat target_stat; ++ int ofd = -1; + + if (deterministic > 0) + obfd->flags |= BFD_DETERMINISTIC_OUTPUT; + ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ /* It's OK to fail; at worst it will result in SMART_RENAME using a slow ++ copy fallback to write the output. */ ++ ofd = dup (fileno (obfd->iostream)); ++ if (lstat (real_name, &target_stat) != 0) ++ skip_stat = TRUE; ++#endif ++ + bfd_close (obfd); + +- smart_rename (ofilename, real_name, 0); ++ smart_rename (ofilename, real_name, ofd, ++ skip_stat ? NULL : &target_stat, 0); + obfd = 0; + free (ofilename); + } +diff --git a/binutils/bucomm.h b/binutils/bucomm.h +index afb8e09..9613b92 100644 +--- a/binutils/bucomm.h ++++ b/binutils/bucomm.h +@@ -71,7 +71,8 @@ extern void print_version (const char *); + /* In rename.c. */ + extern void set_times (const char *, const struct stat *); + +-extern int smart_rename (const char *, const char *, int); ++extern int smart_rename (const char *, const char *, int, struct stat *, int); ++ + + /* In libiberty. */ + void *xmalloc (size_t); +diff --git a/binutils/objcopy.c b/binutils/objcopy.c +index b6cf6ea..04ba95e 100644 +--- a/binutils/objcopy.c ++++ b/binutils/objcopy.c +@@ -4815,6 +4815,7 @@ strip_main (int argc, char *argv[]) + struct stat statbuf; + char *tmpname; + int tmpfd = -1; ++ int copyfd = -1; + + if (get_file_size (argv[i]) < 1) + { +@@ -4828,7 +4829,12 @@ strip_main (int argc, char *argv[]) + else + tmpname = output_file; + +- if (tmpname == NULL) ++ if (tmpname == NULL ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ /* Retain a copy of TMPFD since we will need it for SMART_RENAME. */ ++ || (tmpfd >= 0 && (copyfd = dup (tmpfd)) == -1) ++#endif ++ ) + { + bfd_nonfatal_message (argv[i], NULL, NULL, + _("could not create temporary file to hold stripped copy")); +@@ -4846,12 +4852,18 @@ strip_main (int argc, char *argv[]) + if (output_file != tmpname) + status = (smart_rename (tmpname, + output_file ? output_file : argv[i], +- preserve_dates) != 0); ++ copyfd, &statbuf, preserve_dates) != 0); + if (status == 0) + status = hold_status; + } + else +- unlink_if_ordinary (tmpname); ++ { ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ if (copyfd >= 0) ++ close (copyfd); ++#endif ++ unlink_if_ordinary (tmpname); ++ } + if (output_file != tmpname) + free (tmpname); + } +@@ -5059,6 +5071,7 @@ copy_main (int argc, char *argv[]) + bfd_boolean use_globalize = FALSE; + bfd_boolean use_keep_global = FALSE; + int c, tmpfd = -1; ++ int copyfd = -1; + struct stat statbuf; + const bfd_arch_info_type *input_arch = NULL; + +@@ -5903,9 +5916,16 @@ copy_main (int argc, char *argv[]) + else + tmpname = output_filename; + +- if (tmpname == NULL) +- fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), +- input_filename, strerror (errno)); ++ if (tmpname == NULL ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ /* Retain a copy of TMPFD since we will need it for SMART_RENAME. */ ++ || (tmpfd >= 0 && (copyfd = dup (tmpfd)) == -1) ++#endif ++ ) ++ { ++ fatal (_("warning: could not create temporary file whilst copying '%s', (error: %s)"), ++ input_filename, strerror (errno)); ++ } + + copy_file (input_filename, tmpname, tmpfd, &statbuf, input_target, + output_target, input_arch); +@@ -5914,11 +5934,17 @@ copy_main (int argc, char *argv[]) + if (preserve_dates) + set_times (tmpname, &statbuf); + if (tmpname != output_filename) +- status = (smart_rename (tmpname, input_filename, ++ status = (smart_rename (tmpname, input_filename, copyfd, &statbuf, + preserve_dates) != 0); + } + else +- unlink_if_ordinary (tmpname); ++ { ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++ if (copyfd >= 0) ++ close (copyfd); ++#endif ++ unlink_if_ordinary (tmpname); ++ } + + if (tmpname != output_filename) + free (tmpname); +diff --git a/binutils/rename.c b/binutils/rename.c +index bf3b68d..6b9165e 100644 +--- a/binutils/rename.c ++++ b/binutils/rename.c +@@ -131,17 +131,55 @@ set_times (const char *destination, const struct stat *statbuf) + #endif + #endif + +-/* Rename FROM to TO, copying if TO is a link. +- Return 0 if ok, -1 if error. */ ++#if !defined (_WIN32) || defined (__CYGWIN32__) ++/* Try to preserve the permission bits and ownership of an existing file when ++ rename overwrites it. FD is the file being renamed and TARGET_STAT has the ++ status of the file that was overwritten. */ ++static void ++try_preserve_permissions (int fd, struct stat *target_stat) ++{ ++ struct stat from_stat; ++ int ret = 0; ++ ++ if (fstat (fd, &from_stat) != 0) ++ return; ++ ++ int from_mode = from_stat.st_mode & 0777; ++ int to_mode = target_stat->st_mode & 0777; ++ ++ /* Fix up permissions before we potentially lose ownership with fchown. ++ Clear the setxid bits because in case the fchown below fails then we don't ++ want to end up with a sxid file owned by the invoking user. If the user ++ hasn't changed or if fchown succeeded, we add back the sxid bits at the ++ end. */ ++ if (from_mode != to_mode) ++ fchmod (fd, to_mode); ++ ++ /* Fix up ownership, this will clear the setxid bits. */ ++ if (from_stat.st_uid != target_stat->st_uid ++ || from_stat.st_gid != target_stat->st_gid) ++ ret = fchown (fd, target_stat->st_uid, target_stat->st_gid); ++ ++ /* Fix up the sxid bits if either the fchown wasn't needed or it ++ succeeded. */ ++ if (ret == 0) ++ fchmod (fd, target_stat->st_mode & 07777); ++} ++#endif ++ ++/* Rename FROM to TO, copying if TO is either a link or is not a regular file. ++ FD is an open file descriptor pointing to FROM that we can use to safely fix ++ up permissions of the file after renaming. TARGET_STAT has the file status ++ that is used to fix up permissions and timestamps after rename. Return 0 if ++ ok, -1 if error and FD is closed before returning. */ + + int +-smart_rename (const char *from, const char *to, int preserve_dates ATTRIBUTE_UNUSED) ++smart_rename (const char *from, const char *to, int fd ATTRIBUTE_UNUSED, ++ struct stat *target_stat ATTRIBUTE_UNUSED, ++ int preserve_dates ATTRIBUTE_UNUSED) + { +- bfd_boolean exists; +- struct stat s; + int ret = 0; +- +- exists = lstat (to, &s) == 0; ++ bfd_boolean exists = target_stat != NULL; + + #if defined (_WIN32) && !defined (__CYGWIN32__) + /* Win32, unlike unix, will not erase `to' in `rename(from, to)' but +@@ -158,36 +196,35 @@ smart_rename (const char *from, const char *to, int preserve_dates ATTRIBUTE_UNU + unlink (from); + } + #else +- /* Use rename only if TO is not a symbolic link and has +- only one hard link, and we have permission to write to it. */ ++ /* Avoid a full copy and use rename if we can fix up permissions of the ++ file after renaming, i.e.: ++ ++ - TO is not a symbolic link ++ - TO is a regular file with only one hard link ++ - We have permission to write to TO ++ - FD is available to safely fix up permissions to be the same as the file ++ we overwrote with the rename. ++ ++ Note though that the actual file on disk that TARGET_STAT describes may ++ have changed and we're only trying to preserve the status we know about. ++ At no point do we try to interact with the new file changes, so there can ++ only be two outcomes, i.e. either the external file change survives ++ without knowledge of our change (if it happens after the rename syscall) ++ or our rename and permissions fixup survive without any knowledge of the ++ external change. */ + if (! exists +- || (!S_ISLNK (s.st_mode) +- && S_ISREG (s.st_mode) +- && (s.st_mode & S_IWUSR) +- && s.st_nlink == 1) ++ || (fd >= 0 ++ && !S_ISLNK (target_stat->st_mode) ++ && S_ISREG (target_stat->st_mode) ++ && (target_stat->st_mode & S_IWUSR) ++ && target_stat->st_nlink == 1) + ) + { + ret = rename (from, to); + if (ret == 0) + { + if (exists) +- { +- /* Try to preserve the permission bits and ownership of +- TO. First get the mode right except for the setuid +- bit. Then change the ownership. Then fix the setuid +- bit. We do the chmod before the chown because if the +- chown succeeds, and we are a normal user, we won't be +- able to do the chmod afterward. We don't bother to +- fix the setuid bit first because that might introduce +- a fleeting security problem, and because the chown +- will clear the setuid bit anyhow. We only fix the +- setuid bit if the chown succeeds, because we don't +- want to introduce an unexpected setuid file owned by +- the user running objcopy. */ +- chmod (to, s.st_mode & 0777); +- if (chown (to, s.st_uid, s.st_gid) >= 0) +- chmod (to, s.st_mode & 07777); +- } ++ try_preserve_permissions (fd, target_stat); + } + else + { +@@ -203,9 +240,11 @@ smart_rename (const char *from, const char *to, int preserve_dates ATTRIBUTE_UNU + non_fatal (_("unable to copy file '%s'; reason: %s"), to, strerror (errno)); + + if (preserve_dates) +- set_times (to, &s); ++ set_times (to, target_stat); + unlink (from); + } ++ if (fd >= 0) ++ close (fd); + #endif /* _WIN32 && !__CYGWIN32__ */ + + return ret; +-- +1.8.3.1 + diff --git a/backport-0004-CVE-2021-20197.patch b/backport-0004-CVE-2021-20197.patch new file mode 100644 index 0000000000000000000000000000000000000000..39a16eca1cc8d59055863a4404d3446f321a34fd --- /dev/null +++ b/backport-0004-CVE-2021-20197.patch @@ -0,0 +1,193 @@ +From 95b91a043aeaeb546d2fea556d84a2de1e917770 Mon Sep 17 00:00:00 2001 +From: Alan Modra +Date: Mon, 1 Feb 2021 02:04:41 +1030 +Subject: [PATCH] pr27270 and pr27284, ar segfaults and wrong file mode + + PR 27270 + PR 27284 + PR 26945 + * ar.c: Don't include libbfd.h. + (write_archive): Replace xmalloc+strcpy with xstrdup. Use + bfd_stat rather than fstat on iostream. Move stat and fd tests + outside of _WIN32 ifdef. Delete skip_stat variable. + * arsup.c (temp_name, real_ofd): New static variables. + (ar_open): Use make_tempname and bfd_fdopenw. + (ar_save): Adjust to suit ar_open changes. Move stat output + of _WIN32 ifdef. + * objcopy.c: Don't include libbfd.h. + (copy_file): Use bfd_stat. +--- + binutils/ChangeLog | 16 ++++++++++++++++ + binutils/ar.c | 13 ++++--------- + binutils/arsup.c | 46 +++++++++++++++++++++++++++++----------------- + binutils/objcopy.c | 3 +-- + 4 files changed, 50 insertions(+), 28 deletions(-) + +diff --git a/binutils/ar.c b/binutils/ar.c +index 24ff092..0ecfa33 100644 +--- a/binutils/ar.c ++++ b/binutils/ar.c +@@ -25,7 +25,6 @@ + + #include "sysdep.h" + #include "bfd.h" +-#include "libbfd.h" + #include "libiberty.h" + #include "progress.h" + #include "getopt.h" +@@ -1255,10 +1254,8 @@ write_archive (bfd *iarch) + bfd *contents_head = iarch->archive_next; + int ofd = -1; + struct stat target_stat; +- bfd_boolean skip_stat = FALSE; + +- old_name = (char *) xmalloc (strlen (bfd_get_filename (iarch)) + 1); +- strcpy (old_name, bfd_get_filename (iarch)); ++ old_name = xstrdup (bfd_get_filename (iarch)); + new_name = make_tempname (old_name, &ofd); + + if (new_name == NULL) +@@ -1303,11 +1300,9 @@ write_archive (bfd *iarch) + + #if !defined (_WIN32) || defined (__CYGWIN32__) + ofd = dup (ofd); +- if (iarch == NULL || iarch->iostream == NULL) +- skip_stat = TRUE; +- else if (ofd == -1 || fstat (fileno ((FILE *) iarch->iostream), &target_stat) != 0) +- bfd_fatal (old_name); + #endif ++ if (ofd == -1 || bfd_stat (iarch, &target_stat) != 0) ++ bfd_fatal (old_name); + + if (!bfd_close (obfd)) + bfd_fatal (old_name); +@@ -1318,7 +1313,7 @@ write_archive (bfd *iarch) + /* We don't care if this fails; we might be creating the archive. */ + bfd_close (iarch); + +- if (smart_rename (new_name, old_name, ofd, skip_stat ? NULL : &target_stat, 0) != 0) ++ if (smart_rename (new_name, old_name, ofd, &target_stat, 0) != 0) + xexit (1); + free (old_name); + free (new_name); +diff --git a/binutils/arsup.c b/binutils/arsup.c +index 837011b..a60629f 100644 +--- a/binutils/arsup.c ++++ b/binutils/arsup.c +@@ -42,6 +42,8 @@ extern int deterministic; + + static bfd *obfd; + static char *real_name; ++static char *temp_name; ++static int real_ofd; + static FILE *outfile; + + static void +@@ -149,27 +151,24 @@ maybequit (void) + void + ar_open (char *name, int t) + { +- char *tname; +- const char *bname = lbasename (name); +- real_name = name; ++ real_name = xstrdup (name); ++ temp_name = make_tempname (real_name, &real_ofd); + +- /* Prepend tmp- to the beginning, to avoid file-name clashes after +- truncation on filesystems with limited namespaces (DOS). */ +- if (asprintf (&tname, "%.*stmp-%s", (int) (bname - name), name, bname) == -1) ++ if (temp_name == NULL) + { +- fprintf (stderr, _("%s: Can't allocate memory for temp name (%s)\n"), ++ fprintf (stderr, _("%s: Can't open temporary file (%s)\n"), + program_name, strerror(errno)); + maybequit (); + return; + } + +- obfd = bfd_openw (tname, NULL); ++ obfd = bfd_fdopenw (temp_name, NULL, real_ofd); + + if (!obfd) + { + fprintf (stderr, + _("%s: Can't open output archive %s\n"), +- program_name, tname); ++ program_name, temp_name); + + maybequit (); + } +@@ -344,10 +343,9 @@ ar_save (void) + } + else + { +- char *ofilename = xstrdup (bfd_get_filename (obfd)); + bfd_boolean skip_stat = FALSE; + struct stat target_stat; +- int ofd = -1; ++ int ofd = real_ofd; + + if (deterministic > 0) + obfd->flags |= BFD_DETERMINISTIC_OUTPUT; +@@ -355,17 +353,31 @@ ar_save (void) + #if !defined (_WIN32) || defined (__CYGWIN32__) + /* It's OK to fail; at worst it will result in SMART_RENAME using a slow + copy fallback to write the output. */ +- ofd = dup (fileno ((FILE *) obfd->iostream)); +- if (lstat (real_name, &target_stat) != 0) +- skip_stat = TRUE; ++ ofd = dup (ofd); + #endif +- + bfd_close (obfd); + +- smart_rename (ofilename, real_name, ofd, ++ if (lstat (real_name, &target_stat) != 0) ++ { ++ /* The temp file created in ar_open has mode 0600 as per mkstemp. ++ Create the real empty output file here so smart_rename will ++ update the mode according to the process umask. */ ++ obfd = bfd_openw (real_name, NULL); ++ if (obfd == NULL ++ || bfd_stat (obfd, &target_stat) != 0) ++ skip_stat = TRUE; ++ if (obfd != NULL) ++ { ++ bfd_set_format (obfd, bfd_archive); ++ bfd_close (obfd); ++ } ++ } ++ ++ smart_rename (temp_name, real_name, ofd, + skip_stat ? NULL : &target_stat, 0); + obfd = 0; +- free (ofilename); ++ free (temp_name); ++ free (real_name); + } + } + +diff --git a/binutils/objcopy.c b/binutils/objcopy.c +index 39a1ccb..0e1047e 100644 +--- a/binutils/objcopy.c ++++ b/binutils/objcopy.c +@@ -20,7 +20,6 @@ + + #include "sysdep.h" + #include "bfd.h" +-#include "libbfd.h" + #include "progress.h" + #include "getopt.h" + #include "libiberty.h" +@@ -3768,7 +3767,7 @@ copy_file (const char *input_filename, const char *output_filename, int ofd, + /* To allow us to do "strip *" without dying on the first + non-object file, failures are nonfatal. */ + ibfd = bfd_openr (input_filename, input_target); +- if (ibfd == NULL || fstat (fileno ((FILE *) ibfd->iostream), in_stat) != 0) ++ if (ibfd == NULL || bfd_stat (ibfd, in_stat) != 0) + { + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + status = 1; +-- +1.8.3.1 + diff --git a/backport-Fix-a-build-problem-when-using-FreeBSD-12.patch b/backport-Fix-a-build-problem-when-using-FreeBSD-12.patch new file mode 100644 index 0000000000000000000000000000000000000000..36671d64f8c68feeb6b315819bc7b765b666248b --- /dev/null +++ b/backport-Fix-a-build-problem-when-using-FreeBSD-12.patch @@ -0,0 +1,57 @@ +From b143e2d506bee1020752597f979d5af174edc36d Mon Sep 17 00:00:00 2001 +From: Sebastian Huber +Date: Fri, 11 Dec 2020 13:27:45 +0000 +Subject: [PATCH] Fix a build problem when using FreeBSD 12. + + * ar.c (write_archive): Cast iostream pointer to FILE *. + * arsup.c (ar_save): Likewise. + * objcopy.c (copy_file): Likewise. +--- + binutils/ChangeLog | 6 ++++++ + binutils/ar.c | 2 +- + binutils/arsup.c | 2 +- + binutils/objcopy.c | 2 +- + 4 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/binutils/ar.c b/binutils/ar.c +index 6598dd9..8329223 100644 +--- a/binutils/ar.c ++++ b/binutils/ar.c +@@ -1305,7 +1305,7 @@ write_archive (bfd *iarch) + ofd = dup (ofd); + if (iarch == NULL || iarch->iostream == NULL) + skip_stat = TRUE; +- else if (ofd == -1 || fstat (fileno (iarch->iostream), &target_stat) != 0) ++ else if (ofd == -1 || fstat (fileno ((FILE *) iarch->iostream), &target_stat) != 0) + bfd_fatal (old_name); + #endif + +diff --git a/binutils/arsup.c b/binutils/arsup.c +index 8b4437f..dad4174 100644 +--- a/binutils/arsup.c ++++ b/binutils/arsup.c +@@ -355,7 +355,7 @@ ar_save (void) + #if !defined (_WIN32) || defined (__CYGWIN32__) + /* It's OK to fail; at worst it will result in SMART_RENAME using a slow + copy fallback to write the output. */ +- ofd = dup (fileno (obfd->iostream)); ++ ofd = dup (fileno ((FILE *) obfd->iostream)); + if (lstat (real_name, &target_stat) != 0) + skip_stat = TRUE; + #endif +diff --git a/binutils/objcopy.c b/binutils/objcopy.c +index 06ecf3e..0ea3ea1 100644 +--- a/binutils/objcopy.c ++++ b/binutils/objcopy.c +@@ -3745,7 +3745,7 @@ copy_file (const char *input_filename, const char *output_filename, int ofd, + /* To allow us to do "strip *" without dying on the first + non-object file, failures are nonfatal. */ + ibfd = bfd_openr (input_filename, input_target); +- if (ibfd == NULL || fstat (fileno (ibfd->iostream), in_stat) != 0) ++ if (ibfd == NULL || fstat (fileno ((FILE *) ibfd->iostream), in_stat) != 0) + { + bfd_nonfatal_message (input_filename, NULL, NULL, NULL); + status = 1; +-- +1.8.3.1 + diff --git a/binutils.spec b/binutils.spec index 30092c0bfd63b186800a3e794676999030e59e29..5be388de9c8bfad9cd027fdc9f400a93faaccd4e 100644 --- a/binutils.spec +++ b/binutils.spec @@ -1,7 +1,7 @@ Summary: Binary utilities Name: binutils Version: 2.34 -Release: 8 +Release: 9 License: GPLv3+ URL: https://sourceware.org/binutils @@ -36,6 +36,11 @@ Patch19: metag-uninitialized-memory-read.patch Patch20: Fix-a-potential-use-of-an-uninitialised-value-in-the.patch Patch21: backport-CVE-2020-16592-PR25823-Use-after-free-in-bfd_hash_lookup.patch Patch22: backport-CVE-2020-0551-i386-Generate-lfence.patch +Patch23: backport-0001-CVE-2021-20197.patch +Patch24: backport-0002-CVE-2021-20197.patch +Patch25: backport-0003-CVE-2021-20197.patch +Patch26: backport-Fix-a-build-problem-when-using-FreeBSD-12.patch +Patch27: backport-0004-CVE-2021-20197.patch Provides: bundled(libiberty) @@ -314,6 +319,12 @@ fi %{_infodir}/bfd*info* %changelog +* Fri Apr 16 2021 lirui - 2.34-9 +- Type:CVE +- ID:NA +- SUG:NA +- DESC:fix CVE-2021-20197 + * Tue Mar 23 2021 panxiaohe - 2.34-8 - Type:CVE - ID:NA