diff options
Diffstat (limited to 'patches')
17 files changed, 1196 insertions, 0 deletions
diff --git a/patches/0001-Avoid-set_file_attributes-sign-conversion-warnings.patch b/patches/0001-Avoid-set_file_attributes-sign-conversion-warnings.patch new file mode 100644 index 0000000..d9931c6 --- /dev/null +++ b/patches/0001-Avoid-set_file_attributes-sign-conversion-warnings.patch @@ -0,0 +1,29 @@ +From 3bbebbb29f6fbbf2988b9f2e75695b7c0b1f1c9b Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Wed, 7 Feb 2018 12:01:22 +0100 +Subject: [PATCH 01/17] 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. +--- + src/util.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +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; + +-- +2.11.0 + diff --git a/patches/0002-Test-suite-compatibility-fixes.patch b/patches/0002-Test-suite-compatibility-fixes.patch new file mode 100644 index 0000000..425deb5 --- /dev/null +++ b/patches/0002-Test-suite-compatibility-fixes.patch @@ -0,0 +1,132 @@ +From f6bc5b14bd193859851d15a049bafb1007acd288 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Wed, 7 Feb 2018 12:10:41 +0100 +Subject: [PATCH 02/17] 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. +--- + tests/crlf-handling | 2 +- + tests/git-cleanup | 4 ++-- + tests/merge | 18 ++++++++---------- + tests/test-lib.sh | 21 +++++++-------------- + 4 files changed, 18 insertions(+), 27 deletions(-) + +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: $?' <<EOF + patching file f +diff --git a/tests/merge b/tests/merge +index 22d787b..b628891 100644 +--- a/tests/merge ++++ b/tests/merge +@@ -30,30 +30,28 @@ x2() { + while test $# -gt 0 && test "$1" != -- ; do + echo "$1" + shift +- done > 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' <<EOF +diff --git a/tests/test-lib.sh b/tests/test-lib.sh +index be0d7e3..661da52 100644 +--- a/tests/test-lib.sh ++++ b/tests/test-lib.sh +@@ -41,7 +41,7 @@ use_local_patch() { + + eval 'patch() { + if test -n "$GDB" ; then +- echo -e "\n" >&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 + +-- +2.11.0 + diff --git a/patches/0003-Test-suite-fix-Korn-shell-incompatibility.patch b/patches/0003-Test-suite-fix-Korn-shell-incompatibility.patch new file mode 100644 index 0000000..49c37db --- /dev/null +++ b/patches/0003-Test-suite-fix-Korn-shell-incompatibility.patch @@ -0,0 +1,26 @@ +From 074e2395f81d0ecaa66b71a6c228c70b49db72e5 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Wed, 7 Feb 2018 17:05:00 +0100 +Subject: [PATCH 03/17] Test suite: fix Korn shell incompatibility + +tests/merge: In a Korn shell, shift apparently fails when $# is 0. +--- + tests/merge | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +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 +-- +2.11.0 + diff --git a/patches/0004-Fix-segfault-with-mangled-rename-patch.patch b/patches/0004-Fix-segfault-with-mangled-rename-patch.patch new file mode 100644 index 0000000..c82df27 --- /dev/null +++ b/patches/0004-Fix-segfault-with-mangled-rename-patch.patch @@ -0,0 +1,29 @@ +From f290f48a621867084884bfff87f8093c15195e6a Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Mon, 12 Feb 2018 16:48:24 +0100 +Subject: [PATCH 04/17] 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). +--- + src/pch.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +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"); +-- +2.11.0 + diff --git a/patches/0005-Allow-input-files-to-be-missing-for-ed-style-patches.patch b/patches/0005-Allow-input-files-to-be-missing-for-ed-style-patches.patch new file mode 100644 index 0000000..c58d80f --- /dev/null +++ b/patches/0005-Allow-input-files-to-be-missing-for-ed-style-patches.patch @@ -0,0 +1,33 @@ +From b5a91a01e5d0897facdd0f49d64b76b0f02b43e1 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 6 Apr 2018 11:34:51 +0200 +Subject: [PATCH 05/17] 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. +--- + src/pch.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +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); +-- +2.11.0 + diff --git a/patches/0006-Fix-arbitrary-command-execution-in-ed-style-patches-.patch b/patches/0006-Fix-arbitrary-command-execution-in-ed-style-patches-.patch new file mode 100644 index 0000000..900484a --- /dev/null +++ b/patches/0006-Fix-arbitrary-command-execution-in-ed-style-patches-.patch @@ -0,0 +1,211 @@ +From 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 6 Apr 2018 12:14:49 +0200 +Subject: [PATCH 06/17] 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. +--- + src/pch.c | 91 ++++++++++++++++++++++++++++++++++++++++--------------- + tests/Makefile.am | 1 + + tests/ed-style | 41 +++++++++++++++++++++++++ + 3 files changed, 108 insertions(+), 25 deletions(-) + create mode 100644 tests/ed-style + +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 <io.h> + #endif + #include <safe.h> ++#include <sys/wait.h> + + #define INITHUNKMAX 125 /* initial dynamic allocation size */ + +@@ -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; +- FILE *pipefp = 0; + size_t chars_read; ++ FILE *tmpfp = 0; ++ char const *tmpname; ++ 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. */ ++ ++ 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; +- 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); +- pipefp = popen(buf, binary_transput ? "wb" : "w"); +- if (!pipefp) +- pfatal ("Can't open pipe to %s", quotearg (buf)); +- } + for (;;) { + char ed_command_letter; + beginning_of_this_line = file_tell (pfp); +@@ -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) { +- if (pipefp) +- if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) ++ if (tmpfp) ++ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) + write_fatal (); + if (ed_command_letter != 'd' && ed_command_letter != 's') { + p_pass_comments_through = true; + while ((chars_read = get_line ()) != 0) { +- if (pipefp) +- if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) ++ if (tmpfp) ++ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) + write_fatal (); + if (chars_read == 2 && strEQ (buf, ".\n")) + break; +@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname, + break; + } + } +- if (!pipefp) ++ if (!tmpfp) + return; +- if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0 +- || fflush (pipefp) != 0) ++ if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0 ++ || fflush (tmpfp) != 0) + write_fatal (); +- if (pclose (pipefp) != 0) +- fatal ("%s FAILED", editor_program); ++ ++ 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; ++ *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); ++ } ++ } ++ ++ fclose (tmpfp); ++ safe_unlink (tmpname); + + if (ofp) + { +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, ++# in any medium, are permitted without royalty provided the copyright ++# notice and this notice are preserved. ++ ++. $srcdir/test-lib.sh ++ ++require cat ++use_local_patch ++use_tmpdir ++ ++# ============================================================== ++ ++cat > ed1.diff <<EOF ++0a ++foo ++. ++EOF ++ ++check 'patch -e foo -i ed1.diff' <<EOF ++EOF ++ ++check 'cat foo' <<EOF ++foo ++EOF ++ ++cat > ed2.diff <<EOF ++1337a ++r !echo bar ++,p ++EOF ++ ++check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF ++? ++Status: 2 ++EOF ++ ++check 'cat foo' <<EOF ++foo ++EOF +-- +2.11.0 + diff --git a/patches/0007-Invoke-ed-directly-instead-of-using-the-shell.patch b/patches/0007-Invoke-ed-directly-instead-of-using-the-shell.patch new file mode 100644 index 0000000..4e04c85 --- /dev/null +++ b/patches/0007-Invoke-ed-directly-instead-of-using-the-shell.patch @@ -0,0 +1,38 @@ +From 3fcd042d26d70856e826a42b5f93dc4854d80bf0 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 6 Apr 2018 19:36:15 +0200 +Subject: [PATCH 07/17] 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. +--- + src/pch.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +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 +-- +2.11.0 + diff --git a/patches/0008-Use-gnulib-execute-module.patch b/patches/0008-Use-gnulib-execute-module.patch new file mode 100644 index 0000000..b609d41 --- /dev/null +++ b/patches/0008-Use-gnulib-execute-module.patch @@ -0,0 +1,114 @@ +From ff1d3a67da1e7f7af6a760ba5f0cee70763666da Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 6 Apr 2018 20:24:07 +0200 +Subject: [PATCH 08/17] Use gnulib execute module + +* bootstrap.conf (gnulib_modules): Add execute. +* src/pch.c (do_ed_script): Switch from fork + execlp to execute. +--- + bootstrap.conf | 1 + + m4/.gitignore | 13 +++++++++++++ + src/pch.c | 40 +++++++++++++++++++++------------------- + 3 files changed, 35 insertions(+), 19 deletions(-) + +diff --git a/bootstrap.conf b/bootstrap.conf +index 47255fa..7c49a98 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -25,6 +25,7 @@ diffseq + dirname + dup2 + errno ++execute + exitfail + extensions + faccessat +diff --git a/m4/.gitignore b/m4/.gitignore +index 9a94c0e..a84117a 100644 +--- a/m4/.gitignore ++++ b/m4/.gitignore +@@ -275,3 +275,16 @@ xvasprintf.m4 + /utime.m4 + /utime_h.m4 + /nstrftime.m4 ++/execute.m4 ++/fatal-signal.m4 ++/posix_spawn.m4 ++/rawmemchr.m4 ++/sched_h.m4 ++/sig_atomic_t.m4 ++/sigaction.m4 ++/signalblocking.m4 ++/spawn_h.m4 ++/strchrnul.m4 ++/sys_wait_h.m4 ++/wait-process.m4 ++/waitpid.m4 +diff --git a/src/pch.c b/src/pch.c +index 16e001a..1f14624 100644 +--- a/src/pch.c ++++ b/src/pch.c +@@ -33,7 +33,8 @@ + # include <io.h> + #endif + #include <safe.h> +-#include <sys/wait.h> ++#include <alloca.h> ++#include "execute.h" + + #define INITHUNKMAX 125 /* initial dynamic allocation size */ + +@@ -2453,6 +2454,9 @@ do_ed_script (char const *inname, char const *outname, + + 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 +2465,22 @@ do_ed_script (char const *inname, char const *outname, + } + 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); +-- +2.11.0 + diff --git a/patches/0009-Minor-cleanups-in-do_ed_script.patch b/patches/0009-Minor-cleanups-in-do_ed_script.patch new file mode 100644 index 0000000..57cec52 --- /dev/null +++ b/patches/0009-Minor-cleanups-in-do_ed_script.patch @@ -0,0 +1,96 @@ +From 2a32bf09f5e9572da4be183bb0dbde8164351474 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 6 Apr 2018 20:32:46 +0200 +Subject: [PATCH 09/17] Minor cleanups in do_ed_script + +* src/pch.c (do_ed_script): Minor cleanups. +--- + src/pch.c | 57 +++++++++++++++++++++++++++------------------------------ + 1 file changed, 27 insertions(+), 30 deletions(-) + +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); +-- +2.11.0 + diff --git a/patches/0010-maint-avoid-warnings-from-GCC8.patch b/patches/0010-maint-avoid-warnings-from-GCC8.patch new file mode 100644 index 0000000..e1f9e4e --- /dev/null +++ b/patches/0010-maint-avoid-warnings-from-GCC8.patch @@ -0,0 +1,92 @@ +From ae81be0024ea4eaf139b7ba57e9a8ce9e4a163ec Mon Sep 17 00:00:00 2001 +From: Jim Meyering <jim@meyering.net> +Date: Fri, 6 Apr 2018 17:17:11 -0700 +Subject: [PATCH 10/17] 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 <meyering@fb.com> +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. +--- + src/common.h | 8 ++++++++ + src/patch.c | 2 +- + src/pch.c | 7 +++---- + 3 files changed, 12 insertions(+), 5 deletions(-) + +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; +-- +2.11.0 + diff --git a/patches/0011-Fix-check-of-return-value-of-fwrite.patch b/patches/0011-Fix-check-of-return-value-of-fwrite.patch new file mode 100644 index 0000000..022a90d --- /dev/null +++ b/patches/0011-Fix-check-of-return-value-of-fwrite.patch @@ -0,0 +1,81 @@ +From 1e9104c18019e7dc6b5590aea4b1d4f9d8ecfd56 Mon Sep 17 00:00:00 2001 +From: Bruno Haible <bruno@clisp.org> +Date: Sat, 7 Apr 2018 12:21:04 +0200 +Subject: [PATCH 11/17] 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. +--- + src/patch.c | 4 ++-- + src/pch.c | 14 +++++++++----- + 2 files changed, 11 insertions(+), 7 deletions(-) + +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 (); + +-- +2.11.0 + diff --git a/patches/0012-Fix-ed-style-test-failure.patch b/patches/0012-Fix-ed-style-test-failure.patch new file mode 100644 index 0000000..2ab28df --- /dev/null +++ b/patches/0012-Fix-ed-style-test-failure.patch @@ -0,0 +1,27 @@ +From 458ac51a05426c1af9aa6bf1342ecf60728c19b4 Mon Sep 17 00:00:00 2001 +From: Bruno Haible <bruno@clisp.org> +Date: Sat, 7 Apr 2018 12:34:03 +0200 +Subject: [PATCH 12/17] Fix 'ed-style' test failure. + +* tests/ed-style: Remove '?' line from expected output. +--- + tests/ed-style | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +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: $?"' <<EOF +-? ++check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' <<EOF + Status: 2 + EOF + +-- +2.11.0 + diff --git a/patches/0013-Request-alloca-module-from-gnulib.patch b/patches/0013-Request-alloca-module-from-gnulib.patch new file mode 100644 index 0000000..0fc258c --- /dev/null +++ b/patches/0013-Request-alloca-module-from-gnulib.patch @@ -0,0 +1,33 @@ +From f322a7e0e55924e043b2d9b0d9249b86fb7c271a Mon Sep 17 00:00:00 2001 +From: Bruno Haible <bruno@clisp.org> +Date: Sat, 7 Apr 2018 12:39:03 +0200 +Subject: [PATCH 13/17] Request 'alloca' module from gnulib. + +* bootstrap.conf (gnulib_modules): Add 'alloca'. +--- + bootstrap.conf | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/bootstrap.conf b/bootstrap.conf +index 7c49a98..68cddd7 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -1,6 +1,6 @@ + # Bootstrap configuration. + +-# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc. ++# Copyright (C) 2006-2007, 2009-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 +@@ -18,6 +18,7 @@ + + # gnulib modules used by this package. + gnulib_modules=" ++alloca + argmatch + backupfile + clock-time +-- +2.11.0 + diff --git a/patches/0014-Don-t-leak-temporary-file-on-failed-ed-style-patch.patch b/patches/0014-Don-t-leak-temporary-file-on-failed-ed-style-patch.patch new file mode 100644 index 0000000..eeb9aab --- /dev/null +++ b/patches/0014-Don-t-leak-temporary-file-on-failed-ed-style-patch.patch @@ -0,0 +1,102 @@ +From 19599883ffb6a450d2884f081f8ecf68edbed7ee Mon Sep 17 00:00:00 2001 +From: Jean Delvare <jdelvare@suse.de> +Date: Thu, 3 May 2018 14:31:55 +0200 +Subject: [PATCH 14/17] 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)") +--- + src/common.h | 2 ++ + src/patch.c | 1 + + src/pch.c | 11 +++++------ + 3 files changed, 8 insertions(+), 6 deletions(-) + +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) + { +-- +2.11.0 + diff --git a/patches/0015-Don-t-leak-temporary-file-on-failed-multi-file-ed-st.patch b/patches/0015-Don-t-leak-temporary-file-on-failed-multi-file-ed-st.patch new file mode 100644 index 0000000..20df406 --- /dev/null +++ b/patches/0015-Don-t-leak-temporary-file-on-failed-multi-file-ed-st.patch @@ -0,0 +1,78 @@ +From 369dcccdfa6336e5a873d6d63705cfbe04c55727 Mon Sep 17 00:00:00 2001 +From: Jean Delvare <jdelvare@suse.de> +Date: Mon, 7 May 2018 15:14:45 +0200 +Subject: [PATCH 15/17] 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") +--- + src/patch.c | 1 + + tests/ed-style | 31 +++++++++++++++++++++++++++++++ + 2 files changed, 32 insertions(+) + +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' <<EOF + foo + EOF ++ ++# Test the case where one ed-style patch modifies several files ++ ++cat > ed3.diff <<EOF ++--- foo +++++ foo ++1c ++bar ++. ++--- baz +++++ baz ++0a ++baz ++. ++EOF ++ ++# Apparently we can't create a file with such a patch, while it works fine ++# when the file name is provided on the command line ++cat > baz <<EOF ++EOF ++ ++check 'patch -e -i ed3.diff' <<EOF ++EOF ++ ++check 'cat foo' <<EOF ++bar ++EOF ++ ++check 'cat baz' <<EOF ++baz ++EOF +-- +2.11.0 + diff --git a/patches/0016-Make-the-debug-2-output-more-useful.patch b/patches/0016-Make-the-debug-2-output-more-useful.patch new file mode 100644 index 0000000..c44d8fe --- /dev/null +++ b/patches/0016-Make-the-debug-2-output-more-useful.patch @@ -0,0 +1,45 @@ +From ff81775f4eb6ab9a91b75e4031e8216654c0c76a Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 17 Aug 2018 10:31:22 +0200 +Subject: [PATCH 16/17] 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. +--- + src/pch.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +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); +-- +2.11.0 + diff --git a/patches/0017-Fix-swapping-fake-lines-in-pch_swap.patch b/patches/0017-Fix-swapping-fake-lines-in-pch_swap.patch new file mode 100644 index 0000000..a41a102 --- /dev/null +++ b/patches/0017-Fix-swapping-fake-lines-in-pch_swap.patch @@ -0,0 +1,30 @@ +From 9c986353e420ead6e706262bf204d6e03322c300 Mon Sep 17 00:00:00 2001 +From: Andreas Gruenbacher <agruen@gnu.org> +Date: Fri, 17 Aug 2018 13:35:40 +0200 +Subject: [PATCH 17/17] 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 +--- + src/pch.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +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; +-- +2.11.0 + |