debian: apply security fixes from 2.20.2

Apply these as patches instead of using an upstream tarball because
the upstream tarball has not been created yet.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
diff --git a/debian/changelog b/debian/changelog
index 17e6457..d11fe87 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,28 @@
+git (1:2.11.0-3+deb9u5) stretch-security; urgency=high
+
+  * Apply patches addressing the security issues CVE-2019-1348,
+    CVE-2019-1349, CVE-2019-1352, CVE-2019-1353, and CVE-2019-1387.
+
+    Credit for finding these vulnerabilities goes to Microsoft
+    Security Response Center, in particular to Nicolas Joly. Fixes
+    were provided by Jeff King and Johannes Schindelin with help
+    from Garima Singh.
+
+  * Reject setting "update = !command" in .gitmodules. This makes
+    the behavior better match Git 2.24.1 which made the same change
+    to address the arbitrary code execution issue CVE-2019-19604
+    (which does not affect Git versions before 2.20.0).
+
+    Also reject "update = !command" in fsck. This ensures that if
+    Git is run as a server with "transfer.fsckObjects" enabled,
+    it cannot be used to attack clients vulnerable to
+    CVE-2019-19604.
+
+    Credit for finding this vulnerability goes to Joern
+    Schneeweisz from GitLab.
+
+ -- Jonathan Nieder <jrnieder@gmail.com>  Tue, 10 Dec 2019 08:14:58 +0000
+
 git (1:2.11.0-3+deb9u4) stretch-security; urgency=high
 
   * Fix CVE-2018-17456, arbitrary code execution via submodule URLs
