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>
Nguyễn Thái Ngọc Duy committed
Feb 5, 2011 at 17:52 UTC
c879daa23729547fb28aa7e8783c5e4e619a9e7c
5 files changed
+63
-10
builtin/hash-object.c
+1
-1
@@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
15
struct stat st;
16
unsigned char sha1[20];
17
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))
19
die(write_object
20
? "Unable to add %s to database"
21
: "Unable to hash %s", path);
cache.h
+1
-1
@@ -501,7 +501,7 @@ extern int ie_match_stat(const struct index_state *, struct cache_entry *, struc
501
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
502
503
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);
505
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
506
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
507
read-cache.c
+1
-1
@@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
92
93
if (fd >= 0) {
94
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))
96
match = hashcmp(sha1, ce->sha1);
97
/* index_fd() closed the file descriptor already */
98
}
sha1_file.c
+47
-7
@@ -13,6 +13,7 @@
13
#include "commit.h"
14
#include "tag.h"
15
#include "tree.h"
16
+#include "tree-walk.h"
17
#include "refs.h"
18
#include "pack-revindex.h"
19
#include "sha1-lookup.h"
@@ -2471,8 +2472,37 @@ int has_sha1_file(const unsigned char *sha1)
2472
return has_loose_object(sha1);
2473
}
2474
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
+
2503
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)
2506
{
2507
int ret, re_allocated = 0;
2508
@@ -2490,6 +2520,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
2520
re_allocated = 1;
2521
}
2522
}
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
+ }
2531
2532
if (write_object)
2533
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,
2541
#define SMALL_FILE_SIZE (32*1024)
2542
2543
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)
2545
{
2546
int ret;
2547
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,
2550
struct strbuf sbuf = STRBUF_INIT;
2551
if (strbuf_read(&sbuf, fd, 4096) >= 0)
2552
ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
2515
- type, path);
2553
+ type, path, format_check);
2554
else
2555
ret = -1;
2556
strbuf_release(&sbuf);
2557
} 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);
2560
} else if (size <= SMALL_FILE_SIZE) {
2561
char *buf = xmalloc(size);
2562
if (size == read_in_full(fd, buf, size))
2563
ret = index_mem(sha1, buf, size, write_object, type,
2525
- path);
2564
+ path, format_check);
2565
else
2566
ret = error("short read %s", strerror(errno));
2567
free(buf);
2568
} else {
2569
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);
2572
munmap(buf, size);
2573
}
2574
close(fd);
@@ -2546,7 +2586,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
2586
if (fd < 0)
2587
return error("open(\"%s\"): %s", path,
2588
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)
2590
return error("%s: failed to insert into database",
2591
path);
2592
break;
t/t1007-hash-object.sh
+13
@@ -188,4 +188,17 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
188
pop_repo
189
done
190
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
+
204
test_done