From c5d8d286f6920542a73232d503d92e9a82b7be58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
Date: Mon, 11 Sep 2023 13:09:58 +0300
Subject: [PATCH] adb: implement ADB_BLOCK_EXT flavor for large files

---
 src/adb.c          | 46 +++++++++++++++++++++-------------------------
 src/adb.h          | 38 +++++++++++++++++++++++++++++---------
 src/adb_walk_adb.c |  6 +++---
 src/extract_v3.c   |  2 +-
 4 files changed, 54 insertions(+), 38 deletions(-)

diff --git a/src/adb.c b/src/adb.c
index 3f221878..4bc89d43 100644
--- a/src/adb.c
+++ b/src/adb.c
@@ -18,11 +18,13 @@ static char padding_zeroes[ADB_BLOCK_ALIGNMENT] = {0};
 /* Block enumeration */
 static inline struct adb_block *adb_block_validate(struct adb_block *blk, apk_blob_t b)
 {
-	size_t pos = (char *)blk - b.ptr;
+	size_t pos = (char *)blk - b.ptr, len = (size_t)(b.len - pos);
 	if (pos == b.len) return NULL;
-	if (sizeof(struct adb_block) > b.len - pos) return ERR_PTR(-APKE_ADB_BLOCK);
-	if (adb_block_rawsize(blk) < sizeof(struct adb_block)) return ERR_PTR(-APKE_ADB_BLOCK);
-	if (adb_block_size(blk) > b.len - pos) return ERR_PTR(-APKE_ADB_BLOCK);
+	if (sizeof(uint32_t) > len) return ERR_PTR(-APKE_ADB_BLOCK);
+	size_t hdrlen = adb_block_hdrsize(blk);
+	if (hdrlen > len) return ERR_PTR(-APKE_ADB_BLOCK);
+	if (adb_block_rawsize(blk) < hdrlen) return ERR_PTR(-APKE_ADB_BLOCK);
+	if (adb_block_size(blk) > len) return ERR_PTR(-APKE_ADB_BLOCK);
 	return blk;
 }
 
@@ -94,7 +96,7 @@ static int __adb_m_parse(struct adb *db, apk_blob_t data, struct apk_trust *t,
 		}
 		switch (type) {
 		case ADB_BLOCK_ADB:
-			allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+			allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA);
 			if (b.len < 16) {
 				r = -APKE_ADB_BLOCK;
 				goto err;
@@ -111,15 +113,12 @@ static int __adb_m_parse(struct adb *db, apk_blob_t data, struct apk_trust *t,
 				trusted = 1;
 			break;
 		case ADB_BLOCK_DATA:
-			allowed = BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+			allowed = BIT(ADB_BLOCK_DATA);
 			if (!trusted) {
 				r = -APKE_SIGNATURE_UNTRUSTED;
 				goto err;
 			}
 			break;
-		case ADB_BLOCK_DATAX:
-			r = -APKE_ADB_BLOCK;
-			goto err;
 		}
 		r = cb(db, blk, apk_istream_from_blob(&is, b));
 		if (r < 0) break;
@@ -175,7 +174,6 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
 	void *sig;
 	int r = 0, trusted = (t && t->allow_untrusted) ? 1 : 0;
 	uint32_t type, allowed = BIT(ADB_BLOCK_ADB);
-	size_t sz;
 
 	if (IS_ERR(is)) return PTR_ERR(is);
 
@@ -193,8 +191,11 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
 	}
 
 	do {
-		r = apk_istream_read_max(is, &blk, sizeof blk);
-		if (r != sizeof blk) break;
+		size_t hdrsize = sizeof blk;
+		void *hdrptr = apk_istream_peek(is, sizeof(blk.type_size));
+		if (!IS_ERR(hdrptr)) hdrsize = adb_block_hdrsize(hdrptr);
+		r = apk_istream_read_max(is, &blk, hdrsize);
+		if (r != hdrsize) break;
 
 		type = adb_block_type(&blk);
 		if (!(BIT(type) & allowed)) {
@@ -202,10 +203,10 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
 			break;
 		}
 
-		sz = adb_block_length(&blk);
+		uint64_t sz = adb_block_length(&blk);
 		switch (type) {
 		case ADB_BLOCK_ADB:
-			allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+			allowed = BIT(ADB_BLOCK_SIG) | BIT(ADB_BLOCK_DATA);
 			db->adb.ptr = malloc(sz);
 			db->adb.len = sz;
 			if (db->adb.len < 16) {
@@ -231,15 +232,12 @@ static int __adb_m_stream(struct adb *db, struct apk_istream *is, uint32_t expec
 				trusted = 1;
 			break;
 		case ADB_BLOCK_DATA:
-			allowed = BIT(ADB_BLOCK_DATA) | BIT(ADB_BLOCK_DATAX);
+			allowed = BIT(ADB_BLOCK_DATA);
 			if (!trusted) {
 				r = -APKE_SIGNATURE_UNTRUSTED;
 				goto err;
 			}
 			break;
-		case ADB_BLOCK_DATAX:
-			r = -APKE_ADB_BLOCK;
-			goto err;
 		}
 
 		apk_istream_segment(&seg, is, sz, 0);
@@ -762,7 +760,7 @@ adb_val_t adb_w_adb(struct adb *db, struct adb *valdb)
 	struct adb_block blk = adb_block_init(ADB_BLOCK_ADB, valdb->adb.len);
 	struct iovec vec[] = {
 		{ .iov_base = &bsz, .iov_len = sizeof bsz },
-		{ .iov_base = &blk, .iov_len = sizeof blk },
+		{ .iov_base = &blk, .iov_len = adb_block_hdrsize(&blk) },
 		{ .iov_base = valdb->adb.ptr, .iov_len = valdb->adb.len },
 		{ .iov_base = padding_zeroes, .iov_len = adb_block_padding(&blk) },
 	};
@@ -1089,9 +1087,7 @@ int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val)
 	size_t padding = adb_block_padding(&blk);
 	int r;
 
-	if (val.len & ~0x3fffffff) return -APKE_ADB_LIMIT;
-
-	r = apk_ostream_write(os, &blk, sizeof blk);
+	r = apk_ostream_write(os, &blk, adb_block_hdrsize(&blk));
 	if (r < 0) return r;
 
 	r = apk_ostream_write(os, val.ptr, val.len);
@@ -1105,7 +1101,7 @@ int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t val)
 	return 0;
 }
 
