Commit Diff


commit - 210941d5b54452c8d6c17e171bae6b701622a014
commit + 6c85883bcc71c13fe4fa5bfba2ddf30edabc92b9
blob - 5c41b9f15c9dcf5b1f2cb329af87e3d94e61a8e1
blob + 63393465efc849fc14e7a08eb628b52349ce399d
--- lib/pack.c
+++ lib/pack.c
@@ -1455,8 +1455,8 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
 	const struct got_error *err = NULL;
 	struct got_delta *delta;
 	uint8_t *base_buf = NULL, *accum_buf = NULL, *delta_buf;
-	size_t base_bufsz = 0, accum_size = 0, delta_len;
-	uint64_t max_size;
+	size_t base_bufsz = 0, accum_bufsz = 0, accum_size = 0, delta_len;
+	uint64_t max_size = 0;
 	int n = 0;
 
 	*outbuf = NULL;
@@ -1464,16 +1464,10 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
 
 	if (STAILQ_EMPTY(&deltas->entries))
 		return got_error(GOT_ERR_BAD_DELTA_CHAIN);
-
-	err = got_pack_get_delta_chain_max_size(&max_size, deltas, pack);
-	if (err)
-		return err;
-	accum_buf = malloc(max_size);
-	if (accum_buf == NULL)
-		return got_error_from_errno("malloc");
 
 	/* Deltas are ordered in ascending order. */
 	STAILQ_FOREACH(delta, &deltas->entries, entry) {
+		uint64_t base_size, result_size = 0;
 		int cached = 1;
 		if (n == 0) {
 			size_t delta_data_offset;
@@ -1492,6 +1486,10 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
 				err = got_error(GOT_ERR_PACK_OFFSET);
 				goto done;
 			}
+
+			if (delta->size > max_size)
+				max_size = delta->size;
+
 			if (pack->map) {
 				size_t mapoff = (size_t)delta_data_offset;
 				err = got_inflate_to_mem_mmap(&base_buf,
@@ -1530,6 +1528,36 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
 				goto done;
 			}
 		}
+
+		err = got_delta_get_sizes(&base_size, &result_size,
+		    delta_buf, delta_len);
+		if (err)
+			goto done;
+		if (base_size > max_size)
+			max_size = base_size;
+		if (result_size > max_size)
+			max_size = result_size;
+
+		if (max_size > base_bufsz) {
+			uint8_t *p = realloc(base_buf, max_size);
+			if (p == NULL) {
+				err = got_error_from_errno("realloc");
+				goto done;
+			}
+			base_buf = p;
+			base_bufsz = max_size;
+		}
+
+		if (max_size > accum_bufsz) {
+			uint8_t *p = realloc(accum_buf, max_size);
+			if (p == NULL) {
+				err = got_error_from_errno("realloc");
+				goto done;
+			}
+			accum_buf = p;
+			accum_bufsz = max_size;
+		}
+
 		err = got_delta_apply_in_mem(base_buf, base_bufsz,
 		    delta_buf, delta_len, accum_buf,
 		    &accum_size, max_size);
@@ -1542,24 +1570,11 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
 		if (n < deltas->nentries) {
 			/* Accumulated delta becomes the new base. */
 			uint8_t *tmp = accum_buf;
-			/*
-			 * Base buffer switches roles with accumulation buffer.
-			 * Ensure it can hold the largest result in the delta
-			 * chain. Initial allocation might have been smaller.
-			 */
-			if (base_bufsz < max_size) {
-				uint8_t *p;
-				p = reallocarray(base_buf, 1, max_size);
-				if (p == NULL) {
-					err = got_error_from_errno(
-					    "reallocarray");
-					goto done;
-				}
-				base_buf = p;
-				base_bufsz = max_size;
-			}
+			size_t tmp_size = accum_bufsz;
 			accum_buf = base_buf;
+			accum_bufsz = base_bufsz;
 			base_buf = tmp;
+			base_bufsz = tmp_size;
 		}
 	}