diff --git a/debian/patches/Disallow-dubiously-nested-submodule-git-directories.diff b/debian/patches/Disallow-dubiously-nested-submodule-git-directories.diff
new file mode 100644
index 0000000..1bf9004
--- /dev/null
+++ b/debian/patches/Disallow-dubiously-nested-submodule-git-directories.diff
@@ -0,0 +1,214 @@
+From 392f99a5d2174e6124f829d034bac6755c33119d Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Tue, 1 Oct 2019 23:27:18 +0200
+Subject: Disallow dubiously-nested submodule git directories
+
+commit a8dee3ca610f5a1d403634492136c887f83b59d2 upstream.
+
+Currently it is technically possible to let a submodule's git
+directory point right into the git dir of a sibling submodule.
+
+Example: the git directories of two submodules with the names `hippo`
+and `hippo/hooks` would be `.git/modules/hippo/` and
+`.git/modules/hippo/hooks/`, respectively, but the latter is already
+intended to house the former's hooks.
+
+In most cases, this is just confusing, but there is also a (quite
+contrived) attack vector where Git can be fooled into mistaking remote
+content for file contents it wrote itself during a recursive clone.
+
+Let's plug this bug.
+
+To do so, we introduce the new function `validate_submodule_git_dir()`
+which simply verifies that no git dir exists for any leading directories
+of the submodule name (if there are any).
+
+Note: this patch specifically continues to allow sibling modules names
+of the form `core/lib`, `core/doc`, etc, as long as `core` is not a
+submodule name.
+
+This fixes CVE-2019-1387.
+
+[jn: backported to 2.11.y:
+ - port to git-submodule.sh
+ - use explicit chdir to emulate test_commit -C in test]
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ builtin/submodule--helper.c | 24 ++++++++++++++++++++++
+ git-submodule.sh            |  5 +++++
+ submodule.c                 | 41 +++++++++++++++++++++++++++++++++++++
+ submodule.h                 |  5 +++++
+ t/t7415-submodule-names.sh  | 23 +++++++++++++++++++++
+ 5 files changed, 98 insertions(+)
+
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index 9e0f985501..6f33a665df 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -638,6 +638,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 	} else
+ 		path = xstrdup(path);
+ 
++	if (validate_submodule_git_dir(sm_gitdir, name) < 0)
++		die(_("refusing to create/use '%s' in another submodule's "
++			"git dir"), sm_gitdir);
++
+ 	if (!file_exists(sm_gitdir)) {
+ 		if (safe_create_leading_directories_const(sm_gitdir) < 0)
+ 			die(_("could not create directory '%s'"), sm_gitdir);
+@@ -1111,6 +1115,25 @@ static int check_name(int argc, const char **argv, const char *prefix)
+ 	return 0;
+ }
+ 
++/*
++ * Exit non-zero if the proposed submodule repository path is inside
++ * another submodules' git dir.
++ */
++static int validate_git_dir(int argc, const char **argv, const char *prefix)
++{
++	char *sm_gitdir;
++
++	if (argc != 3)
++		usage("git submodule--helper validate-git-dir <path> <name>");
++	sm_gitdir = xstrdup(argv[1]);
++	if (validate_submodule_git_dir(sm_gitdir, argv[2]) < 0) {
++		free(sm_gitdir);
++		return 1;
++	}
++	free(sm_gitdir);
++	return 0;
++}
++
+ struct cmd_struct {
+ 	const char *cmd;
+ 	int (*fn)(int, const char **, const char *);
+@@ -1127,6 +1150,7 @@ static struct cmd_struct commands[] = {
+ 	{"init", module_init},
+ 	{"remote-branch", resolve_remote_submodule_branch},
+ 	{"check-name", check_name},
++	{"validate-git-dir", validate_git_dir},
+ };
+ 
+ int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
+diff --git a/git-submodule.sh b/git-submodule.sh
+index 81495c018f..f4e6b0204d 100755
+--- a/git-submodule.sh
++++ b/git-submodule.sh
+@@ -242,6 +242,11 @@ Use -f if you really want to add it." >&2
+ 		fi
+ 
+ 	else
++		sm_gitdir=".git/modules/$sm_name"
++		if ! git submodule--helper validate-git-dir "$sm_gitdir" "$sm_name"
++		then
++			die "$(eval_gettextln "refusing to create/use '\$sm_gitdir' in another submodule's git dir")"
++		fi
+ 		if test -d ".git/modules/$sm_name"
+ 		then
+ 			if test -z "$force"
+diff --git a/submodule.c b/submodule.c
+index 6f7d883de9..060e67f7e2 100644
+--- a/submodule.c
++++ b/submodule.c
+@@ -1251,6 +1251,47 @@ int parallel_submodules(void)
+ 	return parallel_jobs;
+ }
+ 
++int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
++{
++	size_t len = strlen(git_dir), suffix_len = strlen(submodule_name);
++	char *p;
++	int ret = 0;
++
++	if (len <= suffix_len || (p = git_dir + len - suffix_len)[-1] != '/' ||
++	    strcmp(p, submodule_name))
++		die("BUG: submodule name '%s' not a suffix of git dir '%s'",
++		    submodule_name, git_dir);
++
++	/*
++	 * We prevent the contents of sibling submodules' git directories to
++	 * clash.
++	 *
++	 * Example: having a submodule named `hippo` and another one named
++	 * `hippo/hooks` would result in the git directories
++	 * `.git/modules/hippo/` and `.git/modules/hippo/hooks/`, respectively,
++	 * but the latter directory is already designated to contain the hooks
++	 * of the former.
++	 */
++	for (; *p; p++) {
++		if (is_dir_sep(*p)) {
++			char c = *p;
++
++			*p = '\0';
++			if (is_git_directory(git_dir))
++				ret = -1;
++			*p = c;
++
++			if (ret < 0)
++				return error(_("submodule git dir '%s' is "
++					       "inside git dir '%.*s'"),
++					     git_dir,
++					     (int)(p - git_dir), git_dir);
++		}
++	}
++
++	return 0;
++}
++
+ void prepare_submodule_repo_env(struct argv_array *out)
+ {
+ 	const char * const *var;
+diff --git a/submodule.h b/submodule.h
+index d9e197a948..4e7d6f12f1 100644
+--- a/submodule.h
++++ b/submodule.h
+@@ -68,6 +68,11 @@ int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_nam
+ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
+ int parallel_submodules(void);
+ 
++/*
++ * Make sure that no submodule's git dir is nested in a sibling submodule's.
++ */
++int validate_submodule_git_dir(char *git_dir, const char *submodule_name);
++
+ /*
+  * Prepare the "env_array" parameter of a "struct child_process" for executing
+  * a submodule by clearing any repo-specific envirionment variables, but
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index b3a421d4fd..fd50ae1fb3 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -183,4 +183,27 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
+ 	! grep gitdir squatting-clone/d/a/git~2
+ '
+ 
++test_expect_success 'git dirs of sibling submodules must not be nested' '
++	git init nested &&
++	(
++		cd nested &&
++		test_commit nested &&
++		cat >.gitmodules <<-EOF &&
++		[submodule "hippo"]
++			url = .
++			path = thing1
++		[submodule "hippo/hooks"]
++			url = .
++			path = thing2
++		EOF
++		git clone . thing1 &&
++		git clone . thing2 &&
++		git add .gitmodules thing1 thing2 &&
++		test_tick &&
++		git commit -m nested
++	) &&
++	test_must_fail git clone --recurse-submodules nested clone 2>err &&
++	test_i18ngrep "is inside git dir" err
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/clone-recurse-submodules-prevent-name-squatting-on-Wi.diff b/debian/patches/clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
new file mode 100644
index 0000000..5b5fa61
--- /dev/null
+++ b/debian/patches/clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
@@ -0,0 +1,238 @@
+From f4e9ab82ab3230776d92b9dd7a007fe66929d21a Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Thu, 12 Sep 2019 14:20:39 +0200
+Subject: clone --recurse-submodules: prevent name squatting on Windows
+
+commit 0060fd1511b94c918928fa3708f69a3f33895a4a upstream.
+
+In addition to preventing `.git` from being tracked by Git, on Windows
+we also have to prevent `git~1` from being tracked, as the default NTFS
+short name (also known as the "8.3 filename") for the file name `.git`
+is `git~1`, otherwise it would be possible for malicious repositories to
+write directly into the `.git/` directory, e.g. a `post-checkout` hook
+that would then be executed _during_ a recursive clone.
+
+When we implemented appropriate protections in 2b4c6efc821 (read-cache:
+optionally disallow NTFS .git variants, 2014-12-16), we had analyzed
+carefully that the `.git` directory or file would be guaranteed to be
+the first directory entry to be written. Otherwise it would be possible
+e.g. for a file named `..git` to be assigned the short name `git~1` and
+subsequently, the short name generated for `.git` would be `git~2`. Or
+`git~3`. Or even `~9999999` (for a detailed explanation of the lengths
+we have to go to protect `.gitmodules`, see the commit message of
+e7cb0b4455c (is_ntfs_dotgit: match other .git files, 2018-05-11)).
+
+However, by exploiting two issues (that will be addressed in a related
+patch series close by), it is currently possible to clone a submodule
+into a non-empty directory:
+
+- On Windows, file names cannot end in a space or a period (for
+  historical reasons: the period separating the base name from the file
+  extension was not actually written to disk, and the base name/file
+  extension was space-padded to the full 8/3 characters, respectively).
+  Helpfully, when creating a directory under the name, say, `sub.`, that
+  trailing period is trimmed automatically and the actual name on disk
+  is `sub`.
+
+  This means that while Git thinks that the submodule names `sub` and
+  `sub.` are different, they both access `.git/modules/sub/`.
+
+- While the backslash character is a valid file name character on Linux,
+  it is not so on Windows. As Git tries to be cross-platform, it
+  therefore allows backslash characters in the file names stored in tree
+  objects.
+
+  Which means that it is totally possible that a submodule `c` sits next
+  to a file `c\..git`, and on Windows, during recursive clone a file
+  called `..git` will be written into `c/`, of course _before_ the
+  submodule is cloned.
+
+Note that the actual exploit is not quite as simple as having a
+submodule `c` next to a file `c\..git`, as we have to make sure that the
+directory `.git/modules/b` already exists when the submodule is checked
+out, otherwise a different code path is taken in `module_clone()` that
+does _not_ allow a non-empty submodule directory to exist already.
+
+Even if we will address both issues nearby (the next commit will
+disallow backslash characters in tree entries' file names on Windows,
+and another patch will disallow creating directories/files with trailing
+spaces or periods), it is a wise idea to defend in depth against this
+sort of attack vector: when submodules are cloned recursively, we now
+_require_ the directory to be empty, addressing CVE-2019-1349.
+
+Note: the code path we patch is shared with the code path of `git
+submodule update --init`, which must not expect, in general, that the
+directory is empty. Hence we have to introduce the new option
+`--force-init` and hand it all the way down from `git submodule` to the
+actual `git submodule--helper` process that performs the initial clone.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ builtin/clone.c             |  2 +-
+ builtin/submodule--helper.c | 13 ++++++++++++-
+ git-submodule.sh            |  6 ++++++
+ t/t7415-submodule-names.sh  | 31 +++++++++++++++++++++++++++++++
+ 4 files changed, 50 insertions(+), 2 deletions(-)
+
+diff --git a/builtin/clone.c b/builtin/clone.c
+index 6c76a6ed66..2e5543ac50 100644
+--- a/builtin/clone.c
++++ b/builtin/clone.c
+@@ -735,7 +735,7 @@ static int checkout(int submodule_progress)
+ 
+ 	if (!err && option_recursive) {
+ 		struct argv_array args = ARGV_ARRAY_INIT;
+-		argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
++		argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
+ 
+ 		if (option_shallow_submodules == 1)
+ 			argv_array_push(&args, "--depth=1");
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index b6801b6c7a..9e0f985501 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -12,6 +12,7 @@
+ #include "remote.h"
+ #include "refs.h"
+ #include "connect.h"
++#include "dir.h"
+ 
+ static char *get_default_remote(void)
+ {
+@@ -584,6 +585,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 	struct strbuf rel_path = STRBUF_INIT;
+ 	struct strbuf sb = STRBUF_INIT;
+ 	struct string_list reference = STRING_LIST_INIT_NODUP;
++	int require_init = 0;
+ 
+ 	struct option module_clone_options[] = {
+ 		OPT_STRING(0, "prefix", &prefix,
+@@ -607,6 +609,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 		OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
+ 		OPT_BOOL(0, "progress", &progress,
+ 			   N_("force cloning progress")),
++		OPT_BOOL(0, "require-init", &require_init,
++			   N_("disallow cloning into non-empty directory")),
+ 		OPT_END()
+ 	};
+ 
+@@ -645,6 +649,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
+ 			die(_("clone of '%s' into submodule path '%s' failed"),
+ 			    url, path);
+ 	} else {
++		if (require_init && !access(path, X_OK) && !is_empty_dir(path))
++			die(_("directory not empty: '%s'"), path);
+ 		if (safe_create_leading_directories_const(path) < 0)
+ 			die(_("could not create directory '%s'"), path);
+ 		strbuf_addf(&sb, "%s/index", sm_gitdir);
+@@ -695,6 +701,7 @@ struct submodule_update_clone {
+ 	int quiet;
+ 	int recommend_shallow;
+ 	struct string_list references;
++	unsigned require_init;
+ 	const char *depth;
+ 	const char *recursive_prefix;
+ 	const char *prefix;
+@@ -710,7 +717,7 @@ struct submodule_update_clone {
+ 	int failed_clones_nr, failed_clones_alloc;
+ };
+ #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
+-	SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, \
++	SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
+ 	NULL, NULL, NULL, \
+ 	STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
+ 
+@@ -820,6 +827,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
+ 		argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
+ 	if (suc->recommend_shallow && sub->recommend_shallow == 1)
+ 		argv_array_push(&child->args, "--depth=1");
++	if (suc->require_init)
++		argv_array_push(&child->args, "--require-init");
+ 	argv_array_pushl(&child->args, "--path", sub->path, NULL);
+ 	argv_array_pushl(&child->args, "--name", sub->name, NULL);
+ 	argv_array_pushl(&child->args, "--url", url, NULL);
+@@ -963,6 +972,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
+ 		OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
+ 		OPT_BOOL(0, "progress", &suc.progress,
+ 			    N_("force cloning progress")),
++		OPT_BOOL(0, "require-init", &suc.require_init,
++			   N_("disallow cloning into non-empty directory")),
+ 		OPT_END()
+ 	};
+ 
+diff --git a/git-submodule.sh b/git-submodule.sh
+index cee4ddc04b..81495c018f 100755
+--- a/git-submodule.sh
++++ b/git-submodule.sh
+@@ -37,6 +37,7 @@ reference=
+ cached=
+ recursive=
+ init=
++require_init=
+ files=
+ remote=
+ nofetch=
+@@ -510,6 +511,10 @@ cmd_update()
+ 		-i|--init)
+ 			init=1
+ 			;;
++		--require-init)
++			init=1
++			require_init=1
++			;;
+ 		--remote)
+ 			remote=1
+ 			;;
+@@ -588,6 +593,7 @@ cmd_update()
+ 		${update:+--update "$update"} \
+ 		${reference:+"$reference"} \
+ 		${depth:+--depth "$depth"} \
++		${require_init:+--require-init} \
+ 		${recommend_shallow:+"$recommend_shallow"} \
+ 		${jobs:+$jobs} \
+ 		"$@" || echo "#unmatched" $?
+diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
+index 0b2a51ffc8..b3a421d4fd 100755
+--- a/t/t7415-submodule-names.sh
++++ b/t/t7415-submodule-names.sh
+@@ -152,4 +152,35 @@ test_expect_success 'fsck detects symlinked .gitmodules file' '
+ 	)
+ '
+ 
++test_expect_success MINGW 'prevent git~1 squatting on Windows' '
++	git init squatting &&
++	(
++		cd squatting &&
++		mkdir a &&
++		touch a/..git &&
++		git add a/..git &&
++		test_tick &&
++		git commit -m initial &&
++
++		modules="$(test_write_lines \
++			"[submodule \"b.\"]" "url = ." "path = c" \
++			"[submodule \"b\"]" "url = ." "path = d\\\\a" |
++			git hash-object -w --stdin)" &&
++		rev="$(git rev-parse --verify HEAD)" &&
++		hash="$(echo x | git hash-object -w --stdin)" &&
++		git update-index --add \
++			--cacheinfo 100644,$modules,.gitmodules \
++			--cacheinfo 160000,$rev,c \
++			--cacheinfo 160000,$rev,d\\a \
++			--cacheinfo 100644,$hash,d./a/x \
++			--cacheinfo 100644,$hash,d./a/..git &&
++		test_tick &&
++		git commit -m "module"
++	) &&
++	test_must_fail git \
++		clone --recurse-submodules squatting squatting-clone 2>err &&
++	test_i18ngrep "directory not empty" err &&
++	! grep gitdir squatting-clone/d/a/git~2
++'
++
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fast-import-delay-creating-leading-directories-for-ex.diff b/debian/patches/fast-import-delay-creating-leading-directories-for-ex.diff
new file mode 100644
index 0000000..4b53be8
--- /dev/null
+++ b/debian/patches/fast-import-delay-creating-leading-directories-for-ex.diff
@@ -0,0 +1,90 @@
+From f6d36ce67e58fad3a35a19c3b3d74914be7fa1c3 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:33:48 -0400
+Subject: fast-import: delay creating leading directories for export-marks
+
+commit 019683025f1b14d7cb671312ab01f7330e9b33e7 upstream.
+
+When we parse the --export-marks option, we don't immediately open the
+file, but we do create any leading directories. This can be especially
+confusing when a command-line option overrides an in-stream one, in
+which case we'd create the leading directory for the in-stream file,
+even though we never actually write the file.
+
+Let's instead create the directories just before opening the file, which
+means we'll create only useful directories. Note that this could change
+the handling of relative paths if we chdir() in between, but we don't
+actually do so; the only permanent chdir is from setup_git_directory()
+which runs before either code path (potentially we should take the
+pre-setup dir into account to avoid surprising the user, but that's an
+orthogonal change).
+
+The test just adapts the existing "override" test to use paths with
+leading directories. This checks both that the correct directory is
+created (which worked before but was not tested), and that the
+overridden one is not (our new fix here).
+
+While we're here, let's also check the error result of
+safe_create_leading_directories(). We'd presumably notice any failure
+immediately after when we try to open the file itself, but we can give a
+more specific error message in this case.
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c          |  7 ++++++-
+ t/t9300-fast-import.sh | 13 +++++++++++--
+ 2 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index b9144e90bd..c841953cdc 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -1860,6 +1860,12 @@ static void dump_marks(void)
+ 	if (!export_marks_file || (import_marks_file && !import_marks_file_done))
+ 		return;
+ 
++	if (safe_create_leading_directories_const(export_marks_file)) {
++		failure |= error_errno("unable to create leading directories of %s",
++				       export_marks_file);
++		return;
++	}
++
+ 	if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
+ 		failure |= error_errno("Unable to write marks file %s",
+ 				       export_marks_file);
+@@ -3259,7 +3265,6 @@ static void option_active_branches(const char *branches)
+ static void option_export_marks(const char *marks)
+ {
+ 	export_marks_file = make_fast_import_path(marks);
+-	safe_create_leading_directories_const(export_marks_file);
+ }
+ 
+ static void option_cat_blob_fd(const char *fd)
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index bf5b937a4c..f6fba4ae16 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2132,8 +2132,17 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ '
+ 
+ test_expect_success 'R: export-marks options can be overridden by commandline options' '
+-	git fast-import --export-marks=other.marks <input &&
+-	grep :1 other.marks
++	cat >input <<-\EOF &&
++	feature export-marks=feature-sub/git.marks
++	blob
++	mark :1
++	data 3
++	hi
++
++	EOF
++	git fast-import --export-marks=cmdline-sub/other.marks <input &&
++	grep :1 cmdline-sub/other.marks &&
++	test_path_is_missing feature-sub
+ '
+ 
+ test_expect_success 'R: catch typo in marks file name' '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fast-import-disallow-feature-export-marks-by-default.diff b/debian/patches/fast-import-disallow-feature-export-marks-by-default.diff
new file mode 100644
index 0000000..cd2f258
--- /dev/null
+++ b/debian/patches/fast-import-disallow-feature-export-marks-by-default.diff
@@ -0,0 +1,262 @@
+From 4c5ac3beb563991d1f0a3002ebe00255b8846c0d Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 14:37:26 -0400
+Subject: fast-import: disallow "feature export-marks" by default
+
+commit 68061e3470210703cb15594194718d35094afdc0 upstream.
+
+The fast-import stream command "feature export-marks=<path>" lets the
+stream write marks to an arbitrary path. This may be surprising if you
+are running fast-import against an untrusted input (which otherwise
+cannot do anything except update Git objects and refs).
+
+Let's disallow the use of this feature by default, and provide a
+command-line option to re-enable it (you can always just use the
+command-line --export-marks as well, but the in-stream version provides
+an easy way for exporters to control the process).
+
+This is a backwards-incompatible change, since the default is flipping
+to the new, safer behavior. However, since the main users of the
+in-stream versions would be import/export-based remote helpers, and
+since we trust remote helpers already (which are already running
+arbitrary code), we'll pass the new option by default when reading a
+remote helper's stream. This should minimize the impact.
+
+Note that the implementation isn't totally simple, as we have to work
+around the fact that fast-import doesn't parse its command-line options
+until after it has read any "feature" lines from the stream. This is how
+it lets command-line options override in-stream. But in our case, it's
+important to parse the new --allow-unsafe-features first.
+
+There are three options for resolving this:
+
+  1. Do a separate "early" pass over the options. This is easy for us to
+     do because there are no command-line options that allow the
+     "unstuck" form (so there's no chance of us mistaking an argument
+     for an option), though it does introduce a risk of incorrect
+     parsing later (e.g,. if we convert to parse-options).
+
+  2. Move the option parsing phase back to the start of the program, but
+     teach the stream-reading code never to override an existing value.
+     This is tricky, because stream "feature" lines override each other
+     (meaning we'd have to start tracking the source for every option).
+
+  3. Accept that we might parse a "feature export-marks" line that is
+     forbidden, as long we don't _act_ on it until after we've parsed
+     the command line options.
+
+     This would, in fact, work with the current code, but only because
+     the previous patch fixed the export-marks parser to avoid touching
+     the filesystem.
+
+     So while it works, it does carry risk of somebody getting it wrong
+     in the future in a rather subtle and unsafe way.
+
+I've gone with option (1) here as simple, safe, and unlikely to cause
+regressions.
+
+This fixes CVE-2019-1348.
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/git-fast-import.txt | 14 ++++++++++++++
+ fast-import.c                     | 25 +++++++++++++++++++++++++
+ t/t9300-fast-import.sh            | 23 +++++++++++++++--------
+ transport-helper.c                |  1 +
+ 4 files changed, 55 insertions(+), 8 deletions(-)
+
+diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
+index 2b762654bf..55b4b203fb 100644
+--- a/Documentation/git-fast-import.txt
++++ b/Documentation/git-fast-import.txt
+@@ -50,6 +50,20 @@ OPTIONS
+ 	memory used by fast-import during this run.  Showing this output
+ 	is currently the default, but can be disabled with --quiet.
+ 
++--allow-unsafe-features::
++	Many command-line options can be provided as part of the
++	fast-import stream itself by using the `feature` or `option`
++	commands. However, some of these options are unsafe (e.g.,
++	allowing fast-import to access the filesystem outside of the
++	repository). These options are disabled by default, but can be
++	allowed by providing this option on the command line.  This
++	currently impacts only the `feature export-marks` command.
+++
++	Only enable this option if you trust the program generating the
++	fast-import stream! This option is enabled automatically for
++	remote-helpers that use the `import` capability, as they are
++	already trusted to run their own code.
++
+ Options for Frontends
+ ~~~~~~~~~~~~~~~~~~~~~
+ 
+diff --git a/fast-import.c b/fast-import.c
+index c841953cdc..7ed61a9dad 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -367,6 +367,7 @@ static uintmax_t next_mark;
+ static struct strbuf new_data = STRBUF_INIT;
+ static int seen_data_command;
+ static int require_explicit_termination;
++static int allow_unsafe_features;
+ 
+ /* Signal handling */
+ static volatile sig_atomic_t checkpoint_requested;
+@@ -3313,6 +3314,8 @@ static int parse_one_option(const char *option)
+ 		show_stats = 0;
+ 	} else if (!strcmp(option, "stats")) {
+ 		show_stats = 1;
++	} else if (!strcmp(option, "allow-unsafe-features")) {
++		; /* already handled during early option parsing */
+ 	} else {
+ 		return 0;
+ 	}
+@@ -3320,6 +3323,13 @@ static int parse_one_option(const char *option)
+ 	return 1;
+ }
+ 
++static void check_unsafe_feature(const char *feature, int from_stream)
++{
++	if (from_stream && !allow_unsafe_features)
++		die(_("feature '%s' forbidden in input without --allow-unsafe-features"),
++		    feature);
++}
++
+ static int parse_one_feature(const char *feature, int from_stream)
+ {
+ 	const char *arg;
+@@ -3331,6 +3341,7 @@ static int parse_one_feature(const char *feature, int from_stream)
+ 	} else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
+ 		option_import_marks(arg, from_stream, 1);
+ 	} else if (skip_prefix(feature, "export-marks=", &arg)) {
++		check_unsafe_feature(feature, from_stream);
+ 		option_export_marks(arg);
+ 	} else if (!strcmp(feature, "get-mark")) {
+ 		; /* Don't die - this feature is supported */
+@@ -3468,6 +3479,20 @@ int cmd_main(int argc, const char **argv)
+ 	avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
+ 	marks = pool_calloc(1, sizeof(struct mark_set));
+ 
++	/*
++	 * We don't parse most options until after we've seen the set of
++	 * "feature" lines at the start of the stream (which allows the command
++	 * line to override stream data). But we must do an early parse of any
++	 * command-line options that impact how we interpret the feature lines.
++	 */
++	for (i = 1; i < argc; i++) {
++		const char *arg = argv[i];
++		if (*arg != '-' || !strcmp(arg, "--"))
++			break;
++		if (!strcmp(arg, "--allow-unsafe-features"))
++			allow_unsafe_features = 1;
++	}
++
+ 	global_argc = argc;
+ 	global_argv = argv;
+ 
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index f6fba4ae16..30b7c8a1f6 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2117,6 +2117,11 @@ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	test_must_fail git fast-import <input
+ '
+ 
++test_expect_success 'R: export-marks feature forbidden by default' '
++	echo "feature export-marks=git.marks" >input &&
++	test_must_fail git fast-import <input
++'
++
+ test_expect_success 'R: export-marks feature results in a marks file being created' '
+ 	cat >input <<-EOF &&
+ 	feature export-marks=git.marks
+@@ -2127,7 +2132,7 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ 
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	grep :1 git.marks
+ '
+ 
+@@ -2140,7 +2145,8 @@ test_expect_success 'R: export-marks options can be overridden by commandline op
+ 	hi
+ 
+ 	EOF
+-	git fast-import --export-marks=cmdline-sub/other.marks <input &&
++	git fast-import --allow-unsafe-features \
++			--export-marks=cmdline-sub/other.marks <input &&
+ 	grep :1 cmdline-sub/other.marks &&
+ 	test_path_is_missing feature-sub
+ '
+@@ -2148,7 +2154,7 @@ test_expect_success 'R: export-marks options can be overridden by commandline op
+ test_expect_success 'R: catch typo in marks file name' '
+ 	test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+ 	echo "feature import-marks=nonexistent.marks" |
+-	test_must_fail git fast-import
++	test_must_fail git fast-import --allow-unsafe-features
+ '
+ 
+ test_expect_success 'R: import and output marks can be the same file' '
+@@ -2253,7 +2259,7 @@ test_expect_success 'R: import to output marks works without any content' '
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2263,7 +2269,7 @@ test_expect_success 'R: import marks prefers commandline marks file over the str
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	git fast-import --import-marks=marks.out <input &&
++	git fast-import --import-marks=marks.out --allow-unsafe-features <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2276,7 +2282,8 @@ test_expect_success 'R: multiple --import-marks= should be honoured' '
+ 
+ 	head -n2 marks.out > one.marks &&
+ 	tail -n +3 marks.out > two.marks &&
+-	git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
++	git fast-import --import-marks=one.marks --import-marks=two.marks \
++		--allow-unsafe-features <input &&
+ 	test_cmp marks.out combined.marks
+ '
+ 
+@@ -2289,7 +2296,7 @@ test_expect_success 'R: feature relative-marks should be honoured' '
+ 
+ 	mkdir -p .git/info/fast-import/ &&
+ 	cp marks.new .git/info/fast-import/relative.in &&
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.new .git/info/fast-import/relative.out
+ '
+ 
+@@ -2301,7 +2308,7 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
+ 	feature export-marks=non-relative.out
+ 	EOF
+ 
+-	git fast-import <input &&
++	git fast-import --allow-unsafe-features <input &&
+ 	test_cmp marks.new non-relative.out
+ '
+ 
+diff --git a/transport-helper.c b/transport-helper.c
+index 91aed35ebb..ed107f5f31 100644
+--- a/transport-helper.c
++++ b/transport-helper.c
+@@ -435,6 +435,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti
+ 	child_process_init(fastimport);
+ 	fastimport->in = helper->out;
+ 	argv_array_push(&fastimport->args, "fast-import");
++	argv_array_push(&fastimport->args, "--allow-unsafe-features");
+ 	argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet");
+ 
+ 	if (data->bidi_import) {
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fast-import-disallow-feature-import-marks-by-default.diff b/debian/patches/fast-import-disallow-feature-import-marks-by-default.diff
new file mode 100644
index 0000000..50d9a85
--- /dev/null
+++ b/debian/patches/fast-import-disallow-feature-import-marks-by-default.diff
@@ -0,0 +1,125 @@
+From edfc470cc2894e9e0771571cb0d7fbf2d8ed995d Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 15:08:42 -0400
+Subject: fast-import: disallow "feature import-marks" by default
+
+commit a52ed76142f6e8d993bb4c50938a408966eb2b7c upstream.
+
+As with export-marks in the previous commit, import-marks can access the
+filesystem. This is significantly less dangerous than export-marks
+because it only involves reading from arbitrary paths, rather than
+writing them. However, it could still be surprising and have security
+implications (e.g., exfiltrating data from a service that accepts
+fast-import streams).
+
+Let's lump it (and its "if-exists" counterpart) in with export-marks,
+and enable the in-stream version only if --allow-unsafe-features is set.
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/git-fast-import.txt |  3 ++-
+ fast-import.c                     |  2 ++
+ t/t9300-fast-import.sh            | 22 +++++++++++++++++-----
+ 3 files changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
+index 55b4b203fb..221f679cb8 100644
+--- a/Documentation/git-fast-import.txt
++++ b/Documentation/git-fast-import.txt
+@@ -57,7 +57,8 @@ OPTIONS
+ 	allowing fast-import to access the filesystem outside of the
+ 	repository). These options are disabled by default, but can be
+ 	allowed by providing this option on the command line.  This
+-	currently impacts only the `feature export-marks` command.
++	currently impacts only the `export-marks`, `import-marks`, and
++	`import-marks-if-exists` feature commands.
+ +
+ 	Only enable this option if you trust the program generating the
+ 	fast-import stream! This option is enabled automatically for
+diff --git a/fast-import.c b/fast-import.c
+index 7ed61a9dad..61796517ac 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3337,8 +3337,10 @@ static int parse_one_feature(const char *feature, int from_stream)
+ 	if (skip_prefix(feature, "date-format=", &arg)) {
+ 		option_date_format(arg);
+ 	} else if (skip_prefix(feature, "import-marks=", &arg)) {
++		check_unsafe_feature("import-marks", from_stream);
+ 		option_import_marks(arg, from_stream, 0);
+ 	} else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
++		check_unsafe_feature("import-marks-if-exists", from_stream);
+ 		option_import_marks(arg, from_stream, 1);
+ 	} else if (skip_prefix(feature, "export-marks=", &arg)) {
+ 		check_unsafe_feature(feature, from_stream);
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 30b7c8a1f6..f8771e2766 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2106,6 +2106,14 @@ test_expect_success 'R: abort on receiving feature after data command' '
+ 	test_must_fail git fast-import <input
+ '
+ 
++test_expect_success 'R: import-marks features forbidden by default' '
++	>git.marks &&
++	echo "feature import-marks=git.marks" >input &&
++	test_must_fail git fast-import <input &&
++	echo "feature import-marks-if-exists=git.marks" >input &&
++	test_must_fail git fast-import <input
++'
++
+ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	>git.marks &&
+ 	>git2.marks &&
+@@ -2114,7 +2122,7 @@ test_expect_success 'R: only one import-marks feature allowed per stream' '
+ 	feature import-marks=git2.marks
+ 	EOF
+ 
+-	test_must_fail git fast-import <input
++	test_must_fail git fast-import --allow-unsafe-features <input
+ '
+ 
+ test_expect_success 'R: export-marks feature forbidden by default' '
+@@ -2210,7 +2218,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	rm -f io.marks &&
+ 	>expect &&
+ 
+-	git fast-import --export-marks=io.marks <<-\EOF &&
++	git fast-import --export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=not_io.marks
+ 	EOF
+ 	test_cmp expect io.marks &&
+@@ -2221,7 +2230,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	echo ":1 $blob" >expect &&
+ 	echo ":2 $blob" >>expect &&
+ 
+-	git fast-import --export-marks=io.marks <<-\EOF &&
++	git fast-import --export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=io.marks
+ 	blob
+ 	mark :2
+@@ -2234,7 +2244,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	echo ":3 $blob" >>expect &&
+ 
+ 	git fast-import --import-marks=io.marks \
+-			--export-marks=io.marks <<-\EOF &&
++			--export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=not_io.marks
+ 	blob
+ 	mark :3
+@@ -2247,7 +2258,8 @@ test_expect_success 'R: feature import-marks-if-exists' '
+ 	>expect &&
+ 
+ 	git fast-import --import-marks-if-exists=not_io.marks \
+-			--export-marks=io.marks <<-\EOF &&
++			--export-marks=io.marks \
++			--allow-unsafe-features <<-\EOF &&
+ 	feature import-marks-if-exists=io.marks
+ 	EOF
+ 	test_cmp expect io.marks
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fast-import-stop-creating-leading-directories-for-imp.diff b/debian/patches/fast-import-stop-creating-leading-directories-for-imp.diff
new file mode 100644
index 0000000..5de88f0
--- /dev/null
+++ b/debian/patches/fast-import-stop-creating-leading-directories-for-imp.diff
@@ -0,0 +1,42 @@
+From 6e50cc232ea775b69ad862245bd64e31447afe3c Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:07:04 -0400
+Subject: fast-import: stop creating leading directories for import-marks
+
+commit e075dba3723875f478654068609f69b2a5af8566 upstream.
+
+When asked to import marks from "subdir/file.marks", we create the
+leading directory "subdir" if it doesn't exist. This makes no sense for
+importing marks, where we only ever open the path for reading.
+
+Most of the time this would be a noop, since if the marks file exists,
+then the leading directories exist, too. But if it doesn't (e.g.,
+because --import-marks-if-exists was used), then we'd create the useless
+directory.
+
+This dates back to 580d5f83e7 (fast-import: always create marks_file
+directories, 2010-03-29). Even then it was useless, so it seems to have
+been added in error alongside the --export-marks case (which _is_
+helpful).
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index e8fbc22ddd..b9144e90bd 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3219,7 +3219,6 @@ static void option_import_marks(const char *marks,
+ 	}
+ 
+ 	import_marks_file = make_fast_import_path(marks);
+-	safe_create_leading_directories_const(import_marks_file);
+ 	import_marks_file_from_stream = from_stream;
+ 	import_marks_file_ignore_missing = ignore_missing;
+ }
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fast-import-tighten-parsing-of-boolean-command-line-o.diff b/debian/patches/fast-import-tighten-parsing-of-boolean-command-line-o.diff
new file mode 100644
index 0000000..74f36b4
--- /dev/null
+++ b/debian/patches/fast-import-tighten-parsing-of-boolean-command-line-o.diff
@@ -0,0 +1,41 @@
+From 04b3de36d735275dd6dc2561e83200c14d65f0af Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 11:25:45 -0400
+Subject: fast-import: tighten parsing of boolean command line options
+
+commit 11e934d56e46875b24d8a047d44b45ff243f6715 upstream.
+
+We parse options like "--max-pack-size=" using skip_prefix(), which
+makes sense to get at the bytes after the "=". However, we also parse
+"--quiet" and "--stats" with skip_prefix(), which allows things like
+"--quiet-nonsense" to behave like "--quiet".
+
+This was a mistaken conversion in 0f6927c229 (fast-import: put option
+parsing code in separate functions, 2009-12-04). Let's tighten this to
+an exact match, which was the original intent.
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fast-import.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/fast-import.c b/fast-import.c
+index cb545d7df5..e8fbc22ddd 100644
+--- a/fast-import.c
++++ b/fast-import.c
+@@ -3305,9 +3305,9 @@ static int parse_one_option(const char *option)
+ 		option_active_branches(option);
+ 	} else if (skip_prefix(option, "export-pack-edges=", &option)) {
+ 		option_export_pack_edges(option);
+-	} else if (starts_with(option, "quiet")) {
++	} else if (!strcmp(option, "quiet")) {
+ 		show_stats = 0;
+-	} else if (starts_with(option, "stats")) {
++	} else if (!strcmp(option, "stats")) {
+ 		show_stats = 1;
+ 	} else {
+ 		return 0;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/fsck-reject-submodule.update-command-in-.gitmodules.diff b/debian/patches/fsck-reject-submodule.update-command-in-.gitmodules.diff
new file mode 100644
index 0000000..cb1d8ca
--- /dev/null
+++ b/debian/patches/fsck-reject-submodule.update-command-in-.gitmodules.diff
@@ -0,0 +1,78 @@
+From 3c3047920680f0c1e91439d0a94162d9b567e9af Mon Sep 17 00:00:00 2001
+From: Jonathan Nieder <jrnieder@gmail.com>
+Date: Thu, 5 Dec 2019 01:30:43 -0800
+Subject: fsck: reject submodule.update = !command in .gitmodules
+
+commit bb92255ebe6bccd76227e023d6d0bc997e318ad0 upstream.
+
+This allows hosting providers to detect whether they are being used
+to attack users using malicious 'update = !command' settings in
+.gitmodules.
+
+Since ac1fbbda2013 (submodule: do not copy unknown update mode from
+.gitmodules, 2013-12-02), in normal cases such settings have been
+treated as 'update = none', so forbidding them should not produce any
+collateral damage to legitimate uses.  A quick search does not reveal
+any repositories making use of this construct, either.
+
+Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fsck.c                      |  6 ++++++
+ t/t7406-submodule-update.sh | 14 ++++++++++++++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/fsck.c b/fsck.c
+index 135fe8c4bc..3a65a3baaf 100644
+--- a/fsck.c
++++ b/fsck.c
+@@ -95,6 +95,7 @@ static int oidhash_contains(struct hashmap *h, const struct object_id *oid)
+ 	FUNC(GITMODULES_SYMLINK, ERROR) \
+ 	FUNC(GITMODULES_URL, ERROR) \
+ 	FUNC(GITMODULES_PATH, ERROR) \
++	FUNC(GITMODULES_UPDATE, ERROR) \
+ 	/* warnings */ \
+ 	FUNC(BAD_FILEMODE, WARN) \
+ 	FUNC(EMPTY_NAME, WARN) \
+@@ -998,6 +999,11 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+ 				    FSCK_MSG_GITMODULES_PATH,
+ 				    "disallowed submodule path: %s",
+ 				    value);
++	if (!strcmp(key, "update") && value && *value == '!')
++		data->ret |= report(data->options, data->obj,
++				    FSCK_MSG_GITMODULES_UPDATE,
++				    "disallowed submodule update setting: %s",
++				    value);
+ 	free(name);
+ 
+ 	return 0;
+diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
+index 3781809bd6..a74e7ea477 100755
+--- a/t/t7406-submodule-update.sh
++++ b/t/t7406-submodule-update.sh
+@@ -397,6 +397,20 @@ test_expect_success 'submodule update - command in .gitmodules is ignored' '
+ 	git -C super submodule update submodule
+ '
+ 
++test_expect_success 'fsck detects command in .gitmodules' '
++	git init command-in-gitmodules &&
++	(
++		cd command-in-gitmodules &&
++		git submodule add ../submodule submodule &&
++		test_commit adding-submodule &&
++
++		git config -f .gitmodules submodule.submodule.update "!false" &&
++		git add .gitmodules &&
++		test_commit configuring-update &&
++		test_must_fail git fsck
++	)
++'
++
+ cat << EOF >expect
+ Execution of 'false $submodulesha1' failed in submodule path 'submodule'
+ EOF
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/is_ntfs_dotgit-only-verify-the-leading-segment.diff b/debian/patches/is_ntfs_dotgit-only-verify-the-leading-segment.diff
new file mode 100644
index 0000000..25e824c
--- /dev/null
+++ b/debian/patches/is_ntfs_dotgit-only-verify-the-leading-segment.diff
@@ -0,0 +1,144 @@
+From 625a6aef62b0b02bd600ac88bbac68e8543e15d3 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 23 Sep 2019 08:58:11 +0200
+Subject: is_ntfs_dotgit(): only verify the leading segment
+
+commit 288a74bcd28229a00c3632f18cba92dbfdf73ee9 upstream.
+
+The config setting `core.protectNTFS` is specifically designed to work
+not only on Windows, but anywhere, to allow for repositories hosted on,
+say, Linux servers to be protected against NTFS-specific attack vectors.
+
+As a consequence, `is_ntfs_dotgit()` manually splits backslash-separated
+paths (but does not do the same for paths separated by forward slashes),
+under the assumption that the backslash might not be a valid directory
+separator on the _current_ Operating System.
+
+However, the two callers, `verify_path()` and `fsck_tree()`, are
+supposed to feed only individual path segments to the `is_ntfs_dotgit()`
+function.
+
+This causes a lot of duplicate scanning (and very inefficient scanning,
+too, as the inner loop of `is_ntfs_dotgit()` was optimized for
+readability rather than for speed.
+
+Let's simplify the design of `is_ntfs_dotgit()` by putting the burden of
+splitting the paths by backslashes as directory separators on the
+callers of said function.
+
+Consequently, the `verify_path()` function, which already splits the
+path by directory separators, now treats backslashes as directory
+separators _explicitly_ when `core.protectNTFS` is turned on, even on
+platforms where the backslash is _not_ a directory separator.
+
+Note that we have to repeat some code in `verify_path()`: if the
+backslash is not a directory separator on the current Operating System,
+we want to allow file names like `\`, but we _do_ want to disallow paths
+that are clearly intended to cause harm when the repository is cloned on
+Windows.
+
+The `fsck_tree()` function (the other caller of `is_ntfs_dotgit()`) now
+needs to look for backslashes in tree entries' names specifically when
+`core.protectNTFS` is turned on. While it would be tempting to
+completely disallow backslashes in that case (much like `fsck` reports
+names containing forward slashes as "full paths"), this would be
+overzealous: when `core.protectNTFS` is turned on in a non-Windows
+setup, backslashes are perfectly valid characters in file names while we
+_still_ want to disallow tree entries that are clearly designed to
+exploit NTFS-specific behavior.
+
+This simplification will make subsequent changes easier to implement,
+such as turning `core.protectNTFS` on by default (not only on Windows)
+or protecting against attack vectors involving NTFS Alternate Data
+Streams.
+
+Incidentally, this change allows for catching malicious repositories
+that contain tree entries of the form `dir\.gitmodules` already on the
+server side rather than only on the client side (and previously only on
+Windows): in contrast to `is_ntfs_dotgit()`, the
+`is_ntfs_dotgitmodules()` function already expects the caller to split
+the paths by directory separators.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ fsck.c       | 18 +++++++++++++++++-
+ path.c       |  5 +----
+ read-cache.c |  8 ++++++++
+ 3 files changed, 26 insertions(+), 5 deletions(-)
+
+diff --git a/fsck.c b/fsck.c
+index def446a3e3..135fe8c4bc 100644
+--- a/fsck.c
++++ b/fsck.c
+@@ -590,7 +590,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
+ 
+ 	while (desc.size) {
+ 		unsigned mode;
+-		const char *name;
++		const char *name, *backslash;
+ 		const struct object_id *oid;
+ 
+ 		oid = tree_entry_extract(&desc, &name, &mode);
+@@ -612,6 +612,22 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
+ 						 ".gitmodules is a symbolic link");
+ 		}
+ 
++		if ((backslash = strchr(name, '\\'))) {
++			while (backslash) {
++				backslash++;
++				has_dotgit |= is_ntfs_dotgit(backslash);
++				if (is_ntfs_dotgitmodules(backslash)) {
++					if (!S_ISLNK(mode))
++						oidhash_insert(&gitmodules_found, oid);
++					else
++						retval += report(options, &item->object,
++								 FSCK_MSG_GITMODULES_SYMLINK,
++								 ".gitmodules is a symbolic link");
++				}
++				backslash = strchr(backslash, '\\');
++			}
++		}
++
+ 		if (update_tree_entry_gently(&desc)) {
+ 			retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
+ 			break;
+diff --git a/path.c b/path.c
+index 0444208dfb..5f23d26668 100644
+--- a/path.c
++++ b/path.c
+@@ -1273,10 +1273,7 @@ int is_ntfs_dotgit(const char *name)
+ 			if (only_spaces_and_periods(name, len, 5) &&
+ 					!strncasecmp(name, "git~1", 5))
+ 				return 1;
+-			if (name[len] != '\\')
+-				return 0;
+-			name += len + 1;
+-			len = -1;
++			return 0;
+ 		}
+ }
+ 
+diff --git a/read-cache.c b/read-cache.c
+index 6dca825751..f0f49dbbb7 100644
+--- a/read-cache.c
++++ b/read-cache.c
+@@ -882,7 +882,15 @@ int verify_path(const char *path, unsigned mode)
+ 			if ((c == '.' && !verify_dotfile(path, mode)) ||
+ 			    is_dir_sep(c) || c == '\0')
+ 				return 0;
++		} else if (c == '\\' && protect_ntfs) {
++			if (is_ntfs_dotgit(path))
++				return 0;
++			if (S_ISLNK(mode)) {
++				if (is_ntfs_dotgitmodules(path))
++					return 0;
++			}
+ 		}
++
+ 		c = *path++;
+ 	}
+ }
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/is_ntfs_dotgit-speed-it-up.diff b/debian/patches/is_ntfs_dotgit-speed-it-up.diff
new file mode 100644
index 0000000..c677714
--- /dev/null
+++ b/debian/patches/is_ntfs_dotgit-speed-it-up.diff
@@ -0,0 +1,114 @@
+From 5c2b396f0c1b08fb152981fd1105bd219a82570d Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Fri, 6 Sep 2019 21:09:35 +0200
+Subject: is_ntfs_dotgit(): speed it up
+
+commit 3a85dc7d534fc2d410ddc0c771c963b20d1b4857 upstream.
+
+Previously, this function was written without focusing on speed,
+intending to make reviewing the code as easy as possible, to avoid any
+bugs in this critical code.
+
+Turns out: we can do much better on both accounts. With this patch, we
+make it as fast as this developer can make it go:
+
+- We avoid the call to `is_dir_sep()` and make all the character
+  comparisons explicit.
+
+- We avoid the cost of calling `strncasecmp()` and unroll the test for
+  `.git` and `git~1`, not even using `tolower()` because it is faster to
+  compare against two constant values.
+
+- We look for `.git` and `.git~1` first thing, and return early if not
+  found.
+
+- We also avoid calling a separate function for detecting chains of
+  spaces and periods.
+
+Each of these improvements has a noticeable impact on the speed of
+`is_ntfs_dotgit()`.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c | 55 ++++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 30 insertions(+), 25 deletions(-)
+
+diff --git a/path.c b/path.c
+index 93e321bbd0..83006824b3 100644
+--- a/path.c
++++ b/path.c
+@@ -1219,20 +1219,6 @@ int daemon_avoid_alias(const char *p)
+ 	}
+ }
+ 
+-static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+-{
+-	if (len < skip)
+-		return 0;
+-	len -= skip;
+-	path += skip;
+-	while (len-- > 0) {
+-		char c = *(path++);
+-		if (c != ' ' && c != '.')
+-			return 0;
+-	}
+-	return 1;
+-}
+-
+ /*
+  * On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
+  * directory:
+@@ -1272,19 +1258,38 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+  */
+ int is_ntfs_dotgit(const char *name)
+ {
+-	size_t len;
++	char c;
+ 
+-	for (len = 0; ; len++)
+-		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len]) ||
+-		    name[len] == ':') {
+-			if (only_spaces_and_periods(name, len, 4) &&
+-					!strncasecmp(name, ".git", 4))
+-				return 1;
+-			if (only_spaces_and_periods(name, len, 5) &&
+-					!strncasecmp(name, "git~1", 5))
+-				return 1;
++	/*
++	 * Note that when we don't find `.git` or `git~1` we end up with `name`
++	 * advanced partway through the string. That's okay, though, as we
++	 * return immediately in those cases, without looking at `name` any
++	 * further.
++	 */
++	c = *(name++);
++	if (c == '.') {
++		/* .git */
++		if (((c = *(name++)) != 'g' && c != 'G') ||
++		    ((c = *(name++)) != 'i' && c != 'I') ||
++		    ((c = *(name++)) != 't' && c != 'T'))
+ 			return 0;
+-		}
++	} else if (c == 'g' || c == 'G') {
++		/* git ~1 */
++		if (((c = *(name++)) != 'i' && c != 'I') ||
++		    ((c = *(name++)) != 't' && c != 'T') ||
++		    *(name++) != '~' ||
++		    *(name++) != '1')
++			return 0;
++	} else
++		return 0;
++
++	for (;;) {
++		c = *(name++);
++		if (!c || c == '\\' || c == '/' || c == ':')
++			return 1;
++		if (c != '.' && c != ' ')
++			return 0;
++	}
+ }
+ 
+ static int is_ntfs_dot_generic(const char *name,
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff b/debian/patches/path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
new file mode 100644
index 0000000..936441b
--- /dev/null
+++ b/debian/patches/path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
@@ -0,0 +1,64 @@
+From 7be9f6f27a64c2041fd9bbeaa61d6193c70c1614 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 28 Aug 2019 12:22:17 +0200
+Subject: path: also guard `.gitmodules` against NTFS Alternate Data Streams
+
+commit 91bd46588e6959e6903e275f78b10bd07830d547 upstream.
+
+We just safe-guarded `.git` against NTFS Alternate Data Stream-related
+attack vectors, and now it is time to do the same for `.gitmodules`.
+
+Note: In the added regression test, we refrain from verifying all kinds
+of variations between short names and NTFS Alternate Data Streams: as
+the new code disallows _all_ Alternate Data Streams of `.gitmodules`, it
+is enough to test one in order to know that all of them are guarded
+against.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c                | 2 +-
+ t/t0060-path-utils.sh | 7 ++++++-
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/path.c b/path.c
+index 4ab11c8d45..93e321bbd0 100644
+--- a/path.c
++++ b/path.c
+@@ -1300,7 +1300,7 @@ static int is_ntfs_dot_generic(const char *name,
+ only_spaces_and_periods:
+ 		for (;;) {
+ 			char c = name[i++];
+-			if (!c)
++			if (!c || c == ':')
+ 				return 1;
+ 			if (c != ' ' && c != '.')
+ 				return 0;
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index f2e58b4604..7298439947 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -406,6 +406,9 @@ test_expect_success 'match .gitmodules' '
+ 		~1000000 \
+ 		~9999999 \
+ 		\
++		.gitmodules:\$DATA \
++		"gitmod~4 . :\$DATA" \
++		\
+ 		--not \
+ 		".gitmodules x"  \
+ 		".gitmodules .x" \
+@@ -430,7 +433,9 @@ test_expect_success 'match .gitmodules' '
+ 		\
+ 		GI7EB~1 \
+ 		GI7EB~01 \
+-		GI7EB~1X
++		GI7EB~1X \
++		\
++		.gitmodules,:\$DATA
+ '
+ 
+ test_done
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff b/debian/patches/path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
new file mode 100644
index 0000000..1442c78
--- /dev/null
+++ b/debian/patches/path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
@@ -0,0 +1,96 @@
+From 3853b2d546c43d5bf4be6342e359b42f81ac2999 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Wed, 28 Aug 2019 12:22:17 +0200
+Subject: path: safeguard `.git` against NTFS Alternate Streams Accesses
+
+commit 7c3745fc6185495d5765628b4dfe1bd2c25a2981 upstream.
+
+Probably inspired by HFS' resource streams, NTFS supports "Alternate
+Data Streams": by appending `:<stream-name>` to the file name,
+information in addition to the file contents can be written and read,
+information that is copied together with the file (unless copied to a
+non-NTFS location).
+
+These Alternate Data Streams are typically used for things like marking
+an executable as having just been downloaded from the internet (and
+hence not necessarily being trustworthy).
+
+In addition to a stream name, a stream type can be appended, like so:
+`:<stream-name>:<stream-type>`. Unless specified, the default stream
+type is `$DATA` for files and `$INDEX_ALLOCATION` for directories. In
+other words, `.git::$INDEX_ALLOCATION` is a valid way to reference the
+`.git` directory!
+
+In our work in Git v2.2.1 to protect Git on NTFS drives under
+`core.protectNTFS`, we focused exclusively on NTFS short names, unaware
+of the fact that NTFS Alternate Data Streams offer a similar attack
+vector.
+
+Let's fix this.
+
+Seeing as it is better to be safe than sorry, we simply disallow paths
+referring to *any* NTFS Alternate Data Stream of `.git`, not just
+`::$INDEX_ALLOCATION`. This also simplifies the implementation.
+
+This closes CVE-2019-1352.
+
+Further reading about NTFS Alternate Data Streams:
+https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c54dec26-1551-4d3a-a0ea-4fa40f848eb3
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c                         | 12 +++++++++++-
+ t/t1014-read-tree-confusing.sh |  1 +
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/path.c b/path.c
+index 5f23d26668..4ab11c8d45 100644
+--- a/path.c
++++ b/path.c
+@@ -1252,10 +1252,19 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+  *   `.git` is the first item in a directory, therefore it will be associated
+  *   with the short name `git~1` (unless short names are disabled).
+  *
++ * - For yet other historical reasons, NTFS supports so-called "Alternate Data
++ *   Streams", i.e. metadata associated with a given file, referred to via
++ *   `<filename>:<stream-name>:<stream-type>`. There exists a default stream
++ *   type for directories, allowing `.git/` to be accessed via
++ *   `.git::$INDEX_ALLOCATION/`.
++ *
+  * When this function returns 1, it indicates that the specified file/directory
+  * name refers to a `.git` file or directory, or to any of these synonyms, and
+  * Git should therefore not track it.
+  *
++ * For performance reasons, _all_ Alternate Data Streams of `.git/` are
++ * forbidden, not just `::$INDEX_ALLOCATION`.
++ *
+  * This function is intended to be used by `git fsck` even on platforms where
+  * the backslash is a regular filename character, therefore it needs to handle
+  * backlash characters in the provided `name` specially: they are interpreted
+@@ -1266,7 +1275,8 @@ int is_ntfs_dotgit(const char *name)
+ 	size_t len;
+ 
+ 	for (len = 0; ; len++)
+-		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
++		if (!name[len] || name[len] == '\\' || is_dir_sep(name[len]) ||
++		    name[len] == ':') {
+ 			if (only_spaces_and_periods(name, len, 4) &&
+ 					!strncasecmp(name, ".git", 4))
+ 				return 1;
+diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
+index 2f5a25d503..da3376b3bb 100755
+--- a/t/t1014-read-tree-confusing.sh
++++ b/t/t1014-read-tree-confusing.sh
+@@ -49,6 +49,7 @@ git~1
+ .git.SPACE .git.{space}
+ .\\\\.GIT\\\\foobar backslashes
+ .git\\\\foobar backslashes2
++.git...:alternate-stream
+ EOF
+ 
+ test_expect_success 'utf-8 paths allowed with core.protectHFS off' '
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/path.c-document-the-purpose-of-is_ntfs_dotgit.diff b/debian/patches/path.c-document-the-purpose-of-is_ntfs_dotgit.diff
new file mode 100644
index 0000000..fc71282
--- /dev/null
+++ b/debian/patches/path.c-document-the-purpose-of-is_ntfs_dotgit.diff
@@ -0,0 +1,59 @@
+From 53b0b78673e950d66f7f21fac8e83fa324f9d83e Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 16 Sep 2019 20:44:31 +0200
+Subject: path.c: document the purpose of `is_ntfs_dotgit()`
+
+commit 525e7fba7854c23ee3530d0bf88d75f106f14c95 upstream.
+
+Previously, this function was completely undocumented. It is worth,
+though, to explain what is going on, as it is not really obvious at all.
+
+Suggested-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ path.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/path.c b/path.c
+index 98c35e0e9c..0444208dfb 100644
+--- a/path.c
++++ b/path.c
+@@ -1233,6 +1233,34 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+ 	return 1;
+ }
+ 
++/*
++ * On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
++ * directory:
++ *
++ * - For historical reasons, file names that end in spaces or periods are
++ *   automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
++ *   to `.git/`.
++ *
++ * - For other historical reasons, file names that do not conform to the 8.3
++ *   format (up to eight characters for the basename, three for the file
++ *   extension, certain characters not allowed such as `+`, etc) are associated
++ *   with a so-called "short name", at least on the `C:` drive by default.
++ *   Which means that `git~1/` is a valid way to refer to `.git/`.
++ *
++ *   Note: Technically, `.git/` could receive the short name `git~2` if the
++ *   short name `git~1` were already used. In Git, however, we guarantee that
++ *   `.git` is the first item in a directory, therefore it will be associated
++ *   with the short name `git~1` (unless short names are disabled).
++ *
++ * When this function returns 1, it indicates that the specified file/directory
++ * name refers to a `.git` file or directory, or to any of these synonyms, and
++ * Git should therefore not track it.
++ *
++ * This function is intended to be used by `git fsck` even on platforms where
++ * the backslash is a regular filename character, therefore it needs to handle
++ * backlash characters in the provided `name` specially: they are interpreted
++ * as directory separators.
++ */
+ int is_ntfs_dotgit(const char *name)
+ {
+ 	size_t len;
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/protect_ntfs-turn-on-NTFS-protection-by-default.diff b/debian/patches/protect_ntfs-turn-on-NTFS-protection-by-default.diff
new file mode 100644
index 0000000..61bd94b
--- /dev/null
+++ b/debian/patches/protect_ntfs-turn-on-NTFS-protection-by-default.diff
@@ -0,0 +1,144 @@
+From 084a768d9ac36e05d3f79211723d383ac8601a05 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 9 Sep 2019 21:04:41 +0200
+Subject: protect_ntfs: turn on NTFS protection by default
+
+commit 9102f958ee5254b10c0be72672aa3305bf4f4704 upstream.
+
+Back in the DOS days, in the FAT file system, file names always
+consisted of a base name of length 8 plus a file extension of length 3.
+Shorter file names were simply padded with spaces to the full 8.3
+format.
+
+Later, the FAT file system was taught to support _also_ longer names,
+with an 8.3 "short name" as primary file name. While at it, the same
+facility allowed formerly illegal file names, such as `.git` (empty base
+names were not allowed), which would have the "short name" `git~1`
+associated with it.
+
+For backwards-compatibility, NTFS supports alternative 8.3 short
+filenames, too, even if starting with Windows Vista, they are only
+generated on the system drive by default.
+
+We addressed the problem that the `.git/` directory can _also_ be
+accessed via `git~1/` (when short names are enabled) in 2b4c6efc821
+(read-cache: optionally disallow NTFS .git variants, 2014-12-16), i.e.
+since Git v1.9.5, by introducing the config setting `core.protectNTFS`
+and enabling it by default on Windows.
+
+In the meantime, Windows 10 introduced the "Windows Subsystem for Linux"
+(short: WSL), i.e. a way to run Linux applications/distributions in a
+thinly-isolated subsystem on Windows (giving rise to many a "2016 is the
+Year of Linux on the Desktop" jokes). WSL is getting increasingly
+popular, also due to the painless way Linux application can operate
+directly ("natively") on files on Windows' file system: the Windows
+drives are mounted automatically (e.g. `C:` as `/mnt/c/`).
+
+Taken together, this means that we now have to enable the safe-guards of
+Git v1.9.5 also in WSL: it is possible to access a `.git` directory
+inside `/mnt/c/` via the 8.3 name `git~1` (unless short name generation
+was disabled manually). Since regular Linux distributions run in WSL,
+this means we have to enable `core.protectNTFS` at least on Linux, too.
+
+To enable Services for Macintosh in Windows NT to store so-called
+resource forks, NTFS introduced "Alternate Data Streams". Essentially,
+these constitute additional metadata that are connected to (and copied
+with) their associated files, and they are accessed via pseudo file
+names of the form `filename:<stream-name>:<stream-type>`.
+
+In a recent patch, we extended `core.protectNTFS` to also protect
+against accesses via NTFS Alternate Data Streams, e.g. to prevent
+contents of the `.git/` directory to be "tracked" via yet another
+alternative file name.
+
+While it is not possible (at least by default) to access files via NTFS
+Alternate Data Streams from within WSL, the defaults on macOS when
+mounting network shares via SMB _do_ allow accessing files and
+directories in that way. Therefore, we need to enable `core.protectNTFS`
+on macOS by default, too, and really, on any Operating System that can
+mount network shares via SMB/CIFS.
+
+A couple of approaches were considered for fixing this:
+
+1. We could perform a dynamic NTFS check similar to the `core.symlinks`
+   check in `init`/`clone`: instead of trying to create a symbolic link
+   in the `.git/` directory, we could create a test file and try to
+   access `.git/config` via 8.3 name and/or Alternate Data Stream.
+
+2. We could simply "flip the switch" on `core.protectNTFS`, to make it
+   "on by default".
+
+The obvious downside of 1. is that it won't protect worktrees that were
+clone with a vulnerable Git version already. We considered patching code
+paths that check out files to check whether we're running on an NTFS
+system dynamically and persist the result in the repository-local config
+setting `core.protectNTFS`, but in the end decided that this solution
+would be too fragile, and too involved.
+
+The obvious downside of 2. is that everybody will have to "suffer" the
+performance penalty incurred from calling `is_ntfs_dotgit()` on every
+path, even in setups where.
+
+After the recent work to accelerate `is_ntfs_dotgit()` in most cases,
+it looks as if the time spent on validating ten million random
+file names increases only negligibly (less than 20ms, well within the
+standard deviation of ~50ms). Therefore the benefits outweigh the cost.
+
+Another downside of this is that paths that might have been acceptable
+previously now will be forbidden. Realistically, though, this is an
+improvement because public Git hosters already would reject any `git
+push` that contains such file names.
+
+Note: There might be a similar problem mounting HFS+ on Linux. However,
+this scenario has been considered unlikely and in light of the cost (in
+the aforementioned benchmark, `core.protectHFS = true` increased the
+time from ~440ms to ~610ms), it was decided _not_ to touch the default
+of `core.protectHFS`.
+
+This change addresses CVE-2019-1353.
+
+Reported-by: Nicolas Joly <Nicolas.Joly@microsoft.com>
+Helped-by: Garima Singh <garima.singh@microsoft.com>
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ config.mak.uname | 2 --
+ environment.c    | 2 +-
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/config.mak.uname b/config.mak.uname
+index 2831a68c3c..1a7e7cf62f 100644
+--- a/config.mak.uname
++++ b/config.mak.uname
+@@ -383,7 +383,6 @@ ifeq ($(uname_S),Windows)
+ 	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
+ 	PTHREAD_LIBS =
+ 	lib =
+-	BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
+ ifndef DEBUG
+ 	BASIC_CFLAGS += -GL -Os -MD
+ 	BASIC_LDFLAGS += -LTCG
+@@ -524,7 +523,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
+ 	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
+ 		compat/win32/pthread.o compat/win32/syslog.o \
+ 		compat/win32/dirent.o
+-	BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
+ 	EXTLIBS += -lws2_32
+ 	GITLIBS += git.res
+ 	PTHREAD_LIBS =
+diff --git a/environment.c b/environment.c
+index 0935ec696e..62ba70176b 100644
+--- a/environment.c
++++ b/environment.c
+@@ -71,7 +71,7 @@ enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
+ int protect_hfs = PROTECT_HFS_DEFAULT;
+ 
+ #ifndef PROTECT_NTFS_DEFAULT
+-#define PROTECT_NTFS_DEFAULT 0
++#define PROTECT_NTFS_DEFAULT 1
+ #endif
+ int protect_ntfs = PROTECT_NTFS_DEFAULT;
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/series b/debian/patches/series
index 693edf4..05f0975 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -42,3 +42,22 @@
 fsck-detect-submodule-urls-starting-with-dash.diff
 fsck-detect-submodule-paths-starting-with-dash.diff
 cvsimport-apply-shell-quoting-regex-globally.diff
+t9300-drop-some-useless-uses-of-cat.diff
+t9300-create-marks-files-for-double-import-marks-test.diff
+fast-import-tighten-parsing-of-boolean-command-line-o.diff
+fast-import-stop-creating-leading-directories-for-imp.diff
+fast-import-delay-creating-leading-directories-for-ex.diff
+fast-import-disallow-feature-export-marks-by-default.diff
+fast-import-disallow-feature-import-marks-by-default.diff
+clone-recurse-submodules-prevent-name-squatting-on-Wi.diff
+path.c-document-the-purpose-of-is_ntfs_dotgit.diff
+is_ntfs_dotgit-only-verify-the-leading-segment.diff
+path-safeguard-.git-against-NTFS-Alternate-Streams-Ac.diff
+path-also-guard-.gitmodules-against-NTFS-Alternate-Da.diff
+is_ntfs_dotgit-speed-it-up.diff
+protect_ntfs-turn-on-NTFS-protection-by-default.diff
+Disallow-dubiously-nested-submodule-git-directories.diff
+unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
+t7406-submodule.-name-.update-command-must-not-be-run.diff
+submodule-reject-submodule.update-command-in-.gitmodu.diff
+fsck-reject-submodule.update-command-in-.gitmodules.diff
diff --git a/debian/patches/submodule-reject-submodule.update-command-in-.gitmodu.diff b/debian/patches/submodule-reject-submodule.update-command-in-.gitmodu.diff
new file mode 100644
index 0000000..920238e
--- /dev/null
+++ b/debian/patches/submodule-reject-submodule.update-command-in-.gitmodu.diff
@@ -0,0 +1,137 @@
+From 301307952288e563850ae8216a36d689837463e5 Mon Sep 17 00:00:00 2001
+From: Jonathan Nieder <jrnieder@gmail.com>
+Date: Thu, 5 Dec 2019 01:28:28 -0800
+Subject: submodule: reject submodule.update = !command in .gitmodules
+
+commit e904deb89d9a9669a76a426182506a084d3f6308 upstream.
+
+Since ac1fbbda2013 (submodule: do not copy unknown update mode from
+.gitmodules, 2013-12-02), Git has been careful to avoid copying
+
+	[submodule "foo"]
+		update = !run an arbitrary scary command
+
+from .gitmodules to a repository's local config, copying in the
+setting 'update = none' instead.  The gitmodules(5) manpage documents
+the intention:
+
+	The !command form is intentionally ignored here for security
+	reasons
+
+Unfortunately, starting with v2.20.0-rc0 (which integrated ee69b2a9
+(submodule--helper: introduce new update-module-mode helper,
+2018-08-13, first released in v2.20.0-rc0)), there are scenarios where
+we *don't* ignore it: if the config store contains no
+submodule.foo.update setting, the submodule-config API falls back to
+reading .gitmodules and the repository-supplied !command gets run
+after all.
+
+This was part of a general change over time in submodule support to
+read more directly from .gitmodules, since unlike .git/config it
+allows a project to change values between branches and over time
+(while still allowing .git/config to override things).  But it was
+never intended to apply to this kind of dangerous configuration.
+
+The behavior change was not advertised in ee69b2a9's commit message
+and was missed in review.
+
+Let's take the opportunity to make the protection more robust, even in
+Git versions that are technically not affected: instead of quietly
+converting 'update = !command' to 'update = none', noisily treat it as
+an error.  Allowing the setting but treating it as meaning something
+else was just confusing; users are better served by seeing the error
+sooner.  Forbidding the construct makes the semantics simpler and
+means we can check for it in fsck (in a separate patch).
+
+As a result, the submodule-config API cannot read this value from
+.gitmodules under any circumstance, and we can declare with confidence
+
+	For security reasons, the '!command' form is not accepted
+	here.
+
+[jn: backported to 2.11.y:
+ - allow submodule-config to read !command after all, since
+   v2.15.0-rc0~120^2~10 (submodule--helper: don't overlay config in
+   update-clone, 2017-08-03) hasn't happened yet
+ - move the code to error out when "git submodule init" encounters
+   !command in .gitmodules to module_init()
+ - cmd_update in git-submodule.sh is responsible for reading the
+   update strategy since v2.20.0-rc0~247^2 (submodule--helper:
+   introduce update-module-mode, 2018-08-13) hasn't happened yet.
+   cmd_update checks config directly and does not read .gitmodules,
+   so we don't have to change it.  Accordingly, t7406 also does
+   not need to change its expectation from ignoring to rejecting
+   commands from .gitmodules.]
+
+Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ Documentation/gitmodules.txt |  5 ++---
+ builtin/submodule--helper.c  |  4 +---
+ t/t7406-submodule-update.sh  | 10 ++++++----
+ 3 files changed, 9 insertions(+), 10 deletions(-)
+
+diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
+index 8f7c50f330..0c10fb01c1 100644
+--- a/Documentation/gitmodules.txt
++++ b/Documentation/gitmodules.txt
+@@ -44,9 +44,8 @@ submodule.<name>.update::
+ 	submodule init` to initialize the configuration variable of
+ 	the same name. Allowed values here are 'checkout', 'rebase',
+ 	'merge' or 'none'. See description of 'update' command in
+-	linkgit:git-submodule[1] for their meaning. Note that the
+-	'!command' form is intentionally ignored here for security
+-	reasons.
++	linkgit:git-submodule[1] for their meaning. For security
++	reasons, the '!command' form is not accepted here.
+ 
+ submodule.<name>.branch::
+ 	A remote branch name for tracking updates in the upstream submodule.
+diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
+index 6f33a665df..68c84a10a4 100644
+--- a/builtin/submodule--helper.c
++++ b/builtin/submodule--helper.c
+@@ -381,9 +381,7 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
+ 	if (git_config_get_string(sb.buf, &upd) &&
+ 	    sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
+ 		if (sub->update_strategy.type == SM_UPDATE_COMMAND) {
+-			fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"),
+-				sub->name);
+-			upd = xstrdup("none");
++			die(_("invalid value for %s"), sb.buf);
+ 		} else
+ 			upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy));
+ 
+diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
+index b957b97370..3781809bd6 100755
+--- a/t/t7406-submodule-update.sh
++++ b/t/t7406-submodule-update.sh
+@@ -453,6 +453,9 @@ test_expect_success 'recursive submodule update - command in .git/config catches
+ '
+ 
+ test_expect_success 'submodule init does not copy command into .git/config' '
++	test_when_finished "git -C super update-index --force-remove submodule1" &&
++	test_when_finished git config -f super/.gitmodules \
++		--remove-section submodule.submodule1 &&
+ 	(cd super &&
+ 	 H=$(git ls-files -s submodule | cut -d" " -f2) &&
+ 	 mkdir submodule1 &&
+@@ -460,10 +463,9 @@ test_expect_success 'submodule init does not copy command into .git/config' '
+ 	 git config -f .gitmodules submodule.submodule1.path submodule1 &&
+ 	 git config -f .gitmodules submodule.submodule1.url ../submodule &&
+ 	 git config -f .gitmodules submodule.submodule1.update !false &&
+-	 git submodule init submodule1 &&
+-	 echo "none" >expect &&
+-	 git config submodule.submodule1.update >actual &&
+-	 test_cmp expect actual
++	 test_must_fail git submodule init submodule1 &&
++	 test_expect_code 1 git config submodule.submodule1.update >actual &&
++	 test_must_be_empty actual
+ 	)
+ '
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/t7406-submodule.-name-.update-command-must-not-be-run.diff b/debian/patches/t7406-submodule.-name-.update-command-must-not-be-run.diff
new file mode 100644
index 0000000..645e5da
--- /dev/null
+++ b/debian/patches/t7406-submodule.-name-.update-command-must-not-be-run.diff
@@ -0,0 +1,46 @@
+From 1742900c4cce993203d088f16a751073547ec350 Mon Sep 17 00:00:00 2001
+From: Stefan Beller <sbeller@google.com>
+Date: Tue, 26 Sep 2017 12:54:13 -0700
+Subject: t7406: submodule.<name>.update command must not be run from
+ .gitmodules
+
+commit 83a17fa83b24ed713e2c2647bf89dae171971b73 upstream.
+
+submodule.<name>.update can be assigned an arbitrary command via setting
+it to "!command". When this command is found in the regular config, Git
+ought to just run that command instead of other update mechanisms.
+
+However if that command is just found in the .gitmodules file, it is
+potentially untrusted, which is why we do not run it.  Add a test
+confirming the behavior.
+
+Suggested-by: Jonathan Nieder <jrnieder@gmail.com>
+Signed-off-by: Stefan Beller <sbeller@google.com>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t7406-submodule-update.sh | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
+index 64f322c4cc..b957b97370 100755
+--- a/t/t7406-submodule-update.sh
++++ b/t/t7406-submodule-update.sh
+@@ -389,6 +389,14 @@ test_expect_success 'submodule update - command in .git/config' '
+ 	)
+ '
+ 
++test_expect_success 'submodule update - command in .gitmodules is ignored' '
++	test_when_finished "git -C super reset --hard HEAD^" &&
++	git -C super config -f .gitmodules submodule.submodule.update "!false" &&
++	git -C super commit -a -m "add command to .gitmodules file" &&
++	git -C super/submodule reset --hard $submodulesha1^ &&
++	git -C super submodule update submodule
++'
++
+ cat << EOF >expect
+ Execution of 'false $submodulesha1' failed in submodule path 'submodule'
+ EOF
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/t9300-create-marks-files-for-double-import-marks-test.diff b/debian/patches/t9300-create-marks-files-for-double-import-marks-test.diff
new file mode 100644
index 0000000..f006d1a
--- /dev/null
+++ b/debian/patches/t9300-create-marks-files-for-double-import-marks-test.diff
@@ -0,0 +1,36 @@
+From 926a9fda9c5e1633837396fefa070fd7d99d4538 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 13:43:23 -0400
+Subject: t9300: create marks files for double-import-marks test
+
+commit 816f806786e12435163c591942a204c5a3bdd795 upstream.
+
+Our tests confirm that providing two "import-marks" options in a
+fast-import stream is an error. However, the invoked command would fail
+even without covering this case, because the marks files themselves do
+not actually exist.  Let's create the files to make sure we fail for the
+right reason (we actually do, because the option parsing happens before
+we open anything, but this future-proofs our test).
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t9300-fast-import.sh | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 9ac3ca187d..bf5b937a4c 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2107,6 +2107,8 @@ test_expect_success 'R: abort on receiving feature after data command' '
+ '
+ 
+ test_expect_success 'R: only one import-marks feature allowed per stream' '
++	>git.marks &&
++	>git2.marks &&
+ 	cat >input <<-EOF &&
+ 	feature import-marks=git.marks
+ 	feature import-marks=git2.marks
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/t9300-drop-some-useless-uses-of-cat.diff b/debian/patches/t9300-drop-some-useless-uses-of-cat.diff
new file mode 100644
index 0000000..5c6861b
--- /dev/null
+++ b/debian/patches/t9300-drop-some-useless-uses-of-cat.diff
@@ -0,0 +1,64 @@
+From b1d7002fab247bd5e25dfd0a7303eefadc9a1cc6 Mon Sep 17 00:00:00 2001
+From: Jeff King <peff@peff.net>
+Date: Thu, 29 Aug 2019 11:19:18 -0400
+Subject: t9300: drop some useless uses of cat
+
+commit f94804c1f2626831c6bdf8cc269a571324e3f2f2 upstream.
+
+These waste a process, and make the line longer than it needs to be.
+
+Signed-off-by: Jeff King <peff@peff.net>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ t/t9300-fast-import.sh | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
+index 2e0ba3ebd8..9ac3ca187d 100755
+--- a/t/t9300-fast-import.sh
++++ b/t/t9300-fast-import.sh
+@@ -2125,12 +2125,12 @@ test_expect_success 'R: export-marks feature results in a marks file being creat
+ 
+ 	EOF
+ 
+-	cat input | git fast-import &&
++	git fast-import <input &&
+ 	grep :1 git.marks
+ '
+ 
+ test_expect_success 'R: export-marks options can be overridden by commandline options' '
+-	cat input | git fast-import --export-marks=other.marks &&
++	git fast-import --export-marks=other.marks <input &&
+ 	grep :1 other.marks
+ '
+ 
+@@ -2242,7 +2242,7 @@ test_expect_success 'R: import to output marks works without any content' '
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	cat input | git fast-import &&
++	git fast-import <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2252,7 +2252,7 @@ test_expect_success 'R: import marks prefers commandline marks file over the str
+ 	feature export-marks=marks.new
+ 	EOF
+ 
+-	cat input | git fast-import --import-marks=marks.out &&
++	git fast-import --import-marks=marks.out <input &&
+ 	test_cmp marks.out marks.new
+ '
+ 
+@@ -2560,7 +2560,7 @@ test_expect_success 'R: quiet option results in no stats being output' '
+ 
+ 	EOF
+ 
+-	cat input | git fast-import 2> output &&
++	git fast-import 2>output <input &&
+ 	test_must_be_empty output
+ '
+ 
+-- 
+2.24.0.393.g34dc348eaf
+
diff --git a/debian/patches/unpack-trees-let-merged_entry-pass-through-do_add_ent.diff b/debian/patches/unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
new file mode 100644
index 0000000..4b80214
--- /dev/null
+++ b/debian/patches/unpack-trees-let-merged_entry-pass-through-do_add_ent.diff
@@ -0,0 +1,44 @@
+From 752250c75bc989d259fb2e6cb29ee5f034777d78 Mon Sep 17 00:00:00 2001
+From: Johannes Schindelin <johannes.schindelin@gmx.de>
+Date: Mon, 9 Sep 2019 13:56:15 +0200
+Subject: unpack-trees: let merged_entry() pass through do_add_entry()'s errors
+
+commit cc756edda63769cf6d7acc99e6ad3a9cbb5dc3ec upstream.
+
+A `git clone` will end with exit code 0 when `merged_entry()` returns a
+positive value during a call of `unpack_trees()` to `traverse_trees()`.
+The reason is that `unpack_trees()` will interpret a positive value not
+to be an error.
+
+The problem is, however, that `add_index_entry()` (which is called by
+`merged_entry()` can report an error, and we really should fail the
+entire clone in such a case.
+
+Let's fix this problem, in preparation for a Windows-specific patch
+disallowing `mkdir()` with directory names that contain a trailing space
+(which is illegal on NTFS): we want `git clone` to abort when a path
+cannot be checked out due to that condition.
+
+Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
+Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
+---
+ unpack-trees.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/unpack-trees.c b/unpack-trees.c
+index ea6bdd20e0..008fe03811 100644
+--- a/unpack-trees.c
++++ b/unpack-trees.c
+@@ -1626,7 +1626,8 @@ static int merged_entry(const struct cache_entry *ce,
+ 		invalidate_ce_path(old, o);
+ 	}
+ 
+-	do_add_entry(o, merge, update, CE_STAGEMASK);
++	if (do_add_entry(o, merge, update, CE_STAGEMASK) < 0)
++		return -1;
+ 	return 1;
+ }
+ 
+-- 
+2.24.0.393.g34dc348eaf
+