diff -up patch-2.7.1/NEWS.CVE-2015-1196 patch-2.7.1/NEWS diff -up patch-2.7.1/src/pch.c.CVE-2015-1196 patch-2.7.1/src/pch.c --- patch-2.7.1/src/pch.c.CVE-2015-1196 2015-01-20 12:23:34.808516117 +0000 +++ patch-2.7.1/src/pch.c 2015-01-20 12:24:15.763652714 +0000 @@ -454,6 +454,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 diff -up patch-2.7.1/src/pch.h.CVE-2015-1196 patch-2.7.1/src/pch.h --- patch-2.7.1/src/pch.h.CVE-2015-1196 2012-09-22 18:37:21.000000000 +0100 +++ patch-2.7.1/src/pch.h 2015-01-20 12:24:15.763652714 +0000 @@ -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; diff -up patch-2.7.1/src/util.c.CVE-2015-1196 patch-2.7.1/src/util.c --- patch-2.7.1/src/util.c.CVE-2015-1196 2015-01-20 12:23:34.808516117 +0000 +++ patch-2.7.1/src/util.c 2015-01-20 12:24:15.764652717 +0000 @@ -478,6 +478,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) diff -up patch-2.7.1/tests/symlinks.CVE-2015-1196 patch-2.7.1/tests/symlinks --- patch-2.7.1/tests/symlinks.CVE-2015-1196 2012-09-19 02:18:42.000000000 +0100 +++ patch-2.7.1/tests/symlinks 2015-01-20 12:24:15.764652717 +0000 @@ -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 <