Skip to content

Commit c879daa

Browse files
pcloudsgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cf7b1ca commit c879daa

File tree

5 files changed

+63
-10
lines changed

5 files changed

+63
-10
lines changed

builtin/hash-object.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
1515
struct stat st;
1616
unsigned char sha1[20];
1717
if (fstat(fd, &st) < 0 ||
18-
index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
18+
index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1))
1919
die(write_object
2020
? "Unable to add %s to database"
2121
: "Unable to hash %s", path);

cache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ extern int ie_match_stat(const struct index_state *, struct cache_entry *, struc
501501
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
502502

503503
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
504-
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
504+
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);
505505
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
506506
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
507507

read-cache.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
9292

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

sha1_file.c

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "commit.h"
1414
#include "tag.h"
1515
#include "tree.h"
16+
#include "tree-walk.h"
1617
#include "refs.h"
1718
#include "pack-revindex.h"
1819
#include "sha1-lookup.h"
@@ -2471,8 +2472,37 @@ int has_sha1_file(const unsigned char *sha1)
24712472
return has_loose_object(sha1);
24722473
}
24732474

2475+
static void check_tree(const void *buf, size_t size)
2476+
{
2477+
struct tree_desc desc;
2478+
struct name_entry entry;
2479+
2480+
init_tree_desc(&desc, buf, size);
2481+
while (tree_entry(&desc, &entry))
2482+
/* do nothing
2483+
* tree_entry() will die() on malformed entries */
2484+
;
2485+
}
2486+
2487+
static void check_commit(const void *buf, size_t size)
2488+
{
2489+
struct commit c;
2490+
memset(&c, 0, sizeof(c));
2491+
if (parse_commit_buffer(&c, buf, size))
2492+
die("corrupt commit");
2493+
}
2494+
2495+
static void check_tag(const void *buf, size_t size)
2496+
{
2497+
struct tag t;
2498+
memset(&t, 0, sizeof(t));
2499+
if (parse_tag_buffer(&t, buf, size))
2500+
die("corrupt tag");
2501+
}
2502+
24742503
static int index_mem(unsigned char *sha1, void *buf, size_t size,
2475-
int write_object, enum object_type type, const char *path)
2504+
int write_object, enum object_type type,
2505+
const char *path, int format_check)
24762506
{
24772507
int ret, re_allocated = 0;
24782508

@@ -2490,6 +2520,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
24902520
re_allocated = 1;
24912521
}
24922522
}
2523+
if (format_check) {
2524+
if (type == OBJ_TREE)
2525+
check_tree(buf, size);
2526+
if (type == OBJ_COMMIT)
2527+
check_commit(buf, size);
2528+
if (type == OBJ_TAG)
2529+
check_tag(buf, size);
2530+
}
24932531

24942532
if (write_object)
24952533
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,
25032541
#define SMALL_FILE_SIZE (32*1024)
25042542

25052543
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
2506-
enum object_type type, const char *path)
2544+
enum object_type type, const char *path, int format_check)
25072545
{
25082546
int ret;
25092547
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,
25122550
struct strbuf sbuf = STRBUF_INIT;
25132551
if (strbuf_read(&sbuf, fd, 4096) >= 0)
25142552
ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
2515-
type, path);
2553+
type, path, format_check);
25162554
else
25172555
ret = -1;
25182556
strbuf_release(&sbuf);
25192557
} else if (!size) {
2520-
ret = index_mem(sha1, NULL, size, write_object, type, path);
2558+
ret = index_mem(sha1, NULL, size, write_object, type, path,
2559+
format_check);
25212560
} else if (size <= SMALL_FILE_SIZE) {
25222561
char *buf = xmalloc(size);
25232562
if (size == read_in_full(fd, buf, size))
25242563
ret = index_mem(sha1, buf, size, write_object, type,
2525-
path);
2564+
path, format_check);
25262565
else
25272566
ret = error("short read %s", strerror(errno));
25282567
free(buf);
25292568
} else {
25302569
void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2531-
ret = index_mem(sha1, buf, size, write_object, type, path);
2570+
ret = index_mem(sha1, buf, size, write_object, type, path,
2571+
format_check);
25322572
munmap(buf, size);
25332573
}
25342574
close(fd);
@@ -2546,7 +2586,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
25462586
if (fd < 0)
25472587
return error("open(\"%s\"): %s", path,
25482588
strerror(errno));
2549-
if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
2589+
if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
25502590
return error("%s: failed to insert into database",
25512591
path);
25522592
break;

t/t1007-hash-object.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,17 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
188188
pop_repo
189189
done
190190

191+
test_expect_success 'corrupt tree' '
192+
echo abc >malformed-tree
193+
test_must_fail git hash-object -t tree malformed-tree
194+
'
195+
196+
test_expect_success 'corrupt commit' '
197+
test_must_fail git hash-object -t commit --stdin </dev/null
198+
'
199+
200+
test_expect_success 'corrupt tag' '
201+
test_must_fail git hash-object -t tag --stdin </dev/null
202+
'
203+
191204
test_done

0 commit comments

Comments
 (0)