SHA256
6
0
forked from pool/patch

2 Commits

20 changed files with 81 additions and 901 deletions

View File

@@ -6,11 +6,11 @@ Date: Tue Jul 30 14:17:32 2024 +0200
see also: https://savannah.gnu.org/bugs/index.php?56683#comment1
diff --git a/src/pch.c b/src/pch.c
index fd9c480..57c76de 100644
--- a/src/pch.c
+++ b/src/pch.c
@@ -1183,8 +1183,11 @@ another_hunk (enum diff difftype, bool rev)
Index: patch-2.8/src/pch.c
===================================================================
--- patch-2.8.orig/src/pch.c
+++ patch-2.8/src/pch.c
@@ -1200,8 +1200,11 @@ another_hunk (enum diff difftype, bool r
while (p_end >= 0) {
if (p_end == p_efake)
p_end = p_bfake; /* don't free twice */
@@ -22,4 +22,4 @@ index fd9c480..57c76de 100644
+ }
p_end--;
}
assert(p_end == -1);
assert (p_end < 0);

View File

@@ -1,48 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 28 Jun 2019 00:30:25 +0200
Subject: Abort when cleaning up fails
Patch-mainline: Yes
Git-commit: b7b028a77bd855f6f56b17c8837fc1cca77b469d
References: boo#1111572 savannah#54845
When a fatal error triggers during cleanup, another attempt will be made to
clean up, which will likely lead to the same fatal error. So instead, bail out
when that happens.
src/patch.c (cleanup): Bail out when called recursively.
(main): There is no need to call output_files() before cleanup() as cleanup()
already does that.
---
src/patch.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
--- a/src/patch.c
+++ b/src/patch.c
@@ -681,7 +681,6 @@ main (int argc, char **argv)
}
if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
write_fatal ();
- output_files (NULL);
cleanup ();
delete_files ();
if (somefailed)
@@ -1977,7 +1976,6 @@ void
fatal_exit (int sig)
{
cleanup ();
-
if (sig)
exit_with_signal (sig);
@@ -1997,6 +1995,12 @@ remove_if_needed (char const *name, bool
static void
cleanup (void)
{
+ static bool already_cleaning_up;
+
+ if (already_cleaning_up)
+ return;
+ already_cleaning_up = true;
+
remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal);
remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal);
remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal);

View File

