Browse Source

Make hash-object more robust against malformed objects

Commits, trees and tags have structure. Don't let users feed git
with malformed ones. Sooner or later git will die() when
encountering them.

Note that this patch does not check semantics. A tree that points
to non-existent objects is perfectly OK (and should be so, users
may choose to add commit first, then its associated tree for example).

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Nguyễn Thái Ngọc Duy 14 years ago committed by Junio C Hamano
parent
commit
c879daa237
  1. 2
      builtin/hash-object.c
  2. 2
      cache.h
  3. 2
      read-cache.c
  4. 54
      sha1_file.c
  5. 13
      t/t1007-hash-object.sh

2
builtin/hash-object.c

@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
struct stat st; struct stat st;
unsigned char sha1[20]; unsigned char sha1[20];
if (fstat(fd, &st) < 0 || if (fstat(fd, &st) < 0 ||
index_fd(sha1, fd, &st, write_object, type_from_string(type), path)) index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1))
die(write_object die(write_object
? "Unable to add %s to database" ? "Unable to add %s to database"
: "Unable to hash %s", path); : "Unable to hash %s", path);

2
cache.h

@ -501,7 +501,7 @@ extern int ie_match_stat(const struct index_state *, struct cache_entry *, struc
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);


extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);



2
read-cache.c

@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)


if (fd >= 0) { if (fd >= 0) {
unsigned char sha1[20]; unsigned char sha1[20];
if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name)) if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name, 0))
match = hashcmp(sha1, ce->sha1); match = hashcmp(sha1, ce->sha1);
/* index_fd() closed the file descriptor already */ /* index_fd() closed the file descriptor already */
} }

54
sha1_file.c

@ -13,6 +13,7 @@
#include "commit.h" #include "commit.h"
#include "tag.h" #include "tag.h"
#include "tree.h" #include "tree.h"
#include "tree-walk.h"
#include "refs.h" #include "refs.h"
#include "pack-revindex.h" #include "pack-revindex.h"
#include "sha1-lookup.h" #include "sha1-lookup.h"
@ -2471,8 +2472,37 @@ int has_sha1_file(const unsigned char *sha1)
return has_loose_object(sha1); return has_loose_object(sha1);
} }


static void check_tree(const void *buf, size_t size)
{
struct tree_desc desc;
struct name_entry entry;

init_tree_desc(&desc, buf, size);
while (tree_entry(&desc, &entry))
/* do nothing
* tree_entry() will die() on malformed entries */
;
}

static void check_commit(const void *buf, size_t size)
{
struct commit c;
memset(&c, 0, sizeof(c));
if (parse_commit_buffer(&c, buf, size))
die("corrupt commit");
}

static void check_tag(const void *buf, size_t size)
{
struct tag t;
memset(&t, 0, sizeof(t));
if (parse_tag_buffer(&t, buf, size))
die("corrupt tag");
}

static int index_mem(unsigned char *sha1, void *buf, size_t size, static int index_mem(unsigned char *sha1, void *buf, size_t size,
int write_object, enum object_type type, const char *path) int write_object, enum object_type type,
const char *path, int format_check)
{ {
int ret, re_allocated = 0; int ret, re_allocated = 0;


@ -2490,6 +2520,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
re_allocated = 1; re_allocated = 1;
} }
} }
if (format_check) {
if (type == OBJ_TREE)
check_tree(buf, size);
if (type == OBJ_COMMIT)
check_commit(buf, size);
if (type == OBJ_TAG)
check_tag(buf, size);
}


if (write_object) if (write_object)
ret = write_sha1_file(buf, size, typename(type), sha1); ret = write_sha1_file(buf, size, typename(type), sha1);
@ -2503,7 +2541,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
#define SMALL_FILE_SIZE (32*1024) #define SMALL_FILE_SIZE (32*1024)


int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
enum object_type type, const char *path) enum object_type type, const char *path, int format_check)
{ {
int ret; int ret;
size_t size = xsize_t(st->st_size); size_t size = xsize_t(st->st_size);
@ -2512,23 +2550,25 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
struct strbuf sbuf = STRBUF_INIT; struct strbuf sbuf = STRBUF_INIT;
if (strbuf_read(&sbuf, fd, 4096) >= 0) if (strbuf_read(&sbuf, fd, 4096) >= 0)
ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object, ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
type, path); type, path, format_check);
else else
ret = -1; ret = -1;
strbuf_release(&sbuf); strbuf_release(&sbuf);
} else if (!size) { } else if (!size) {
ret = index_mem(sha1, NULL, size, write_object, type, path); ret = index_mem(sha1, NULL, size, write_object, type, path,
format_check);
} else if (size <= SMALL_FILE_SIZE) { } else if (size <= SMALL_FILE_SIZE) {
char *buf = xmalloc(size); char *buf = xmalloc(size);
if (size == read_in_full(fd, buf, size)) if (size == read_in_full(fd, buf, size))
ret = index_mem(sha1, buf, size, write_object, type, ret = index_mem(sha1, buf, size, write_object, type,
path); path, format_check);
else else
ret = error("short read %s", strerror(errno)); ret = error("short read %s", strerror(errno));
free(buf); free(buf);
} else { } else {
void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
ret = index_mem(sha1, buf, size, write_object, type, path); ret = index_mem(sha1, buf, size, write_object, type, path,
format_check);
munmap(buf, size); munmap(buf, size);
} }
close(fd); close(fd);
@ -2546,7 +2586,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
if (fd < 0) if (fd < 0)
return error("open(\"%s\"): %s", path, return error("open(\"%s\"): %s", path,
strerror(errno)); strerror(errno));
if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0) if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
return error("%s: failed to insert into database", return error("%s: failed to insert into database",
path); path);
break; break;

13
t/t1007-hash-object.sh

@ -188,4 +188,17 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
pop_repo pop_repo
done done


test_expect_success 'corrupt tree' '
echo abc >malformed-tree
test_must_fail git hash-object -t tree malformed-tree
'

test_expect_success 'corrupt commit' '
test_must_fail git hash-object -t commit --stdin </dev/null
'

test_expect_success 'corrupt tag' '
test_must_fail git hash-object -t tag --stdin </dev/null
'

test_done test_done

Loading…
Cancel
Save