debian/0000775000000000000000000000000013263141205007165 5ustar debian/rules0000775000000000000000000000323012504020634010242 0ustar #!/usr/bin/make -f #-*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 #export DEB_BUILD_MAINT_OPTIONS = hardening=+all #DPKG_EXPORT_BUILDFLAGS = 1 #include /usr/share/dpkg/buildflags.mk DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE)) CONFFLAGS = --build $(DEB_HOST_GNU_TYPE) else CONFFLAGS = --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) endif CFLAGS = -Wall -g ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O2 endif configure: configure-stamp configure-stamp: dh_testdir CFLAGS="$(CFLAGS)" ac_cv_sys_long_file_names=yes ac_cv_path_ED=ed \ ./configure $(CONFFLAGS) --prefix=/usr --mandir=\$${prefix}/share/man touch configure-stamp build-indep: build build-arch: build-stamp build-stamp: configure-stamp dh_testdir $(MAKE) dh_auto_test # make sure we call ed as "ed", not "/bin/ed" or something grep '#define *EDITOR_PROGRAM *"ed"' config.h touch build-stamp clean: dh_testdir [ ! -f Makefile ] || $(MAKE) distclean dh_clean build-stamp configure-stamp .version install: build dh_testdir dh_testroot dh_prep dh_installdirs $(MAKE) install prefix=$(CURDIR)/debian/patch/usr binary-arch: install dh_testdir dh_testroot dh_installchangelogs ChangeLog dh_installdocs README NEWS AUTHORS -dh_lintian dh_link dh_strip dh_compress dh_fixperms dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary-indep: binary: binary-indep binary-arch .PHONY: configure build build-arch build-indep clean install binary-arch binary-indep binary debian/source/0000775000000000000000000000000012235712464010476 5ustar debian/source/format0000664000000000000000000000001412164040044011671 0ustar 3.0 (quilt) debian/watch0000664000000000000000000000020112164040044010205 0ustar version=3 http://ftp.gnu.org/gnu/patch/patch-(.*).tar.(?:gz|bz2|xz) #http://alpha.gnu.org/gnu/patch/patch-(.*).tar.(?:gz|bz2|xz) debian/control0000664000000000000000000000134412536373437010612 0ustar Source: patch Section: vcs Priority: standard Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Laszlo Boszormenyi (GCS) Build-Depends: debhelper (>= 7), ed, automake1.11 Standards-Version: 3.9.5 Vcs-Git: git://git.debian.org/collab-maint/patch.git Vcs-Browser: http://git.debian.org/?p=collab-maint/patch.git Package: patch Depends: ${shlibs:Depends}, ${misc:Depends} Suggests: ed, diffutils-doc Multi-Arch: foreign Architecture: any Description: Apply a diff file to an original Patch will take a patch file containing any of the four forms of difference listing produced by the diff program and apply those differences to an original file, producing a patched version. debian/copyright0000664000000000000000000000247312235716050011132 0ustar This is Debian's prepackaged version of patch, Larry Wall's program that allows you to apply diffs to a file automagically. The current maintainer for this package is Laszlo Boszormenyi (GCS) . Previous maintainers were Bill Mitchell , Darren Stalder , Adrian Bunk , Michael Fedrowitz and Christoph Berg . The upstream source is available at 'ftp://ftp.gnu.org/gnu/patch/', resp. 'ftp://alpha.gnu.org/gnu/diffutils/'. Upstream Maintainer: Paul Eggert Copyright: Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall Copyright (C) 1989-2012 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. On Debian systems the complete text of the GNU General Public License can be found in '/usr/share/common-licenses/GPL'. debian/compat0000664000000000000000000000000211523515177010375 0ustar 7 debian/NEWS0000664000000000000000000000203211523606135007666 0ustar patch (2.6.1-1) unstable; urgency=low The options -U --unified-reject-files and --global-reject-file have now been removed. -- Christoph Berg Sun, 06 Feb 2011 20:17:11 +0100 patch (2.6-1) unstable; urgency=low The patch versions shipped in Debian Etch and Lenny (2.5.9-4 and 2.5.9-5) contained two patches that would create reject files in unified format, and collect all rejects in a single, "global" reject file. These patches are now part of patch 2.6. However, the arguments have been renamed: * -U or --unified-reject-files is now --reject-format=format. Additionally, reject files will automatically be in unified format if the input patch is in that format. * --global-reject-file=file is now a synonym for --reject-file=file (-r) which has been fixed not to overwrite reject hunks from different files. The old syntax is still supported, but will be removed for the Debian release following Squeeze. -- Christoph Berg Mon, 30 Nov 2009 14:01:54 +0100 debian/patches/0000775000000000000000000000000013263142613010620 5ustar debian/patches/m-merge0000664000000000000000000000033512164040044012067 0ustar --- a/src/patch.c +++ b/src/patch.c @@ -684,7 +684,7 @@ } static char const shortopts[] = "bB:cd:D:eEfF:g:i:l" -#if 0 && defined ENABLE_MERGE +#if defined ENABLE_MERGE "m" #endif "nNo:p:r:RstTuvV:x:Y:z:Z"; debian/patches/series0000664000000000000000000000043613263142613012040 0ustar path_max 558485-backupmode m-merge #disable-update-version add_manpage_time.patch patch-bug-1306412.diff CVE-2014-9637.patch CVE-2015-1196.patch CVE-2015-1395.patch CVE-2015-1396.patch CVE-2016-10713.patch CVE-2018-1000156.patch CVE-2018-6951.patch 0001-Fix-ed-style-test-failure.patch debian/patches/CVE-2018-1000156.patch0000664000000000000000000001320613263142571013465 0ustar Backported of: From 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Fri, 6 Apr 2018 12:14:49 +0200 Subject: [PATCH] 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 | 89 +++++++++++++++++++++++++++++++++++++++++-------------- tests/Makefile.am | 1 + tests/ed-style | 41 +++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 tests/ed-style diff --git a/src/pch.c b/src/pch.c index 92f6269..8532d94 100644 --- a/src/pch.c +++ b/src/pch.c @@ -32,6 +32,7 @@ # include #endif #include +#include #define INITHUNKMAX 125 /* initial dynamic allocation size */ @@ -2357,22 +2358,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 = -1; + 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; - assert (! inerrno); - *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); @@ -2383,14 +2390,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; @@ -2403,13 +2410,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); if (ofp) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 03bd45a..21acfce 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,6 +28,7 @@ TESTS = \ criss-cross \ crlf-handling \ dash-o-append \ + ed-style \ empty-files \ fifo \ file-modes \ 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 < ed2.diff < /dev/null || echo "Status: $?"' < #endif +#include #define INITHUNKMAX 125 /* initial dynamic allocation size */ @@ -136,8 +137,11 @@ open_patch_file (char const *filename) else { size_t charsread; - int fd = make_tempfile (&TMPPATNAME, 'p', NULL, O_RDWR | O_BINARY, 0); + int fd; FILE *read_pfp = pfp; + fd = make_tempfile (&TMPPATNAME, 'p', NULL, O_RDWR | O_BINARY, 0); + if (fd == -1) + pfatal ("Can't create temporary file %s", TMPPATNAME); TMPPATNAME_needs_removal = true; pfp = fdopen (fd, "w+b"); if (! pfp) @@ -386,29 +390,6 @@ skip_hex_digits (char const *str) return s == str ? NULL : s; } -/* Check if we are in the root of a particular filesystem namespace ("/" on - UNIX or a particular drive's root on DOS-like systems). */ -static bool -cwd_is_root (char const *name) -{ - unsigned int prefix_len = FILE_SYSTEM_PREFIX_LEN (name); - char root[prefix_len + 2]; - struct stat st; - dev_t root_dev; - ino_t root_ino; - - memcpy (root, name, prefix_len); - root[prefix_len] = '/'; - root[prefix_len + 1] = 0; - if (stat (root, &st)) - return false; - root_dev = st.st_dev; - root_ino = st.st_ino; - if (stat (".", &st)) - return false; - return root_dev == st.st_dev && root_ino == st.st_ino; -} - static bool name_is_valid (char const *name) { @@ -424,21 +405,7 @@ name_is_valid (char const *name) return false; } - if (IS_ABSOLUTE_FILE_NAME (name)) - is_valid = false; - else - for (n = name; *n; ) - { - if (*n == '.' && *++n == '.' && ( ! *++n || ISSLASH (*n))) - { - is_valid = false; - break; - } - while (*n && ! ISSLASH (*n)) - n++; - while (ISSLASH (*n)) - n++; - } + is_valid = filename_is_safe (name); /* Allow any filename if we are in the filesystem root. */ if (! is_valid && cwd_is_root (name)) @@ -453,60 +420,6 @@ name_is_valid (char const *name) return is_valid; } -bool -symlink_target_is_valid (char const *target, char const *to) -{ - bool is_valid; - - if (IS_ABSOLUTE_FILE_NAME (to)) - is_valid = true; - else if (IS_ABSOLUTE_FILE_NAME (target)) - is_valid = false; - else - { - unsigned int depth = 0; - char const *t; - - is_valid = true; - t = to; - while (*t) - { - while (*t && ! ISSLASH (*t)) - t++; - if (ISSLASH (*t)) - { - while (ISSLASH (*t)) - t++; - depth++; - } - } - - t = target; - while (*t) - { - if (*t == '.' && *++t == '.' && (! *++t || ISSLASH (*t))) - { - if (! depth--) - { - is_valid = false; - break; - } - } - else - { - while (*t && ! ISSLASH (*t)) - t++; - depth++; - } - while (ISSLASH (*t)) - t++; - } - } - - /* Allow any symlink target if we are in the filesystem root. */ - return is_valid || cwd_is_root (to); -} - /* Determine what kind of diff is in the remaining part of the patch file. */ static enum diff @@ -1104,7 +1017,7 @@ prefix_components (char *filename, bool if (checkdirs) { *f = '\0'; - stat_result = stat (filename, &stat_buf); + stat_result = safe_stat (filename, &stat_buf); *f = '/'; if (! (stat_result == 0 && S_ISDIR (stat_buf.st_mode))) break; Index: patch-2.7.1/src/pch.h =================================================================== --- patch-2.7.1.orig/src/pch.h 2015-06-22 14:33:07.396899310 -0500 +++ patch-2.7.1/src/pch.h 2015-06-22 14:33:07.392899329 -0500 @@ -37,7 +37,6 @@ bool pch_write_line (lin, FILE *); bool there_is_another_patch (bool, mode_t *); char *pfetch (lin) _GL_ATTRIBUTE_PURE; char pch_char (lin) _GL_ATTRIBUTE_PURE; -bool symlink_target_is_valid (char const *, char const *); int another_hunk (enum diff, bool); int pch_says_nonexistent (bool) _GL_ATTRIBUTE_PURE; size_t pch_line_len (lin) _GL_ATTRIBUTE_PURE; Index: patch-2.7.1/src/util.c =================================================================== --- patch-2.7.1.orig/src/util.c 2015-06-22 14:33:07.396899310 -0500 +++ patch-2.7.1/src/util.c 2015-06-22 14:33:07.392899329 -0500 @@ -51,6 +51,8 @@ # include "verror.h" #endif +#include + /* make GNU/Hurd happy */ #ifndef PATH_MAX # define PATH_MAX 8192 @@ -220,6 +222,9 @@ copy_attr (char const *src_path, char co .quote = copy_attr_quote, .quote_free = copy_attr_free }; + /* FIXME: We are copying between files we know we can safely access by + * pathname. A safe_ version of attr_copy_file() might still be slightly + * more efficient for deep paths. */ return attr_copy_file (src_path, dst_path, copy_attr_check, &ctx); } @@ -248,7 +253,7 @@ set_file_attributes (char const *to, enu times[0] = get_stat_atime (st); times[1] = get_stat_mtime (st); } - if (lutimens (to, times) != 0) + if (safe_lutimens (to, times) != 0) pfatal ("Failed to set the timestamps of %s %s", S_ISLNK (mode) ? "symbolic link" : "file", quotearg (to)); @@ -271,10 +276,10 @@ set_file_attributes (char const *to, enu /* May fail if we are not privileged to set the file owner, or we are not in group instat.st_gid. Ignore those errors. */ if ((uid != -1 || gid != -1) - && lchown (to, uid, gid) != 0 + && safe_lchown (to, uid, gid) != 0 && (errno != EPERM || (uid != -1 - && lchown (to, (uid = -1), gid) != 0 + && safe_lchown (to, (uid = -1), gid) != 0 && errno != EPERM))) pfatal ("Failed to set the %s of %s %s", (uid == -1) ? "owner" : "owning group", @@ -293,7 +298,7 @@ set_file_attributes (char const *to, enu systems where we could. */ if (lchmod (to, mode)) #else - if (! S_ISLNK (mode) && chmod (to, mode) != 0) + if (! S_ISLNK (mode) && safe_chmod (to, mode) != 0) #endif pfatal ("Failed to set the permissions of %s %s", S_ISLNK (mode) ? "symbolic link" : "file", @@ -386,8 +391,8 @@ create_backup (char const *to, const str say ("Creating empty file %s\n", quotearg (bakname)); try_makedirs_errno = ENOENT; - unlink (bakname); - while ((fd = creat (bakname, 0666)) < 0) + safe_unlink (bakname); + while ((fd = safe_open (bakname, O_CREAT | O_WRONLY | O_TRUNC, 0666)) < 0) { if (errno != try_makedirs_errno) pfatal ("Can't create file %s", quotearg (bakname)); @@ -404,7 +409,7 @@ create_backup (char const *to, const str if (debug & 4) say ("Renaming file %s to %s\n", quotearg_n (0, to), quotearg_n (1, bakname)); - while (rename (to, bakname) != 0) + while (safe_rename (to, bakname) != 0) { if (errno == try_makedirs_errno) { @@ -415,7 +420,7 @@ create_backup (char const *to, const str { create_backup_copy (to, bakname, to_st, try_makedirs_errno == 0); - unlink (to); + safe_unlink (to); break; } else @@ -462,7 +467,7 @@ move_file (char const *from, bool *from_ char *buffer = xmalloc (PATH_MAX); int fd, size = 0, i; - if ((fd = open (from, O_RDONLY | O_BINARY)) < 0) + if ((fd = safe_open (from, O_RDONLY | O_BINARY, 0)) < 0) pfatal ("Can't reopen file %s", quotearg (from)); while ((i = read (fd, buffer + size, PATH_MAX - size)) > 0) size += i; @@ -470,27 +475,20 @@ move_file (char const *from, bool *from_ read_fatal (); buffer[size] = 0; - if (! symlink_target_is_valid (buffer, to)) - { - fprintf (stderr, "symbolic link target '%s' is invalid\n", - buffer); - fatal_exit (0); - } - if (! backup) { - if (unlink (to) == 0) + if (safe_unlink (to) == 0) to_dir_known_to_exist = true; } - if (symlink (buffer, to) != 0) + if (safe_symlink (buffer, to) != 0) { if (errno == ENOENT && ! to_dir_known_to_exist) makedirs (to); - if (symlink (buffer, to) != 0) + if (safe_symlink (buffer, to) != 0) pfatal ("Can't create %s %s", "symbolic link", to); } free (buffer); - if (lstat (to, &to_st) != 0) + if (safe_lstat (to, &to_st) != 0) pfatal ("Can't get file attributes of %s %s", "symbolic link", to); insert_file_id (&to_st, CREATED); } @@ -500,7 +498,7 @@ move_file (char const *from, bool *from_ say ("Renaming file %s to %s\n", quotearg_n (0, from), quotearg_n (1, to)); - if (rename (from, to) != 0) + if (safe_rename (from, to) != 0) { bool to_dir_known_to_exist = false; @@ -509,7 +507,7 @@ move_file (char const *from, bool *from_ { makedirs (to); to_dir_known_to_exist = true; - if (rename (from, to) == 0) + if (safe_rename (from, to) == 0) goto rename_succeeded; } @@ -518,7 +516,7 @@ move_file (char const *from, bool *from_ struct stat tost; if (! backup) { - if (unlink (to) == 0) + if (safe_unlink (to) == 0) to_dir_known_to_exist = true; else if (errno != ENOENT) pfatal ("Can't remove file %s", quotearg (to)); @@ -547,7 +545,7 @@ move_file (char const *from, bool *from_ { if (debug & 4) say ("Removing file %s\n", quotearg (to)); - if (unlink (to) != 0 && errno != ENOENT) + if (safe_unlink (to) != 0 && errno != ENOENT) pfatal ("Can't remove file %s", quotearg (to)); } } @@ -566,8 +564,8 @@ create_file (char const *file, int open_ do { if (! (O_CREAT && O_TRUNC)) - close (creat (file, mode)); - fd = open (file, O_CREAT | O_TRUNC | open_flags, mode); + close (safe_open (file, O_CREAT | O_WRONLY | O_TRUNC, mode)); + fd = safe_open (file, O_CREAT | O_TRUNC | open_flags, mode); if (fd < 0) { char *f; @@ -588,7 +586,7 @@ copy_to_fd (const char *from, int tofd) int fromfd; ssize_t i; - if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0) + if ((fromfd = safe_open (from, O_RDONLY | O_BINARY, 0)) < 0) pfatal ("Can't reopen file %s", quotearg (from)); while ((i = read (fromfd, buf, bufsize)) != 0) { @@ -618,11 +616,11 @@ copy_file (char const *from, char const { char *buffer = xmalloc (PATH_MAX); - if (readlink (from, buffer, PATH_MAX) < 0) + if (safe_readlink (from, buffer, PATH_MAX) < 0) pfatal ("Can't read %s %s", "symbolic link", from); - if (symlink (buffer, to) != 0) + if (safe_symlink (buffer, to) != 0) pfatal ("Can't create %s %s", "symbolic link", to); - if (tost && lstat (to, tost) != 0) + if (tost && safe_lstat (to, tost) != 0) pfatal ("Can't get file attributes of %s %s", "symbolic link", to); free (buffer); } @@ -646,7 +644,7 @@ append_to_file (char const *from, char c { int tofd; - if ((tofd = open (to, O_WRONLY | O_BINARY | O_APPEND)) < 0) + if ((tofd = safe_open (to, O_WRONLY | O_BINARY | O_APPEND, 0)) < 0) pfatal ("Can't reopen file %s", quotearg (to)); copy_to_fd (from, tofd); if (close (tofd) != 0) @@ -713,8 +711,8 @@ version_controller (char const *filename sprintf (trybuf, "%s/", dir); -#define try1(f,a1) (sprintf (trybuf + dirlen, f, a1), stat (trybuf, &cstat) == 0) -#define try2(f,a1,a2) (sprintf (trybuf + dirlen, f, a1,a2), stat (trybuf, &cstat) == 0) +#define try1(f,a1) (sprintf (trybuf + dirlen, f, a1), safe_stat (trybuf, &cstat) == 0) +#define try2(f,a1,a2) (sprintf (trybuf + dirlen, f, a1,a2), safe_stat (trybuf, &cstat) == 0) /* Check that RCS file is not working file. Some hosts don't report file name length errors. */ @@ -845,7 +843,7 @@ version_get (char const *filename, char cs, readonly ? "" : " with lock"); if (systemic (getbuf) != 0) fatal ("Can't get file %s from %s", quotearg (filename), cs); - if (stat (filename, filestat) != 0) + if (safe_stat (filename, filestat) != 0) pfatal ("%s", quotearg (filename)); } @@ -1284,6 +1282,9 @@ makedirs (char const *name) char *f; char *flim = replace_slashes (filename); + /* FIXME: Now with the pathname lookup cache, there is no reason for + deferring the creation of directories. Callers should be updated. */ + if (flim) { /* Create any missing directories, replacing NULs by '/'s. @@ -1294,7 +1295,7 @@ makedirs (char const *name) for (f = filename; f <= flim; f++) if (!*f) { - mkdir (filename, + safe_mkdir (filename, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IWGRP|S_IXGRP |S_IROTH|S_IWOTH|S_IXOTH); @@ -1324,7 +1325,7 @@ removedirs (char const *name) || ISSLASH (filename[i - 3]))))))) { filename[i] = '\0'; - if (rmdir (filename) == 0 && verbosity == VERBOSE) + if (safe_rmdir (filename) == 0 && verbosity == VERBOSE) say ("Removed empty directory %s\n", quotearg (filename)); filename[i] = '/'; } @@ -1642,10 +1643,15 @@ make_tempfile (char const **name, char l { int fd; + /* gen_tempname(..., GT_NOCREATE) calls lstat() to check if a file + already exists. In the worst case, this leads to a template that + follows a symbolic link and that we cannot use; safe_open() will + detect that. */ + if (gen_tempname (template, 0, flags, GT_NOCREATE)) - pfatal ("Can't create temporary file %s", template); + goto out; retry: - fd = open (template, O_CREAT | O_EXCL | flags, mode); + fd = safe_open (template, O_CREAT | O_EXCL | flags, mode); if (fd == -1) { if (errno == try_makedirs_errno) @@ -1658,8 +1664,8 @@ make_tempfile (char const **name, char l } if (errno == EEXIST) continue; - pfatal ("Can't create temporary file %s", template); } +out: *name = template; return fd; } @@ -1668,7 +1674,52 @@ make_tempfile (char const **name, char l int stat_file (char const *filename, struct stat *st) { int (*xstat)(char const *, struct stat *) = - follow_symlinks ? stat : lstat; + follow_symlinks ? safe_stat : safe_lstat; return xstat (filename, st) == 0 ? 0 : errno; } + +/* Check if a filename is relative and free of ".." components. + Such a path cannot lead to files outside the working tree + as long as the working tree only contains symlinks that are + "filename_is_safe" when followed. */ +bool +filename_is_safe (char const *name) +{ + if (IS_ABSOLUTE_FILE_NAME (name)) + return false; + while (*name) + { + if (*name == '.' && *++name == '.' + && ( ! *++name || ISSLASH (*name))) + return false; + while (*name && ! ISSLASH (*name)) + name++; + while (ISSLASH (*name)) + name++; + } + return true; +} + +/* Check if we are in the root of a particular filesystem namespace ("/" on + UNIX or a particular drive's root on DOS-like systems). */ +bool +cwd_is_root (char const *name) +{ + unsigned int prefix_len = FILE_SYSTEM_PREFIX_LEN (name); + char root[prefix_len + 2]; + struct stat st; + dev_t root_dev; + ino_t root_ino; + + memcpy (root, name, prefix_len); + root[prefix_len] = '/'; + root[prefix_len + 1] = 0; + if (stat (root, &st)) + return false; + root_dev = st.st_dev; + root_ino = st.st_ino; + if (stat (".", &st)) + return false; + return root_dev == st.st_dev && root_ino == st.st_ino; +} Index: patch-2.7.1/src/util.h =================================================================== --- patch-2.7.1.orig/src/util.h 2015-06-22 14:33:07.396899310 -0500 +++ patch-2.7.1/src/util.h 2015-06-22 14:33:07.392899329 -0500 @@ -69,6 +69,8 @@ enum file_id_type lookup_file_id (struct void set_queued_output (struct stat const *, bool); bool has_queued_output (struct stat const *); int stat_file (char const *, struct stat *); +bool filename_is_safe (char const *); +bool cwd_is_root (char const *); enum file_attributes { FA_TIMES = 1, Index: patch-2.7.1/tests/symlinks =================================================================== --- patch-2.7.1.orig/tests/symlinks 2015-06-22 14:33:07.396899310 -0500 +++ patch-2.7.1/tests/symlinks 2015-06-22 14:33:07.392899329 -0500 @@ -59,6 +59,14 @@ check 'patch --follow-symlinks < modify. patching file l EOF +check 'cat f' < f.diff < retraverse.diff < d/f +ln -s d ld + +cat > ld.diff < eld.diff < follow-bad-symlink.diff < symlink-target.diff < follow-symlink.diff < bad-symlink-target1.diff < bad-symlink-target2.diff < good-absolute.diff < symlink.orig && cat targ b EOF rm -f target2 + +# -------------------------------------------------------------- +# Make sure we do follow symlinks to patch files. + +ncheck 'mkdir d' +cat > d/ab.diff < and + Andreas Gruenbacher . + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dirname.h" + +#include +#include +#include + +#define XTERN extern +#include "common.h" + +#include "util.h" +#include "list.h" + +#ifndef EFTYPE +# define EFTYPE 0 +#endif + +static const unsigned int MAX_PATH_COMPONENTS = 1024; + +/* Path lookup results are cached in a hash table + LRU list. When the + cache is full, the oldest entries are removed. */ + +unsigned int dirfd_cache_misses; + +struct cached_dirfd { + struct list_head lru_link; + struct list_head children_link, children; + struct cached_dirfd *parent; + + char *name; + int fd; +}; + +static Hash_table *cached_dirfds = NULL; +static size_t max_cached_fds; +LIST_HEAD (lru_list); + +static size_t hash_cached_dirfd (const void *entry, size_t table_size) +{ + const struct cached_dirfd *d = entry; + size_t strhash = hash_string (d->name, table_size); + return (strhash * 31 + d->parent->fd) % table_size; +} + +static bool compare_cached_dirfds (const void *_a, + const void *_b) +{ + const struct cached_dirfd *a = _a; + const struct cached_dirfd *b = _b; + + return (a->parent->fd == b->parent->fd && + !strcmp (a->name, b->name)); +} + +static void free_cached_dirfd (struct cached_dirfd *entry) +{ + list_del (&entry->children_link); + free (entry->name); + free (entry); +} + +static void init_dirfd_cache (void) +{ + struct rlimit nofile; + + max_cached_fds = 8; + if (getrlimit (RLIMIT_NOFILE, &nofile) == 0) + max_cached_fds = MAX (nofile.rlim_cur / 4, max_cached_fds); + + cached_dirfds = hash_initialize (max_cached_fds, + NULL, + hash_cached_dirfd, + compare_cached_dirfds, + NULL); + + if (!cached_dirfds) + xalloc_die (); +} + +static struct cached_dirfd *lookup_cached_dirfd (struct cached_dirfd *dir, const char *name) +{ + struct cached_dirfd *entry = NULL; + + if (cached_dirfds) + { + struct cached_dirfd key; + key.parent = dir; + key.name = (char *) name; + entry = hash_lookup (cached_dirfds, &key); + } + + return entry; +} + +static void remove_cached_dirfd (struct cached_dirfd *entry) +{ + while (! list_empty (&entry->children)) + { + struct cached_dirfd *child = + list_entry (entry->children.next, struct cached_dirfd, children_link); + list_del_init (&child->children_link); + /* assert (list_empty (&child->children_link)); */ + hash_delete (cached_dirfds, child); /* noop when not hashed */ + } + list_del (&entry->lru_link); + hash_delete (cached_dirfds, entry); /* noop when not hashed */ + close (entry->fd); + free_cached_dirfd (entry); +} + +static void insert_cached_dirfd (struct cached_dirfd *entry, int keepfd) +{ + if (cached_dirfds == NULL) + init_dirfd_cache (); + + /* Trim off the least recently used entries */ + while (hash_get_n_entries (cached_dirfds) >= max_cached_fds) + { + struct cached_dirfd *last = + list_entry (lru_list.prev, struct cached_dirfd, lru_link); + if (&last->lru_link == &lru_list) + break; + if (last->fd == keepfd) + { + last = list_entry (last->lru_link.prev, struct cached_dirfd, lru_link); + if (&last->lru_link == &lru_list) + break; + } + remove_cached_dirfd (last); + } + + /* Only insert if the parent still exists. */ + if (! list_empty (&entry->children_link)) + assert (hash_insert (cached_dirfds, entry) == entry); +} + +static void invalidate_cached_dirfd (int dirfd, const char *name) +{ + struct cached_dirfd dir, key, *entry; + if (!cached_dirfds) + return; + + dir.fd = dirfd; + key.parent = &dir; + key.name = (char *) name; + entry = hash_lookup (cached_dirfds, &key); + if (entry) + remove_cached_dirfd (entry); +} + +/* Put the looked up path back onto the lru list. Return the file descriptor + of the top entry. */ +static int put_path (struct cached_dirfd *entry) +{ + int fd = entry->fd; + + while (entry) + { + struct cached_dirfd *parent = entry->parent; + if (! parent) + break; + list_add (&entry->lru_link, &lru_list); + entry = parent; + } + + return fd; +} + +static struct cached_dirfd *new_cached_dirfd (struct cached_dirfd *dir, const char *name, int fd) +{ + struct cached_dirfd *entry = xmalloc (sizeof (struct cached_dirfd)); + + INIT_LIST_HEAD (&entry->lru_link); + list_add (&entry->children_link, &dir->children); + INIT_LIST_HEAD (&entry->children); + entry->parent = dir; + entry->name = xstrdup (name); + entry->fd = fd; + return entry; +} + +static struct cached_dirfd *openat_cached (struct cached_dirfd *dir, const char *name, int keepfd) +{ + int fd; + struct cached_dirfd *entry = lookup_cached_dirfd (dir, name); + + if (entry) + { + list_del_init (&entry->lru_link); + /* assert (list_empty (&entry->lru_link)); */ + goto out; + } + dirfd_cache_misses++; + + /* Actually get the new directory file descriptor. Don't follow + symbolic links. */ + fd = openat (dir->fd, name, O_DIRECTORY | O_NOFOLLOW); + + /* Don't cache errors. */ + if (fd < 0) + return NULL; + + /* Store new cache entry */ + entry = new_cached_dirfd (dir, name, fd); + insert_cached_dirfd (entry, keepfd); + +out: + return entry; +} + +static unsigned int count_path_components (const char *path) +{ + unsigned int components; + + while (ISSLASH (*path)) + path++; + if (! *path) + return 1; + for (components = 0; *path; components++) + { + while (*path && ! ISSLASH (*path)) + path++; + while (ISSLASH (*path)) + path++; + } + return components; +} + +/* A symlink to resolve. */ +struct symlink { + struct symlink *prev; + const char *path; + char buffer[0]; +}; + +static void push_symlink (struct symlink **stack, struct symlink *symlink) +{ + symlink->prev = *stack; + *stack = symlink; +} + +static void pop_symlink (struct symlink **stack) +{ + struct symlink *top = *stack; + *stack = top->prev; + free (top); +} + +int cwd_stat_errno = -1; +struct stat cwd_stat; + +static struct symlink *read_symlink(int dirfd, const char *name) +{ + int saved_errno = errno; + struct stat st; + struct symlink *symlink; + ssize_t ret; + + if (fstatat (dirfd, name, &st, AT_SYMLINK_NOFOLLOW) + || ! S_ISLNK (st.st_mode)) + { + errno = saved_errno; + return NULL; + } + symlink = xmalloc (sizeof (*symlink) + st.st_size + 1); + ret = readlinkat (dirfd, name, symlink->buffer, st.st_size); + if (ret <= 0) + goto fail; + symlink->buffer[ret] = 0; + symlink->path = symlink->buffer; + if (ISSLASH (*symlink->path)) + { + char *end; + + if (cwd_stat_errno == -1) + { + cwd_stat_errno = stat (".", &cwd_stat) == 0 ? 0 : errno; + if (cwd_stat_errno) + goto fail_exdev; + } + end = symlink->buffer + ret; + for (;;) + { + char slash; + int rv; + + slash = *end; *end = 0; + rv = stat (symlink->path, &st); + *end = slash; + + if (rv == 0 + && st.st_dev == cwd_stat.st_dev + && st.st_ino == cwd_stat.st_ino) + { + while (ISSLASH (*end)) + end++; + symlink->path = end; + return symlink; + } + end--; + if (end == symlink->path) + break; + while (end != symlink->path + 1 && ! ISSLASH (*end)) + end--; + while (end != symlink->path + 1 && ISSLASH (*(end - 1))) + end--; + } + goto fail_exdev; + } + return symlink; + +fail_exdev: + errno = EXDEV; +fail: + free (symlink); + return NULL; +} + +/* Resolve the next path component in PATH inside DIR. If it is a symlink, + read it and returned it in TOP. */ +static struct cached_dirfd * +traverse_next (struct cached_dirfd *dir, const char **path, int keepfd, + struct symlink **symlink) +{ + const char *p = *path; + struct cached_dirfd *entry = dir; + char *name; + + while (*p && ! ISSLASH (*p)) + p++; + if (**path == '.' && *path + 1 == p) + goto skip; + if (**path == '.' && *(*path + 1) == '.' && *path + 2 == p) + { + entry = dir->parent; + if (! entry) + { + /* Must not leave the working tree. */ + errno = EXDEV; + goto out; + } + assert (list_empty (&dir->lru_link)); + list_add (&dir->lru_link, &lru_list); + goto skip; + } + name = alloca (p - *path + 1); + memcpy(name, *path, p - *path); + name[p - *path] = 0; + + entry = openat_cached (dir, name, keepfd); + if (! entry) + { + if (errno == ELOOP + || errno == EMLINK /* FreeBSD 10.1: Too many links */ + || errno == EFTYPE /* NetBSD 6.1: Inappropriate file type or format */ + || errno == ENOTDIR) + { + if ((*symlink = read_symlink (dir->fd, name))) + { + entry = dir; + goto skip; + } + errno = ELOOP; + } + goto out; + } +skip: + while (ISSLASH (*p)) + p++; +out: + *path = p; + return entry; +} + +/* Traverse PATHNAME. Updates PATHNAME to point to the last path component and + returns a file descriptor to its parent directory (which can be AT_FDCWD). + When KEEPFD is given, make sure that the cache entry for DIRFD is not + removed from the cache (and KEEPFD remains open). + + When this function is not running, all cache entries are on the lru list, + and all cache entries which still have a parent are also in the hash table. + While this function is running, all cache entries on the path being looked + up are off the lru list but in the hash table. + */ +static int traverse_another_path (const char **pathname, int keepfd) +{ + static struct cached_dirfd cwd = { + .fd = AT_FDCWD, + }; + + unsigned int misses = dirfd_cache_misses; + const char *path = *pathname, *last; + struct cached_dirfd *dir = &cwd; + struct symlink *stack = NULL; + unsigned int steps = count_path_components (path); + struct cached_dirfd *traversed_symlink = NULL; + + INIT_LIST_HEAD (&cwd.children); + + if (steps > MAX_PATH_COMPONENTS) + { + errno = ELOOP; + return -1; + } + + if (! *path || IS_ABSOLUTE_FILE_NAME (path)) + return AT_FDCWD; + + /* Find the last pathname component */ + last = strrchr (path, 0) - 1; + if (ISSLASH (*last)) + { + while (last != path) + if (! ISSLASH (*--last)) + break; + } + while (last != path && ! ISSLASH (*(last - 1))) + last--; + if (last == path) + return AT_FDCWD; + + if (debug & 32) + printf ("Resolving path \"%.*s\"", (int) (last - path), path); + + while (stack || path != last) + { + struct cached_dirfd *entry; + struct symlink *symlink = NULL; + const char *prev = path; + + entry = traverse_next (dir, stack ? &stack->path : &path, keepfd, &symlink); + if (! entry) + { + if (debug & 32) + { + printf (" (failed)\n"); + fflush (stdout); + } + goto fail; + } + dir = entry; + if (! stack && symlink) + { + const char *p = prev; + char *name; + + while (*p && ! ISSLASH (*p)) + p++; + name = alloca (p - prev + 1); + memcpy (name, prev, p - prev); + name[p - prev] = 0; + + traversed_symlink = new_cached_dirfd (dir, name, -1); + } + if (stack && ! *stack->path) + pop_symlink (&stack); + if (symlink && *symlink->path) + { + push_symlink (&stack, symlink); + steps += count_path_components (symlink->path); + if (steps > MAX_PATH_COMPONENTS) + { + errno = ELOOP; + goto fail; + } + } + else if (symlink) + pop_symlink (&symlink); + if (traversed_symlink && ! stack) + { + traversed_symlink->fd = + entry->fd == AT_FDCWD ? AT_FDCWD : dup (entry->fd); + if (traversed_symlink->fd != -1) + { + insert_cached_dirfd (traversed_symlink, keepfd); + list_add (&traversed_symlink->lru_link, &lru_list); + } + else + free_cached_dirfd (traversed_symlink); + traversed_symlink = NULL; + } + } + *pathname = last; + if (debug & 32) + { + misses = (signed int) dirfd_cache_misses - (signed int) misses; + if (! misses) + printf(" (cached)\n"); + else + printf (" (%u miss%s)\n", misses, misses == 1 ? "" : "es"); + fflush (stdout); + } + return put_path (dir); + +fail: + if (traversed_symlink) + free_cached_dirfd (traversed_symlink); + put_path (dir); + while (stack) + pop_symlink (&stack); + return -1; +} + +/* Just traverse PATHNAME; see traverse_another_path(). */ +static int traverse_path (const char **pathname) +{ + return traverse_another_path (pathname, -1); +} + +static int safe_xstat (const char *pathname, struct stat *buf, int flags) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return fstatat (dirfd, pathname, buf, flags); +} + +/* Replacement for stat() */ +int safe_stat (const char *pathname, struct stat *buf) +{ + return safe_xstat (pathname, buf, 0); +} + +/* Replacement for lstat() */ +int safe_lstat (const char *pathname, struct stat *buf) +{ + return safe_xstat (pathname, buf, AT_SYMLINK_NOFOLLOW); +} + +/* Replacement for open() */ +int safe_open (const char *pathname, int flags, mode_t mode) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return openat (dirfd, pathname, flags, mode); +} + +/* Replacement for rename() */ +int safe_rename (const char *oldpath, const char *newpath) +{ + int olddirfd, newdirfd; + int ret; + + olddirfd = traverse_path (&oldpath); + if (olddirfd < 0 && olddirfd != AT_FDCWD) + return olddirfd; + + newdirfd = traverse_another_path (&newpath, olddirfd); + if (newdirfd < 0 && newdirfd != AT_FDCWD) + return newdirfd; + + ret = renameat (olddirfd, oldpath, newdirfd, newpath); + if (! ret) + { + invalidate_cached_dirfd (olddirfd, oldpath); + invalidate_cached_dirfd (newdirfd, newpath); + } + return ret; +} + +/* Replacement for mkdir() */ +int safe_mkdir (const char *pathname, mode_t mode) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return mkdirat (dirfd, pathname, mode); +} + +/* Replacement for rmdir() */ +int safe_rmdir (const char *pathname) +{ + int dirfd; + int ret; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + + ret = unlinkat (dirfd, pathname, AT_REMOVEDIR); + if (! ret) + invalidate_cached_dirfd (dirfd, pathname); + return ret; +} + +/* Replacement for unlink() */ +int safe_unlink (const char *pathname) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return unlinkat (dirfd, pathname, 0); +} + +/* Replacement for symlink() */ +int safe_symlink (const char *target, const char *linkpath) +{ + int dirfd; + + dirfd = traverse_path (&linkpath); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return symlinkat (target, dirfd, linkpath); +} + +/* Replacement for chmod() */ +int safe_chmod (const char *pathname, mode_t mode) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return fchmodat (dirfd, pathname, mode, 0); +} + +/* Replacement for lchown() */ +int safe_lchown (const char *pathname, uid_t owner, gid_t group) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return fchownat (dirfd, pathname, owner, group, AT_SYMLINK_NOFOLLOW); +} + +/* Replacement for lutimens() */ +int safe_lutimens (const char *pathname, struct timespec const times[2]) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return utimensat (dirfd, pathname, times, AT_SYMLINK_NOFOLLOW); +} + +/* Replacement for readlink() */ +ssize_t safe_readlink (const char *pathname, char *buf, size_t bufsiz) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return readlinkat (dirfd, pathname, buf, bufsiz); +} + +/* Replacement for access() */ +int safe_access (const char *pathname, int mode) +{ + int dirfd; + + dirfd = traverse_path (&pathname); + if (dirfd < 0 && dirfd != AT_FDCWD) + return dirfd; + return faccessat (dirfd, pathname, mode, 0); +} Index: patch-2.7.1/src/safe.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ patch-2.7.1/src/safe.h 2015-06-22 14:33:07.392899329 -0500 @@ -0,0 +1,33 @@ +/* safe path traversal functions for 'patch' */ + +/* Copyright (C) 2015 Free Software Foundation, Inc. + + Written by Tim Waugh and + Andreas Gruenbacher . + + 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 . */ + +int safe_stat (const char *pathname, struct stat *buf); +int safe_lstat (const char *pathname, struct stat *buf); +int safe_open (const char *pathname, int flags, mode_t mode); +int safe_rename (const char *oldpath, const char *newpath); +int safe_mkdir (const char *pathname, mode_t mode); +int safe_rmdir (const char *pathname); +int safe_unlink (const char *pathname); +int safe_symlink (const char *target, const char *linkpath); +int safe_chmod (const char *pathname, mode_t mode); +int safe_lchown (const char *pathname, uid_t owner, gid_t group); +int safe_lutimens (const char *pathname, struct timespec const times[2]); +ssize_t safe_readlink(const char *pathname, char *buf, size_t bufsiz); +int safe_access(const char *pathname, int mode); Index: patch-2.7.1/tests/deep-directories =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ patch-2.7.1/tests/deep-directories 2015-06-22 14:33:07.392899329 -0500 @@ -0,0 +1,28 @@ +# Copyright (C) 2015 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 + +# ============================================================== +# Exercise the directory file descriptor cache + +# Artificially limit to 8 cache entries +ulimit -n 32 >& /dev/null || exit 77 + +cat > ab.diff < +#include /* Input-file-with-indexable-lines abstract type */ @@ -231,7 +232,7 @@ plan_a (char const *filename) { if (S_ISREG (instat.st_mode)) { - int ifd = open (filename, O_RDONLY|binary_transput); + int ifd = safe_open (filename, O_RDONLY|binary_transput, 0); size_t buffered = 0, n; if (ifd < 0) pfatal ("can't open file %s", quotearg (filename)); @@ -262,7 +263,7 @@ plan_a (char const *filename) else if (S_ISLNK (instat.st_mode)) { ssize_t n; - n = readlink (filename, buffer, size); + n = safe_readlink (filename, buffer, size); if (n < 0) pfatal ("can't read %s %s", "symbolic link", quotearg (filename)); size = n; @@ -333,6 +334,7 @@ plan_a (char const *filename) static void plan_b (char const *filename) { + int ifd; FILE *ifp; int c; size_t len; @@ -345,7 +347,8 @@ plan_b (char const *filename) if (instat.st_size == 0) filename = NULL_DEVICE; - if (! (ifp = fopen (filename, binary_transput ? "rb" : "r"))) + if ((ifd = safe_open (filename, O_RDONLY | binary_transput, 0)) < 0 + || ! (ifp = fdopen (ifd, binary_transput ? "rb" : "r"))) pfatal ("Can't open file %s", quotearg (filename)); if (TMPINNAME_needs_removal) { @@ -356,6 +359,8 @@ plan_b (char const *filename) { tifd = make_tempfile (&TMPINNAME, 'i', NULL, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR); + if (tifd == -1) + pfatal ("Can't create temporary file %s", TMPINNAME); TMPINNAME_needs_removal = true; } i = 0; Index: patch-2.7.1/src/patch.c =================================================================== --- patch-2.7.1.orig/src/patch.c 2015-06-22 14:33:07.396899310 -0500 +++ patch-2.7.1/src/patch.c 2015-06-22 14:33:07.396899310 -0500 @@ -33,6 +33,7 @@ #include #include #include +#include /* procedures */ @@ -113,6 +114,7 @@ main (int argc, char **argv) struct stat tmpoutst; char numbuf[LINENUM_LENGTH_BOUND + 1]; bool written_to_rejname = false; + bool skip_reject_file = false; bool apply_empty_patch = false; mode_t file_type; int outfd = -1; @@ -188,6 +190,7 @@ main (int argc, char **argv) there_is_another_patch (! (inname || posixly_correct), &file_type) || apply_empty_patch; reinitialize_almost_everything(), + skip_reject_file = false, apply_empty_patch = false ) { /* for each patch in patch file */ int hunk = 0; @@ -290,7 +293,7 @@ main (int argc, char **argv) if (read_only_behavior != RO_IGNORE && ! inerrno && ! S_ISLNK (instat.st_mode) - && access (inname, W_OK) != 0) + && safe_access (inname, W_OK) != 0) { say ("File %s is read-only; ", quotearg (inname)); if (read_only_behavior == RO_WARN) @@ -307,7 +310,20 @@ main (int argc, char **argv) outfd = make_tempfile (&TMPOUTNAME, 'o', outname, O_WRONLY | binary_transput, instat.st_mode & S_IRWXUGO); - TMPOUTNAME_needs_removal = true; + if (outfd == -1) + { + if (errno == ELOOP || errno == EXDEV) + { + say ("Invalid file name %s -- skipping patch\n", quotearg (outname)); + skip_rest_of_patch = true; + skip_reject_file = true; + somefailed = true; + } + else + pfatal ("Can't create temporary file %s", TMPOUTNAME); + } + else + TMPOUTNAME_needs_removal = true; if (diff_type == ED_DIFF) { outstate.zero_output = false; somefailed |= skip_rest_of_patch; @@ -338,6 +354,8 @@ main (int argc, char **argv) outstate.ofp = fdopen(outfd, binary_transput ? "wb" : "w"); if (! outstate.ofp) pfatal ("%s", TMPOUTNAME); + /* outstate.ofp now owns the file descriptor */ + outfd = -1; } /* find out where all the lines are */ @@ -448,7 +466,8 @@ main (int argc, char **argv) || ! where || ! apply_hunk (&outstate, where)))) { - abort_hunk (outname, ! failed, reverse); + if (! skip_reject_file) + abort_hunk (outname, ! failed, reverse); failed++; if (verbosity == VERBOSE || (! skip_rest_of_patch && verbosity != SILENT)) @@ -589,7 +608,7 @@ main (int argc, char **argv) if (diff_type != ED_DIFF) { struct stat rejst; - if (failed) { + if (failed && ! skip_reject_file) { if (fstat (fileno (rejfp), &rejst) != 0 || fclose (rejfp) != 0) write_fatal (); rejfp = NULL; @@ -1578,6 +1597,8 @@ init_reject (char const *outname) int fd; fd = make_tempfile (&TMPREJNAME, 'r', outname, O_WRONLY | binary_transput, 0666); + if (fd == -1) + pfatal ("Can't create temporary file %s", TMPREJNAME); TMPREJNAME_needs_removal = true; rejfp = fdopen (fd, binary_transput ? "wb" : "w"); if (! rejfp) @@ -1914,7 +1935,7 @@ output_files (struct stat const *st) from_st, file_to_output->to, file_to_output->mode, file_to_output->backup); if (file_to_output->to && from_needs_removal) - unlink (file_to_output->from); + safe_unlink (file_to_output->from); if (st && st->st_dev == from_st->st_dev && st->st_ino == from_st->st_ino) { @@ -1944,7 +1965,7 @@ forget_output_files (void) { const struct file_to_output *file_to_output = elt; - unlink (file_to_output->from); + safe_unlink (file_to_output->from); } gl_list_iterator_free (&iter); gl_list_clear (files_to_output); @@ -1968,7 +1989,7 @@ remove_if_needed (char const *name, bool { if (*needs_removal) { - unlink (name); + safe_unlink (name); *needs_removal = false; } } Index: patch-2.7.1/src/list.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ patch-2.7.1/src/list.h 2015-06-22 14:33:07.396899310 -0500 @@ -0,0 +1,55 @@ +#ifndef __LIST_H +#define __LIST_H + +#include + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void +list_add (struct list_head *entry, struct list_head *head) +{ + struct list_head *next = head->next; + entry->prev = head; + entry->next = next; + next->prev = head->next = entry; +} + +static inline void +list_del (struct list_head *entry) +{ + struct list_head *next = entry->next; + struct list_head *prev = entry->prev; + next->prev = prev; + prev->next = next; +} + +static inline void +list_del_init (struct list_head *entry) +{ + list_del(entry); + INIT_LIST_HEAD(entry); +} + +static inline bool +list_empty (const struct list_head *head) +{ + return head->next == head; +} + +#define list_entry(ptr, type, member) \ + (type *)( (char *)(ptr) - offsetof(type, member) ) + +#endif /* __LIST_H */ debian/patches/CVE-2016-10713.patch0000664000000000000000000000166513262672244013334 0ustar From a0d7fe4589651c64bd16ddaaa634030bb0455866 Mon Sep 17 00:00:00 2001 From: Hanno Boeck Date: Wed, 10 Aug 2016 00:06:41 +0200 Subject: [PATCH] Fix out-of-bounds access to lines in a patch This bug can trigger with malformed patches. * src/pch.c (pch_write_line): Avoid out-of-bounds access to p_line[line][p_len[line] - 1] when p_len[line] is 0. --- src/pch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Index: patch-2.7.1/src/pch.c =================================================================== --- patch-2.7.1.orig/src/pch.c +++ patch-2.7.1/src/pch.c @@ -2245,7 +2245,7 @@ pfetch (lin line) bool pch_write_line (lin line, FILE *file) { - bool after_newline = p_line[line][p_len[line] - 1] == '\n'; + 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)) write_fatal (); return after_newline; debian/patches/patch-bug-1306412.diff0000664000000000000000000000064512504022477014151 0ustar --- patch/src/patch.c 2014-04-11 11:24:37.000000000 +0100 +++ patch/src/patch.c 2014-04-11 11:24:00.589844724 +0100 @@ -1975,5 +1975,6 @@ cleanup (void) remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); - forget_output_files (); + if (files_to_output) + forget_output_files (); } debian/patches/CVE-2015-1196.patch0000664000000000000000000001220612536372306013250 0ustar From 4e9269a5fc1fe80a1095a92593dd85db871e1fd3 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 19 Jan 2015 23:18:30 +0100 Subject: [PATCH] Make sure symlinks don't point outside working directory (CVE-2015-119) When creating symlinks from git-style patches, make sure the symlinks don't point above the current working directory. Otherwise, a subsequent patch could use the symlink to write outside the working directory. * src/pch.c (symlink_target_is_valid): New function to check for valid symlink targets. * src/util.c (move_file): Use symlink_target_is_valid() here. * tests/symlinks: Add valid and invalid symlink test cases. Origin: backport, http://git.savannah.gnu.org/cgit/patch.git/commit/?id=4e9269a5fc1fe80a1095a92593dd85db871e1fd3 Bug: https://savannah.gnu.org/bugs/?44048 --- src/pch.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pch.h | 1 + src/util.c | 7 +++++++ tests/symlinks | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) Index: patch-2.7.1/src/pch.c =================================================================== --- patch-2.7.1.orig/src/pch.c 2015-06-11 15:51:05.965475867 -0500 +++ patch-2.7.1/src/pch.c 2015-06-11 15:51:05.961475884 -0500 @@ -453,6 +453,60 @@ name_is_valid (char const *name) return is_valid; } +bool +symlink_target_is_valid (char const *target, char const *to) +{ + bool is_valid; + + if (IS_ABSOLUTE_FILE_NAME (to)) + is_valid = true; + else if (IS_ABSOLUTE_FILE_NAME (target)) + is_valid = false; + else + { + unsigned int depth = 0; + char const *t; + + is_valid = true; + t = to; + while (*t) + { + while (*t && ! ISSLASH (*t)) + t++; + if (ISSLASH (*t)) + { + while (ISSLASH (*t)) + t++; + depth++; + } + } + + t = target; + while (*t) + { + if (*t == '.' && *++t == '.' && (! *++t || ISSLASH (*t))) + { + if (! depth--) + { + is_valid = false; + break; + } + } + else + { + while (*t && ! ISSLASH (*t)) + t++; + depth++; + } + while (ISSLASH (*t)) + t++; + } + } + + /* Allow any symlink target if we are in the filesystem root. */ + return is_valid || cwd_is_root (to); +} + /* Determine what kind of diff is in the remaining part of the patch file. */ static enum diff Index: patch-2.7.1/src/pch.h =================================================================== --- patch-2.7.1.orig/src/pch.h 2015-06-11 15:51:05.965475867 -0500 +++ patch-2.7.1/src/pch.h 2015-06-11 15:51:05.961475884 -0500 @@ -37,6 +37,7 @@ bool pch_write_line (lin, FILE *); bool there_is_another_patch (bool, mode_t *); char *pfetch (lin) _GL_ATTRIBUTE_PURE; char pch_char (lin) _GL_ATTRIBUTE_PURE; +bool symlink_target_is_valid (char const *, char const *); int another_hunk (enum diff, bool); int pch_says_nonexistent (bool) _GL_ATTRIBUTE_PURE; size_t pch_line_len (lin) _GL_ATTRIBUTE_PURE; Index: patch-2.7.1/src/util.c =================================================================== --- patch-2.7.1.orig/src/util.c 2015-06-11 15:51:05.965475867 -0500 +++ patch-2.7.1/src/util.c 2015-06-11 15:51:05.961475884 -0500 @@ -470,6 +470,13 @@ move_file (char const *from, bool *from_ read_fatal (); buffer[size] = 0; + if (! symlink_target_is_valid (buffer, to)) + { + fprintf (stderr, "symbolic link target '%s' is invalid\n", + buffer); + fatal_exit (0); + } + if (! backup) { if (unlink (to) == 0) Index: patch-2.7.1/tests/symlinks =================================================================== --- patch-2.7.1.orig/tests/symlinks 2015-06-11 15:51:05.965475867 -0500 +++ patch-2.7.1/tests/symlinks 2015-06-11 15:51:05.961475884 -0500 @@ -146,6 +146,59 @@ ncheck 'test ! -L symlink' # -------------------------------------------------------------- +# Patch should not create symlinks which point outside the working directory. + +cat > symlink-target.diff < bad-symlink-target1.diff < bad-symlink-target2.diff </dev/null 2>/dev/null \ - && test -z "`git rev-parse --show-cdup`" ; then - set -- `git describe --tags HEAD 2> /dev/null || \ - git rev-parse --short HEAD` \ - `git update-index --refresh --unmerged > /dev/null - if git diff-index --name-only HEAD | read dummy; then - echo -dirty - fi` - if test "`expr substr $1 1 1`" = v ; then - set -- "`expr substr $1 2 \( length $1 - 1 \)`" $2 - fi - echo $1$2 > .$version.tmp - if test ! -e $version \ - || ! cmp -s .$version.tmp $version ; then - mv .$version.tmp $version - else - rm -f .$version.tmp - fi -fi +#!/bin/true debian/patches/CVE-2015-1395.patch0000664000000000000000000001036412542061360013244 0ustar From 17953b5893f7c9835f0dd2a704ba04e0371d2cbd Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 21 Jan 2015 13:01:08 +0100 Subject: [PATCH] For renames and copies, make sure that both file names are valid * src/patch.c (main): Allow there_is_another_patch() to set the skip_rest_of_patch flag. * src/pch.c (intuit_diff_type): For renames and copies, also check the "other" file name. (pch_copy, pch_rename): Now that both names are checked in intuit_diff_type(), we know they are defined here. Origin: upstream, http://git.savannah.gnu.org/cgit/patch.git/commit/?id=17953b5893f7c9835f0dd2a704ba04e0371d2cbd Origin: upstream, http://git.savannah.gnu.org/cgit/patch.git/commit/?id=db9f39507e3bb739b696c523274cb34adc5e4895 Bug: https://savannah.gnu.org/bugs/?44059 --- src/patch.c | 3 +++ src/pch.c | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) Index: patch-2.7.1/src/patch.c =================================================================== --- patch-2.7.1.orig/src/patch.c 2015-06-18 15:43:40.077413676 -0500 +++ patch-2.7.1/src/patch.c 2015-06-22 14:32:19.609121721 -0500 @@ -195,6 +195,9 @@ main (int argc, char **argv) bool mismatch = false; char const *outname = NULL; + if (skip_rest_of_patch) + somefailed = true; + if (have_git_diff != pch_git_diff ()) { if (have_git_diff) @@ -239,7 +242,7 @@ main (int argc, char **argv) if (outfile) outname = outfile; else if (pch_copy () || pch_rename ()) - outname = pch_name (! strcmp (inname, pch_name (OLD))); + outname = pch_name (! reverse); else outname = inname; } @@ -344,16 +347,18 @@ main (int argc, char **argv) if (verbosity != SILENT) { bool renamed = strcmp (inname, outname); + bool skip_rename = ! renamed && pch_rename (); say ("%s %s %s%c", dry_run ? "checking" : "patching", S_ISLNK (file_type) ? "symbolic link" : "file", - quotearg (outname), renamed ? ' ' : '\n'); - if (renamed) - say ("(%s from %s)\n", + quotearg (outname), renamed || skip_rename ? ' ' : '\n'); + if (renamed || skip_rename) + say ("(%s%s from %s)\n", + skip_rename ? "already " : "", pch_copy () ? "copied" : (pch_rename () ? "renamed" : "read"), - inname); + ! skip_rename ? inname : pch_name (! strcmp (inname, pch_name (OLD)))); if (verbosity == VERBOSE) say ("Using Plan %s...\n", using_plan_a ? "A" : "B"); } Index: patch-2.7.1/src/pch.c =================================================================== --- patch-2.7.1.orig/src/pch.c 2015-06-18 15:43:40.077413676 -0500 +++ patch-2.7.1/src/pch.c 2015-06-22 14:32:07.000000000 -0500 @@ -1055,6 +1055,16 @@ intuit_diff_type (bool need_header, mode } } + if ((pch_rename () || pch_copy ()) + && ! inname + && ! ((i == OLD || i == NEW) && + p_name[! reverse] && + name_is_valid (p_name[! reverse]))) + { + say ("Cannot %s file without two valid file names\n", pch_rename () ? "rename" : "copy"); + skip_rest_of_patch = true; + } + if (i == NONE) { if (inname) @@ -2227,14 +2237,12 @@ pch_name (enum nametype type) bool pch_copy (void) { - return p_copy[OLD] && p_copy[NEW] - && p_name[OLD] && p_name[NEW]; + return p_copy[OLD] && p_copy[NEW]; } bool pch_rename (void) { - return p_rename[OLD] && p_rename[NEW] - && p_name[OLD] && p_name[NEW]; + return p_rename[OLD] && p_rename[NEW]; } /* Return the specified line position in the old file of the old context. */ Index: patch-2.7.1/tests/copy-rename =================================================================== --- patch-2.7.1.orig/tests/copy-rename 2012-07-31 14:54:07.000000000 -0500 +++ patch-2.7.1/tests/copy-rename 2015-06-22 14:32:19.609121721 -0500 @@ -63,6 +63,30 @@ check 'cat h' < h + +check 'patch -p1 < rename.diff || echo "Status: $?"' < Bug-Debian: http://bugs.debian.org/674052 Last-Update: 2013-11-04 --- --- patch-2.7.1.orig/patch.man +++ patch-2.7.1/patch.man @@ -7,7 +7,7 @@ .if t .sp .3 .if n .sp .. -.TH PATCH 1 \*(Dt GNU +.TH PATCH 1 2012-09-28 GNU .ta 3n .SH NAME patch \- apply a diff file to an original debian/patches/path_max0000664000000000000000000000033412164040044012336 0ustar --- a/src/util.c +++ b/src/util.c @@ -51,6 +51,11 @@ # include "verror.h" #endif +/* make GNU/Hurd happy */ +#ifndef PATH_MAX +# define PATH_MAX 8192 +#endif + static void makedirs (char const *); typedef struct debian/patches/0001-Fix-ed-style-test-failure.patch0000664000000000000000000000124413263141141017067 0ustar From 458ac51a05426c1af9aa6bf1342ecf60728c19b4 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 7 Apr 2018 12:34:03 +0200 Subject: [PATCH] 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: $?"' < /dev/null 2> /dev/null || echo "Status: $?"' < Date: Tue, 20 Jan 2015 12:20:00 +0100 Subject: [PATCH] Fail when out of memory in set_hunkmax() src/pch.c (another_hunk): Call set_hunkmax() from here to make sure it is called even when falling back from plan A to plan B. (open_patch_file): No need to call set_hunkmax() anymore. src/pch.c (set_hunkmax): Fail when out of memory. Make static. src/pch.h: Remove set_hunkmax() prototype. Origin: upstream, http://git.savannah.gnu.org/cgit/patch.git/commit/?id=0c08d7a902c6fdd49b704623a12d8d672ef18944 Bug: https://savannah.gnu.org/bugs/?44051 --- src/pch.c | 11 ++++++----- src/pch.h | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) Index: patch-2.7.1/src/pch.c =================================================================== --- patch-2.7.1.orig/src/pch.c 2015-06-11 15:49:47.309816385 -0500 +++ patch-2.7.1/src/pch.c 2015-06-11 15:49:47.305816401 -0500 @@ -157,20 +157,19 @@ open_patch_file (char const *filename) if (p_filesize != (file_offset) p_filesize) fatal ("patch file is too long"); next_intuit_at (file_pos, 1); - set_hunkmax(); } /* Make sure our dynamically realloced tables are malloced to begin with. */ -void +static void set_hunkmax (void) { if (!p_line) - p_line = (char **) malloc (hunkmax * sizeof *p_line); + p_line = (char **) xmalloc (hunkmax * sizeof *p_line); if (!p_len) - p_len = (size_t *) malloc (hunkmax * sizeof *p_len); + p_len = (size_t *) xmalloc (hunkmax * sizeof *p_len); if (!p_Char) - p_Char = malloc (hunkmax * sizeof *p_Char); + p_Char = xmalloc (hunkmax * sizeof *p_Char); } /* Enlarge the arrays containing the current hunk of patch. */ @@ -1196,6 +1195,8 @@ another_hunk (enum diff difftype, bool r char numbuf2[LINENUM_LENGTH_BOUND + 1]; char numbuf3[LINENUM_LENGTH_BOUND + 1]; + set_hunkmax(); + while (p_end >= 0) { if (p_end == p_efake) p_end = p_bfake; /* don't free twice */ Index: patch-2.7.1/src/pch.h =================================================================== --- patch-2.7.1.orig/src/pch.h 2015-06-11 15:49:47.309816385 -0500 +++ patch-2.7.1/src/pch.h 2015-06-11 15:49:47.305816401 -0500 @@ -46,7 +46,6 @@ bool pch_rename (void) _GL_ATTRIBUTE_PUR void do_ed_script (char const *, char const *, bool *, FILE *); void open_patch_file (char const *); void re_patch (void); -void set_hunkmax (void); void pch_normalize (enum diff); XTERN struct timespec p_timestamp[2]; /* timestamps in patch headers */ debian/patches/CVE-2018-6951.patch0000664000000000000000000000175613262672363013272 0ustar From f290f48a621867084884bfff87f8093c15195e6a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 12 Feb 2018 16:48:24 +0100 Subject: [PATCH] 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(-) Index: patch-2.7.1/src/pch.c =================================================================== --- patch-2.7.1.orig/src/pch.c +++ patch-2.7.1/src/pch.c @@ -972,7 +972,8 @@ intuit_diff_type (bool need_header, mode 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"); debian/changelog0000664000000000000000000004144513263141205011047 0ustar patch (2.7.1-4ubuntu2.4) trusty-security; urgency=medium * SECURITY UPDATE: Out-of-bounds access - debian/patches/CVE-2016-10713.patch: fix in src/pch.c. - CVE-2016-10713 * SECURITY UPDATE: Input validation vulnerability - debian/patches/CVE-2018-1000156.patch: fix in src/pch.c adding tests in Makefile.in, tests/ed-style. - debian/patches/0001-Fix-ed-style-test-failure.patch: - CVE-2018-1000156 * SECURITY UPDATE: NULL pointer dereference - debian/patches/CVE-2018-6951.patch: fix in src/pch.c. - CVE-2018-6951 -- Leonidas S. Barbosa Mon, 09 Apr 2018 11:14:01 -0300 patch (2.7.1-4ubuntu2.3) trusty-security; urgency=medium * SECURITY UPDATE: Denial of service via crafted patch - debian/patches/CVE-2014-9637.patch: Detect and exit upon memory allocation failures - CVE-2014-9637 * SECURITY UPDATE: Directory traversal via crafted patch - debian/patches/CVE-2015-1196.patch: Don't allow symlink targets to point outside of the current directory - CVE-2015-1196 * SECURITY UPDATE: Directory traversal via crafted patch - debian/patches/CVE-2015-1395.patch: Check the validity of both filenames during a rename or copy - CVE-2015-1395 * SECURITY UPDATE: Directory traversal via crafted patch - debian/patches/CVE-2015-1396.patch: Don't allow symlink targets to point outside of the current directory. This patch corrects the incomplete fix for CVE-2015-1196. - CVE-2015-1396 * debian/control: Add automake1.11 as a build-depends since some of the patches adjust Makefile.am files -- Tyler Hicks Mon, 22 Jun 2015 14:33:17 -0500 patch (2.7.1-4ubuntu2) trusty-proposed; urgency=medium * Fix the check for ed to be more robust against compiler optimizations. Fixes the build failure seen in trusty-updates. LP: #1435353. See Debian #729132 for the proposed fix. -- Matthias Klose Mon, 23 Mar 2015 15:22:31 +0100 patch (2.7.1-4ubuntu1) trusty-proposed; urgency=low [ James Hunt ] * Fix segfault due to incorrect usage (LP: #1306412). -- Brian Murray Thu, 01 May 2014 09:24:26 -0700 patch (2.7.1-4) unstable; urgency=low * New maintainer (closes: #728664). * Add manual last change date (closes: #674052). * Update Standards-Version to 3.9.5 . -- Laszlo Boszormenyi (GCS) Mon, 04 Nov 2013 12:36:11 +0000 patch (2.7.1-3) unstable; urgency=low * Call 'ed' without a path. Closes: #714423. * Update copyright for GPL v3. Closes: #664640. -- Christoph Berg Sun, 30 Jun 2013 16:14:19 +0200 patch (2.7.1-2) experimental; urgency=low * Add PATH_MAX workaround for GNU/Hurd. -- Christoph Berg Fri, 04 Jan 2013 11:08:47 +0100 patch (2.7.1-1) experimental; urgency=low * New upstream release. -- Christoph Berg Thu, 03 Jan 2013 17:34:45 +0100 patch (2.6.1.136-31a7-1) experimental; urgency=low * New upstream snapshot. * Use dh_auto_test. Closes: #627196 * 3.0 (quilt). * Fix Suggests broken in the last upload. -- Christoph Berg Wed, 25 Jan 2012 15:03:09 +0100 patch (2.6.1-2.1) unstable; urgency=low * NMU with maintainer approval * Set patch as Multi-Arch: foreign to allow use when cross-compiling -- Riku Voipio Mon, 16 Jan 2012 14:13:59 +0200 patch (2.6.1.85-423d-3) experimental; urgency=low * Remove lenny compatibility options -U --unified-reject-files and --global-reject-file. -- Christoph Berg Sun, 06 Feb 2011 20:42:58 +0100 patch (2.6.1.85-423d-2) experimental; urgency=low * Enable -m short option for --merge as documented in help and manpage. Closes: #597305. -- Christoph Berg Wed, 22 Sep 2010 22:09:13 +0200 patch (2.6.1.85-423d-1) experimental; urgency=low * New upstream snapshot. + Improved CR stripping heuristics: Closes: #484539. + Refuses to patch symlinks: Closes: #243309. + Preserves uid/gid/mode where possible: Closes: #262737. + Refuses to patch read-only files unless -f or -t are used: Closes: #274079. -- Christoph Berg Tue, 11 May 2010 09:59:01 +0200 patch (2.6.1-3) unstable; urgency=low * 3.0 (quilt). * Fix Suggests broken in the last upload. -- Christoph Berg Wed, 25 Jan 2012 15:00:41 +0100 patch (2.6.1-2) unstable; urgency=low * Use dh_auto_test. Closes: #627196 -- Christoph Berg Fri, 20 May 2011 23:45:48 +0200 patch (2.6.1-1) unstable; urgency=low * New upstream version. + Improved CR stripping heuristics. Closes: #484539 + Fixes: creates files and directories instead of asking for the file location. Closes: #568248 * Remove lenny compatibility options -U --unified-reject-files and --global-reject-file. * Null update-version.sh file so it doesn't interfere with the git repository the package is hosted in. -- Christoph Berg Sun, 06 Feb 2011 20:19:30 +0100 patch (2.6-2) unstable; urgency=low * Update watch file. * Section: vcs. * Suggests: diffutils-doc instead of diff-doc, thanks Christoph Anton Mitterer for spotting. Closes: #558974. -- Christoph Berg Wed, 02 Dec 2009 10:25:26 +0100 patch (2.6-1) unstable; urgency=low * New upstream version. Fixes: + Closes: #402737: append to .rej files instead of overwriting them + Closes: #372769: patch aborts with "pch.c:622: intuit_diff_type: Assertion `i0 != NONE' failed." + Closes: #558485: patch creates backup files with 000 perms for newly created files + Closes: #386188: -o creates empty output on null patch + Closes: #271946: fails to create new files when POSIXLY_CORRECT is set + Closes: #445309: output result to stdout * Document in NEWS that the unified-reject-files and global-reject-file options are now included upstream, but called differently. * Enable test suite. -- Christoph Berg Mon, 30 Nov 2009 15:22:49 +0100 patch (2.5.9-5) unstable; urgency=low * Convert packaging to quilt. * Tell lintian that part of the changelog is in a different format. * Bump Standards-Version. -- Christoph Berg Mon, 21 Apr 2008 21:04:02 +0200 patch (2.5.9-4) unstable; urgency=low * New maintainer (Closes: #349323). * Use dpatch, add patches: + unified-reject-files: write unified reject files (Closes: #26675). + global-reject-file: write a global reject file. + manpage-char: fix weird character. * Suggests: diff-doc. * Bump Standards-Version. -- Christoph Berg Sat, 28 Jan 2006 18:46:28 +0100 patch (2.5.9-3) unstable; urgency=low * Orphaned. -- Michael Fedrowitz Sun, 22 Jan 2006 11:40:22 +0100 patch (2.5.9-2) unstable; urgency=low * Standards-Version 3.6.1 (no changes required). * Applied upstream patch to fix CR stripping. (Closes: #196297) * Applied a patch from SUSE to prevent previously created backup files from being overwritten. (Closes: #248950) * Ran aclocal and autoconf to make the above patch work. * Touch aclocal.m4 and configure during build to prevent the usual time-skew problems. * Removed emacs vars from changelog. -- Michael Fedrowitz Sun, 18 Jul 2004 12:56:02 +0200 patch (2.5.9-1) unstable; urgency=low * New upstream release: - Handles filenames with spaces. (closes: #99808) - Don't try to stat output file after skipping a patch. (closes: #157232) - CR in hunk header doesn't trigger CR stripping. (closes: #192272) * Standards-Version 3.5.10 (no changes required). * Remove obsolete CFLAGS. (closes: #193403) * Update copyright file. -- Michael Fedrowitz Tue, 20 May 2003 21:13:37 +0200 patch (2.5.8-2) experimental; urgency=low * Standards-Version 3.5.8: - Support DEB_BUILD_GNU_TYPE and DEB_HOST_GNU_TYPE. - Support DEB_BUILD_OPTIONS noopt instead of debug. - Always build with -g. - Build depend on debhelper (>= 4.1.0) to get rid of /usr/doc link. * Use debhelper v4. * Minor fixes to copyright file. * Don't try to stat the output file if the user elected to skip a patch. (Fixes #157232, not closing since this is for experimental.) * Applied patch from upstream to strip trailing whitespace from filenames found in patch headers. (This fixes the glibc build.) -- Michael Fedrowitz Thu, 2 Jan 2003 19:37:49 +0100 patch (2.5.8-1) experimental; urgency=low * New upstream testing release. -- Michael Fedrowitz Mon, 3 Jun 2002 16:53:35 +0200 patch (2.5.7-1) experimental; urgency=low * New upstream testing release (which is likely to become the new stable release). -- Michael Fedrowitz Fri, 31 May 2002 19:01:55 +0200 patch (2.5.6-1) experimental; urgency=low * New upstream testing release (plus two patches from upstream which will be in the next version), which incorporates all of our patches or fixes the problems in different ways. * Uploading to experimental until final release. -- Michael Fedrowitz Wed, 29 May 2002 22:30:18 +0200 patch (2.5.4-11) unstable; urgency=low * Fix patch -D. (closes: #140247) * Improve indentation guessing for ed diffs. (closes: #140642) -- Michael Fedrowitz Sat, 6 Apr 2002 20:19:19 +0200 patch (2.5.4-10) unstable; urgency=low * Fix segfault on illegal arguments to certain options. (closes: #137222) -- Michael Fedrowitz Tue, 12 Mar 2002 22:48:53 +0100 patch (2.5.4-9) unstable; urgency=low * Debhelperized debian/rules. -- Michael Fedrowitz Sun, 10 Feb 2002 19:09:16 +0100 patch (2.5.4-8) unstable; urgency=low * New maintainer. (closes: #130835) * Call configure with ac_cv_sys_long_file_names=yes (all Debian systems have long file names, but the test may fail if the build target is invoked under fakeroot or is invoked as root while /usr is mounted read-only). (closes: #129257) * Don't build with -g by default. * Support DEB_BUILD_OPTIONS. * Standards-Version 3.5.6. -- Michael Fedrowitz Sat, 26 Jan 2002 15:17:35 +0100 patch (2.5.4-7) unstable; urgency=low * Orphaned this package. -- Adrian Bunk Fri, 25 Jan 2002 13:07:18 +0100 patch (2.5.4-6) unstable; urgency=high * Applied patches from RedHat for the following bugs: - Correct default suffix to .orig. (closes: #122476) - 'no such file' does now appear before the skip patch question. - Fix a possible segfault. -- Adrian Bunk Wed, 5 Dec 2001 12:15:10 +0100 patch (2.5.4-5) unstable; urgency=low * s/GNU Public License/GNU General Public License/ in debian/copyright. (closes: #102238) -- Adrian Bunk Sun, 15 Jul 2001 17:44:36 +0200 patch (2.5.4-4) unstable; urgency=low * Added a build dependency and a suggestion for ed. (closes: #86822) * Strip the binary better. -- Adrian Bunk Sat, 2 Jun 2001 18:35:53 +0200 patch (2.5.4-3) unstable; urgency=low * Added a patch from Alessandro Rubini that corrects messages about offsets. (closes: #68943) -- Adrian Bunk Tue, 16 Jan 2001 00:10:03 +0100 patch (2.5.4-2) unstable; urgency=low * Removed the build dependency on patch. There's no longer a debian/patches since 2.5.4-1 (all problems are fixed upstream) and therefore no call to patch when building the package. (closes: #76205) * Upload sponsored by Tony Mancill . -- Adrian Bunk Sun, 5 Nov 2000 17:48:00 +0100 patch (2.5.4-1) unstable; urgency=low * New upstream release. (closes: #49119) * Upload sponsored by Tony Mancill . -- Adrian Bunk Sat, 5 Aug 2000 22:46:32 +0200 patch (2.5-3) unstable; urgency=low * New Maintainer * Removed patch.log from source * Debian patches to the source are now in debian/patches * /usr/doc -> /usr/share/doc * /usr/man -> /usr/share/man * Added Build-Depends to debian/control * Added Section to debian/control * Added "-isp" to dpkg-gencontrol * Added "-g -Wall" to CFLAGS * Removed "-s" from LDFLAGS * Standards-Version: 3.1.1.1 * Updated copyright * Approved the patches from the NMUs Closes: #57100, #56621, #58811 * Upload sponsored by Tony Mancill -- Adrian Bunk Mon, 29 May 2000 14:29:05 +0200 patch (2.5-2.2) frozen unstable; urgency=medium * NMU * Fixed a buffer overrun in mktemp (closes: #58811) -- Randolph Chung Sat, 26 Feb 2000 12:27:09 -0700 patch (2.5-2.1) frozen unstable; urgency=medium * Non-maintainer upload * Update location of GPL and Artistic license in copyright * Apply patch from Colin Phipps to fix unsafe file handling, Closes: Bug#56621 * Add -D_XOPEN_SOURCE=500 to CFLAGS so patch builds correctly again Closes: Bug#57100 -- Wichert Akkerman Sun, 20 Feb 2000 16:39:13 +0100 patch (2.5-2) unstable; urgency=low * Apply patch from "James A" to fix segv when skipping a patch. This does fix (Bug #14693, #16116, #16391). It might fix #14653 but I need more information. * Don't use su during build - allow fakeroot and friends to do their stuff. (Fixes #15459, #18319) * Use original source. * Updated standards to 2.4.0.0. (No changes.) * lintian clean. -- Darren Stalder Sat, 14 Mar 1998 19:11:46 -0800 patch (2.5-1) unstable; urgency=low * New upstream version. * Problem in patch with Index overriding filenames fixed in upstream. (Bug #10420) * Compiled for libc6. (Bug #11699) * Updated to conform to debian packaging standards 2.3.0.0. -- Darren Stalder Fri, 19 Sep 1997 23:41:38 +0000 patch (2.2-1) unstable; urgency=low * Larry Wall said that patch is under the Artistic License. (Bug #7215) * Make sure that ChangeLog actually becomes /usr/doc/patch/changelog.gz to conform to policy. (Bug #7237) * Upstream changes fix the problem with basename. (Bug #8324, #8811) * Upstream changes fix "no newline" problem. (Bug #9754) * Updated to conform to debian packaging standards 2.1.3.2. -- Darren Stalder Fri, 19 Sep 1997 23:23:32 +0000 patch (2.1-11) unstable; urgency=high * Built completely as root to bypass autoconf bug where it doesn't detect long filename support * Waiting on Larry Wall to see if patch will have to be moved to non-free. -- Darren Stalder Mon, 10 Feb 1997 03:18:58 -0800 patch (2.1-10) unstable; urgency=low * Updated to dpkg standards version 2.1.1.2, changed the rules file to the format that I'm used to. This fixes #3348. * Reverted the Makefile.in to the upstream version since the changed parameters can be passed on the command line * Reverted util.h and backupfile.c since the basename problem isn't there anymore. * Applied patch by Herbert Xu to handle long lines. -- Darren Stalder Sat, 1 Feb 1997 17:08:10 -0800 The following changes are from Bill Mitchell: Changes for the debian patch-2.1-9 elf package * rebuilt for elf Changes for the debian patch-2.1-8 binary distribution 1. Install patch.1 in /usr/man/man1, not /usr/man (oops). Changes for the debian patch-2.1-7 binary distribution 1. Changed debian.rules file to Ian Murdock style. 2. Cleaned up control file description and extended description. 3. In debian.rules, add -DLINUX to CFLAGS. 4. In util.h, added #ifndef LINUX block around obsolete basename() declaration. It conflicts with the declaration in the file /usr/include/unistd.h in the linux 1.2.1 kernel sources. 5. In backupfile, added #ifndef LINUX block around obsolete basename() declarations. Changes for the debian patch-2.1-6 binary distribution 1. Removed leading tab on "include debian.rules.inc" line of debian.rules Changes for the debian patch-2.1-5 binary distribution 1. Changes for new packaging guidelines preinst and postinst scripts return exit status use debian.rules.inc 1.6 to support new package naming conventions debian.rules changes for new debian.rules.inc Changes for the debian patch-2.1-4 binary distribution 1. Modified debian.rules to use debian.rules.inc Changes for the debian patch-2.1-3 binary distribution 1. For the debian.rules target build: pass CFLAGS and LDFLAGS to Makefile Changes for the debian patch-2.1-2 binary distribution 1. Corrected permissions on man pages from 640 to 644 Changes for the patch-2.1-1 debian binary distribution 1. Renamed README.debian to debian.README 2. Renamed COPYRIGHT.debian to debian.COPYRIGHT 3. Added debian.control 4. Added debian.rules debian/patch.lintian-overrides0000664000000000000000000000005011523515177013651 0ustar patch: syntax-error-in-debian-changelog