@@ -1,103 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Mon, 15 Jul 2019 16:21:48 +0200
Subject: Don't follow symlinks unless --follow-symlinks is given
Patch-mainline: Yes
Git-commit: dce4683cbbe107a95f1f0d45fabc304acfb5d71a
References: boo#1142041 CVE-2019-13636
* src/inp.c (plan_a, plan_b), src/util.c (copy_to_fd, copy_file,
append_to_file): Unless the --follow-symlinks option is given, open files with
the O_NOFOLLOW flag to avoid following symlinks. So far, we were only doing
that consistently for input files.
* src/util.c (create_backup): When creating empty backup files, (re)create them
with O_CREAT | O_EXCL to avoid following symlinks in that case as well.
---
src/inp.c | 12 ++++++++++--
src/util.c | 14 +++++++++++---
2 files changed, 21 insertions(+), 5 deletions(-)
--- a/src/inp.c
+++ b/src/inp.c
@@ -238,8 +238,13 @@ plan_a (char const *filename)
{
if (S_ISREG (instat.st_mode))
{
- int ifd = safe_open (filename, O_RDONLY|binary_transput, 0);
+ int flags = O_RDONLY | binary_transput;
size_t buffered = 0, n;
+ int ifd;
+
+ if (! follow_symlinks)
+ flags |= O_NOFOLLOW;
+ ifd = safe_open (filename, flags, 0);
if (ifd < 0)
pfatal ("can't open file %s", quotearg (filename));
@@ -340,6 +345,7 @@ plan_a (char const *filename)
static void
plan_b (char const *filename)
{
+ int flags = O_RDONLY | binary_transput;
int ifd;
FILE *ifp;
int c;
@@ -353,7 +359,9 @@ plan_b (char const *filename)
if (instat.st_size == 0)
filename = NULL_DEVICE;
- if ((ifd = safe_open (filename, O_RDONLY | binary_transput, 0)) < 0
+ if (! follow_symlinks)
+ flags |= O_NOFOLLOW;
+ if ((ifd = safe_open (filename, flags, 0)) < 0
|| ! (ifp = fdopen (ifd, binary_transput ? "rb" : "r")))
pfatal ("Can't open file %s", quotearg (filename));
if (TMPINNAME_needs_removal)
--- a/src/util.c
+++ b/src/util.c
@@ -388,7 +388,7 @@ create_backup (char const *to, const str
try_makedirs_errno = ENOENT;
safe_unlink (bakname);
- while ((fd = safe_open (bakname, O_CREAT | O_WRONLY | O_TRUNC, 0666)) < 0)
+ while ((fd = safe_open (bakname, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, 0666)) < 0)
{
if (errno != try_makedirs_errno)
pfatal ("Can't create file %s", quotearg (bakname));
@@ -579,10 +579,13 @@ create_file (char const *file, int open_
static void
copy_to_fd (const char *from, int tofd)
{
+ int from_flags = O_RDONLY | O_BINARY;
int fromfd;
ssize_t i;
- if ((fromfd = safe_open (from, O_RDONLY | O_BINARY, 0)) < 0)
+ if (! follow_symlinks)
+ from_flags |= O_NOFOLLOW;
+ if ((fromfd = safe_open (from, from_flags, 0)) < 0)
pfatal ("Can't reopen file %s", quotearg (from));
while ((i = read (fromfd, buf, bufsize)) != 0)
{
@@ -625,6 +628,8 @@ copy_file (char const *from, char const
else
{
assert (S_ISREG (mode));
+ if (! follow_symlinks)
+ to_flags |= O_NOFOLLOW;
tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode,
to_dir_known_to_exist);
copy_to_fd (from, tofd);
@@ -640,9 +645,12 @@ copy_file (char const *from, char const
void
append_to_file (char const *from, char const *to)
{
+ int to_flags = O_WRONLY | O_APPEND | O_BINARY;
int tofd;
- if ((tofd = safe_open (to, O_WRONLY | O_BINARY | O_APPEND, 0)) < 0)
+ if (! follow_symlinks)
+ to_flags |= O_NOFOLLOW;
+ if ((tofd = safe_open (to, to_flags, 0)) < 0)
pfatal ("Can't reopen file %s", quotearg (to));
copy_to_fd (from, tofd);
if (close (tofd) != 0)

View File

@@ -1,30 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 6 Apr 2018 11:34:51 +0200
Subject: Allow input files to be missing for ed-style patches
Patch-mainline: yes
Git-commit: b5a91a01e5d0897facdd0f49d64b76b0f02b43e1
References: bsc#1088420, savannah#53566, CVE-2018-1000156
* 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(-)
--- a/src/pch.c
+++ b/src/pch.c
@@ -2394,9 +2394,11 @@ do_ed_script (char const *inname, char c
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);

View File

@@ -1,201 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 6 Apr 2018 12:14:49 +0200
Subject: Fix arbitrary command execution in ed-style patches
Patch-mainline: yes
Git-commit: 123eaff0d5d1aebe128295959435b9ca5909c26d
References: bsc#1088420, savannah#53566, 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(-)
--- 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 c
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 c
}
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 c
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)
{
--- 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 \
--- /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

View File

@@ -1,35 +0,0 @@
From: Jean Delvare <jdelvare@suse.de>
Subject: Update tests/Makefile.in
Patch-mainline: no, temporary integration
References: bsc#1088420, savannah#53566, CVE-2018-1000156
Previous patch modifies tests/Makefile.am. Mirror the changes to
tests/Makefile.in so that we don't need automake.
---
tests/Makefile.in | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -1308,6 +1308,7 @@ TESTS = \
crlf-handling \
dash-o-append \
deep-directories \
+ ed-style \
empty-files \
false-match \
fifo \
@@ -1638,6 +1639,13 @@ deep-directories.log: deep-directories
$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
--log-file $$b.log --trs-file $$b.trs \
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+ed-style.log: ed-style
+ @p='ed-style'; \
+ b='ed-style'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
"$$tst" $(AM_TESTS_FD_REDIRECT)
empty-files.log: empty-files
@p='empty-files'; \

View File

@@ -1,35 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 6 Apr 2018 19:36:15 +0200
Subject: Invoke ed directly instead of using the shell
Git-commit: 3fcd042d26d70856e826a42b5f93dc4854d80bf0
Patch-mainline: yes
References: bsc#1088420, savannah#53566, CVE-2018-1000156
* 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(-)
--- a/src/pch.c
+++ b/src/pch.c
@@ -2459,9 +2459,6 @@ do_ed_script (char const *inname, char c
*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 c
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

View File

@@ -1,95 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 6 Apr 2018 20:32:46 +0200
Subject: Minor cleanups in do_ed_script
Git-commit: 2a32bf09f5e9572da4be183bb0dbde8164351474
Patch-mainline: yes
References: bsc#1088420, savannah#53566, CVE-2018-1000156
* src/pch.c (do_ed_script): Minor cleanups.
Backporting notes: adjusted because we don't have commit ff1d3a67da1e
("Use gnulib execute module") so the context is very different.
---
src/pch.c | 56 +++++++++++++++++++++++++++-----------------------------
1 file changed, 27 insertions(+), 29 deletions(-)
--- a/src/pch.c
+++ b/src/pch.c
@@ -2395,6 +2395,8 @@ do_ed_script (char const *inname, char c
char const *tmpname;
int tmpfd;
pid_t pid;
+ int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+
if (! dry_run && ! skip_rest_of_patch)
{
@@ -2442,7 +2444,7 @@ do_ed_script (char const *inname, char c
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)
@@ -2451,35 +2453,31 @@ do_ed_script (char const *inname, char c
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;
+ 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);
-
- 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);
- }
- }
+ copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+ }
+ 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);
+ }
fclose (tmpfp);
safe_unlink (tmpname);

View File

@@ -1,24 +0,0 @@
From: Bruno Haible <bruno@clisp.org>
Date: Sat, 7 Apr 2018 12:34:03 +0200
Subject: Fix 'ed-style' test failure
Git-commit: 458ac51a05426c1af9aa6bf1342ecf60728c19b4
Patch-mainline: yes
References: bsc#1088420, savannah#53566, CVE-2018-1000156
* tests/ed-style: Remove '?' line from expected output.
---
tests/ed-style | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
--- 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

View File

@@ -1,95 +0,0 @@
From: Jean Delvare <jdelvare@suse.de>
Date: Thu, 3 May 2018 14:31:55 +0200
Subject: Don't leak temporary file on failed ed-style patch
Git-commit: 19599883ffb6a450d2884f081f8ecf68edbed7ee
Patch-mainline: yes
References: bsc#1092500, savannah#53820
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(-)
--- 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;
--- 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);
}
--- a/src/pch.c
+++ b/src/pch.c
@@ -2392,7 +2392,6 @@ do_ed_script (char const *inname, char c
file_offset beginning_of_this_line;
size_t chars_read;
FILE *tmpfp = 0;
- char const *tmpname;
int tmpfd;
pid_t pid;
int exclusive = *outname_needs_removal ? 0 : O_EXCL;
@@ -2406,12 +2405,13 @@ do_ed_script (char const *inname, char c
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 (;;) {
@@ -2451,7 +2451,7 @@ do_ed_script (char const *inname, char c
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)
{
@@ -2480,7 +2480,6 @@ do_ed_script (char const *inname, char c
}
fclose (tmpfp);
- safe_unlink (tmpname);
if (ofp)
{

View File

@@ -1,72 +0,0 @@
From: Jean Delvare <jdelvare@suse.de>
Date: Mon, 7 May 2018 15:14:45 +0200
Subject: Don't leak temporary file on failed multi-file ed-style patch
Git-commit: 369dcccdfa6336e5a873d6d63705cfbe04c55727
Patch-mainline: yes
References: bsc#1092500, savannah#53820
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(+)
--- 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)
{
--- 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

View File

@@ -1,25 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Mon, 12 Feb 2018 16:48:24 +0100
Subject: Fix segfault with mangled rename patch
Patch-mainline: yes
Git-commit: f290f48a621867084884bfff87f8093c15195e6a
References: bsc#1080951, CVE-2018-6951, savannah#53133
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");

View File

@@ -1,27 +0,0 @@
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 17 Aug 2018 13:35:40 +0200
Subject: Fix swapping fake lines in pch_swap
Patch-mainline: Yes
Git-commit: 9c986353e420ead6e706262bf204d6e03322c300
References: boo#1080985 savannah#53133 CVE-2018-6952
* 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(-)
--- a/src/pch.c
+++ b/src/pch.c
@@ -2115,7 +2115,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;

View File

@@ -1,59 +0,0 @@
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 6 Apr 2022 10:48:35 +0200
Subject: Pass the correct stat to backup files
Patch-mainline: Yes
Git-commit: c835ecc67b7e37c0d0b7dd7e032209fdaa285808
References: boo#1198106 savananh#62364
The last case to call output_file() in the main loop is
output_file (outname, NULL, &tmpoutst, NULL, NULL,
file_type | 0, backup);
and this essentially means to create a backup file (where to=NULL)
only if backup=true, and does nothing else.
And, in the current code, the passed file stat (&tmpoutst) is a file
stat of the temporary file that has been processed, not the original
file (outname) to be backed up. When the backup is performed
immediately, this is no big problem. However, output_file() may
schedule the deferred handling, and the given file may be backed up at
a later point. The problem is that create_backup() tries to avoid the
backup of the same file twice, and it checks the given stat i-node
number in the hash list. Since it's a stat of a temporary file, the
same i-node number may be reused once a temp file is deleted and
another is created. This results in a false-positive detection of the
already existing file, eventually missing a backup file.
This patch attempts to address the issue:
- Modify the condition for better understanding, clearly indicating
that the code there is for creating a backup file
- Pass the stat of the original file instead of a temporary file
BugLink: https://bugzilla.opensuse.org/show_bug.cgi?id=1198106
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jean Delvare <jdelvare@suse.de>
---
src/patch.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
--- a/src/patch.c
+++ b/src/patch.c
@@ -611,9 +611,16 @@ main (int argc, char **argv)
output_file (NULL, NULL, NULL, inname, &instat,
mode, backup);
}
- else
- output_file (outname, NULL, &tmpoutst, NULL, NULL,
- file_type | 0, backup);
+ else if (backup)
+ {
+ struct stat outstat;
+
+ if (stat_file (outname, &outstat) != 0)
+ say ("Cannot stat file %s, skipping backup\n", outname);
+ else
+ output_file (outname, NULL, &outstat, NULL, NULL,
+ file_type | 0, true);
+ }
}
}
}

BIN
patch-2.7.6.tar.xz (Stored with Git LFS)

Binary file not shown.

View File

@@ -1,16 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJaed0dAAoJENW/n+sDE2U62ScQAK02GcPxJccBefkcuC6q/or9
f1im2lIpc1YJqxHmmhDeRu9twjuFycUV55hud+OroJe2xYKZrI6oUwJBldKTRfHu
whlhRzERO3U4z9pvi8XWbKvObsmqSBIgsM72oby4aPLCWk7IpJprR6BnRZdtnBg1
jzM3Yka8k01+dmVH2rsoSEGAe9sZbXJazBoYg8N/wHKe2+NY4W3esZ7flxQJ9RvB
GxjVU/KbyoNXIoFU4EnMalcLTZTHThhv2kQ1/cQZ+gt/1+f00DoieaUaIg3qB8jX
IqYE4GvXILgx8+REE3utt0zKv7pYGBNRkuACUE2hLZoY4SporJ0J63/7Y8zrzjxQ
GE27+DcjxBQGd1GnpO/Xb4kpqBGyn4KrlBIiHkhk2GgyBewpXPMog3cJki7A/1vz
Qb+JTY8PBqvOe7DmxW4Bp1vX6eOKn14FDQ7q3ZPjAd52Jtn7GUEt4etCduQh7ZNt
ElLLvpPro1wxG1bTbA3+TysCd+9XWWjwKJlPK5Jbdii0R73iy386UZGN1t1kmBzS
1mn3nh82z/XO9lPU3e1WP0BANAzTrNqA66ZbfQ9fIu6UO8R/+ygT7U5yie+X3xwP
kM6HR6oD0eDkqbPbOr8hey0kPG3FAWkZ47Oju5ad1ntUBFj4buLybEY0e08hncJS
gdt7wrbeKKxzdrcyQ1qy
=mjHo
-----END PGP SIGNATURE-----

3
patch-2.8.tar.xz Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f87cee69eec2b4fcbf60a396b030ad6aa3415f192aa5f7ee84cad5e11f7f5ae3
size 907208

16
patch-2.8.tar.xz.sig Normal file
View File

@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEJZs3krPW0xkhLMTc1b+f6wMTZToFAmfoJSwACgkQ1b+f6wMT
ZTolhg/9H4SnfU3oAfoVU//U5hCbTnFElWTNqfJNxlrgFb/aP64G61vcCm/deNVB
JeDxChxjGywwzAZh1GwMSXsZkKwZHlxwXqf/GDYesTUqbTeQOpk93WTmfW5ryY4G
5y7vPeE7BV3OHU4iLdDzLkal16eT537G+n2j1Nil4RwAELcnPa707dtNVaP5MEL8
E+zYY2EKFRzyJGk2i7a8lBGij8Qh2SG5OYdkjqlYxVFCx7/iVt4N2CDwRMJd549W
5eGg4EYrZ0lnKN6kyu2+DRPJQrTxrQOqh4wb6G/NDArrlH5HnpqVdCtuKQ8g49Lk
uAiDUkfF0Jg9/S8x6Rzvin2V9xm+SrwJ+Sknb7nSTG9+rvmUwqtaXPvtAgl0J8ya
2I7JLQWu46EKp0zH+uXn2bXumb105YbGcOHMtwGqrpmsnFCpNrmDlO+Vi450PY2n
zq6NUsIXwrgF4/ox2/UYEGYnwPXKM4N4QT2bey29mB98r9mLwVRuEauhRcYEe/CG
3L+x/17XuGNNPwlYTTcU5GWChkC0dK9rfN6PauIjfxGxI24LwnzCjT/fkiaYE8sQ
DnB7Ook5KSFotB6K+30LMXmLnNEbCnclCY+TqkrhOjiIkJy37fo5gx38tM2v/HSF
4LYZjeDupQfuwBfUN1axLYEgqd+TVSL1k0gigOWpYMulQNe29UI=
=uVYO
-----END PGP SIGNATURE-----

View File

@@ -1,3 +1,45 @@
-------------------------------------------------------------------
Sat Apr 12 19:05:33 UTC 2025 - Andreas Stieger <andreas.stieger@gmx.de>
- update to 2.8:
* The --follow-symlinks option now applies to output files as
well as input
* 'patch' now supports file timestamps after 2038 even on
traditional GNU/Linux platforms where time_t defaults to 32
bits.
* 'patch' no longer creates files with names containing
newlines, as encouraged by POSIX.1-2024.
* Patches can no longer contain NUL ('\0') bytes in diff
directive lines. These bytes would otherwise cause
unpredictable behavior.
* Patches can now contain sequences of spaces and tabs around
line numbers and in other places where POSIX requires support
for these sequences.
* --enable-gcc-warnings no longer uses expensive static checking.
Use --enable-gcc-warnings=expensive if you still want it.
* Fix undefined or ill-defined behavior in unusual cases, such as
very large sizes, possible stack overflow, I/O errors, memory
exhaustion, races with other processes, and signals arriving at
inopportune moments.
* Remove old "Plan B" code, designed for machines with 16-bit
pointers
* Assume C99 or later; previously it assumed C89 or later.
* Port to current GCC, Autoconf, Gnulib, etc.
- drop patches included pstream or obsolete:
* abort-when-cleaning-up-fails.patch
* dont-follow-symlinks-unless-asked.patch
* ed-style-01-missing-input-files.patch
* ed-style-02-fix-arbitrary-command-execution.patch
* ed-style-03-update-test-Makefile.patch
* ed-style-04-invoke-ed-directly.patch
* ed-style-05-minor-cleanups.patch
* ed-style-06-fix-test-failure.patch
* ed-style-07-dont-leak-tmp-file.patch
* ed-style-08-dont-leak-tmp-file-multi.patch
* fix-segfault-mangled-rename.patch
* fix-swapping-fake-lines-in-pch_swap.patch
* pass-the-correct-stat-to-backup-files.patch
-------------------------------------------------------------------
Tue Jul 30 12:19:54 UTC 2024 - Wolfgang Frisch <wolfgang.frisch@suse.com>

View File

@@ -2,6 +2,7 @@
# spec file for package patch
#
# Copyright (c) 2024 SUSE LLC
# Copyright (c) 2025 Andreas Stieger <Andreas.Stieger@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,37 +18,21 @@
Name: patch
Version: 2.7.6
Version: 2.8
Release: 0
Summary: GNU patch
License: GPL-3.0-or-later
Group: Productivity/Text/Utilities
URL: http://ftp.gnu.org/gnu/patch/
Source: http://ftp.gnu.org/gnu/patch/%{name}-%{version}.tar.xz
Source2: http://ftp.gnu.org/gnu/patch/%{name}-%{version}.tar.xz.sig
URL: https://ftp.gnu.org/gnu/patch/
Source: https://ftp.gnu.org/gnu/patch/%{name}-%{version}.tar.xz
Source2: https://ftp.gnu.org/gnu/patch/%{name}-%{version}.tar.xz.sig
# https://savannah.gnu.org/people/viewgpg.php?user_id=15000
Source3: patch.keyring
Patch1: fix-segfault-mangled-rename.patch
Patch2: ed-style-01-missing-input-files.patch
Patch3: ed-style-02-fix-arbitrary-command-execution.patch
Patch4: ed-style-03-update-test-Makefile.patch
Patch5: ed-style-04-invoke-ed-directly.patch
Patch6: ed-style-05-minor-cleanups.patch
Patch7: ed-style-06-fix-test-failure.patch
Patch8: ed-style-07-dont-leak-tmp-file.patch
Patch9: ed-style-08-dont-leak-tmp-file-multi.patch
Patch10: fix-swapping-fake-lines-in-pch_swap.patch
Patch11: abort-when-cleaning-up-fails.patch
Patch12: dont-follow-symlinks-unless-asked.patch
Patch13: pass-the-correct-stat-to-backup-files.patch
Patch14: CVE-2019-20633.patch
BuildRequires: ed
# See bnc#662957. The fix for CVE-2010-4651 breaks the way interdiff was
# invoking patch, so interdiff had to be fixed too.
Conflicts: patchutils < 0.3.2
BuildRequires: ed
%if 0%{?suse_version} < 1220
BuildRequires: xz
%endif
%description
The GNU patch program is used to apply diffs between original and
@@ -57,20 +42,22 @@ changed files (generated by the diff command) to the original files.
%autosetup -p1
%build
export CFLAGS="%{optflags} -Wall -O2 -pipe"
%configure
make %{?_smp_mflags}
%configure \
# https://lists.gnu.org/archive/html/bug-patch/2025-04/msg00002.html
# --enable-gcc-warnings \
%{nil}
%make_build
%check
make %{?_smp_mflags} check
%make_build check
%install
make install DESTDIR=%{buildroot}
%make_install
%files
%doc AUTHORS NEWS README
%license COPYING
%{_bindir}/patch
%{_mandir}/man1/patch.1%{ext_man}
%{_mandir}/man1/patch.1%{?ext_man}
%changelog