Revision e1d911dd4c7b76a5a8cec0f5c8de15981e34da83 authored by Johannes Schindelin on 12 September 2019, 12:54:05 UTC, committed by Johannes Schindelin on 04 December 2019, 12:20:05 UTC
The backslash character is not a valid part of a file name on Windows.
Hence it is dangerous to allow writing files that were unpacked from
tree objects, when the stored file name contains a backslash character:
it will be misinterpreted as directory separator.

This not only causes ambiguity when a tree contains a blob `a\b` and a
tree `a` that contains a blob `b`, but it also can be used as part of an
attack vector to side-step the careful protections against writing into
the `.git/` directory during a clone of a maliciously-crafted
repository.

Let's prevent that, addressing CVE-2019-1354.

Note: we guard against backslash characters in tree objects' file names
_only_ on Windows (because on other platforms, even on those where NTFS
volumes can be mounted, the backslash character is _not_ a directory
separator), and _only_ when `core.protectNTFS = true` (because users
might need to generate tree objects for other platforms, of course
without touching the worktree, e.g. using `git update-index
--cacheinfo`).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 0060fd1
Raw File
oidset.c
#include "cache.h"
#include "oidset.h"

struct oidset_entry {
	struct hashmap_entry hash;
	struct object_id oid;
};

static int oidset_hashcmp(const void *unused_cmp_data,
			  const void *va, const void *vb,
			  const void *vkey)
{
	const struct oidset_entry *a = va, *b = vb;
	const struct object_id *key = vkey;
	return oidcmp(&a->oid, key ? key : &b->oid);
}

int oidset_contains(const struct oidset *set, const struct object_id *oid)
{
	struct hashmap_entry key;

	if (!set->map.cmpfn)
		return 0;

	hashmap_entry_init(&key, sha1hash(oid->hash));
	return !!hashmap_get(&set->map, &key, oid);
}

int oidset_insert(struct oidset *set, const struct object_id *oid)
{
	struct oidset_entry *entry;

	if (!set->map.cmpfn)
		hashmap_init(&set->map, oidset_hashcmp, NULL, 0);

	if (oidset_contains(set, oid))
		return 1;

	entry = xmalloc(sizeof(*entry));
	hashmap_entry_init(&entry->hash, sha1hash(oid->hash));
	oidcpy(&entry->oid, oid);

	hashmap_add(&set->map, entry);
	return 0;
}

void oidset_clear(struct oidset *set)
{
	hashmap_free(&set->map, 1);
}
back to top