Commit e13d315a authored by Gao Xiang's avatar Gao Xiang
Browse files

erofs: avoid infinite loops due to corrupted subpage compact indexes



Robert reported an infinite loop observed by two crafted images.

The root cause is that `clusterofs` can be larger than `lclustersize`
for !NONHEAD `lclusters` in corrupted subpage compact indexes, e.g.:

  blocksize = lclustersize = 512   lcn = 6   clusterofs = 515

Move the corresponding check for full compress indexes to
`z_erofs_load_lcluster_from_disk()` to also cover subpage compact
compress indexes.

It also fixes the position of `m->type >= Z_EROFS_LCLUSTER_TYPE_MAX`
check, since it should be placed right after
`z_erofs_load_{compact,full}_lcluster()`.

Fixes: 8d2517aa ("erofs: fix up compacted indexes for block size < 4096")
Fixes: 1a5223c1 ("erofs: do sanity check on m->type in z_erofs_load_compact_lcluster()")
Reported-by: default avatarRobert Morris <rtm@csail.mit.edu>
Closes: https://lore.kernel.org/r/35167.1760645886@localhost


Reviewed-by: default avatarHongbo Li <lihongbo22@huawei.com>
Signed-off-by: default avatarGao Xiang <hsiangkao@linux.alibaba.com>
parent a429b761
Loading
Loading
Loading
Loading
+18 −14
Original line number Diff line number Diff line
@@ -55,10 +55,6 @@ static int z_erofs_load_full_lcluster(struct z_erofs_maprecorder *m,
	} else {
		m->partialref = !!(advise & Z_EROFS_LI_PARTIAL_REF);
		m->clusterofs = le16_to_cpu(di->di_clusterofs);
		if (m->clusterofs >= 1 << vi->z_lclusterbits) {
			DBG_BUGON(1);
			return -EFSCORRUPTED;
		}
		m->pblk = le32_to_cpu(di->di_u.blkaddr);
	}
	return 0;
@@ -240,21 +236,29 @@ static int z_erofs_load_compact_lcluster(struct z_erofs_maprecorder *m,
static int z_erofs_load_lcluster_from_disk(struct z_erofs_maprecorder *m,
					   unsigned int lcn, bool lookahead)
{
	struct erofs_inode *vi = EROFS_I(m->inode);
	int err;

	if (vi->datalayout == EROFS_INODE_COMPRESSED_COMPACT) {
		err = z_erofs_load_compact_lcluster(m, lcn, lookahead);
	} else {
		DBG_BUGON(vi->datalayout != EROFS_INODE_COMPRESSED_FULL);
		err = z_erofs_load_full_lcluster(m, lcn);
	}
	if (err)
		return err;

	if (m->type >= Z_EROFS_LCLUSTER_TYPE_MAX) {
		erofs_err(m->inode->i_sb, "unknown type %u @ lcn %u of nid %llu",
			  m->type, lcn, EROFS_I(m->inode)->nid);
		DBG_BUGON(1);
		return -EOPNOTSUPP;
	} else if (m->type != Z_EROFS_LCLUSTER_TYPE_NONHEAD &&
		   m->clusterofs >= (1 << vi->z_lclusterbits)) {
		DBG_BUGON(1);
		return -EFSCORRUPTED;
	}

	switch (EROFS_I(m->inode)->datalayout) {
	case EROFS_INODE_COMPRESSED_FULL:
		return z_erofs_load_full_lcluster(m, lcn);
	case EROFS_INODE_COMPRESSED_COMPACT:
		return z_erofs_load_compact_lcluster(m, lcn, lookahead);
	default:
		return -EINVAL;
	}
	return 0;
}

static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,