"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
verify checksums of loose objects
To:
gameoftrees@openbsd.org
Date:
Sun, 4 Jul 2021 22:54:09 +0200

Download raw body.

Thread
  • Stefan Sperling:

    verify checksums of loose objects

This patch implements checksum verification while loose objects are
being accessed. A checksum failure implies a corrupt object file and
is only recoverable by locating a known-good backup of the object.

To do this as efficiently as possible we can mix bytes into a SHA1
context while decompressing the loose object file. This avoids a
separate scan over the file in order to calculate the checksum.

We need to pass the expected object ID to allow helper programs to
verify the checksum once decompression has completed. More passing
around of object IDs results in a bit of unfortunate churn.

Doing this for packed objects would also be possible but would require
modifications all the way down into lib/delta.c. I'm leaving this for
a future patch.

ok?

diff 1d2da32b459cb03fa97dae33887bb432737bb35e 14127e2b145723f3cf4ca1926b6947c3e8547748
blob - 851d4ad4128c5bcac0cd5616088fb5063844a83f
blob + 729eacbd1d8c93ed5bc78ab33a4e7517d1456546
--- include/got_error.h
+++ include/got_error.h
@@ -146,6 +146,7 @@
 #define GOT_ERR_BAD_SYMLINK	129
 #define GOT_ERR_GIT_REPO_EXT	130
 #define GOT_ERR_CANNOT_PACK	131
+#define GOT_ERR_OBJ_CSUM	132
 
 static const struct got_error {
 	int code;
@@ -299,6 +300,7 @@ static const struct got_error {
 	    "version control" },
 	{ GOT_ERR_GIT_REPO_EXT, "unsupported repository format extension" },
 	{ GOT_ERR_CANNOT_PACK, "not enough objects to pack" },
+	{ GOT_ERR_OBJ_CSUM, "bad object checksum" },
 };
 
 /*
blob - ad8c052645bdb1fad1e36b8ca14e324f41bff53d
blob + 3833fedc8b779ac09969970c0ae89d5732c651cf
--- lib/got_lib_inflate.h
+++ lib/got_lib_inflate.h
@@ -20,6 +20,12 @@ struct got_inflate_checksum {
 
 	/* If not NULL, mix input bytes into this SHA1 context. */
 	SHA1_CTX *input_sha1;
