From 6c1aee86666fdba70df8f7d90bdb116ff1e23a04 Mon Sep 17 00:00:00 2001 From: Than Ngo Date: Jul 29 2019 13:50:01 +0000 Subject: fixed #1733917, CVE-2019-13638 patch: OS shell command injection when processing crafted patch files --- diff --git a/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch b/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch new file mode 100644 index 0000000..257a300 --- /dev/null +++ b/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch @@ -0,0 +1,25 @@ +commit 9c986353e420ead6e706262bf204d6e03322c300 +Author: Andreas Gruenbacher +Date: Fri Aug 17 13:35:40 2018 +0200 + + Fix swapping fake lines in pch_swap + + * src/pch.c (pch_swap): Fix swapping p_bfake and p_efake when there is a + blank line in the middle of a context-diff hunk: that empty line stays + in the middle of the hunk and isn't swapped. + + Fixes: https://savannah.gnu.org/bugs/index.php?53133 + +diff --git a/src/pch.c b/src/pch.c +index e92bc64..a500ad9 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2122,7 +2122,7 @@ pch_swap (void) + } + if (p_efake >= 0) { /* fix non-freeable ptr range */ + if (p_efake <= i) +- n = p_end - i + 1; ++ n = p_end - p_ptrn_lines; + else + n = -i; + p_efake += n; diff --git a/patch-2.7.6-CVE-2018-6952.patch b/patch-2.7.6-CVE-2018-6952.patch deleted file mode 100644 index 4e93f93..0000000 --- a/patch-2.7.6-CVE-2018-6952.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/pch.c b/src/pch.c -index e92bc64..a500ad9 100644 ---- a/src/pch.c -+++ b/src/pch.c -@@ -2122,7 +2122,7 @@ pch_swap (void) - } - if (p_efake >= 0) { /* fix non-freeable ptr range */ - if (p_efake <= i) -- n = p_end - i + 1; -+ n = p_end - p_ptrn_lines; - else - n = -i; - p_efake += n; diff --git a/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch b/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch new file mode 100644 index 0000000..4a9f2d6 --- /dev/null +++ b/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch @@ -0,0 +1,33 @@ +commit 3fcd042d26d70856e826a42b5f93dc4854d80bf0 +Author: Andreas Gruenbacher +Date: Fri Apr 6 19:36:15 2018 +0200 + + Invoke ed directly instead of using the shell + + * src/pch.c (do_ed_script): Invoke ed directly instead of using a shell + command to avoid quoting vulnerabilities. + +diff --git a/src/pch.c b/src/pch.c +index 4fd5a05..16e001a 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2459,9 +2459,6 @@ do_ed_script (char const *inname, char const *outname, + *outname_needs_removal = true; + copy_file (inname, outname, 0, exclusive, instat.st_mode, true); + } +- sprintf (buf, "%s %s%s", editor_program, +- verbosity == VERBOSE ? "" : "- ", +- outname); + fflush (stdout); + + pid = fork(); +@@ -2470,7 +2467,8 @@ do_ed_script (char const *inname, char const *outname, + else if (pid == 0) + { + dup2 (tmpfd, 0); +- execl ("/bin/sh", "sh", "-c", buf, (char *) 0); ++ assert (outname[0] != '!' && outname[0] != '-'); ++ execlp (editor_program, editor_program, "-", outname, (char *) NULL); + _exit (2); + } + else diff --git a/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch b/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch new file mode 100644 index 0000000..34b8fb4 --- /dev/null +++ b/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch @@ -0,0 +1,28 @@ +commit b5a91a01e5d0897facdd0f49d64b76b0f02b43e1 +Author: Andreas Gruenbacher +Date: Fri Apr 6 11:34:51 2018 +0200 + + Allow input files to be missing for ed-style patches + + * src/pch.c (do_ed_script): Allow input files to be missing so that new + files will be created as with non-ed-style patches. + +diff --git a/src/pch.c b/src/pch.c +index bc6278c..0c5cc26 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2394,9 +2394,11 @@ do_ed_script (char const *inname, char const *outname, + + if (! dry_run && ! skip_rest_of_patch) { + int exclusive = *outname_needs_removal ? 0 : O_EXCL; +- assert (! inerrno); +- *outname_needs_removal = true; +- copy_file (inname, outname, 0, exclusive, instat.st_mode, true); ++ if (inerrno != ENOENT) ++ { ++ *outname_needs_removal = true; ++ copy_file (inname, outname, 0, exclusive, instat.st_mode, true); ++ } + sprintf (buf, "%s %s%s", editor_program, + verbosity == VERBOSE ? "" : "- ", + outname); diff --git a/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch b/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch new file mode 100644 index 0000000..39b59d1 --- /dev/null +++ b/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch @@ -0,0 +1,21 @@ +commit 15b158db3ae11cb835f2eb8d2eb48e09d1a4af48 +Author: Andreas Gruenbacher +Date: Mon Jul 15 19:10:02 2019 +0200 + + Avoid invalid memory access in context format diffs + + * src/pch.c (another_hunk): Avoid invalid memory access in context format + diffs. + +diff --git a/src/pch.c b/src/pch.c +index a500ad9..cb54e03 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -1328,6 +1328,7 @@ another_hunk (enum diff difftype, bool rev) + ptrn_prefix_context = context; + ptrn_suffix_context = context; + if (repl_beginning ++ || p_end <= 0 + || (p_end + != p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n'))) + { diff --git a/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch b/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch new file mode 100644 index 0000000..dd57034 --- /dev/null +++ b/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch @@ -0,0 +1,24 @@ +commit 3bbebbb29f6fbbf2988b9f2e75695b7c0b1f1c9b +Author: Andreas Gruenbacher +Date: Wed Feb 7 12:01:22 2018 +0100 + + Avoid set_file_attributes sign conversion warnings + + * src/util.c (set_file_attributes): Avoid sign conversion warnings when + assigning -1 to uid_t / gid_t. + +diff --git a/src/util.c b/src/util.c +index b1c7266..1cc08ba 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -256,8 +256,8 @@ set_file_attributes (char const *to, enum file_attributes attr, + } + if (attr & FA_IDS) + { +- static uid_t euid = -1; +- static gid_t egid = -1; ++ static uid_t euid = (uid_t)-1; ++ static gid_t egid = (gid_t)-1; + uid_t uid; + uid_t gid; + diff --git a/patch-2.7.6-avoid-warnings-gcc8.patch b/patch-2.7.6-avoid-warnings-gcc8.patch new file mode 100644 index 0000000..5cc3366 --- /dev/null +++ b/patch-2.7.6-avoid-warnings-gcc8.patch @@ -0,0 +1,85 @@ +commit ae81be0024ea4eaf139b7ba57e9a8ce9e4a163ec +Author: Jim Meyering +Date: Fri Apr 6 17:17:11 2018 -0700 + + maint: avoid warnings from GCC8 + + Hi Andreas, + + I configured with --enable-gcc-warnings and bleeding-edge gcc + (version 8.0.1 20180406) and hit some warning-escalated-to-errors. + This fixes them: + + >From a71ddb200dbe7ac0f9258796b5a51979b2740e88 Mon Sep 17 00:00:00 2001 + From: Jim Meyering + Date: Fri, 6 Apr 2018 16:47:00 -0700 + Subject: [PATCH] maint: avoid warnings from GCC8 + + * src/common.h (FALLTHROUGH): Define. + * src/patch.c (abort_hunk_context): Use FALLTHROUGH macro in place of + a comment. This avoids a warning from -Wimplicit-fallthrough=. + * src/pch.c (do_ed_script): Add otherwise unnecessary initialization + to avoid warning from -Wmaybe-uninitialized. + (another_hunk): Use FALLTHROUGH macro here, too, twice. + +diff --git a/src/common.h b/src/common.h +index ec50b40..904a3f8 100644 +--- a/src/common.h ++++ b/src/common.h +@@ -218,3 +218,11 @@ bool merge_hunk (int hunk, struct outstate *, lin where, bool *); + #else + # define merge_hunk(hunk, outstate, where, somefailed) false + #endif ++ ++#ifndef FALLTHROUGH ++# if __GNUC__ < 7 ++# define FALLTHROUGH ((void) 0) ++# else ++# define FALLTHROUGH __attribute__ ((__fallthrough__)) ++# endif ++#endif +diff --git a/src/patch.c b/src/patch.c +index 0fe6d72..1ae91d9 100644 +--- a/src/patch.c ++++ b/src/patch.c +@@ -1381,7 +1381,7 @@ abort_hunk_context (bool header, bool reverse) + break; + case ' ': case '-': case '+': case '!': + fprintf (rejfp, "%c ", pch_char (i)); +- /* fall into */ ++ FALLTHROUGH; + case '\n': + pch_write_line (i, rejfp); + break; +diff --git a/src/pch.c b/src/pch.c +index 1055542..cda3dfa 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -1735,7 +1735,7 @@ another_hunk (enum diff difftype, bool rev) + break; + case '=': + ch = ' '; +- /* FALL THROUGH */ ++ FALLTHROUGH; + case ' ': + if (fillsrc > p_ptrn_lines) { + free(s); +@@ -1756,7 +1756,7 @@ another_hunk (enum diff difftype, bool rev) + p_end = fillsrc-1; + return -1; + } +- /* FALL THROUGH */ ++ FALLTHROUGH; + case '+': + if (filldst > p_end) { + free(s); +@@ -2394,8 +2394,7 @@ do_ed_script (char const *inname, char const *outname, + size_t chars_read; + FILE *tmpfp = 0; + char const *tmpname; +- int tmpfd; +- pid_t pid; ++ int tmpfd = -1; /* placate gcc's -Wmaybe-uninitialized */ + int exclusive = *outname_needs_removal ? 0 : O_EXCL; + char const **ed_argv; + int stdin_dup, status; diff --git a/patch-2.7.6-check-of-return-value-of-fwrite.patch b/patch-2.7.6-check-of-return-value-of-fwrite.patch new file mode 100644 index 0000000..01242c0 --- /dev/null +++ b/patch-2.7.6-check-of-return-value-of-fwrite.patch @@ -0,0 +1,75 @@ +commit 1e9104c18019e7dc6b5590aea4b1d4f9d8ecfd56 +Author: Bruno Haible +Date: Sat Apr 7 12:21:04 2018 +0200 + + Fix check of return value of fwrite(). + + * src/patch.c (copy_till): Consider incomplete fwrite() write as an error. + * src/pch.c (pch_write_line, do_ed_script): Likewise. + +diff --git a/src/patch.c b/src/patch.c +index 1ae91d9..3fcaec5 100644 +--- a/src/patch.c ++++ b/src/patch.c +@@ -2,7 +2,7 @@ + + /* Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall + +- Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software ++ Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2018 Free Software + Foundation, Inc. + + This program is free software: you can redistribute it and/or modify +@@ -1641,7 +1641,7 @@ copy_till (struct outstate *outstate, lin lastline) + if (size) + { + if ((! outstate->after_newline && putc ('\n', fp) == EOF) +- || ! fwrite (s, sizeof *s, size, fp)) ++ || fwrite (s, sizeof *s, size, fp) < size) + write_fatal (); + outstate->after_newline = s[size - 1] == '\n'; + outstate->zero_output = false; +diff --git a/src/pch.c b/src/pch.c +index cda3dfa..79a3c99 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2279,8 +2279,11 @@ pfetch (lin line) + bool + pch_write_line (lin line, FILE *file) + { +- bool after_newline = (p_len[line] > 0) && (p_line[line][p_len[line] - 1] == '\n'); +- if (! fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file)) ++ bool after_newline = ++ (p_len[line] > 0) && (p_line[line][p_len[line] - 1] == '\n'); ++ ++ if (fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file) ++ < p_len[line]) + write_fatal (); + return after_newline; + } +@@ -2427,13 +2430,14 @@ do_ed_script (char const *inname, char const *outname, + ed_command_letter = get_ed_command_letter (buf); + if (ed_command_letter) { + if (tmpfp) +- if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) ++ if (fwrite (buf, sizeof *buf, chars_read, tmpfp) < chars_read) + write_fatal (); + if (ed_command_letter != 'd' && ed_command_letter != 's') { + p_pass_comments_through = true; + while ((chars_read = get_line ()) != 0) { + if (tmpfp) +- if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) ++ if (fwrite (buf, sizeof *buf, chars_read, tmpfp) ++ < chars_read) + write_fatal (); + if (chars_read == 2 && strEQ (buf, ".\n")) + break; +@@ -2448,7 +2452,7 @@ do_ed_script (char const *inname, char const *outname, + } + if (dry_run || skip_rest_of_patch) + return; +- if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0 ++ if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) < (size_t) 4 + || fflush (tmpfp) != 0) + write_fatal (); + diff --git a/patch-2.7.6-cleanups-in-do_ed_script.patch b/patch-2.7.6-cleanups-in-do_ed_script.patch new file mode 100644 index 0000000..1fab381 --- /dev/null +++ b/patch-2.7.6-cleanups-in-do_ed_script.patch @@ -0,0 +1,91 @@ +commit 2a32bf09f5e9572da4be183bb0dbde8164351474 +Author: Andreas Gruenbacher +Date: Fri Apr 6 20:32:46 2018 +0200 + + Minor cleanups in do_ed_script + + * src/pch.c (do_ed_script): Minor cleanups. + +diff --git a/src/pch.c b/src/pch.c +index 1f14624..1055542 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2396,6 +2396,10 @@ do_ed_script (char const *inname, char const *outname, + char const *tmpname; + int tmpfd; + pid_t pid; ++ int exclusive = *outname_needs_removal ? 0 : O_EXCL; ++ char const **ed_argv; ++ int stdin_dup, status; ++ + + if (! dry_run && ! skip_rest_of_patch) + { +@@ -2443,7 +2447,7 @@ do_ed_script (char const *inname, char const *outname, + break; + } + } +- if (!tmpfp) ++ if (dry_run || skip_rest_of_patch) + return; + if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0 + || fflush (tmpfp) != 0) +@@ -2452,36 +2456,29 @@ do_ed_script (char const *inname, char const *outname, + if (lseek (tmpfd, 0, SEEK_SET) == -1) + pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname)); + +- if (! dry_run && ! skip_rest_of_patch) { +- int exclusive = *outname_needs_removal ? 0 : O_EXCL; +- char const **ed_argv; +- int stdin_dup, status; +- ++ if (inerrno != ENOENT) ++ { + *outname_needs_removal = true; +- if (inerrno != ENOENT) +- { +- *outname_needs_removal = true; +- copy_file (inname, outname, 0, exclusive, instat.st_mode, true); +- } +- fflush (stdout); +- +- if ((stdin_dup = dup (0)) == -1 +- || dup2 (tmpfd, 0) == -1) +- pfatal ("Failed to duplicate standard input"); +- assert (outname[0] != '!' && outname[0] != '-'); +- ed_argv = alloca (4 * sizeof * ed_argv); +- ed_argv[0] = editor_program; +- ed_argv[1] = "-"; +- ed_argv[2] = outname; +- ed_argv[3] = (char *) NULL; +- status = execute (editor_program, editor_program, (char **)ed_argv, +- false, false, false, false, true, false, NULL); +- if (status) +- fatal ("%s FAILED", editor_program); +- if (dup2 (stdin_dup, 0) == -1 +- || close (stdin_dup) == -1) +- pfatal ("Failed to duplicate standard input"); +- } ++ copy_file (inname, outname, 0, exclusive, instat.st_mode, true); ++ } ++ fflush (stdout); ++ ++ if ((stdin_dup = dup (0)) == -1 ++ || dup2 (tmpfd, 0) == -1) ++ pfatal ("Failed to duplicate standard input"); ++ assert (outname[0] != '!' && outname[0] != '-'); ++ ed_argv = alloca (4 * sizeof * ed_argv); ++ ed_argv[0] = editor_program; ++ ed_argv[1] = "-"; ++ ed_argv[2] = outname; ++ ed_argv[3] = (char *) NULL; ++ status = execute (editor_program, editor_program, (char **)ed_argv, ++ false, false, false, false, true, false, NULL); ++ if (status) ++ fatal ("%s FAILED", editor_program); ++ if (dup2 (stdin_dup, 0) == -1 ++ || close (stdin_dup) == -1) ++ pfatal ("Failed to duplicate standard input"); + + fclose (tmpfp); + safe_unlink (tmpname); diff --git a/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch b/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch new file mode 100644 index 0000000..d0c7869 --- /dev/null +++ b/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch @@ -0,0 +1,95 @@ +commit 19599883ffb6a450d2884f081f8ecf68edbed7ee +Author: Jean Delvare +Date: Thu May 3 14:31:55 2018 +0200 + + Don't leak temporary file on failed ed-style patch + + Now that we write ed-style patches to a temporary file before we + apply them, we need to ensure that the temporary file is removed + before we leave, even on fatal error. + + * src/pch.c (do_ed_script): Use global TMPEDNAME instead of local + tmpname. Don't unlink the file directly, instead tag it for removal + at exit time. + * src/patch.c (cleanup): Unlink TMPEDNAME at exit. + + This closes bug #53820: + https://savannah.gnu.org/bugs/index.php?53820 + + Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)") + +diff --git a/src/common.h b/src/common.h +index 904a3f8..53c5e32 100644 +--- a/src/common.h ++++ b/src/common.h +@@ -94,10 +94,12 @@ XTERN char const *origsuff; + XTERN char const * TMPINNAME; + XTERN char const * TMPOUTNAME; + XTERN char const * TMPPATNAME; ++XTERN char const * TMPEDNAME; + + XTERN bool TMPINNAME_needs_removal; + XTERN bool TMPOUTNAME_needs_removal; + XTERN bool TMPPATNAME_needs_removal; ++XTERN bool TMPEDNAME_needs_removal; + + #ifdef DEBUGGING + XTERN int debug; +diff --git a/src/patch.c b/src/patch.c +index 3fcaec5..9146597 100644 +--- a/src/patch.c ++++ b/src/patch.c +@@ -1999,6 +1999,7 @@ cleanup (void) + remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal); + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); + remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); ++ remove_if_needed (TMPEDNAME, &TMPEDNAME_needs_removal); + remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); + output_files (NULL); + } +diff --git a/src/pch.c b/src/pch.c +index 79a3c99..1bb3153 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -2396,7 +2396,6 @@ do_ed_script (char const *inname, char const *outname, + file_offset beginning_of_this_line; + size_t chars_read; + FILE *tmpfp = 0; +- char const *tmpname; + int tmpfd = -1; /* placate gcc's -Wmaybe-uninitialized */ + int exclusive = *outname_needs_removal ? 0 : O_EXCL; + char const **ed_argv; +@@ -2411,12 +2410,13 @@ do_ed_script (char const *inname, char const *outname, + invalid commands and treats the next line as a new command, which + can lead to arbitrary command execution. */ + +- tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0); ++ tmpfd = make_tempfile (&TMPEDNAME, 'e', NULL, O_RDWR | O_BINARY, 0); + if (tmpfd == -1) +- pfatal ("Can't create temporary file %s", quotearg (tmpname)); ++ pfatal ("Can't create temporary file %s", quotearg (TMPEDNAME)); ++ TMPEDNAME_needs_removal = true; + tmpfp = fdopen (tmpfd, "w+b"); + if (! tmpfp) +- pfatal ("Can't open stream for file %s", quotearg (tmpname)); ++ pfatal ("Can't open stream for file %s", quotearg (TMPEDNAME)); + } + + for (;;) { +@@ -2457,7 +2457,7 @@ do_ed_script (char const *inname, char const *outname, + write_fatal (); + + if (lseek (tmpfd, 0, SEEK_SET) == -1) +- pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname)); ++ pfatal ("Can't rewind to the beginning of file %s", quotearg (TMPEDNAME)); + + if (inerrno != ENOENT) + { +@@ -2484,7 +2484,6 @@ do_ed_script (char const *inname, char const *outname, + pfatal ("Failed to duplicate standard input"); + + fclose (tmpfp); +- safe_unlink (tmpname); + + if (ofp) + { diff --git a/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch b/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch new file mode 100644 index 0000000..959a59c --- /dev/null +++ b/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch @@ -0,0 +1,71 @@ +commit 369dcccdfa6336e5a873d6d63705cfbe04c55727 +Author: Jean Delvare +Date: Mon May 7 15:14:45 2018 +0200 + + Don't leak temporary file on failed multi-file ed-style patch + + The previous fix worked fine with single-file ed-style patches, but + would still leak temporary files in the case of multi-file ed-style + patch. Fix that case as well, and extend the test case to check for + it. + + * src/patch.c (main): Unlink TMPEDNAME if needed before moving to + the next file in a patch. + + This closes bug #53820: + https://savannah.gnu.org/bugs/index.php?53820 + + Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)") + Fixes: 19599883ffb6 ("Don't leak temporary file on failed ed-style patch") + +diff --git a/src/patch.c b/src/patch.c +index 9146597..81c7a02 100644 +--- a/src/patch.c ++++ b/src/patch.c +@@ -236,6 +236,7 @@ main (int argc, char **argv) + } + remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); + } ++ remove_if_needed (TMPEDNAME, &TMPEDNAME_needs_removal); + + if (! skip_rest_of_patch && ! file_type) + { +diff --git a/tests/ed-style b/tests/ed-style +index 6b6ef9d..504e6e5 100644 +--- a/tests/ed-style ++++ b/tests/ed-style +@@ -38,3 +38,34 @@ EOF + check 'cat foo' < ed3.diff < baz < +Date: Sat Apr 7 12:34:03 2018 +0200 + + Fix 'ed-style' test failure. + + * tests/ed-style: Remove '?' line from expected output. + +diff --git a/tests/ed-style b/tests/ed-style +index d8c0689..6b6ef9d 100644 +--- a/tests/ed-style ++++ b/tests/ed-style +@@ -31,8 +31,7 @@ r !echo bar + ,p + EOF + +-check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' < /dev/null 2> /dev/null || echo "Status: $?"' < +Date: Wed Feb 7 17:05:00 2018 +0100 + + Test suite: fix Korn shell incompatibility + + tests/merge: In a Korn shell, shift apparently fails when $# is 0. + +diff --git a/tests/merge b/tests/merge +index b628891..e950b92 100644 +--- a/tests/merge ++++ b/tests/merge +@@ -32,7 +32,7 @@ x2() { + shift + done > b.sed + echo "$body" | sed -f b.sed > b +- shift ++ test $# -eq 0 || shift + while test $# -gt 0 ; do + echo "$1" + shift diff --git a/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch b/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch new file mode 100644 index 0000000..c5bcba5 --- /dev/null +++ b/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch @@ -0,0 +1,24 @@ +commit f290f48a621867084884bfff87f8093c15195e6a +Author: Andreas Gruenbacher +Date: Mon Feb 12 16:48:24 2018 +0100 + + Fix segfault with mangled rename patch + + http://savannah.gnu.org/bugs/?53132 + * src/pch.c (intuit_diff_type): Ensure that two filenames are specified + for renames and copies (fix the existing check). + +diff --git a/src/pch.c b/src/pch.c +index ff9ed2c..bc6278c 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -974,7 +974,8 @@ intuit_diff_type (bool need_header, mode_t *p_file_type) + if ((pch_rename () || pch_copy ()) + && ! inname + && ! ((i == OLD || i == NEW) && +- p_name[! reverse] && ++ p_name[reverse] && p_name[! reverse] && ++ name_is_valid (p_name[reverse]) && + name_is_valid (p_name[! reverse]))) + { + say ("Cannot %s file without two valid file names\n", pch_rename () ? "rename" : "copy"); diff --git a/patch-2.7.6-make-debug-output-more-useful.patch b/patch-2.7.6-make-debug-output-more-useful.patch new file mode 100644 index 0000000..69e79c2 --- /dev/null +++ b/patch-2.7.6-make-debug-output-more-useful.patch @@ -0,0 +1,40 @@ +commit ff81775f4eb6ab9a91b75e4031e8216654c0c76a +Author: Andreas Gruenbacher +Date: Fri Aug 17 10:31:22 2018 +0200 + + Make the (debug & 2) output more useful + + * src/pch.c (another_hunk): In the (debug & 2) output, fix how empty + lines that are not part of the patch context are printed. Also, add + newlines to lines that are missing them to keep the output readable. + +diff --git a/src/pch.c b/src/pch.c +index 1bb3153..e92bc64 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -1916,8 +1916,13 @@ another_hunk (enum diff difftype, bool rev) + lin i; + + for (i = 0; i <= p_end + 1; i++) { +- fprintf (stderr, "%s %c", +- format_linenum (numbuf0, i), ++ fputs (format_linenum (numbuf0, i), stderr); ++ if (p_Char[i] == '\n') ++ { ++ fputc('\n', stderr); ++ continue; ++ } ++ fprintf (stderr, " %c", + p_Char[i]); + if (p_Char[i] == '*') + fprintf (stderr, " %s,%s\n", +@@ -1930,7 +1935,8 @@ another_hunk (enum diff difftype, bool rev) + else if (p_Char[i] != '^') + { + fputs(" |", stderr); +- pch_write_line (i, stderr); ++ if (! pch_write_line (i, stderr)) ++ fputc('\n', stderr); + } + else + fputc('\n', stderr); diff --git a/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch b/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch new file mode 100644 index 0000000..e9738ad --- /dev/null +++ b/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch @@ -0,0 +1,20 @@ +commit a5b442ce01b80a758606ede316f739426a12bc33 +Author: Andreas Gruenbacher +Date: Thu Jun 27 11:09:31 2019 +0200 + + Skip "ed" test when the ed utility is not installed + + * tests/ed-style: Require ed. + +diff --git a/tests/ed-style b/tests/ed-style +index 504e6e5..9907cb6 100644 +--- a/tests/ed-style ++++ b/tests/ed-style +@@ -7,6 +7,7 @@ + . $srcdir/test-lib.sh + + require cat ++require ed + use_local_patch + use_tmpdir + diff --git a/patch-2.7.6-switch-from-fork-execlp-to-execute.patch b/patch-2.7.6-switch-from-fork-execlp-to-execute.patch new file mode 100644 index 0000000..4212552 --- /dev/null +++ b/patch-2.7.6-switch-from-fork-execlp-to-execute.patch @@ -0,0 +1,1774 @@ +diff -up patch-2.7.6/lib/execute.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/execute.c +--- patch-2.7.6/lib/execute.c.switch-from-fork-execlp-to-execute 2019-07-29 14:40:53.264464824 +0200 ++++ patch-2.7.6/lib/execute.c 2019-07-29 14:40:53.264464824 +0200 +@@ -0,0 +1,273 @@ ++/* Creation of autonomous subprocesses. ++ Copyright (C) 2001-2004, 2006-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2001. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++ ++#include ++ ++/* Specification. */ ++#include "execute.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "error.h" ++#include "fatal-signal.h" ++#include "wait-process.h" ++#include "gettext.h" ++ ++#define _(str) gettext (str) ++ ++#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ ++ ++/* Native Windows API. */ ++# include ++# include "w32spawn.h" ++ ++#else ++ ++/* Unix API. */ ++# include ++ ++#endif ++ ++ ++#if defined EINTR && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) ++ ++/* EINTR handling for close(), open(). ++ These functions can return -1/EINTR even though we don't have any ++ signal handlers set up, namely when we get interrupted via SIGSTOP. */ ++ ++static int ++nonintr_close (int fd) ++{ ++ int retval; ++ ++ do ++ retval = close (fd); ++ while (retval < 0 && errno == EINTR); ++ ++ return retval; ++} ++#define close nonintr_close ++ ++static int ++nonintr_open (const char *pathname, int oflag, mode_t mode) ++{ ++ int retval; ++ ++ do ++ retval = open (pathname, oflag, mode); ++ while (retval < 0 && errno == EINTR); ++ ++ return retval; ++} ++#undef open /* avoid warning on VMS */ ++#define open nonintr_open ++ ++#endif ++ ++ ++/* Execute a command, optionally redirecting any of the three standard file ++ descriptors to /dev/null. Return its exit code. ++ If it didn't terminate correctly, exit if exit_on_error is true, otherwise ++ return 127. ++ If slave_process is true, the child process will be terminated when its ++ creator receives a catchable fatal signal. */ ++int ++execute (const char *progname, ++ const char *prog_path, char **prog_argv, ++ bool ignore_sigpipe, ++ bool null_stdin, bool null_stdout, bool null_stderr, ++ bool slave_process, bool exit_on_error, ++ int *termsigp) ++{ ++#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ ++ ++ /* Native Windows API. */ ++ int orig_stdin; ++ int orig_stdout; ++ int orig_stderr; ++ int exitcode; ++ int nullinfd; ++ int nulloutfd; ++ ++ /* FIXME: Need to free memory allocated by prepare_spawn. */ ++ prog_argv = prepare_spawn (prog_argv); ++ ++ /* Save standard file handles of parent process. */ ++ if (null_stdin) ++ orig_stdin = dup_safer_noinherit (STDIN_FILENO); ++ if (null_stdout) ++ orig_stdout = dup_safer_noinherit (STDOUT_FILENO); ++ if (null_stderr) ++ orig_stderr = dup_safer_noinherit (STDERR_FILENO); ++ exitcode = -1; ++ ++ /* Create standard file handles of child process. */ ++ nullinfd = -1; ++ nulloutfd = -1; ++ if ((!null_stdin ++ || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0 ++ && (nullinfd == STDIN_FILENO ++ || (dup2 (nullinfd, STDIN_FILENO) >= 0 ++ && close (nullinfd) >= 0)))) ++ && (!(null_stdout || null_stderr) ++ || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0 ++ && (!null_stdout ++ || nulloutfd == STDOUT_FILENO ++ || dup2 (nulloutfd, STDOUT_FILENO) >= 0) ++ && (!null_stderr ++ || nulloutfd == STDERR_FILENO ++ || dup2 (nulloutfd, STDERR_FILENO) >= 0) ++ && ((null_stdout && nulloutfd == STDOUT_FILENO) ++ || (null_stderr && nulloutfd == STDERR_FILENO) ++ || close (nulloutfd) >= 0)))) ++ /* Use spawnvpe and pass the environment explicitly. This is needed if ++ the program has modified the environment using putenv() or [un]setenv(). ++ On Windows, programs have two environments, one in the "environment ++ block" of the process and managed through SetEnvironmentVariable(), and ++ one inside the process, in the location retrieved by the 'environ' ++ macro. When using spawnvp() without 'e', the child process inherits a ++ copy of the environment block - ignoring the effects of putenv() and ++ [un]setenv(). */ ++ { ++ exitcode = spawnvpe (P_WAIT, prog_path, (const char **) prog_argv, ++ (const char **) environ); ++ if (exitcode < 0 && errno == ENOEXEC) ++ { ++ /* prog is not a native executable. Try to execute it as a ++ shell script. Note that prepare_spawn() has already prepended ++ a hidden element "sh.exe" to prog_argv. */ ++ --prog_argv; ++ exitcode = spawnvpe (P_WAIT, prog_argv[0], (const char **) prog_argv, ++ (const char **) environ); ++ } ++ } ++ if (nulloutfd >= 0) ++ close (nulloutfd); ++ if (nullinfd >= 0) ++ close (nullinfd); ++ ++ /* Restore standard file handles of parent process. */ ++ if (null_stderr) ++ undup_safer_noinherit (orig_stderr, STDERR_FILENO); ++ if (null_stdout) ++ undup_safer_noinherit (orig_stdout, STDOUT_FILENO); ++ if (null_stdin) ++ undup_safer_noinherit (orig_stdin, STDIN_FILENO); ++ ++ if (termsigp != NULL) ++ *termsigp = 0; ++ ++ if (exitcode == -1) ++ { ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, errno, ++ _("%s subprocess failed"), progname); ++ return 127; ++ } ++ ++ return exitcode; ++ ++#else ++ ++ /* Unix API. */ ++ /* Note about 127: Some errors during posix_spawnp() cause the function ++ posix_spawnp() to return an error code; some other errors cause the ++ subprocess to exit with return code 127. It is implementation ++ dependent which error is reported which way. We treat both cases as ++ equivalent. */ ++ sigset_t blocked_signals; ++ posix_spawn_file_actions_t actions; ++ bool actions_allocated; ++ posix_spawnattr_t attrs; ++ bool attrs_allocated; ++ int err; ++ pid_t child; ++ ++ if (slave_process) ++ { ++ sigprocmask (SIG_SETMASK, NULL, &blocked_signals); ++ block_fatal_signals (); ++ } ++ actions_allocated = false; ++ attrs_allocated = false; ++ if ((err = posix_spawn_file_actions_init (&actions)) != 0 ++ || (actions_allocated = true, ++ (null_stdin ++ && (err = posix_spawn_file_actions_addopen (&actions, ++ STDIN_FILENO, ++ "/dev/null", O_RDONLY, ++ 0)) ++ != 0) ++ || (null_stdout ++ && (err = posix_spawn_file_actions_addopen (&actions, ++ STDOUT_FILENO, ++ "/dev/null", O_RDWR, ++ 0)) ++ != 0) ++ || (null_stderr ++ && (err = posix_spawn_file_actions_addopen (&actions, ++ STDERR_FILENO, ++ "/dev/null", O_RDWR, ++ 0)) ++ != 0) ++ || (slave_process ++ && ((err = posix_spawnattr_init (&attrs)) != 0 ++ || (attrs_allocated = true, ++ (err = posix_spawnattr_setsigmask (&attrs, ++ &blocked_signals)) ++ != 0 ++ || (err = posix_spawnattr_setflags (&attrs, ++ POSIX_SPAWN_SETSIGMASK)) ++ != 0))) ++ || (err = posix_spawnp (&child, prog_path, &actions, ++ attrs_allocated ? &attrs : NULL, prog_argv, ++ environ)) ++ != 0)) ++ { ++ if (actions_allocated) ++ posix_spawn_file_actions_destroy (&actions); ++ if (attrs_allocated) ++ posix_spawnattr_destroy (&attrs); ++ if (slave_process) ++ unblock_fatal_signals (); ++ if (termsigp != NULL) ++ *termsigp = 0; ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, err, ++ _("%s subprocess failed"), progname); ++ return 127; ++ } ++ posix_spawn_file_actions_destroy (&actions); ++ if (attrs_allocated) ++ posix_spawnattr_destroy (&attrs); ++ if (slave_process) ++ { ++ register_slave_subprocess (child); ++ unblock_fatal_signals (); ++ } ++ ++ return wait_subprocess (child, progname, ignore_sigpipe, null_stderr, ++ slave_process, exit_on_error, termsigp); ++ ++#endif ++} +diff -up patch-2.7.6/lib/execute.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/execute.h +--- patch-2.7.6/lib/execute.h.switch-from-fork-execlp-to-execute 2019-07-29 14:40:53.264464824 +0200 ++++ patch-2.7.6/lib/execute.h 2019-07-29 14:40:53.264464824 +0200 +@@ -0,0 +1,44 @@ ++/* Creation of autonomous subprocesses. ++ Copyright (C) 2001-2003, 2008-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2001. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _EXECUTE_H ++#define _EXECUTE_H ++ ++#include ++ ++/* Execute a command, optionally redirecting any of the three standard file ++ descriptors to /dev/null. Return its exit code. ++ If it didn't terminate correctly, exit if exit_on_error is true, otherwise ++ return 127. ++ If ignore_sigpipe is true, consider a subprocess termination due to SIGPIPE ++ as equivalent to a success. This is suitable for processes whose only ++ purpose is to write to standard output. ++ If slave_process is true, the child process will be terminated when its ++ creator receives a catchable fatal signal. ++ If termsigp is not NULL, *termsig will be set to the signal that terminated ++ the subprocess (if supported by the platform: not on native Windows ++ platforms), otherwise 0. ++ It is recommended that no signal is blocked or ignored while execute() ++ is called. See pipe.h for the reason. */ ++extern int execute (const char *progname, ++ const char *prog_path, char **prog_argv, ++ bool ignore_sigpipe, ++ bool null_stdin, bool null_stdout, bool null_stderr, ++ bool slave_process, bool exit_on_error, ++ int *termsigp); ++ ++#endif /* _EXECUTE_H */ +diff -up patch-2.7.6/lib/fatal-signal.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/fatal-signal.c +--- patch-2.7.6/lib/fatal-signal.c.switch-from-fork-execlp-to-execute 2019-07-29 14:51:00.441882754 +0200 ++++ patch-2.7.6/lib/fatal-signal.c 2019-07-29 14:51:00.441882754 +0200 +@@ -0,0 +1,286 @@ ++/* Emergency actions in case of a fatal signal. ++ Copyright (C) 2003-2004, 2006-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2003. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++ ++#include ++ ++/* Specification. */ ++#include "fatal-signal.h" ++ ++#include ++#include ++#include ++#include ++ ++#include "sig-handler.h" ++#include "xalloc.h" ++ ++#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) ++ ++/* ========================================================================= */ ++ ++ ++/* The list of fatal signals. ++ These are those signals whose default action is to terminate the process ++ without a core dump, except ++ SIGKILL - because it cannot be caught, ++ SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications ++ often use them for their own purpose, ++ SIGPROF SIGVTALRM - because they are used for profiling, ++ SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS, ++ SIGSYS - because it is more similar to SIGABRT, SIGSEGV, ++ SIGPWR - because it of too special use, ++ SIGRTMIN...SIGRTMAX - because they are reserved for application use. ++ plus ++ SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM. */ ++ ++static int fatal_signals[] = ++ { ++ /* ISO C 99 signals. */ ++#ifdef SIGINT ++ SIGINT, ++#endif ++#ifdef SIGTERM ++ SIGTERM, ++#endif ++ /* POSIX:2001 signals. */ ++#ifdef SIGHUP ++ SIGHUP, ++#endif ++#ifdef SIGPIPE ++ SIGPIPE, ++#endif ++ /* BSD signals. */ ++#ifdef SIGXCPU ++ SIGXCPU, ++#endif ++#ifdef SIGXFSZ ++ SIGXFSZ, ++#endif ++ /* Native Windows signals. */ ++#ifdef SIGBREAK ++ SIGBREAK, ++#endif ++ 0 ++ }; ++ ++#define num_fatal_signals (SIZEOF (fatal_signals) - 1) ++ ++/* Eliminate signals whose signal handler is SIG_IGN. */ ++ ++static void ++init_fatal_signals (void) ++{ ++ static bool fatal_signals_initialized = false; ++ if (!fatal_signals_initialized) ++ { ++ size_t i; ++ ++ for (i = 0; i < num_fatal_signals; i++) ++ { ++ struct sigaction action; ++ ++ if (sigaction (fatal_signals[i], NULL, &action) >= 0 ++ && get_handler (&action) == SIG_IGN) ++ fatal_signals[i] = -1; ++ } ++ ++ fatal_signals_initialized = true; ++ } ++} ++ ++ ++/* ========================================================================= */ ++ ++ ++typedef void (*action_t) (void); ++ ++/* Type of an entry in the actions array. ++ The 'action' field is accessed from within the fatal_signal_handler(), ++ therefore we mark it as 'volatile'. */ ++typedef struct ++{ ++ volatile action_t action; ++} ++actions_entry_t; ++ ++/* The registered cleanup actions. */ ++static actions_entry_t static_actions[32]; ++static actions_entry_t * volatile actions = static_actions; ++static sig_atomic_t volatile actions_count = 0; ++static size_t actions_allocated = SIZEOF (static_actions); ++ ++ ++/* The saved signal handlers. ++ Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34. */ ++static struct sigaction saved_sigactions[64]; ++ ++ ++/* Uninstall the handlers. */ ++static void ++uninstall_handlers (void) ++{ ++ size_t i; ++ ++ for (i = 0; i < num_fatal_signals; i++) ++ if (fatal_signals[i] >= 0) ++ { ++ int sig = fatal_signals[i]; ++ if (saved_sigactions[sig].sa_handler == SIG_IGN) ++ saved_sigactions[sig].sa_handler = SIG_DFL; ++ sigaction (sig, &saved_sigactions[sig], NULL); ++ } ++} ++ ++ ++/* The signal handler. It gets called asynchronously. */ ++static void ++fatal_signal_handler (int sig) ++{ ++ for (;;) ++ { ++ /* Get the last registered cleanup action, in a reentrant way. */ ++ action_t action; ++ size_t n = actions_count; ++ if (n == 0) ++ break; ++ n--; ++ actions_count = n; ++ action = actions[n].action; ++ /* Execute the action. */ ++ action (); ++ } ++ ++ /* Now execute the signal's default action. ++ If the signal being delivered was blocked, the re-raised signal would be ++ delivered when this handler returns. But the way we install this handler, ++ no signal is blocked, and the re-raised signal is delivered already ++ during raise(). */ ++ uninstall_handlers (); ++ raise (sig); ++} ++ ++ ++/* Install the handlers. */ ++static void ++install_handlers (void) ++{ ++ size_t i; ++ struct sigaction action; ++ ++ action.sa_handler = &fatal_signal_handler; ++ /* If we get a fatal signal while executing fatal_signal_handler, enter ++ fatal_signal_handler recursively, since it is reentrant. Hence no ++ SA_RESETHAND. */ ++ action.sa_flags = SA_NODEFER; ++ sigemptyset (&action.sa_mask); ++ for (i = 0; i < num_fatal_signals; i++) ++ if (fatal_signals[i] >= 0) ++ { ++ int sig = fatal_signals[i]; ++ ++ if (!(sig < sizeof (saved_sigactions) / sizeof (saved_sigactions[0]))) ++ abort (); ++ sigaction (sig, &action, &saved_sigactions[sig]); ++ } ++} ++ ++ ++/* Register a cleanup function to be executed when a catchable fatal signal ++ occurs. */ ++void ++at_fatal_signal (action_t action) ++{ ++ static bool cleanup_initialized = false; ++ if (!cleanup_initialized) ++ { ++ init_fatal_signals (); ++ install_handlers (); ++ cleanup_initialized = true; ++ } ++ ++ if (actions_count == actions_allocated) ++ { ++ /* Extend the actions array. Note that we cannot use xrealloc(), ++ because then the cleanup() function could access an already ++ deallocated array. */ ++ actions_entry_t *old_actions = actions; ++ size_t old_actions_allocated = actions_allocated; ++ size_t new_actions_allocated = 2 * actions_allocated; ++ actions_entry_t *new_actions = ++ XNMALLOC (new_actions_allocated, actions_entry_t); ++ size_t k; ++ ++ /* Don't use memcpy() here, because memcpy takes non-volatile arguments ++ and is therefore not guaranteed to complete all memory stores before ++ the next statement. */ ++ for (k = 0; k < old_actions_allocated; k++) ++ new_actions[k] = old_actions[k]; ++ actions = new_actions; ++ actions_allocated = new_actions_allocated; ++ /* Now we can free the old actions array. */ ++ if (old_actions != static_actions) ++ free (old_actions); ++ } ++ /* The two uses of 'volatile' in the types above (and ISO C 99 section ++ 5.1.2.3.(5)) ensure that we increment the actions_count only after ++ the new action has been written to the memory location ++ actions[actions_count]. */ ++ actions[actions_count].action = action; ++ actions_count++; ++} ++ ++ ++/* ========================================================================= */ ++ ++ ++static sigset_t fatal_signal_set; ++ ++static void ++init_fatal_signal_set (void) ++{ ++ static bool fatal_signal_set_initialized = false; ++ if (!fatal_signal_set_initialized) ++ { ++ size_t i; ++ ++ init_fatal_signals (); ++ ++ sigemptyset (&fatal_signal_set); ++ for (i = 0; i < num_fatal_signals; i++) ++ if (fatal_signals[i] >= 0) ++ sigaddset (&fatal_signal_set, fatal_signals[i]); ++ ++ fatal_signal_set_initialized = true; ++ } ++} ++ ++/* Temporarily delay the catchable fatal signals. */ ++void ++block_fatal_signals (void) ++{ ++ init_fatal_signal_set (); ++ sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL); ++} ++ ++/* Stop delaying the catchable fatal signals. */ ++void ++unblock_fatal_signals (void) ++{ ++ init_fatal_signal_set (); ++ sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); ++} +diff -up patch-2.7.6/lib/fatal-signal.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/fatal-signal.h +--- patch-2.7.6/lib/fatal-signal.h.switch-from-fork-execlp-to-execute 2019-07-29 14:51:09.977920729 +0200 ++++ patch-2.7.6/lib/fatal-signal.h 2019-07-29 14:51:09.977920729 +0200 +@@ -0,0 +1,76 @@ ++/* Emergency actions in case of a fatal signal. ++ Copyright (C) 2003-2004, 2009-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2003. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* It is often useful to do some cleanup action when a usually fatal signal ++ terminates the process, like removing a temporary file or killing a ++ subprocess that may be stuck waiting for a device, pipe or network input. ++ Such signals are SIGHUP, SIGINT, SIGPIPE, SIGTERM, and possibly others. ++ The limitation of this facility is that it cannot work for SIGKILL. ++ ++ Signals with a SIG_IGN handler are considered to be non-fatal. The ++ functions in this file assume that when a SIG_IGN handler is installed ++ for a signal, it was installed before any functions in this file were ++ called and it stays so for the whole lifetime of the process. */ ++ ++/* Register a cleanup function to be executed when a catchable fatal signal ++ occurs. ++ ++ Restrictions for the cleanup function: ++ - The cleanup function can do all kinds of system calls. ++ - It can also access application dependent memory locations and data ++ structures provided they are in a consistent state. One way to ensure ++ this is through block_fatal_signals()/unblock_fatal_signals(), see ++ below. Another - more tricky - way to ensure this is the careful use ++ of 'volatile'. ++ However, ++ - malloc() and similarly complex facilities are not safe to be called ++ because they are not guaranteed to be in a consistent state. ++ - Also, the cleanup function must not block the catchable fatal signals ++ and leave them blocked upon return. ++ ++ The cleanup function is executed asynchronously. It is unspecified ++ whether during its execution the catchable fatal signals are blocked ++ or not. */ ++extern void at_fatal_signal (void (*function) (void)); ++ ++ ++/* Sometimes it is necessary to block the usually fatal signals while the ++ data structures being accessed by the cleanup action are being built or ++ reorganized. This is the case, for example, when a temporary file or ++ directory is created through mkstemp() or mkdtemp(), because these ++ functions create the temporary file or directory _before_ returning its ++ name to the application. */ ++ ++/* Temporarily delay the catchable fatal signals. ++ The signals will be blocked (= delayed) until the next call to ++ unblock_fatal_signals(). If the signals are already blocked, a further ++ call to block_fatal_signals() has no effect. */ ++extern void block_fatal_signals (void); ++ ++/* Stop delaying the catchable fatal signals. */ ++extern void unblock_fatal_signals (void); ++ ++ ++#ifdef __cplusplus ++} ++#endif +diff -up patch-2.7.6/lib/gnulib.mk.switch-from-fork-execlp-to-execute patch-2.7.6/lib/gnulib.mk +--- patch-2.7.6/lib/gnulib.mk.switch-from-fork-execlp-to-execute 2018-02-03 14:31:15.000000000 +0100 ++++ patch-2.7.6/lib/gnulib.mk 2019-07-29 14:49:33.437536285 +0200 +@@ -21,7 +21,7 @@ + # the same distribution terms as the rest of that program. + # + # Generated by gnulib-tool. +-# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libpatch --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl argmatch backupfile clock-time diffseq dirname dup2 errno exitfail extensions faccessat fchmodat fchownat fcntl-h fstatat full-write getdate getopt-gnu gettime git-version-gen gitlog-to-changelog gnupload hash ignore-value intprops largefile linked-list maintainer-makefile malloc manywarnings memchr minmax mkdirat nstrftime openat progname quotearg readlinkat realloc renameat setenv signal size_max ssize_t stat-time stdbool stdlib symlinkat sys_stat tempname time unistd unlinkat update-copyright utimensat verror xalloc xlist xmemdup0 ++# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libpatch --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl argmatch backupfile clock-time diffseq dirname dup2 errno execute exitfail extensions faccessat fchmodat fchownat fcntl-h fstatat full-write getdate getopt-gnu gettime git-version-gen gitlog-to-changelog gnupload hash ignore-value intprops largefile linked-list maintainer-makefile malloc manywarnings memchr minmax mkdirat nstrftime openat progname quotearg readlinkat realloc renameat setenv signal size_max ssize_t stat-time stdbool stdlib symlinkat sys_stat tempname time unistd unlinkat update-copyright utimensat verror xalloc xlist xmemdup0 wait-process fatal-signal + + + MOSTLYCLEANFILES += core *.stackdump +@@ -378,6 +378,12 @@ EXTRA_libpatch_a_SOURCES += euidaccess.c + + ## end gnulib module euidaccess + ++## begin gnulib module execute ++ ++libpatch_a_SOURCES += execute.h execute.c w32spawn.h ++ ++## end gnulib module execute ++ + ## begin gnulib module exitfail + + libpatch_a_SOURCES += exitfail.c +@@ -2481,6 +2487,28 @@ libpatch_a_SOURCES += verror.h verror.c + + ## end gnulib module verror + ++## begin gnulib module wait-process ++ ++libpatch_a_SOURCES += wait-process.h wait-process.c ++ ++## end gnulib module wait-process ++ ++## begin gnulib module fatal-signal ++ ++libpatch_a_SOURCES += fatal-signal.h fatal-signal.c ++ ++## end gnulib module fatal-signal ++ ++## begin gnulib module sigaction ++ ++libpatch_a_SOURCES += sig-handler.c ++ ++EXTRA_DIST += sig-handler.h sigaction.c ++ ++EXTRA_libpatch_a_SOURCES += sigaction.c ++ ++## end gnulib module sigaction ++ + ## begin gnulib module wchar + + BUILT_SOURCES += wchar.h +@@ -2694,6 +2722,7 @@ EXTRA_libpatch_a_SOURCES += xmemdup0.c + + ## end gnulib module xmemdup0 + ++ + ## begin gnulib module xsize + + libpatch_a_SOURCES += xsize.h xsize.c +diff -up patch-2.7.6/lib/sigaction.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sigaction.c +--- patch-2.7.6/lib/sigaction.c.switch-from-fork-execlp-to-execute 2019-07-29 14:50:31.833768833 +0200 ++++ patch-2.7.6/lib/sigaction.c 2019-07-29 14:50:31.833768833 +0200 +@@ -0,0 +1,204 @@ ++/* POSIX compatible signal blocking. ++ Copyright (C) 2008-2018 Free Software Foundation, Inc. ++ Written by Eric Blake , 2008. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++/* Specification. */ ++#include ++ ++#include ++#include ++#include ++ ++/* This implementation of sigaction is tailored to native Windows behavior: ++ signal() has SysV semantics (ie. the handler is uninstalled before ++ it is invoked). This is an inherent data race if an asynchronous ++ signal is sent twice in a row before we can reinstall our handler, ++ but there's nothing we can do about it. Meanwhile, sigprocmask() ++ is not present, and while we can use the gnulib replacement to ++ provide critical sections, it too suffers from potential data races ++ in the face of an ill-timed asynchronous signal. And we compound ++ the situation by reading static storage in a signal handler, which ++ POSIX warns is not generically async-signal-safe. Oh well. ++ ++ Additionally: ++ - We don't implement SA_NOCLDSTOP or SA_NOCLDWAIT, because SIGCHLD ++ is not defined. ++ - We don't implement SA_ONSTACK, because sigaltstack() is not present. ++ - We ignore SA_RESTART, because blocking native Windows API calls are ++ not interrupted anyway when an asynchronous signal occurs, and the ++ MSVCRT runtime never sets errno to EINTR. ++ - We don't implement SA_SIGINFO because it is impossible to do so ++ portably. ++ ++ POSIX states that an application should not mix signal() and ++ sigaction(). We support the use of signal() within the gnulib ++ sigprocmask() substitute, but all other application code linked ++ with this module should stick with only sigaction(). */ ++ ++/* Check some of our assumptions. */ ++#if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT ++# error "Revisit the assumptions made in the sigaction module" ++#endif ++ ++/* Out-of-range substitutes make a good fallback for uncatchable ++ signals. */ ++#ifndef SIGKILL ++# define SIGKILL (-1) ++#endif ++#ifndef SIGSTOP ++# define SIGSTOP (-1) ++#endif ++ ++/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias ++ for the signal SIGABRT. Only one signal handler is stored for both ++ SIGABRT and SIGABRT_COMPAT. SIGABRT_COMPAT is not a signal of its own. */ ++#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ ++# undef SIGABRT_COMPAT ++# define SIGABRT_COMPAT 6 ++#endif ++ ++/* A signal handler. */ ++typedef void (*handler_t) (int signal); ++ ++/* Set of current actions. If sa_handler for an entry is NULL, then ++ that signal is not currently handled by the sigaction handler. */ ++static struct sigaction volatile action_array[NSIG] /* = 0 */; ++ ++/* Signal handler that is installed for signals. */ ++static void ++sigaction_handler (int sig) ++{ ++ handler_t handler; ++ sigset_t mask; ++ sigset_t oldmask; ++ int saved_errno = errno; ++ if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler) ++ { ++ /* Unexpected situation; be careful to avoid recursive abort. */ ++ if (sig == SIGABRT) ++ signal (SIGABRT, SIG_DFL); ++ abort (); ++ } ++ ++ /* Reinstall the signal handler when required; otherwise update the ++ bookkeeping so that the user's handler may call sigaction and get ++ accurate results. We know the signal isn't currently blocked, or ++ we wouldn't be in its handler, therefore we know that we are not ++ interrupting a sigaction() call. There is a race where any ++ asynchronous instance of the same signal occurring before we ++ reinstall the handler will trigger the default handler; oh ++ well. */ ++ handler = action_array[sig].sa_handler; ++ if ((action_array[sig].sa_flags & SA_RESETHAND) == 0) ++ signal (sig, sigaction_handler); ++ else ++ action_array[sig].sa_handler = NULL; ++ ++ /* Block appropriate signals. */ ++ mask = action_array[sig].sa_mask; ++ if ((action_array[sig].sa_flags & SA_NODEFER) == 0) ++ sigaddset (&mask, sig); ++ sigprocmask (SIG_BLOCK, &mask, &oldmask); ++ ++ /* Invoke the user's handler, then restore prior mask. */ ++ errno = saved_errno; ++ handler (sig); ++ saved_errno = errno; ++ sigprocmask (SIG_SETMASK, &oldmask, NULL); ++ errno = saved_errno; ++} ++ ++/* Change and/or query the action that will be taken on delivery of ++ signal SIG. If not NULL, ACT describes the new behavior. If not ++ NULL, OACT is set to the prior behavior. Return 0 on success, or ++ set errno and return -1 on failure. */ ++int ++sigaction (int sig, const struct sigaction *restrict act, ++ struct sigaction *restrict oact) ++{ ++ sigset_t mask; ++ sigset_t oldmask; ++ int saved_errno; ++ ++ if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP ++ || (act && act->sa_handler == SIG_ERR)) ++ { ++ errno = EINVAL; ++ return -1; ++ } ++ ++#ifdef SIGABRT_COMPAT ++ if (sig == SIGABRT_COMPAT) ++ sig = SIGABRT; ++#endif ++ ++ /* POSIX requires sigaction() to be async-signal-safe. In other ++ words, if an asynchronous signal can occur while we are anywhere ++ inside this function, the user's handler could then call ++ sigaction() recursively and expect consistent results. We meet ++ this rule by using sigprocmask to block all signals before ++ modifying any data structure that could be read from a signal ++ handler; this works since we know that the gnulib sigprocmask ++ replacement does not try to use sigaction() from its handler. */ ++ if (!act && !oact) ++ return 0; ++ sigfillset (&mask); ++ sigprocmask (SIG_BLOCK, &mask, &oldmask); ++ if (oact) ++ { ++ if (action_array[sig].sa_handler) ++ *oact = action_array[sig]; ++ else ++ { ++ /* Safe to change the handler at will here, since all ++ signals are currently blocked. */ ++ oact->sa_handler = signal (sig, SIG_DFL); ++ if (oact->sa_handler == SIG_ERR) ++ goto failure; ++ signal (sig, oact->sa_handler); ++ oact->sa_flags = SA_RESETHAND | SA_NODEFER; ++ sigemptyset (&oact->sa_mask); ++ } ++ } ++ ++ if (act) ++ { ++ /* Safe to install the handler before updating action_array, ++ since all signals are currently blocked. */ ++ if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN) ++ { ++ if (signal (sig, act->sa_handler) == SIG_ERR) ++ goto failure; ++ action_array[sig].sa_handler = NULL; ++ } ++ else ++ { ++ if (signal (sig, sigaction_handler) == SIG_ERR) ++ goto failure; ++ action_array[sig] = *act; ++ } ++ } ++ sigprocmask (SIG_SETMASK, &oldmask, NULL); ++ return 0; ++ ++ failure: ++ saved_errno = errno; ++ sigprocmask (SIG_SETMASK, &oldmask, NULL); ++ errno = saved_errno; ++ return -1; ++} +diff -up patch-2.7.6/lib/sig-handler.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sig-handler.c +--- patch-2.7.6/lib/sig-handler.c.switch-from-fork-execlp-to-execute 2019-07-29 14:50:17.265710820 +0200 ++++ patch-2.7.6/lib/sig-handler.c 2019-07-29 14:48:19.707242671 +0200 +@@ -0,0 +1,3 @@ ++#include ++#define SIG_HANDLER_INLINE _GL_EXTERN_INLINE ++#include "sig-handler.h" +diff -up patch-2.7.6/lib/sig-handler.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sig-handler.h +--- patch-2.7.6/lib/sig-handler.h.switch-from-fork-execlp-to-execute 2019-07-29 14:50:12.249690845 +0200 ++++ patch-2.7.6/lib/sig-handler.h 2019-07-29 14:48:23.099256180 +0200 +@@ -0,0 +1,54 @@ ++/* Convenience declarations when working with . ++ ++ Copyright (C) 2008-2018 Free Software Foundation, Inc. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_SIG_HANDLER_H ++#define _GL_SIG_HANDLER_H ++ ++#include ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++#ifndef SIG_HANDLER_INLINE ++# define SIG_HANDLER_INLINE _GL_INLINE ++#endif ++ ++/* Convenience type when working with signal handlers. */ ++typedef void (*sa_handler_t) (int); ++ ++/* Return the handler of a signal, as a sa_handler_t value regardless ++ of its true type. The resulting function can be compared to ++ special values like SIG_IGN but it is not portable to call it. */ ++SIG_HANDLER_INLINE sa_handler_t _GL_ATTRIBUTE_PURE ++get_handler (struct sigaction const *a) ++{ ++#ifdef SA_SIGINFO ++ /* POSIX says that special values like SIG_IGN can only occur when ++ action.sa_flags does not contain SA_SIGINFO. But in Linux 2.4, ++ for example, sa_sigaction and sa_handler are aliases and a signal ++ is ignored if sa_sigaction (after casting) equals SIG_IGN. So ++ use (and cast) sa_sigaction in that case. */ ++ if (a->sa_flags & SA_SIGINFO) ++ return (sa_handler_t) a->sa_sigaction; ++#endif ++ return a->sa_handler; ++} ++ ++_GL_INLINE_HEADER_END ++ ++#endif /* _GL_SIG_HANDLER_H */ +diff -up patch-2.7.6/lib/w32spawn.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/w32spawn.h +--- patch-2.7.6/lib/w32spawn.h.switch-from-fork-execlp-to-execute 2019-07-29 14:40:53.265464828 +0200 ++++ patch-2.7.6/lib/w32spawn.h 2019-07-29 14:40:53.265464828 +0200 +@@ -0,0 +1,233 @@ ++/* Auxiliary functions for the creation of subprocesses. Native Windows API. ++ Copyright (C) 2001, 2003-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2003. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef __KLIBC__ ++/* Get declarations of the native Windows API functions. */ ++# define WIN32_LEAN_AND_MEAN ++# include ++#endif ++ ++/* Get _open_osfhandle(). */ ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* Get _get_osfhandle(). */ ++# if GNULIB_MSVC_NOTHROW ++# include "msvc-nothrow.h" ++# else ++# include ++# endif ++ ++#include "cloexec.h" ++#include "xalloc.h" ++ ++/* Duplicates a file handle, making the copy uninheritable. ++ Returns -1 for a file handle that is equivalent to closed. */ ++static int ++dup_noinherit (int fd) ++{ ++ fd = dup_cloexec (fd); ++ if (fd < 0 && errno == EMFILE) ++ error (EXIT_FAILURE, errno, _("_open_osfhandle failed")); ++ ++ return fd; ++} ++ ++/* Returns a file descriptor equivalent to FD, except that the resulting file ++ descriptor is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. ++ FD must be open and non-inheritable. The result will be non-inheritable as ++ well. ++ If FD < 0, FD itself is returned. */ ++static int ++fd_safer_noinherit (int fd) ++{ ++ if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) ++ { ++ /* The recursion depth is at most 3. */ ++ int nfd = fd_safer_noinherit (dup_noinherit (fd)); ++ int saved_errno = errno; ++ close (fd); ++ errno = saved_errno; ++ return nfd; ++ } ++ return fd; ++} ++ ++/* Duplicates a file handle, making the copy uninheritable and ensuring the ++ result is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. ++ Returns -1 for a file handle that is equivalent to closed. */ ++static int ++dup_safer_noinherit (int fd) ++{ ++ return fd_safer_noinherit (dup_noinherit (fd)); ++} ++ ++/* Undoes the effect of TEMPFD = dup_safer_noinherit (ORIGFD); */ ++static void ++undup_safer_noinherit (int tempfd, int origfd) ++{ ++ if (tempfd >= 0) ++ { ++ if (dup2 (tempfd, origfd) < 0) ++ error (EXIT_FAILURE, errno, _("cannot restore fd %d: dup2 failed"), ++ origfd); ++ close (tempfd); ++ } ++ else ++ { ++ /* origfd was closed or open to no handle at all. Set it to a closed ++ state. This is (nearly) equivalent to the original state. */ ++ close (origfd); ++ } ++} ++ ++/* Prepares an argument vector before calling spawn(). ++ Note that spawn() does not by itself call the command interpreter ++ (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ++ ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); ++ GetVersionEx(&v); ++ v.dwPlatformId == VER_PLATFORM_WIN32_NT; ++ }) ? "cmd.exe" : "command.com"). ++ Instead it simply concatenates the arguments, separated by ' ', and calls ++ CreateProcess(). We must quote the arguments since Windows CreateProcess() ++ interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a ++ special way: ++ - Space and tab are interpreted as delimiters. They are not treated as ++ delimiters if they are surrounded by double quotes: "...". ++ - Unescaped double quotes are removed from the input. Their only effect is ++ that within double quotes, space and tab are treated like normal ++ characters. ++ - Backslashes not followed by double quotes are not special. ++ - But 2*n+1 backslashes followed by a double quote become ++ n backslashes followed by a double quote (n >= 0): ++ \" -> " ++ \\\" -> \" ++ \\\\\" -> \\" ++ - '*', '?' characters may get expanded through wildcard expansion in the ++ callee: By default, in the callee, the initialization code before main() ++ takes the result of GetCommandLine(), wildcard-expands it, and passes it ++ to main(). The exceptions to this rule are: ++ - programs that inspect GetCommandLine() and ignore argv, ++ - mingw programs that have a global variable 'int _CRT_glob = 0;', ++ - Cygwin programs, when invoked from a Cygwin program. ++ */ ++#ifndef __KLIBC__ ++# define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037*?" ++# define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" ++#else ++# define SHELL_SPECIAL_CHARS "" ++# define SHELL_SPACE_CHARS "" ++#endif ++static char ** ++prepare_spawn (char **argv) ++{ ++ size_t argc; ++ char **new_argv; ++ size_t i; ++ ++ /* Count number of arguments. */ ++ for (argc = 0; argv[argc] != NULL; argc++) ++ ; ++ ++ /* Allocate new argument vector. */ ++ new_argv = XNMALLOC (1 + argc + 1, char *); ++ ++ /* Add an element upfront that can be used when argv[0] turns out to be a ++ script, not a program. ++ On Unix, this would be "/bin/sh". On native Windows, "sh" is actually ++ "sh.exe". We have to omit the directory part and rely on the search in ++ PATH, because the mingw "mount points" are not visible inside Windows ++ CreateProcess(). */ ++ *new_argv++ = "sh.exe"; ++ ++ /* Put quoted arguments into the new argument vector. */ ++ for (i = 0; i < argc; i++) ++ { ++ const char *string = argv[i]; ++ ++ if (string[0] == '\0') ++ new_argv[i] = xstrdup ("\"\""); ++ else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) ++ { ++ bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); ++ size_t length; ++ unsigned int backslashes; ++ const char *s; ++ char *quoted_string; ++ char *p; ++ ++ length = 0; ++ backslashes = 0; ++ if (quote_around) ++ length++; ++ for (s = string; *s != '\0'; s++) ++ { ++ char c = *s; ++ if (c == '"') ++ length += backslashes + 1; ++ length++; ++ if (c == '\\') ++ backslashes++; ++ else ++ backslashes = 0; ++ } ++ if (quote_around) ++ length += backslashes + 1; ++ ++ quoted_string = (char *) xmalloc (length + 1); ++ ++ p = quoted_string; ++ backslashes = 0; ++ if (quote_around) ++ *p++ = '"'; ++ for (s = string; *s != '\0'; s++) ++ { ++ char c = *s; ++ if (c == '"') ++ { ++ unsigned int j; ++ for (j = backslashes + 1; j > 0; j--) ++ *p++ = '\\'; ++ } ++ *p++ = c; ++ if (c == '\\') ++ backslashes++; ++ else ++ backslashes = 0; ++ } ++ if (quote_around) ++ { ++ unsigned int j; ++ for (j = backslashes; j > 0; j--) ++ *p++ = '\\'; ++ *p++ = '"'; ++ } ++ *p = '\0'; ++ ++ new_argv[i] = quoted_string; ++ } ++ else ++ new_argv[i] = (char *) string; ++ } ++ new_argv[argc] = NULL; ++ ++ return new_argv; ++} +diff -up patch-2.7.6/lib/wait-process.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/wait-process.c +--- patch-2.7.6/lib/wait-process.c.switch-from-fork-execlp-to-execute 2019-07-29 14:50:49.937840928 +0200 ++++ patch-2.7.6/lib/wait-process.c 2019-07-29 14:45:17.196515863 +0200 +@@ -0,0 +1,361 @@ ++/* Waiting for a subprocess to finish. ++ Copyright (C) 2001-2003, 2005-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2001. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++ ++#include ++ ++/* Specification. */ ++#include "wait-process.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "error.h" ++#include "fatal-signal.h" ++#include "xalloc.h" ++#include "gettext.h" ++ ++#define _(str) gettext (str) ++ ++#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) ++ ++ ++#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ ++ ++# define WIN32_LEAN_AND_MEAN ++# include ++ ++/* The return value of spawnvp() is really a process handle as returned ++ by CreateProcess(). Therefore we can kill it using TerminateProcess. */ ++# define kill(pid,sig) TerminateProcess ((HANDLE) (pid), sig) ++ ++#endif ++ ++ ++/* Type of an entry in the slaves array. ++ The 'used' bit determines whether this entry is currently in use. ++ (If pid_t was an atomic type like sig_atomic_t, we could just set the ++ 'child' field to 0 when unregistering a slave process, and wouldn't need ++ the 'used' field.) ++ The 'used' and 'child' fields are accessed from within the cleanup_slaves() ++ action, therefore we mark them as 'volatile'. */ ++typedef struct ++{ ++ volatile sig_atomic_t used; ++ volatile pid_t child; ++} ++slaves_entry_t; ++ ++/* The registered slave subprocesses. */ ++static slaves_entry_t static_slaves[32]; ++static slaves_entry_t * volatile slaves = static_slaves; ++static sig_atomic_t volatile slaves_count = 0; ++static size_t slaves_allocated = SIZEOF (static_slaves); ++ ++/* The termination signal for slave subprocesses. ++ 2003-10-07: Terminator becomes Governator. */ ++#ifdef SIGHUP ++# define TERMINATOR SIGHUP ++#else ++# define TERMINATOR SIGTERM ++#endif ++ ++/* The cleanup action. It gets called asynchronously. */ ++static void ++cleanup_slaves (void) ++{ ++ for (;;) ++ { ++ /* Get the last registered slave. */ ++ size_t n = slaves_count; ++ if (n == 0) ++ break; ++ n--; ++ slaves_count = n; ++ /* Skip unused entries in the slaves array. */ ++ if (slaves[n].used) ++ { ++ pid_t slave = slaves[n].child; ++ ++ /* Kill the slave. */ ++ kill (slave, TERMINATOR); ++ } ++ } ++} ++ ++/* Register a subprocess as being a slave process. This means that the ++ subprocess will be terminated when its creator receives a catchable fatal ++ signal or exits normally. Registration ends when wait_subprocess() ++ notices that the subprocess has exited. */ ++void ++register_slave_subprocess (pid_t child) ++{ ++ static bool cleanup_slaves_registered = false; ++ if (!cleanup_slaves_registered) ++ { ++ atexit (cleanup_slaves); ++ at_fatal_signal (cleanup_slaves); ++ cleanup_slaves_registered = true; ++ } ++ ++ /* Try to store the new slave in an unused entry of the slaves array. */ ++ { ++ slaves_entry_t *s = slaves; ++ slaves_entry_t *s_end = s + slaves_count; ++ ++ for (; s < s_end; s++) ++ if (!s->used) ++ { ++ /* The two uses of 'volatile' in the slaves_entry_t type above ++ (and ISO C 99 section 5.1.2.3.(5)) ensure that we mark the ++ entry as used only after the child pid has been written to the ++ memory location s->child. */ ++ s->child = child; ++ s->used = 1; ++ return; ++ } ++ } ++ ++ if (slaves_count == slaves_allocated) ++ { ++ /* Extend the slaves array. Note that we cannot use xrealloc(), ++ because then the cleanup_slaves() function could access an already ++ deallocated array. */ ++ slaves_entry_t *old_slaves = slaves; ++ size_t new_slaves_allocated = 2 * slaves_allocated; ++ slaves_entry_t *new_slaves = ++ (slaves_entry_t *) ++ malloc (new_slaves_allocated * sizeof (slaves_entry_t)); ++ if (new_slaves == NULL) ++ { ++ /* xalloc_die() will call exit() which will invoke cleanup_slaves(). ++ Additionally we need to kill child, because it's not yet among ++ the slaves list. */ ++ kill (child, TERMINATOR); ++ xalloc_die (); ++ } ++ memcpy (new_slaves, old_slaves, ++ slaves_allocated * sizeof (slaves_entry_t)); ++ slaves = new_slaves; ++ slaves_allocated = new_slaves_allocated; ++ /* Now we can free the old slaves array. */ ++ if (old_slaves != static_slaves) ++ free (old_slaves); ++ } ++ /* The three uses of 'volatile' in the types above (and ISO C 99 section ++ 5.1.2.3.(5)) ensure that we increment the slaves_count only after the ++ new slave and its 'used' bit have been written to the memory locations ++ that make up slaves[slaves_count]. */ ++ slaves[slaves_count].child = child; ++ slaves[slaves_count].used = 1; ++ slaves_count++; ++} ++ ++/* Unregister a child from the list of slave subprocesses. */ ++static void ++unregister_slave_subprocess (pid_t child) ++{ ++ /* The easiest way to remove an entry from a list that can be used by ++ an asynchronous signal handler is just to mark it as unused. For this, ++ we rely on sig_atomic_t. */ ++ slaves_entry_t *s = slaves; ++ slaves_entry_t *s_end = s + slaves_count; ++ ++ for (; s < s_end; s++) ++ if (s->used && s->child == child) ++ s->used = 0; ++} ++ ++ ++/* Wait for a subprocess to finish. Return its exit code. ++ If it didn't terminate correctly, exit if exit_on_error is true, otherwise ++ return 127. */ ++int ++wait_subprocess (pid_t child, const char *progname, ++ bool ignore_sigpipe, bool null_stderr, ++ bool slave_process, bool exit_on_error, ++ int *termsigp) ++{ ++#if HAVE_WAITID && defined WNOWAIT && 0 ++ /* Commented out because waitid() without WEXITED and with WNOWAIT doesn't ++ work: On Solaris 7 and OSF/1 4.0, it returns -1 and sets errno = ECHILD, ++ and on HP-UX 10.20 it just hangs. */ ++ /* Use of waitid() with WNOWAIT avoids a race condition: If slave_process is ++ true, and this process sleeps a very long time between the return from ++ waitpid() and the execution of unregister_slave_subprocess(), and ++ meanwhile another process acquires the same PID as child, and then - still ++ before unregister_slave_subprocess() - this process gets a fatal signal, ++ it would kill the other totally unrelated process. */ ++ siginfo_t info; ++ ++ if (termsigp != NULL) ++ *termsigp = 0; ++ for (;;) ++ { ++ if (waitid (P_PID, child, &info, WEXITED | (slave_process ? WNOWAIT : 0)) ++ < 0) ++ { ++# ifdef EINTR ++ if (errno == EINTR) ++ continue; ++# endif ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, errno, ++ _("%s subprocess"), progname); ++ return 127; ++ } ++ ++ /* info.si_code is set to one of CLD_EXITED, CLD_KILLED, CLD_DUMPED, ++ CLD_TRAPPED, CLD_STOPPED, CLD_CONTINUED. Loop until the program ++ terminates. */ ++ if (info.si_code == CLD_EXITED ++ || info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED) ++ break; ++ } ++ ++ /* The child process has exited or was signalled. */ ++ ++ if (slave_process) ++ { ++ /* Unregister the child from the list of slave subprocesses, so that ++ later, when we exit, we don't kill a totally unrelated process which ++ may have acquired the same pid. */ ++ unregister_slave_subprocess (child); ++ ++ /* Now remove the zombie from the process list. */ ++ for (;;) ++ { ++ if (waitid (P_PID, child, &info, WEXITED) < 0) ++ { ++# ifdef EINTR ++ if (errno == EINTR) ++ continue; ++# endif ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, errno, ++ _("%s subprocess"), progname); ++ return 127; ++ } ++ break; ++ } ++ } ++ ++ switch (info.si_code) ++ { ++ case CLD_KILLED: ++ case CLD_DUMPED: ++ if (termsigp != NULL) ++ *termsigp = info.si_status; /* TODO: or info.si_signo? */ ++# ifdef SIGPIPE ++ if (info.si_status == SIGPIPE && ignore_sigpipe) ++ return 0; ++# endif ++ if (exit_on_error || (!null_stderr && termsigp == NULL)) ++ error (exit_on_error ? EXIT_FAILURE : 0, 0, ++ _("%s subprocess got fatal signal %d"), ++ progname, info.si_status); ++ return 127; ++ case CLD_EXITED: ++ if (info.si_status == 127) ++ { ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, 0, ++ _("%s subprocess failed"), progname); ++ return 127; ++ } ++ return info.si_status; ++ default: ++ abort (); ++ } ++#else ++ /* waitpid() is just as portable as wait() nowadays. */ ++ int status; ++ ++ if (termsigp != NULL) ++ *termsigp = 0; ++ status = 0; ++ for (;;) ++ { ++ int result = waitpid (child, &status, 0); ++ ++ if (result != child) ++ { ++# ifdef EINTR ++ if (errno == EINTR) ++ continue; ++# endif ++# if 0 /* defined ECHILD */ ++ if (errno == ECHILD) ++ { ++ /* Child process nonexistent?! Assume it terminated ++ successfully. */ ++ status = 0; ++ break; ++ } ++# endif ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, errno, ++ _("%s subprocess"), progname); ++ return 127; ++ } ++ ++ /* One of WIFSIGNALED (status), WIFEXITED (status), WIFSTOPPED (status) ++ must always be true, since we did not specify WCONTINUED in the ++ waitpid() call. Loop until the program terminates. */ ++ if (!WIFSTOPPED (status)) ++ break; ++ } ++ ++ /* The child process has exited or was signalled. */ ++ ++ if (slave_process) ++ /* Unregister the child from the list of slave subprocesses, so that ++ later, when we exit, we don't kill a totally unrelated process which ++ may have acquired the same pid. */ ++ unregister_slave_subprocess (child); ++ ++ if (WIFSIGNALED (status)) ++ { ++ if (termsigp != NULL) ++ *termsigp = WTERMSIG (status); ++# ifdef SIGPIPE ++ if (WTERMSIG (status) == SIGPIPE && ignore_sigpipe) ++ return 0; ++# endif ++ if (exit_on_error || (!null_stderr && termsigp == NULL)) ++ error (exit_on_error ? EXIT_FAILURE : 0, 0, ++ _("%s subprocess got fatal signal %d"), ++ progname, (int) WTERMSIG (status)); ++ return 127; ++ } ++ if (!WIFEXITED (status)) ++ abort (); ++ if (WEXITSTATUS (status) == 127) ++ { ++ if (exit_on_error || !null_stderr) ++ error (exit_on_error ? EXIT_FAILURE : 0, 0, ++ _("%s subprocess failed"), progname); ++ return 127; ++ } ++ return WEXITSTATUS (status); ++#endif ++} +diff -up patch-2.7.6/lib/wait-process.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/wait-process.h +--- patch-2.7.6/lib/wait-process.h.switch-from-fork-execlp-to-execute 2019-07-29 14:50:46.505827261 +0200 ++++ patch-2.7.6/lib/wait-process.h 2019-07-29 14:45:20.715529876 +0200 +@@ -0,0 +1,74 @@ ++/* Waiting for a subprocess to finish. ++ Copyright (C) 2001-2003, 2006, 2008-2018 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2001. ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _WAIT_PROCESS_H ++#define _WAIT_PROCESS_H ++ ++/* Get pid_t. */ ++#include ++#include ++#include ++ ++#include ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* Wait for a subprocess to finish. Return its exit code. ++ If it didn't terminate correctly, exit if exit_on_error is true, otherwise ++ return 127. ++ Arguments: ++ - child is the pid of the subprocess. ++ - progname is the name of the program executed by the subprocess, used for ++ error messages. ++ - If ignore_sigpipe is true, consider a subprocess termination due to ++ SIGPIPE as equivalent to a success. This is suitable for processes whose ++ only purpose is to write to standard output. This flag can be safely set ++ to false when the process' standard output is known to go to DEV_NULL. ++ - If null_stderr is true, the usual error message to stderr will be omitted. ++ This is suitable when the subprocess does not fulfill an important task. ++ - slave_process should be set to true if the process has been launched as a ++ slave process. ++ - If exit_on_error is true, any error will cause the main process to exit ++ with an error status. ++ - If termsigp is not NULL: *termsig will be set to the signal that ++ terminated the subprocess (if supported by the platform: not on native ++ Windows platforms), otherwise 0, and the error message about the signal ++ that terminated the subprocess will be omitted. ++ Prerequisites: The signal handler for SIGCHLD should not be set to SIG_IGN, ++ otherwise this function will not work. */ ++extern int wait_subprocess (pid_t child, const char *progname, ++ bool ignore_sigpipe, bool null_stderr, ++ bool slave_process, bool exit_on_error, ++ int *termsigp); ++ ++/* Register a subprocess as being a slave process. This means that the ++ subprocess will be terminated when its creator receives a catchable fatal ++ signal or exits normally. Registration ends when wait_subprocess() ++ notices that the subprocess has exited. */ ++extern void register_slave_subprocess (pid_t child); ++ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++#endif /* _WAIT_PROCESS_H */ +diff -up patch-2.7.6/src/pch.c.switch-from-fork-execlp-to-execute patch-2.7.6/src/pch.c +--- patch-2.7.6/src/pch.c.switch-from-fork-execlp-to-execute 2019-07-29 14:40:53.262464816 +0200 ++++ patch-2.7.6/src/pch.c 2019-07-29 15:01:10.338312098 +0200 +@@ -33,7 +33,8 @@ + # include + #endif + #include +-#include ++#include ++#include "execute.h" + + #define INITHUNKMAX 125 /* initial dynamic allocation size */ + +@@ -2453,6 +2463,9 @@ do_ed_script (char const *inname, char c + + if (! dry_run && ! skip_rest_of_patch) { + int exclusive = *outname_needs_removal ? 0 : O_EXCL; ++ char const **ed_argv; ++ int stdin_dup, status; ++ + *outname_needs_removal = true; + if (inerrno != ENOENT) + { +@@ -2461,24 +2474,22 @@ do_ed_script (char const *inname, char c + } + fflush (stdout); + +- pid = fork(); +- if (pid == -1) +- pfatal ("Can't fork"); +- else if (pid == 0) +- { +- dup2 (tmpfd, 0); +- assert (outname[0] != '!' && outname[0] != '-'); +- execlp (editor_program, editor_program, "-", outname, (char *) NULL); +- _exit (2); +- } +- else +- { +- int wstatus; +- if (waitpid (pid, &wstatus, 0) == -1 +- || ! WIFEXITED (wstatus) +- || WEXITSTATUS (wstatus) != 0) +- fatal ("%s FAILED", editor_program); +- } ++ if ((stdin_dup = dup (0)) == -1 ++ || dup2 (tmpfd, 0) == -1) ++ pfatal ("Failed to duplicate standard input"); ++ assert (outname[0] != '!' && outname[0] != '-'); ++ ed_argv = alloca (4 * sizeof * ed_argv); ++ ed_argv[0] = editor_program; ++ ed_argv[1] = "-"; ++ ed_argv[2] = outname; ++ ed_argv[3] = (char *) NULL; ++ status = execute (editor_program, editor_program, (char **)ed_argv, ++ false, false, false, false, true, false, NULL); ++ if (status) ++ fatal ("%s FAILED", editor_program); ++ if (dup2 (stdin_dup, 0) == -1 ++ || close (stdin_dup) == -1) ++ pfatal ("Failed to duplicate standard input"); + } + + fclose (tmpfp); diff --git a/patch-2.7.6-test-suite-compatibility-fixes.patch b/patch-2.7.6-test-suite-compatibility-fixes.patch new file mode 100644 index 0000000..ce3e36f --- /dev/null +++ b/patch-2.7.6-test-suite-compatibility-fixes.patch @@ -0,0 +1,124 @@ +commit f6bc5b14bd193859851d15a049bafb1007acd288 +Author: Andreas Gruenbacher +Date: Wed Feb 7 12:10:41 2018 +0100 + + Test suite compatibility fixes + + * tests/crlf-handling, tests/git-cleanup, tests/test-lib.sh: Use printf + instead of echo -e / echo -n for compatibility with systems that don't + support these echo options. + * tests/merge: Minor other cleanups. + +diff --git a/tests/crlf-handling b/tests/crlf-handling +index 239149c..c192cac 100644 +--- a/tests/crlf-handling ++++ b/tests/crlf-handling +@@ -14,7 +14,7 @@ use_local_patch + use_tmpdir + + lf2crlf() { +- while read l; do echo -e "$l\r"; done ++ while read l; do printf "%s\r\n" "$l"; done + } + + echo 1 > a +diff --git a/tests/git-cleanup b/tests/git-cleanup +index 2e3e4c6..ca527a1 100644 +--- a/tests/git-cleanup ++++ b/tests/git-cleanup +@@ -36,8 +36,8 @@ BAD PATCH + EOF + + echo 1 > f +-echo -n '' > g +-echo -n '' > h ++printf '' > g ++printf '' > h + + check 'patch -f -i 1.diff || echo status: $?' < a.sed +- echo "$body" | sed -f a.sed > b ++ done > b.sed ++ echo "$body" | sed -f b.sed > b + shift + while test $# -gt 0 ; do + echo "$1" + shift +- done > b.sed +- echo "$body" | sed -f b.sed > c +- rm -f a.sed b.sed ++ done > c.sed ++ echo "$body" | sed -f c.sed > c ++ rm -f b.sed c.sed + output=`diff -u a b | patch $ARGS -f c` + status=$? + echo "$output" | sed -e '/^$/d' -e '/^patching file c$/d' + cat c +- test $status == 0 || echo "Status: $status" ++ test $status = 0 || echo "Status: $status" + } + + x() { +- ARGS="$ARGS --merge" x2 "$@" ++ ARGS="--merge" x2 "$@" + echo +- ARGS="$ARGS --merge=diff3" x2 "$@" ++ ARGS="--merge=diff3" x2 "$@" + } + +-unset ARGS +- + # ============================================================== + + check 'x 3' <&3 ++ printf "\n\n" >&3 + gdbserver localhost:53153 $PATCH "$@" 2>&3 + else + $PATCH "$@" +@@ -113,22 +113,15 @@ cleanup() { + exit $status + } + +-if test -z "`echo -n`"; then +- if eval 'test -n "${BASH_LINENO[0]}" 2>/dev/null'; then +- eval ' +- _start_test() { +- echo -n "[${BASH_LINENO[2]}] $* -- " +- }' +- else +- eval ' +- _start_test() { +- echo -n "* $* -- " +- }' +- fi ++if eval 'test -n "${BASH_LINENO[0]}" 2>/dev/null'; then ++ eval ' ++ _start_test() { ++ printf "[${BASH_LINENO[2]}] %s -- " "$*" ++ }' + else + eval ' + _start_test() { +- echo "* $*" ++ printf "* %s -- " "$*" + }' + fi + diff --git a/patch-CVE-2018-1000156.patch b/patch-CVE-2018-1000156.patch index fc3b0c2..988964f 100644 --- a/patch-CVE-2018-1000156.patch +++ b/patch-CVE-2018-1000156.patch @@ -1,6 +1,19 @@ -diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c ---- patch-2.7.6/src/pch.c.CVE-2018-1000156 2018-02-03 12:41:49.000000000 +0000 -+++ patch-2.7.6/src/pch.c 2018-05-03 12:50:43.374036905 +0100 +commit 123eaff0d5d1aebe128295959435b9ca5909c26d +Author: Andreas Gruenbacher +Date: Fri Apr 6 12:14:49 2018 +0200 + + Fix arbitrary command execution in ed-style patches (CVE-2018-1000156) + + * src/pch.c (do_ed_script): Write ed script to a temporary file instead + of piping it to ed: this will cause ed to abort on invalid commands + instead of rejecting them and carrying on. + * tests/ed-style: New test case. + * tests/Makefile.am (TESTS): Add test case. + +diff --git a/src/pch.c b/src/pch.c +index 0c5cc26..4fd5a05 100644 +--- a/src/pch.c ++++ b/src/pch.c @@ -33,6 +33,7 @@ # include #endif @@ -9,7 +22,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c #define INITHUNKMAX 125 /* initial dynamic allocation size */ -@@ -2388,22 +2389,28 @@ do_ed_script (char const *inname, char c +@@ -2389,24 +2390,28 @@ do_ed_script (char const *inname, char const *outname, static char const editor_program[] = EDITOR_PROGRAM; file_offset beginning_of_this_line; @@ -17,30 +30,32 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c size_t chars_read; + FILE *tmpfp = 0; + char const *tmpname; -+ int tmpfd = -1; ++ int tmpfd; + pid_t pid; + + if (! dry_run && ! skip_rest_of_patch) + { -+ /* Write ed script to a temporary file. This causes ed to abort on -+ invalid commands such as when line numbers or ranges exceed the -+ number of available lines. When ed reads from a pipe, it rejects -+ invalid commands and treats the next line as a new command, which -+ can lead to arbitrary command execution. */ ++ /* Write ed script to a temporary file. This causes ed to abort on ++ invalid commands such as when line numbers or ranges exceed the ++ number of available lines. When ed reads from a pipe, it rejects ++ invalid commands and treats the next line as a new command, which ++ can lead to arbitrary command execution. */ + -+ tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0); -+ if (tmpfd == -1) -+ pfatal ("Can't create temporary file %s", quotearg (tmpname)); -+ tmpfp = fdopen (tmpfd, "w+b"); -+ if (! tmpfp) -+ pfatal ("Can't open stream for file %s", quotearg (tmpname)); ++ tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0); ++ if (tmpfd == -1) ++ pfatal ("Can't create temporary file %s", quotearg (tmpname)); ++ tmpfp = fdopen (tmpfd, "w+b"); ++ if (! tmpfp) ++ pfatal ("Can't open stream for file %s", quotearg (tmpname)); + } - if (! dry_run && ! skip_rest_of_patch) { - int exclusive = *outname_needs_removal ? 0 : O_EXCL; -- assert (! inerrno); -- *outname_needs_removal = true; -- copy_file (inname, outname, 0, exclusive, instat.st_mode, true); +- if (inerrno != ENOENT) +- { +- *outname_needs_removal = true; +- copy_file (inname, outname, 0, exclusive, instat.st_mode, true); +- } - sprintf (buf, "%s %s%s", editor_program, - verbosity == VERBOSE ? "" : "- ", - outname); @@ -52,7 +67,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c for (;;) { char ed_command_letter; beginning_of_this_line = file_tell (pfp); -@@ -2414,14 +2421,14 @@ do_ed_script (char const *inname, char c +@@ -2417,14 +2422,14 @@ do_ed_script (char const *inname, char const *outname, } ed_command_letter = get_ed_command_letter (buf); if (ed_command_letter) { @@ -71,7 +86,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c write_fatal (); if (chars_read == 2 && strEQ (buf, ".\n")) break; -@@ -2434,13 +2441,50 @@ do_ed_script (char const *inname, char c +@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname, break; } } @@ -90,47 +105,60 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c + pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname)); + + if (! dry_run && ! skip_rest_of_patch) { -+ int exclusive = *outname_needs_removal ? 0 : O_EXCL; -+ *outname_needs_removal = true; -+ if (inerrno != ENOENT) -+ { -+ *outname_needs_removal = true; -+ copy_file (inname, outname, 0, exclusive, instat.st_mode, true); -+ } -+ sprintf (buf, "%s %s%s", editor_program, -+ verbosity == VERBOSE ? "" : "- ", -+ outname); -+ fflush (stdout); ++ int exclusive = *outname_needs_removal ? 0 : O_EXCL; ++ *outname_needs_removal = true; ++ if (inerrno != ENOENT) ++ { ++ *outname_needs_removal = true; ++ copy_file (inname, outname, 0, exclusive, instat.st_mode, true); ++ } ++ sprintf (buf, "%s %s%s", editor_program, ++ verbosity == VERBOSE ? "" : "- ", ++ outname); ++ fflush (stdout); + -+ pid = fork(); -+ if (pid == -1) -+ pfatal ("Can't fork"); -+ else if (pid == 0) -+ { -+ dup2 (tmpfd, 0); -+ execl ("/bin/sh", "sh", "-c", buf, (char *) 0); -+ _exit (2); -+ } -+ else -+ { -+ int wstatus; -+ if (waitpid (pid, &wstatus, 0) == -1 -+ || ! WIFEXITED (wstatus) -+ || WEXITSTATUS (wstatus) != 0) -+ fatal ("%s FAILED", editor_program); -+ } ++ pid = fork(); ++ if (pid == -1) ++ pfatal ("Can't fork"); ++ else if (pid == 0) ++ { ++ dup2 (tmpfd, 0); ++ execl ("/bin/sh", "sh", "-c", buf, (char *) 0); ++ _exit (2); ++ } ++ else ++ { ++ int wstatus; ++ if (waitpid (pid, &wstatus, 0) == -1 ++ || ! WIFEXITED (wstatus) ++ || WEXITSTATUS (wstatus) != 0) ++ fatal ("%s FAILED", editor_program); ++ } + } + + fclose (tmpfp); -+ unlink (tmpname); -+ free((char*) tmpname); ++ safe_unlink (tmpname); if (ofp) { -diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style ---- patch-2.7.6/tests/ed-style.CVE-2018-1000156 2018-05-03 12:50:28.988937938 +0100 -+++ patch-2.7.6/tests/ed-style 2018-05-03 12:51:35.841397873 +0100 -@@ -0,0 +1,40 @@ +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 6b6df63..16f8693 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -32,6 +32,7 @@ TESTS = \ + crlf-handling \ + dash-o-append \ + deep-directories \ ++ ed-style \ + empty-files \ + false-match \ + fifo \ +diff --git a/tests/ed-style b/tests/ed-style +new file mode 100644 +index 0000000..d8c0689 +--- /dev/null ++++ b/tests/ed-style +@@ -0,0 +1,41 @@ +# Copyright (C) 2018 Free Software Foundation, Inc. +# +# Copying and distribution of this file, with or without modification, @@ -139,7 +167,7 @@ diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style + +. $srcdir/test-lib.sh + -+require_cat ++require cat +use_local_patch +use_tmpdir + @@ -164,46 +192,11 @@ diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style +,p +EOF + -+check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' < /dev/null || echo "Status: $?"' < - 2.7.6-11 +- fixed #1733917, CVE-2019-13638 patch: OS shell command injection when processing crafted patch files + * Wed Jul 24 2019 Than Ngo - 2.7.6-10 - backported patch, abort when cleaning up fails - backported patch, improve support for memory leak detection