Commit 5b7e13a71fb6ddb69c2a98e5c8d5192d8805519b

Stefan Sperling 2018-04-02T12:46:33

put a limit on delta chain recursion

diff --git a/include/got_error.h b/include/got_error.h
index 4927b60..86c785d 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -45,6 +45,7 @@
 #define GOT_ERR_WORKTREE_BUSY	29
 #define GOT_ERR_DIR_OBSTRUCTED	30
 #define GOT_ERR_FILE_OBSTRUCTED	31
+#define GOT_ERR_RECURSION	32
 
 static const struct got_error {
 	int code;
@@ -78,6 +79,7 @@ static const struct got_error {
 	{ GOT_ERR_WORKTREE_META,"bad worktree meta data" },
 	{ GOT_ERR_WORKTREE_VERS,"unsupported worktree format version" },
 	{ GOT_ERR_WORKTREE_BUSY,"worktree already locked" },
+	{ GOT_ERR_RECURSION,	"recursion limit reached" },
 };
 
 /*
diff --git a/lib/pack.c b/lib/pack.c
index d7b25da..710d4ed 100644
--- a/lib/pack.c
+++ b/lib/pack.c
@@ -56,6 +56,8 @@
 				SHA1_DIGEST_STRING_LENGTH - 1 + \
 				strlen(GOT_PACKIDX_SUFFIX))
 
+#define GOT_DELTA_CHAIN_RECURSION_MAX	100
+
 #ifndef MIN
 #define	MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
 #endif
@@ -734,7 +736,7 @@ parse_offset_delta(off_t *base_offset, FILE *packfile, off_t offset)
 
 static const struct got_error *
 resolve_delta_chain(struct got_delta_chain *, struct got_repository *,
-    FILE *, const char *, off_t, size_t, int, size_t);
+    FILE *, const char *, off_t, size_t, int, size_t, unsigned int);
 
 static const struct got_error *
 add_delta(struct got_delta_chain *deltas, const char *path_packfile,
@@ -757,7 +759,8 @@ add_delta(struct got_delta_chain *deltas, const char *path_packfile,
 static const struct got_error *
 resolve_offset_delta(struct got_delta_chain *deltas,
     struct got_repository *repo, FILE *packfile, const char *path_packfile,
-    off_t delta_offset,size_t tslen, int delta_type, size_t delta_size)
+    off_t delta_offset,size_t tslen, int delta_type, size_t delta_size,
+    unsigned int recursion)
 
 {
 	const struct got_error *err;
@@ -796,13 +799,13 @@ resolve_offset_delta(struct got_delta_chain *deltas,
 		return err;
 
 	return resolve_delta_chain(deltas, repo, packfile, path_packfile,
-	    base_offset, base_tslen, base_type, base_size);
+	    base_offset, base_tslen, base_type, base_size, recursion - 1);
 }
 
 static const struct got_error *
 resolve_ref_delta(struct got_delta_chain *deltas, struct got_repository *repo,
     FILE *packfile, const char *path_packfile, off_t delta_offset,
-    size_t tslen, int delta_type, size_t delta_size)
+    size_t tslen, int delta_type, size_t delta_size, unsigned int recursion)
 {
 	const struct got_error *err;
 	struct got_object_id id;
@@ -868,7 +871,7 @@ resolve_ref_delta(struct got_delta_chain *deltas, struct got_repository *repo,
 
 	err = resolve_delta_chain(deltas, repo, base_pack->packfile,
 	    path_base_packfile, base_offset, base_tslen, base_type,
-	    base_size);
+	    base_size, recursion - 1);
 done:
 	free(path_base_packfile);
 	return err;
@@ -877,10 +880,13 @@ done:
 static const struct got_error *
 resolve_delta_chain(struct got_delta_chain *deltas, struct got_repository *repo,
     FILE *packfile, const char *path_packfile, off_t delta_offset, size_t tslen,
-    int delta_type, size_t delta_size)
+    int delta_type, size_t delta_size, unsigned int recursion)
 {
 	const struct got_error *err = NULL;
 
+	if (--recursion == 0)
+		return got_error(GOT_ERR_RECURSION);
+
 	switch (delta_type) {
 	case GOT_OBJ_TYPE_COMMIT:
 	case GOT_OBJ_TYPE_TREE:
@@ -893,12 +899,12 @@ resolve_delta_chain(struct got_delta_chain *deltas, struct got_repository *repo,
 	case GOT_OBJ_TYPE_OFFSET_DELTA:
 		err = resolve_offset_delta(deltas, repo, packfile,
 		    path_packfile, delta_offset, tslen, delta_type,
-		    delta_size);
+		    delta_size, recursion - 1);
 		break;
 	case GOT_OBJ_TYPE_REF_DELTA:
 		err = resolve_ref_delta(deltas, repo, packfile,
 		    path_packfile, delta_offset, tslen, delta_type,
-		    delta_size);
+		    delta_size, recursion - 1);
 		break;
 	default:
 		return got_error(GOT_ERR_OBJ_TYPE);
@@ -937,7 +943,8 @@ open_delta_object(struct got_object **obj, struct got_repository *repo,
 	(*obj)->flags |= GOT_OBJ_FLAG_DELTIFIED;
 
 	err = resolve_delta_chain(&(*obj)->deltas, repo, packfile,
-	    path_packfile, offset, tslen, delta_type, delta_size);
+	    path_packfile, offset, tslen, delta_type, delta_size,
+	    GOT_DELTA_CHAIN_RECURSION_MAX);
 	if (err)
 		goto done;