+
+	/* If not NULL, mix output bytes into this CRC checksum. */
+	uint32_t *output_crc;
+
+	/* If not NULL, mix output bytes into this SHA1 context. */
+	SHA1_CTX *output_sha1;
 };
 
 struct got_inflate_buf {
blob - 7f91b3bcc5e247a002e805274461026d32267fe5
blob + f107a850167bfb93f97e10313c9351627cd8eb68
--- lib/got_lib_object.h
+++ lib/got_lib_object.h
@@ -103,7 +103,7 @@ const struct got_error *got_object_open_loose_fd(int *
 const struct got_error *got_object_open_packed(struct got_object **,
     struct got_object_id *, struct got_repository *);
 const struct got_error *got_object_read_header_privsep(struct got_object **,
-    struct got_repository *, int);
+    struct got_object_id *, struct got_repository *, int);
 const struct got_error *got_object_open(struct got_object **,
     struct got_repository *, struct got_object_id *);
 const struct got_error *got_object_raw_open(struct got_raw_object **,
blob - 852db60a614ec79d99a3416e34b5e0b018f27ed9
blob + 7592979912f4df4afc7d084b4a3779e769d66b04
--- lib/got_lib_object_parse.h
+++ lib/got_lib_object_parse.h
@@ -42,6 +42,7 @@ const struct got_error *got_read_file_to_mem(uint8_t *
 
 struct got_pack;
 struct got_packidx;
+struct got_inflate_checksum;
 
 const struct got_error *got_object_parse_header(struct got_object **, char *, size_t);
 const struct got_error *got_object_read_header(struct got_object **, int);
blob - dc1687fd0232a98260fd1d1f91425364de21521b
blob + e35958fa1bceef186dcfd71f9d4a132377b31766
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -369,6 +369,13 @@ struct got_imsg_pack {
 } __attribute__((__packed__));
 
 /*
+ * Structure for GOT_IMSG_OBJECT_REQUEST, GOT_IMSG_BLOB_REQUEST,
+ * GOT_IMSG_TREE_REQUEST, GOT_IMSG_COMMIT_REQUEST, and
+ * GOT_IMSG_TAG_REQUEST data.
+ */
+struct got_object_id;
+
+/*
  * Structure for GOT_IMSG_PACKED_OBJECT_REQUEST and
  * GOT_IMSG_PACKED_RAW_OBJECT_REQUEST data.
  */
@@ -428,8 +435,10 @@ const struct got_error *got_privsep_recv_imsg(struct i
 void got_privsep_send_error(struct imsgbuf *, const struct got_error *);
 const struct got_error *got_privsep_send_ack(struct imsgbuf *);
 const struct got_error *got_privsep_wait_ack(struct imsgbuf *);
-const struct got_error *got_privsep_send_obj_req(struct imsgbuf *, int);
-const struct got_error *got_privsep_send_raw_obj_req(struct imsgbuf *, int);
+const struct got_error *got_privsep_send_obj_req(struct imsgbuf *, int,
+    struct got_object_id *);
+const struct got_error *got_privsep_send_raw_obj_req(struct imsgbuf *, int,
+    struct got_object_id *);
 const struct got_error *got_privsep_send_raw_obj_outfd(struct imsgbuf *, int);
 const struct got_error *got_privsep_send_commit_req(struct imsgbuf *, int,
     struct got_object_id *, int);
blob - aa993fb6f2b096a240a470976cbe73249014215a
blob + 092ae34c1a0658b86b579f6a6c65fb12acb3ea81
--- lib/inflate.c
+++ lib/inflate.c
@@ -93,6 +93,15 @@ csum_input(struct got_inflate_checksum *csum, const ch
 		SHA1Update(csum->input_sha1, buf, len);
 }
 
+static void
+csum_output(struct got_inflate_checksum *csum, const char *buf, size_t len)
+{
+	if (csum->output_crc)
+		*csum->output_crc = crc32(*csum->output_crc, buf, len);
+
+	if (csum->output_sha1)
+		SHA1Update(csum->output_sha1, buf, len);
+}
 const struct got_error *
 got_inflate_read(struct got_inflate_buf *zb, FILE *f, size_t *outlenp,
    size_t *consumed)
@@ -109,8 +118,8 @@ got_inflate_read(struct got_inflate_buf *zb, FILE *f, 
 	if (consumed)
 		*consumed = 0;
 	do {
-		char *csum_in = NULL;
-		size_t csum_avail = 0;
+		char *csum_in = NULL, *csum_out = NULL;
+		size_t csum_avail_in = 0, csum_avail_out = 0;
 
 		if (z->avail_in == 0) {
 			size_t n = fread(zb->inbuf, 1, zb->inlen, f);
@@ -126,11 +135,17 @@ got_inflate_read(struct got_inflate_buf *zb, FILE *f, 
 		}
 		if (zb->csum) {
 			csum_in = z->next_in;
-			csum_avail = z->avail_in;
+			csum_avail_in = z->avail_in;
+			csum_out = z->next_out;
+			csum_avail_out = z->avail_out;
 		}
 		ret = inflate(z, Z_SYNC_FLUSH);
-		if (zb->csum)
-			csum_input(zb->csum, csum_in, csum_avail - z->avail_in);
+		if (zb->csum) {
+			csum_input(zb->csum, csum_in,
+			    csum_avail_in - z->avail_in);
+			csum_output(zb->csum, csum_out,
+			    csum_avail_out - z->avail_out);
+		}
 	} while (ret == Z_OK && z->avail_out > 0);
 
 	if (ret == Z_OK || ret == Z_BUF_ERROR) {
@@ -163,8 +178,8 @@ got_inflate_read_fd(struct got_inflate_buf *zb, int fd
 	if (consumed)
 		*consumed = 0;
 	do {
-		char *csum_in = NULL;
-		size_t csum_avail = 0;
+		char *csum_in = NULL, *csum_out = NULL;
+		size_t csum_avail_in = 0, csum_avail_out = 0;
 
 		if (z->avail_in == 0) {
 			ssize_t n = read(fd, zb->inbuf, zb->inlen);
@@ -180,11 +195,17 @@ got_inflate_read_fd(struct got_inflate_buf *zb, int fd
 		}
 		if (zb->csum) {
 			csum_in = z->next_in;
-			csum_avail = z->avail_in;
+			csum_avail_in = z->avail_in;
+			csum_out = z->next_out;
+			csum_avail_out = z->avail_out;
 		}
 		ret = inflate(z, Z_SYNC_FLUSH);
-		if (zb->csum)
-			csum_input(zb->csum, csum_in, csum_avail - z->avail_in);
+		if (zb->csum) {
+			csum_input(zb->csum, csum_in,
+			    csum_avail_in - z->avail_in);
+			csum_output(zb->csum, csum_out,
+			    csum_avail_out - z->avail_out);
+		}
 	} while (ret == Z_OK && z->avail_out > 0);
 
 	if (ret == Z_OK || ret == Z_BUF_ERROR) {
@@ -216,8 +237,8 @@ got_inflate_read_mmap(struct got_inflate_buf *zb, uint
 	*consumed = 0;
 
 	do {
-		char *csum_in = NULL;
-		size_t csum_avail = 0;
+		char *csum_in = NULL, *csum_out = NULL;
+		size_t csum_avail_in = 0, csum_avail_out = 0;
 		size_t last_total_in = zb->z.total_in;
 
 		if (z->avail_in == 0) {
@@ -231,11 +252,17 @@ got_inflate_read_mmap(struct got_inflate_buf *zb, uint
 		}
 		if (zb->csum) {
 			csum_in = z->next_in;
-			csum_avail = z->avail_in;
+			csum_avail_in = z->avail_in;
+			csum_out = z->next_out;
+			csum_avail_out = z->avail_out;
 		}
 		ret = inflate(z, Z_SYNC_FLUSH);
-		if (zb->csum)
-			csum_input(zb->csum, csum_in, csum_avail - z->avail_in);
+		if (zb->csum) {
+			csum_input(zb->csum, csum_in,
+			    csum_avail_in - z->avail_in);
+			csum_output(zb->csum, csum_out,
+			    csum_avail_out - z->avail_out);
+		}
 		*consumed += z->total_in - last_total_in;
 	} while (ret == Z_OK && z->avail_out > 0);
 
blob - 5c5f3bb280d592509f3a929c1b15d6003d2faa97
blob + 7d430b8b6f2f66d1195b5d9b05b4f172cde310ea
--- lib/object.c
+++ lib/object.c
@@ -366,14 +366,15 @@ done:
 }
 
 static const struct got_error *
-request_object(struct got_object **obj, struct got_repository *repo, int fd)
+request_object(struct got_object **obj, struct got_object_id *id,
+    struct got_repository *repo, int fd)
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf *ibuf;
 
 	ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].ibuf;
 
-	err = got_privsep_send_obj_req(ibuf, fd);
+	err = got_privsep_send_obj_req(ibuf, fd, id);
 	if (err)
 		return err;
 
@@ -382,7 +383,7 @@ request_object(struct got_object **obj, struct got_rep
 
 static const struct got_error *
 request_raw_object(uint8_t **outbuf, off_t *size, size_t *hdrlen, int outfd,
-    struct got_repository *repo, int infd)
+    struct got_object_id *id, struct got_repository *repo, int infd)
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf *ibuf;
@@ -394,7 +395,7 @@ request_raw_object(uint8_t **outbuf, off_t *size, size
 	if (outfd_child == -1)
 		return got_error_from_errno("dup");
 
-	err = got_privsep_send_raw_obj_req(ibuf, infd);
+	err = got_privsep_send_raw_obj_req(ibuf, infd, id);
 	if (err)
 		return err;
 
@@ -452,12 +453,12 @@ start_read_object_child(struct got_repository *repo)
 
 const struct got_error *
 got_object_read_header_privsep(struct got_object **obj,
-    struct got_repository *repo, int obj_fd)
+    struct got_object_id *id, struct got_repository *repo, int obj_fd)
 {
 	const struct got_error *err;
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
-		return request_object(obj, repo, obj_fd);
+		return request_object(obj, id, repo, obj_fd);
 
 	err = start_read_object_child(repo);
 	if (err) {
@@ -465,24 +466,26 @@ got_object_read_header_privsep(struct got_object **obj
 		return err;
 	}
 
-	return request_object(obj, repo, obj_fd);
+	return request_object(obj, id, repo, obj_fd);
 }
 
 static const struct got_error *
 read_object_raw_privsep(uint8_t **outbuf, off_t *size, size_t *hdrlen,
-    int outfd, struct got_repository *repo, int obj_fd)
+    int outfd, struct got_object_id *id, struct got_repository *repo,
+    int obj_fd)
 {
 	const struct got_error *err;
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_OBJECT].imsg_fd != -1)
-		return request_raw_object(outbuf, size, hdrlen, outfd, repo,
-		    obj_fd);
+		return request_raw_object(outbuf, size, hdrlen, outfd, id,
+		    repo, obj_fd);
 
 	err = start_read_object_child(repo);
 	if (err)
 		return err;
 
-	return request_raw_object(outbuf, size, hdrlen, outfd, repo, obj_fd);
+	return request_raw_object(outbuf, size, hdrlen, outfd, id, repo,
+	    obj_fd);
 }
 
 const struct got_error *
@@ -513,7 +516,7 @@ got_object_open(struct got_object **obj, struct got_re
 		return err;
 	}
 
-	err = got_object_read_header_privsep(obj, repo, fd);
+	err = got_object_read_header_privsep(obj, id, repo, fd);
 	if (err)
 		return err;
 
@@ -566,7 +569,7 @@ got_object_raw_open(struct got_raw_object **obj, struc
 		if (err)
 			goto done;
 		err = read_object_raw_privsep(&outbuf, &size, &hdrlen, outfd,
-		    repo, fd);
+		    id, repo, fd);
 	}
 
 	*obj = calloc(1, sizeof(**obj));
@@ -743,14 +746,14 @@ read_packed_commit_privsep(struct got_commit_object **
 
 static const struct got_error *
 request_commit(struct got_commit_object **commit, struct got_repository *repo,
-    int fd)
+    int fd, struct got_object_id *id)
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf *ibuf;
 
 	ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf;
 
-	err = got_privsep_send_commit_req(ibuf, fd, NULL, -1);
+	err = got_privsep_send_commit_req(ibuf, fd, id, -1);
 	if (err)
 		return err;
 
@@ -759,7 +762,7 @@ request_commit(struct got_commit_object **commit, stru
 
 static const struct got_error *
 read_commit_privsep(struct got_commit_object **commit, int obj_fd,
-    struct got_repository *repo)
+    struct got_object_id *id, struct got_repository *repo)
 {
 	const struct got_error *err;
 	int imsg_fds[2];
@@ -767,7 +770,7 @@ read_commit_privsep(struct got_commit_object **commit,
 	struct imsgbuf *ibuf;
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].imsg_fd != -1)
-		return request_commit(commit, repo, obj_fd);
+		return request_commit(commit, repo, obj_fd, id);
 
 	ibuf = calloc(1, sizeof(*ibuf));
 	if (ibuf == NULL)
@@ -802,7 +805,7 @@ read_commit_privsep(struct got_commit_object **commit,
 	imsg_init(ibuf, imsg_fds[0]);
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_COMMIT].ibuf = ibuf;
 
-	return request_commit(commit, repo, obj_fd);
+	return request_commit(commit, repo, obj_fd, id);
 }
 
 
@@ -847,7 +850,7 @@ open_commit(struct got_commit_object **commit,
 		err = got_object_open_loose_fd(&fd, id, repo);
 		if (err)
 			return err;
-		err = read_commit_privsep(commit, fd, repo);
+		err = read_commit_privsep(commit, fd, id, repo);
 	}
 
 	if (err == NULL) {
@@ -956,14 +959,14 @@ read_packed_tree_privsep(struct got_tree_object **tree
 
 static const struct got_error *
 request_tree(struct got_tree_object **tree, struct got_repository *repo,
-    int fd)
+    int fd, struct got_object_id *id)
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf *ibuf;
 
 	ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf;
 
-	err = got_privsep_send_tree_req(ibuf, fd, NULL, -1);
+	err = got_privsep_send_tree_req(ibuf, fd, id, -1);
 	if (err)
 		return err;
 
@@ -972,7 +975,7 @@ request_tree(struct got_tree_object **tree, struct got
 
 const struct got_error *
 read_tree_privsep(struct got_tree_object **tree, int obj_fd,
-    struct got_repository *repo)
+    struct got_object_id *id, struct got_repository *repo)
 {
 	const struct got_error *err;
 	int imsg_fds[2];
@@ -980,7 +983,7 @@ read_tree_privsep(struct got_tree_object **tree, int o
 	struct imsgbuf *ibuf;
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].imsg_fd != -1)
-		return request_tree(tree, repo, obj_fd);
+		return request_tree(tree, repo, obj_fd, id);
 
 	ibuf = calloc(1, sizeof(*ibuf));
 	if (ibuf == NULL)
@@ -1016,7 +1019,7 @@ read_tree_privsep(struct got_tree_object **tree, int o
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TREE].ibuf = ibuf;
 
 
-	return request_tree(tree, repo, obj_fd);
+	return request_tree(tree, repo, obj_fd, id);
 }
 
 static const struct got_error *
@@ -1060,7 +1063,7 @@ open_tree(struct got_tree_object **tree, struct got_re
 		err = got_object_open_loose_fd(&fd, id, repo);
 		if (err)
 			return err;
-		err = read_tree_privsep(tree, fd, repo);
+		err = read_tree_privsep(tree, fd, id, repo);
 	}
 
 	if (err == NULL) {
@@ -1297,7 +1300,7 @@ read_packed_blob_privsep(uint8_t **outbuf, size_t *siz
 
 static const struct got_error *
 request_blob(uint8_t **outbuf, size_t *size, size_t *hdrlen, int outfd,
-    int infd, struct imsgbuf *ibuf)
+    int infd, struct got_object_id *id, struct imsgbuf *ibuf)
 {
 	const struct got_error *err = NULL;
 	int outfd_child;
@@ -1306,7 +1309,7 @@ request_blob(uint8_t **outbuf, size_t *size, size_t *h
 	if (outfd_child == -1)
 		return got_error_from_errno("dup");
 
-	err = got_privsep_send_blob_req(ibuf, infd, NULL, -1);
+	err = got_privsep_send_blob_req(ibuf, infd, id, -1);
 	if (err)
 		return err;
 
@@ -1326,7 +1329,7 @@ request_blob(uint8_t **outbuf, size_t *size, size_t *h
 
 static const struct got_error *
 read_blob_privsep(uint8_t **outbuf, size_t *size, size_t *hdrlen,
-    int outfd, int infd, struct got_repository *repo)
+    int outfd, int infd, struct got_object_id *id, struct got_repository *repo)
 {
 	const struct got_error *err;
 	int imsg_fds[2];
@@ -1335,7 +1338,8 @@ read_blob_privsep(uint8_t **outbuf, size_t *size, size
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].imsg_fd != -1) {
 		ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf;
-		return request_blob(outbuf, size, hdrlen, outfd, infd, ibuf);
+		return request_blob(outbuf, size, hdrlen, outfd, infd, id,
+		    ibuf);
 	}
 
 	ibuf = calloc(1, sizeof(*ibuf));
@@ -1371,7 +1375,7 @@ read_blob_privsep(uint8_t **outbuf, size_t *size, size
 	imsg_init(ibuf, imsg_fds[0]);
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_BLOB].ibuf = ibuf;
 
-	return request_blob(outbuf, size, hdrlen, outfd, infd, ibuf);
+	return request_blob(outbuf, size, hdrlen, outfd, infd, id, ibuf);
 }
 
 static const struct got_error *
@@ -1425,7 +1429,7 @@ open_blob(struct got_blob_object **blob, struct got_re
 		if (err)
 			goto done;
 		err = read_blob_privsep(&outbuf, &size, &hdrlen, outfd, infd,
-		    repo);
+		    id, repo);
 	}
 	if (err)
 		goto done;
@@ -1669,14 +1673,14 @@ read_packed_tag_privsep(struct got_tag_object **tag,
 
 static const struct got_error *
 request_tag(struct got_tag_object **tag, struct got_repository *repo,
-    int fd)
+    int fd, struct got_object_id *id)
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf *ibuf;
 
 	ibuf = repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf;
 
-	err = got_privsep_send_tag_req(ibuf, fd, NULL, -1);
+	err = got_privsep_send_tag_req(ibuf, fd, id, -1);
 	if (err)
 		return err;
 
@@ -1685,7 +1689,7 @@ request_tag(struct got_tag_object **tag, struct got_re
 
 static const struct got_error *
 read_tag_privsep(struct got_tag_object **tag, int obj_fd,
-    struct got_repository *repo)
+    struct got_object_id *id, struct got_repository *repo)
 {
 	const struct got_error *err;
 	int imsg_fds[2];
@@ -1693,7 +1697,7 @@ read_tag_privsep(struct got_tag_object **tag, int obj_
 	struct imsgbuf *ibuf;
 
 	if (repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].imsg_fd != -1)
-		return request_tag(tag, repo, obj_fd);
+		return request_tag(tag, repo, obj_fd, id);
 
 	ibuf = calloc(1, sizeof(*ibuf));
 	if (ibuf == NULL)
@@ -1728,7 +1732,7 @@ read_tag_privsep(struct got_tag_object **tag, int obj_
 	imsg_init(ibuf, imsg_fds[0]);
 	repo->privsep_children[GOT_REPO_PRIVSEP_CHILD_TAG].ibuf = ibuf;
 
-	return request_tag(tag, repo, obj_fd);
+	return request_tag(tag, repo, obj_fd, id);
 }
 
 static const struct got_error *
@@ -1785,7 +1789,7 @@ open_tag(struct got_tag_object **tag, struct got_repos
 		err = got_object_open_loose_fd(&fd, id, repo);
 		if (err)
 			return err;
-		err = got_object_read_header_privsep(&obj, repo, fd);
+		err = got_object_read_header_privsep(&obj, id, repo, fd);
 		if (err)
 			return err;
 		obj_type = obj->type;
@@ -1796,7 +1800,7 @@ open_tag(struct got_tag_object **tag, struct got_repos
 		err = got_object_open_loose_fd(&fd, id, repo);
 		if (err)
 			return err;
-		err = read_tag_privsep(tag, fd, repo);
+		err = read_tag_privsep(tag, fd, id, repo);
 	}
 
 	if (err == NULL) {
blob - acb20d28e5fa0d935383503926b1be70789b9e42
blob + 9b4a7ee1895805282b36a747dfb244b5dd775ef8
--- lib/privsep.c
+++ lib/privsep.c
@@ -250,20 +250,22 @@ got_privsep_send_stop(int fd)
 }
 
 const struct got_error *
-got_privsep_send_obj_req(struct imsgbuf *ibuf, int fd)
+got_privsep_send_obj_req(struct imsgbuf *ibuf, int fd,
+    struct got_object_id *id)
 {
-	if (imsg_compose(ibuf, GOT_IMSG_OBJECT_REQUEST, 0, 0, fd, NULL, 0)
-	    == -1)
+	if (imsg_compose(ibuf, GOT_IMSG_OBJECT_REQUEST, 0, 0, fd,
+	    id, sizeof(*id)) == -1)
 		return got_error_from_errno("imsg_compose OBJECT_REQUEST");
 
 	return flush_imsg(ibuf);
 }
 
 const struct got_error *
-got_privsep_send_raw_obj_req(struct imsgbuf *ibuf, int fd)
+got_privsep_send_raw_obj_req(struct imsgbuf *ibuf, int fd,
+    struct got_object_id *id)
 {
-	if (imsg_compose(ibuf, GOT_IMSG_RAW_OBJECT_REQUEST, 0, 0, fd, NULL, 0)
-	    == -1)
+	if (imsg_compose(ibuf, GOT_IMSG_RAW_OBJECT_REQUEST, 0, 0, fd,
+	    id, sizeof(*id)) == -1)
 		return got_error_from_errno("imsg_compose RAW_OBJECT_REQUEST");
 
 	return flush_imsg(ibuf);
@@ -384,20 +386,21 @@ got_privsep_send_commit_req(struct imsgbuf *ibuf, int 
     struct got_object_id *id, int pack_idx)
 {
 	const struct got_error *err = NULL;
-	struct got_imsg_packed_object iobj, *iobjp;
+	struct got_imsg_packed_object iobj;
+	void *data;
 	size_t len;
 
-	if (id) { /* commit is packed */
+	if (pack_idx != -1) { /* commit is packed */
 		iobj.idx = pack_idx;
 		memcpy(iobj.id, id->sha1, sizeof(iobj.id));
-		iobjp = &iobj;
+		data = &iobj;
 		len = sizeof(iobj);
 	} else {
-		iobjp = NULL;
-		len = 0;
+		data = id;
+		len = sizeof(*id);
 	}
 
-	if (imsg_compose(ibuf, GOT_IMSG_COMMIT_REQUEST, 0, 0, fd, iobjp, len)
+	if (imsg_compose(ibuf, GOT_IMSG_COMMIT_REQUEST, 0, 0, fd, data, len)
 	    == -1) {
 		err = got_error_from_errno("imsg_compose COMMIT_REQUEST");
 		close(fd);
@@ -413,19 +416,24 @@ got_privsep_send_tree_req(struct imsgbuf *ibuf, int fd
 {
 	const struct got_error *err = NULL;
 	struct ibuf *wbuf;
-	size_t len = id ? sizeof(struct got_imsg_packed_object) : 0;
+	size_t len;
 
+	if (pack_idx != -1)
+		len = sizeof(struct got_imsg_packed_object);
+	else
+		len = sizeof(*id);
+
 	wbuf = imsg_create(ibuf, GOT_IMSG_TREE_REQUEST, 0, 0, len);
 	if (wbuf == NULL)
 		return got_error_from_errno("imsg_create TREE_REQUEST");
 
-	if (id) { /* tree is packed */
-		if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1) {
-			err = got_error_from_errno("imsg_add TREE_ENTRY");
-			ibuf_free(wbuf);
-			return err;
-		}
+	if (imsg_add(wbuf, id->sha1, SHA1_DIGEST_LENGTH) == -1) {
+		err = got_error_from_errno("imsg_add TREE_ENTRY");
+		ibuf_free(wbuf);
+		return err;
+	}
 
+	if (pack_idx != -1) { /* tree is packed */
 		if (imsg_add(wbuf, &pack_idx, sizeof(pack_idx)) == -1) {
 			err = got_error_from_errno("imsg_add TREE_ENTRY");
 			ibuf_free(wbuf);
@@ -443,20 +451,21 @@ const struct got_error *
 got_privsep_send_tag_req(struct imsgbuf *ibuf, int fd,
     struct got_object_id *id, int pack_idx)
 {
-	struct got_imsg_packed_object iobj, *iobjp;
+	struct got_imsg_packed_object iobj;
+	void *data;
 	size_t len;
 
-	if (id) { /* tag is packed */
+	if (pack_idx != -1) { /* tag is packed */
 		iobj.idx = pack_idx;
 		memcpy(iobj.id, id->sha1, sizeof(iobj.id));
-		iobjp = &iobj;
+		data = &iobj;
 		len = sizeof(iobj);
 	} else {
-		iobjp = NULL;
-		len = 0;
+		data = id;
+		len = sizeof(*id);
 	}
 
-	if (imsg_compose(ibuf, GOT_IMSG_TAG_REQUEST, 0, 0, fd, iobjp, len)
+	if (imsg_compose(ibuf, GOT_IMSG_TAG_REQUEST, 0, 0, fd, data, len)
 	    == -1)
 		return got_error_from_errno("imsg_compose TAG_REQUEST");
 
@@ -468,20 +477,21 @@ got_privsep_send_blob_req(struct imsgbuf *ibuf, int in
     struct got_object_id *id, int pack_idx)
 {
 	const struct got_error *err = NULL;
-	struct got_imsg_packed_object iobj, *iobjp;
+	struct got_imsg_packed_object iobj;
+	void *data;
 	size_t len;
 
-	if (id) { /* blob is packed */
+	if (pack_idx != -1) { /* blob is packed */
 		iobj.idx = pack_idx;
 		memcpy(iobj.id, id->sha1, sizeof(iobj.id));
-		iobjp = &iobj;
+		data = &iobj;
 		len = sizeof(iobj);
 	} else {
-		iobjp = NULL;
-		len = 0;
+		data = id;
+		len = sizeof(*id);
 	}
 
-	if (imsg_compose(ibuf, GOT_IMSG_BLOB_REQUEST, 0, 0, infd, iobjp, len)
+	if (imsg_compose(ibuf, GOT_IMSG_BLOB_REQUEST, 0, 0, infd, data, len)
 	    == -1) {
 		err = got_error_from_errno("imsg_compose BLOB_REQUEST");
 		close(infd);
blob - 391b44aff9089bc0157349e22029d60c4765029c
blob + a39642e4914da88a723a53b232ba198d6bc408e7
--- lib/repository_admin.c
+++ lib/repository_admin.c
@@ -664,7 +664,8 @@ get_loose_object_ids(struct got_object_idset **loose_i
 				err = got_error_from_errno("fstat");
 				goto done;
 			}
-			err = got_object_read_header_privsep(&obj, repo, fd);
+			err = got_object_read_header_privsep(&obj, &id, repo,
+			    fd);
 			if (err)
 				goto done;
 			fd = -1; /* already closed */
blob - aac58b1e8c9fc029985629a172044af08fa045df
blob + 6ba4490d952e266de433edbadd35609faaeff1bf
--- libexec/got-index-pack/got-index-pack.c
+++ libexec/got-index-pack/got-index-pack.c
@@ -186,6 +186,7 @@ read_packed_object(struct got_pack *pack, struct got_i
 	size_t mapoff = obj->off;
 	struct got_inflate_checksum csum;
 
+	memset(&csum, 0, sizeof(csum));
 	csum.input_sha1 = pack_sha1_ctx;
 	csum.input_crc = &obj->crc;
 
blob - 59f45b5cd15bd0d3e89af07aecad3d6cf6798541
blob + c5a389ba424f515e20c4b3afb8f183a6b2cdd9ae
--- libexec/got-read-blob/got-read-blob.c
+++ libexec/got-read-blob/got-read-blob.c
@@ -38,6 +38,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
 #include "got_lib_privsep.h"
+#include "got_lib_sha1.h"
 
 static volatile sig_atomic_t sigint_received;
 
@@ -73,7 +74,15 @@ main(int argc, char *argv[])
 		size_t size;
 		struct got_object *obj = NULL;
 		uint8_t *buf = NULL;
+		struct got_object_id id;
+		struct got_object_id expected_id;
+		struct got_inflate_checksum csum;
+		SHA1_CTX sha1_ctx;
 
+		SHA1Init(&sha1_ctx);
+		memset(&csum, 0, sizeof(csum));
+		csum.output_sha1 = &sha1_ctx;
+
 		memset(&imsg, 0, sizeof(imsg));
 		imsg.fd = -1;
 		memset(&imsg_outfd, 0, sizeof(imsg_outfd));
@@ -99,6 +108,13 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen != sizeof(expected_id)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		memcpy(&expected_id, imsg.data, sizeof(expected_id));
+
 		if (imsg.fd == -1) {
 			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
 			goto done;
@@ -146,14 +162,23 @@ main(int argc, char *argv[])
 
 		if (obj->size + obj->hdrlen <=
 		    GOT_PRIVSEP_INLINE_BLOB_DATA_MAX) {
-			err = got_inflate_to_mem(&buf, &size, NULL, NULL, f);
+			err = got_inflate_to_mem(&buf, &size, NULL, &csum, f);
 			if (err)
 				goto done;
 		} else {
-			err = got_inflate_to_fd(&size, f, NULL, imsg_outfd.fd);
+			err = got_inflate_to_fd(&size, f, &csum, imsg_outfd.fd);
 			if (err)
 				goto done;
 		}
+		SHA1Final(id.sha1, &sha1_ctx);
+		if (memcmp(expected_id.sha1, id.sha1, SHA1_DIGEST_LENGTH) != 0) {
+			char buf[SHA1_DIGEST_STRING_LENGTH];
+			err = got_error_fmt(GOT_ERR_OBJ_CSUM,
+			    "checksum failure for object %s",
+			    got_sha1_digest_to_str(expected_id.sha1, buf,
+			    sizeof(buf)));
+			goto done;
+		}
 
 		if (size < obj->hdrlen) {
 			err = got_error(GOT_ERR_BAD_OBJ_HDR);
blob - 93245b30e1136486de36bd3eb72dc2f86f739952
blob + 867dc5954e47c0673e815db7c0e25ae703e11658
--- libexec/got-read-commit/got-read-commit.c
+++ libexec/got-read-commit/got-read-commit.c
@@ -38,6 +38,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
 #include "got_lib_privsep.h"
+#include "got_lib_sha1.h"
 
 static volatile sig_atomic_t sigint_received;
 
@@ -48,22 +49,38 @@ catch_sigint(int signo)
 }
 
 static const struct got_error *
-read_commit_object(struct got_commit_object **commit, FILE *f)
+read_commit_object(struct got_commit_object **commit, FILE *f,
+    struct got_object_id *expected_id)
 {
-	struct got_object *obj;
+	struct got_object *obj = NULL;
 	const struct got_error *err = NULL;
 	size_t len;
 	uint8_t *p;
+	struct got_inflate_checksum csum;
+	SHA1_CTX sha1_ctx;
+	struct got_object_id id;
 
-	err = got_inflate_to_mem(&p, &len, NULL, NULL, f);
+	SHA1Init(&sha1_ctx);
+	memset(&csum, 0, sizeof(csum));
+	csum.output_sha1 = &sha1_ctx;
+
+	err = got_inflate_to_mem(&p, &len, NULL, &csum, f);
 	if (err)
 		return err;
 
+	SHA1Final(id.sha1, &sha1_ctx);
+	if (memcmp(expected_id->sha1, id.sha1, SHA1_DIGEST_LENGTH) != 0) {
+		char buf[SHA1_DIGEST_STRING_LENGTH];
+		err = got_error_fmt(GOT_ERR_OBJ_CSUM,
+		    "checksum failure for object %s",
+		    got_sha1_digest_to_str(expected_id->sha1, buf,
+		    sizeof(buf)));
+		goto done;
+	}
+
 	err = got_object_parse_header(&obj, p, len);
-	if (err) {
-		free(p);
-		return err;
-	}
+	if (err)
+		goto done;
 
 	if (len < obj->hdrlen + obj->size) {
 		err = got_error(GOT_ERR_BAD_OBJ_DATA);
@@ -80,7 +97,8 @@ read_commit_object(struct got_commit_object **commit, 
 	err = got_object_parse_commit(commit, p + obj->hdrlen, len);
 done:
 	free(p);
-	got_object_close(obj);
+	if (obj)
+		got_object_close(obj);
 	return err;
 }
 
@@ -89,6 +107,7 @@ main(int argc, char *argv[])
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf ibuf;
+	size_t datalen;
 
 	signal(SIGINT, catch_sigint);
 
@@ -107,6 +126,7 @@ main(int argc, char *argv[])
 		struct imsg imsg;
 		FILE *f = NULL;
 		struct got_commit_object *commit = NULL;
+		struct got_object_id expected_id;
 
 		if (sigint_received) {
 			err = got_error(GOT_ERR_CANCELLED);
@@ -128,6 +148,13 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen != sizeof(expected_id)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		memcpy(&expected_id, imsg.data, sizeof(expected_id));
+
 		if (imsg.fd == -1) {
 			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
 			goto done;
@@ -140,7 +167,7 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
-		err = read_commit_object(&commit, f);
+		err = read_commit_object(&commit, f, &expected_id);
 		if (err)
 			goto done;
 
blob - d91d50bca13d75a6d38de4482f7e5605960b206c
blob + 4c0bf0f3e8dcb076c30c707c610ceca0aa0bdf80
--- libexec/got-read-object/got-read-object.c
+++ libexec/got-read-object/got-read-object.c
@@ -38,6 +38,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
 #include "got_lib_privsep.h"
+#include "got_lib_sha1.h"
 
 #ifndef nitems
 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
@@ -57,13 +58,22 @@ catch_sigint(int signo)
 }
 
 static const struct got_error *
-send_raw_obj(struct imsgbuf *ibuf, struct got_object *obj, int fd, int outfd)
+send_raw_obj(struct imsgbuf *ibuf, struct got_object *obj,
+    struct got_object_id *expected_id,
+    int fd, int outfd)
 {
 	const struct got_error *err = NULL;
 	uint8_t *data = NULL;
 	size_t len = 0, consumed;
 	FILE *f;
+	struct got_object_id id;
+	struct got_inflate_checksum csum;
+	SHA1_CTX sha1_ctx;
 
+	SHA1Init(&sha1_ctx);
+	memset(&csum, 0, sizeof(csum));
+	csum.output_sha1 = &sha1_ctx;
+
 	if (lseek(fd, SEEK_SET, 0) == -1) {
 		err = got_error_from_errno("lseek");
 		close(fd);
@@ -78,9 +88,9 @@ send_raw_obj(struct imsgbuf *ibuf, struct got_object *
 	}
 
 	if (obj->size + obj->hdrlen <= GOT_PRIVSEP_INLINE_OBJECT_DATA_MAX)
-		err = got_inflate_to_mem(&data, &len, &consumed, NULL, f);
+		err = got_inflate_to_mem(&data, &len, &consumed, &csum, f);
 	else
-		err = got_inflate_to_fd(&len, f, NULL, outfd);
+		err = got_inflate_to_fd(&len, f, &csum, outfd);
 	if (err)
 		goto done;
 
@@ -89,6 +99,17 @@ send_raw_obj(struct imsgbuf *ibuf, struct got_object *
 		goto done;
 	}
 
+	SHA1Final(id.sha1, &sha1_ctx);
+	if (memcmp(expected_id->sha1, id.sha1, SHA1_DIGEST_LENGTH) != 0) {
+		char buf[SHA1_DIGEST_STRING_LENGTH];
+		err = got_error_fmt(GOT_ERR_OBJ_CSUM,
+		    "checksum failure for object %s",
+		    got_sha1_digest_to_str(expected_id->sha1, buf,
+		    sizeof(buf)));
+		goto done;
+	}
+
+
 	err = got_privsep_send_raw_obj(ibuf, obj->size, obj->hdrlen, data);
 
 done:
@@ -107,6 +128,7 @@ main(int argc, char *argv[])
 	struct imsg imsg;
 	struct imsgbuf ibuf;
 	size_t datalen;
+	struct got_object_id expected_id;
 
 	signal(SIGINT, catch_sigint);
 
@@ -144,10 +166,11 @@ main(int argc, char *argv[])
 		}
 
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
-		if (datalen != 0) {
+		if (datalen != sizeof(expected_id)) {
 			err = got_error(GOT_ERR_PRIVSEP_LEN);
 			goto done;
 		}
+		memcpy(&expected_id, imsg.data, sizeof(expected_id));
 
 		if (imsg.fd == -1) {
 			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
@@ -160,6 +183,7 @@ main(int argc, char *argv[])
 
 		if (imsg.hdr.type == GOT_IMSG_RAW_OBJECT_REQUEST) {
 			struct imsg imsg_outfd;
+
 			err = got_privsep_recv_imsg(&imsg_outfd, &ibuf, 0);
 			if (err) {
 				if (imsg_outfd.hdr.len == 0)
@@ -189,7 +213,8 @@ main(int argc, char *argv[])
 				imsg_free(&imsg_outfd);
 				goto done;
 			}
-			err = send_raw_obj(&ibuf, obj, imsg.fd, imsg_outfd.fd);
+			err = send_raw_obj(&ibuf, obj, &expected_id,
+			    imsg.fd, imsg_outfd.fd);
 			imsg.fd = -1; /* imsg.fd is owned by send_raw_obj() */
 			if (close(imsg_outfd.fd) == -1 && err == NULL)
 				err = got_error_from_errno("close");
blob - f3ce48de8619073672d23d6c63b6e2e5208861c6
blob + 54b081f2cd307441ac03be98dfdf41222edcf15c
--- libexec/got-read-tag/got-read-tag.c
+++ libexec/got-read-tag/got-read-tag.c
@@ -38,6 +38,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
 #include "got_lib_privsep.h"
+#include "got_lib_sha1.h"
 
 static volatile sig_atomic_t sigint_received;
 
@@ -48,20 +49,38 @@ catch_sigint(int signo)
 }
 
 static const struct got_error *
-read_tag_object(struct got_tag_object **tag, FILE *f)
+read_tag_object(struct got_tag_object **tag, FILE *f,
+    struct got_object_id *expected_id)
 {
 	const struct got_error *err = NULL;
-	struct got_object *obj;
+	struct got_object *obj = NULL;
 	size_t len;
 	uint8_t *p;
+	struct got_inflate_checksum csum;
+	SHA1_CTX sha1_ctx;
+	struct got_object_id id;
 
-	err = got_inflate_to_mem(&p, &len, NULL, NULL, f);
+	SHA1Init(&sha1_ctx);
+	memset(&csum, 0, sizeof(csum));
+	csum.output_sha1 = &sha1_ctx;
+
+	err = got_inflate_to_mem(&p, &len, NULL, &csum, f);
 	if (err)
 		return err;
 
+	SHA1Final(id.sha1, &sha1_ctx);
+	if (memcmp(expected_id->sha1, id.sha1, SHA1_DIGEST_LENGTH) != 0) {
+		char buf[SHA1_DIGEST_STRING_LENGTH];
+		err = got_error_fmt(GOT_ERR_OBJ_CSUM,
+		    "checksum failure for object %s",
+		    got_sha1_digest_to_str(expected_id->sha1, buf,
+		    sizeof(buf)));
+		goto done;
+	}
+
 	err = got_object_parse_header(&obj, p, len);
 	if (err)
-		return err;
+		goto done;
 
 	if (len < obj->hdrlen + obj->size) {
 		err = got_error(GOT_ERR_BAD_OBJ_DATA);
@@ -73,7 +92,8 @@ read_tag_object(struct got_tag_object **tag, FILE *f)
 	err = got_object_parse_tag(tag, p + obj->hdrlen, len);
 done:
 	free(p);
-	got_object_close(obj);
+	if (obj)
+		got_object_close(obj);
 	return err;
 }
 
@@ -82,6 +102,7 @@ main(int argc, char *argv[])
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf ibuf;
+	size_t datalen;
 
 	signal(SIGINT, catch_sigint);
 
@@ -100,6 +121,7 @@ main(int argc, char *argv[])
 		struct imsg imsg;
 		FILE *f = NULL;
 		struct got_tag_object *tag = NULL;
+		struct got_object_id expected_id;
 
 		if (sigint_received) {
 			err = got_error(GOT_ERR_CANCELLED);
@@ -121,6 +143,13 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen != sizeof(expected_id)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		memcpy(&expected_id, imsg.data, sizeof(expected_id));
+
 		if (imsg.fd == -1) {
 			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
 			goto done;
@@ -133,7 +162,7 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
-		err = read_tag_object(&tag, f);
+		err = read_tag_object(&tag, f, &expected_id);
 		if (err)
 			goto done;
 
blob - aab6f584103a469db2e8b399113952f556555726
blob + 95e34c068c96ce1a4a00a1ea034531e243919dfd
--- libexec/got-read-tree/got-read-tree.c
+++ libexec/got-read-tree/got-read-tree.c
@@ -39,6 +39,7 @@
 #include "got_lib_object.h"
 #include "got_lib_object_parse.h"
 #include "got_lib_privsep.h"
+#include "got_lib_sha1.h"
 
 static volatile sig_atomic_t sigint_received;
 
@@ -50,19 +51,36 @@ catch_sigint(int signo)
 
 static const struct got_error *
 read_tree_object(struct got_pathlist_head *entries, int *nentries,
-    uint8_t **p, FILE *f)
+    uint8_t **p, FILE *f, struct got_object_id *expected_id)
 {
 	const struct got_error *err = NULL;
-	struct got_object *obj;
+	struct got_object *obj = NULL;
 	size_t len;
+	struct got_inflate_checksum csum;
+	SHA1_CTX sha1_ctx;
+	struct got_object_id id;
 
-	err = got_inflate_to_mem(p, &len, NULL, NULL, f);
+	SHA1Init(&sha1_ctx);
+	memset(&csum, 0, sizeof(csum));
+	csum.output_sha1 = &sha1_ctx;
+
+	err = got_inflate_to_mem(p, &len, NULL, &csum, f);
 	if (err)
 		return err;
 
+	SHA1Final(id.sha1, &sha1_ctx);
+	if (memcmp(expected_id->sha1, id.sha1, SHA1_DIGEST_LENGTH) != 0) {
+		char buf[SHA1_DIGEST_STRING_LENGTH];
+		err = got_error_fmt(GOT_ERR_OBJ_CSUM,
+		    "checksum failure for object %s",
+		    got_sha1_digest_to_str(expected_id->sha1, buf,
+		    sizeof(buf)));
+		goto done;
+	}
+
 	err = got_object_parse_header(&obj, *p, len);
 	if (err)
-		return err;
+		goto done;
 
 	if (len < obj->hdrlen + obj->size) {
 		err = got_error(GOT_ERR_BAD_OBJ_DATA);
@@ -73,7 +91,8 @@ read_tree_object(struct got_pathlist_head *entries, in
 	len -= obj->hdrlen;
 	err = got_object_parse_tree(entries, nentries, *p + obj->hdrlen, len);
 done:
-	got_object_close(obj);
+	if (obj)
+		got_object_close(obj);
 	return err;
 }
 
@@ -82,6 +101,7 @@ main(int argc, char *argv[])
 {
 	const struct got_error *err = NULL;
 	struct imsgbuf ibuf;
+	size_t datalen;
 
 	signal(SIGINT, catch_sigint);
 
@@ -102,6 +122,7 @@ main(int argc, char *argv[])
 		struct got_pathlist_head entries;
 		int nentries = 0;
 		uint8_t *buf = NULL;
+		struct got_object_id expected_id;
 
 		TAILQ_INIT(&entries);
 
@@ -125,6 +146,13 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+		if (datalen != sizeof(expected_id)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			goto done;
+		}
+		memcpy(&expected_id, imsg.data, sizeof(expected_id));
+
 		if (imsg.fd == -1) {
 			err = got_error(GOT_ERR_PRIVSEP_NO_FD);
 			goto done;
@@ -137,7 +165,8 @@ main(int argc, char *argv[])
 			goto done;
 		}
 
-		err = read_tree_object(&entries, &nentries, &buf, f);
+		err = read_tree_object(&entries, &nentries, &buf, f,
+		    &expected_id);
 		if (err)
 			goto done;