diff --git a/sha1_file.c b/sha1_file.c
index ce90e200cbf..76f66e6a85c 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -711,17 +711,39 @@ int legacy_loose_object(unsigned char *map)
 		return 0;
 }
 
+static unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
+{
+	unsigned shift;
+	unsigned char c;
+	unsigned long size;
+	unsigned long used = 0;
+
+	c = buf[used++];
+	*type = (c >> 4) & 7;
+	size = c & 15;
+	shift = 4;
+	while (c & 0x80) {
+		if (len <= used)
+			return 0;
+		if (sizeof(long) * 8 <= shift)
+			return 0;
+		c = buf[used++];
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+	*sizep = size;
+	return used;
+}
+
 static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
-	unsigned char c;
-	unsigned int bits;
-	unsigned long size;
-	static const char *typename[8] = {
-		NULL,	/* OBJ_EXT */
-		"commit", "tree", "blob", "tag",
-		NULL, NULL, NULL
+	unsigned long size, used;
+	static const char valid_loose_object_type[8] = {
+		0, /* OBJ_EXT */
+		1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */
+		0, /* "delta" and others are invalid in a loose object */
 	};
-	const char *type;
+	enum object_type type;
 
 	/* Get the data stream */
 	memset(stream, 0, sizeof(*stream));
@@ -735,22 +757,11 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
 		return inflate(stream, 0);
 	}
 
-	c = *map++;
-	mapsize--;
-	type = typename[(c >> 4) & 7];
-	if (!type)
+	used = unpack_object_header_gently(map, mapsize, &type, &size);
+	if (!used || !valid_loose_object_type[type])
 		return -1;
-
-	bits = 4;
-	size = c & 0xf;
-	while ((c & 0x80)) {
-		if (bits >= 8*sizeof(long))
-			return -1;
-		c = *map++;
-		size += (c & 0x7f) << bits;
-		bits += 7;
-		mapsize--;
-	}
+	map += used;
+	mapsize -= used;
 
 	/* Set up the stream for the rest.. */
 	stream->next_in = map;
@@ -758,7 +769,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
 	inflateInit(stream);
 
 	/* And generate the fake traditional header */
-	stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
+	stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
+					 type_names[type], size);
 	return 0;
 }
 
@@ -916,25 +928,18 @@ static int packed_delta_info(unsigned char *base_sha1,
 static unsigned long unpack_object_header(struct packed_git *p, unsigned long offset,
 	enum object_type *type, unsigned long *sizep)
 {
-	unsigned shift;
-	unsigned char c;
-	unsigned long size;
+	unsigned long used;
 
-	if (offset >= p->pack_size)
+	if (p->pack_size <= offset)
 		die("object offset outside of pack file");
-	c = *((unsigned char *)p->pack_base + offset++);
-	*type = (c >> 4) & 7;
-	size = c & 15;
-	shift = 4;
-	while (c & 0x80) {
-		if (offset >= p->pack_size)
-			die("object offset outside of pack file");
-		c = *((unsigned char *)p->pack_base + offset++);
-		size += (c & 0x7f) << shift;
-		shift += 7;
-	}
-	*sizep = size;
-	return offset;
+
+	used = unpack_object_header_gently((unsigned char *)p->pack_base +
+					   offset,
+					   p->pack_size - offset, type, sizep);
+	if (!used)
+		die("object offset outside of pack file");
+
+	return offset + used;
 }
 
 int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,