[PATCH v2] erofs-utils: lib: Fix 8MB bug on uncompressed extent size

Kelvin Zhang zhangkelvin at google.com
Tue Feb 8 04:39:58 AEDT 2022


Previously, uncompressed extent can be at most 8MB before mkfs.erofs
crashes on some error condition. This is due to a minor bug in how
compressed indices are encoded. This patch fixes the issue.

Signed-off-by: Kelvin Zhang <zhangkelvin at google.com>
---
 lib/compress.c | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/lib/compress.c b/lib/compress.c
index 98be7a2..2f7ffa7 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -97,7 +97,37 @@ static void vle_write_indexes(struct z_erofs_vle_compress_ctx *ctx,
 		} else if (d0) {
 			type = Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD;
 
-			di.di_u.delta[0] = cpu_to_le16(d0);
+			/*
+			 * If the |Z_EROFS_VLE_DI_D0_CBLKCNT| bit is set, parser
+			 * will interpret |delta[0]| as size of pcluster, rather
+			 * than distance to last head cluster. Normally this
+			 * isn't a problem, because uncompressed extent size are
+			 * below Z_EROFS_VLE_DI_D0_CBLKCNT * BLOCK_SIZE = 8MB.
+			 * But with large pcluster it's possible to go over this
+			 * number, resulting in corrupted compressed indices.
+			 * To solve this, we replace d0 with a number that's
+			 * smaller and doesn't have the
+			 * Z_EROFS_VLE_DI_D0_CBLKCNT bit set if
+			 * the uncompressed extent size goes above 8MB. This is
+			 * OK because if kernel sees another non-head cluster
+			 * after going back by |delta[0]| blocks, kernel will
+			 * just keep looking back.
+			 * The largest number smaller than d0 that doesn't have
+			 * Z_EROFS_VLE_DI_D0_CBLKCNT bit set is obtained by
+			 * first clearing Z_EROFS_VLE_DI_D0_CBLKCNT bit, then
+			 * set all bits before Z_EROFS_VLE_DI_D0_CBLKCNT to 1.
+			 * Using Z_EROFS_VLE_DI_D0_CBLKCNT-1 would work, but it
+			 * produces suboptimal indices in certain cases. e.g.
+			 * (Z_EROFS_VLE_DI_D0_CBLKCNT<<4)|
+			 * (Z_EROFS_VLE_DI_D0_CBLKCNT)
+			 */
+			if (d0 & Z_EROFS_VLE_DI_D0_CBLKCNT) {
+				di.di_u.delta[0] = cpu_to_le16(
+					(d0 & (~Z_EROFS_VLE_DI_D0_CBLKCNT)) |
+					(Z_EROFS_VLE_DI_D0_CBLKCNT-1));
+			} else {
+				di.di_u.delta[0] = cpu_to_le16(d0);
+			}
 			di.di_u.delta[1] = cpu_to_le16(d1);
 		} else {
 			type = raw ? Z_EROFS_VLE_CLUSTER_TYPE_PLAIN :
-- 
2.35.0.263.gb82422642f-goog



More information about the Linux-erofs mailing list