-int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is)
+int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint64_t size, struct apk_istream *is)
 {
 	struct adb_block blk = adb_block_init(ADB_BLOCK_DATA, size + hdr.len);
 	size_t padding = adb_block_padding(&blk);
@@ -1114,7 +1110,7 @@ int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, stru
 	if (IS_ERR(os)) return PTR_ERR(os);
 	if (IS_ERR(is)) return apk_ostream_cancel(os, PTR_ERR(is));
 
-	r = apk_ostream_write(os, &blk, sizeof blk);
+	r = apk_ostream_write(os, &blk, adb_block_hdrsize(&blk));
 	if (r < 0) return r;
 
 	r = apk_ostream_write(os, hdr.ptr, hdr.len);
@@ -1133,7 +1129,7 @@ int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, stru
 
 int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *vfy)
 {
-	size_t blk_sz = adb_block_length(b);
+	uint64_t blk_sz = adb_block_length(b);
 	size_t padding = adb_block_padding(b);
 	int r;
 
diff --git a/src/adb.h b/src/adb.h
index 86499893..32152ae2 100644
--- a/src/adb.h
+++ b/src/adb.h
@@ -57,21 +57,41 @@ struct adb_file_header {
 #define ADB_BLOCK_ADB		0
 #define ADB_BLOCK_SIG		1
 #define ADB_BLOCK_DATA		2
-#define ADB_BLOCK_DATAX		3
+#define ADB_BLOCK_EXT		3
 
 struct adb_block {
 	uint32_t type_size;
+	uint32_t reserved;
+	uint64_t x_size;
 };
 
-static inline struct adb_block adb_block_init(uint32_t type, uint32_t length) {
-	return (struct adb_block) { .type_size = htole32((type << 30) + sizeof(struct adb_block) + length)};
+static inline struct adb_block adb_block_init(uint32_t type, uint64_t length) {
+	if (length <= 0x3fffffff) {
+		return (struct adb_block) {
+			.type_size = htole32((type << 30) + sizeof(uint32_t) + length),
+		};
+	}
+	return (struct adb_block) {
+		.type_size = htole32((ADB_BLOCK_EXT << 30) + type),
+		.x_size = htole64(sizeof(struct adb_block) + length),
+	};
 }
-static inline uint32_t adb_block_type(struct adb_block *b) { return le32toh((b)->type_size) >> 30; }
-static inline uint32_t adb_block_rawsize(struct adb_block *b) { return le32toh((b)->type_size) & 0x3fffffff; }
-static inline uint32_t adb_block_size(struct adb_block *b) { return ROUND_UP(adb_block_rawsize(b), ADB_BLOCK_ALIGNMENT); }
-static inline uint32_t adb_block_length(struct adb_block *b) { return adb_block_rawsize(b) - sizeof(struct adb_block); }
+static inline uint32_t adb_block_is_ext(struct adb_block *b) {
+	return (le32toh((b)->type_size) >> 30) == ADB_BLOCK_EXT;
+}
+static inline uint32_t adb_block_type(struct adb_block *b) {
+	return adb_block_is_ext(b) ? (le32toh(b->type_size) & 0x3fffffff) : (le32toh(b->type_size) >> 30);
+}
+static inline uint64_t adb_block_rawsize(struct adb_block *b) {
+	return adb_block_is_ext(b) ? le64toh(b->x_size) : (le32toh(b->type_size) & 0x3fffffff);
+}
+static inline uint32_t adb_block_hdrsize(struct adb_block *b) {
+	return adb_block_is_ext(b) ? sizeof *b : sizeof b->type_size;
+}
+static inline uint64_t adb_block_size(struct adb_block *b) { return ROUND_UP(adb_block_rawsize(b), ADB_BLOCK_ALIGNMENT); }
+static inline uint64_t adb_block_length(struct adb_block *b) { return adb_block_rawsize(b) - adb_block_hdrsize(b); }
 static inline uint32_t adb_block_padding(struct adb_block *b) { return adb_block_size(b) - adb_block_rawsize(b); }
-static inline void *adb_block_payload(struct adb_block *b) { return b + 1; }
+static inline void *adb_block_payload(struct adb_block *b) { return (char*)b + adb_block_hdrsize(b); }
 static inline apk_blob_t adb_block_blob(struct adb_block *b) {
 	return APK_BLOB_PTR_LEN(adb_block_payload(b), adb_block_length(b));
 }
@@ -243,7 +263,7 @@ int adb_s_field_by_name(const struct adb_object_schema *, const char *);
 /* Creation */
 int adb_c_header(struct apk_ostream *os, struct adb *db);
 int adb_c_block(struct apk_ostream *os, uint32_t type, apk_blob_t);
-int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint32_t size, struct apk_istream *is);
+int adb_c_block_data(struct apk_ostream *os, apk_blob_t hdr, uint64_t size, struct apk_istream *is);
 int adb_c_block_copy(struct apk_ostream *os, struct adb_block *b, struct apk_istream *is, struct adb_verify_ctx *);
 int adb_c_adb(struct apk_ostream *os, struct adb *db, struct apk_trust *t);
 int adb_c_create(struct apk_ostream *os, struct adb *db, struct apk_trust *t);
diff --git a/src/adb_walk_adb.c b/src/adb_walk_adb.c
index 3ceac566..e1a686dc 100644
--- a/src/adb_walk_adb.c
+++ b/src/adb_walk_adb.c
@@ -115,7 +115,7 @@ static int adb_walk_block(struct adb *db, struct adb_block *b, struct apk_istrea
 	struct adb_sign_hdr *s;
 	uint32_t schema_magic = ctx->db.schema;
 	const struct adb_db_schema *ds;
-	size_t sz = adb_block_length(b);
+	uint64_t sz = adb_block_length(b);
 	apk_blob_t data, c = APK_BLOB_BUF(tmp);
 	int r;
 
@@ -142,10 +142,10 @@ static int adb_walk_block(struct adb *db, struct adb_block *b, struct apk_istrea
 		apk_blob_push_fmt(&c, ": %s", r ? apk_error_str(r) : "OK");
 		break;
 	case ADB_BLOCK_DATA:
-		apk_blob_push_fmt(&c, "data block, size: %zu", sz);
+		apk_blob_push_fmt(&c, "data block, size: %" PRIu64, sz);
 		break;
 	default:
-		apk_blob_push_fmt(&c, "unknown block %d, size: %zu", adb_block_type(b), sz);
+		apk_blob_push_fmt(&c, "unknown block %d, size: %" PRIu64, adb_block_type(b), sz);
 		break;
 	}
 	d->ops->comment(d, apk_blob_pushed(APK_BLOB_BUF(tmp), c));
diff --git a/src/extract_v3.c b/src/extract_v3.c
index 2ee31c0b..f6478670 100644
--- a/src/extract_v3.c
+++ b/src/extract_v3.c
@@ -182,7 +182,7 @@ static int apk_extract_v3_data_block(struct adb *db, struct adb_block *b, struct
 	struct apk_extract_v3_ctx *ctx = container_of(db, struct apk_extract_v3_ctx, db);
 	struct apk_extract_ctx *ectx = ctx->ectx;
 	struct adb_data_package *hdr;
-	size_t sz = adb_block_length(b);
+	uint64_t sz = adb_block_length(b);
 	int r;
 
 	if (adb_block_type(b) != ADB_BLOCK_DATA) return 0;
-- 
GitLab