erofs: fix inconsistent per-file compression format
commit 118a8cf504d7dfa519562d000f423ee3ca75d2c4 upstream. EROFS can select compression algorithms on a per-file basis, and each per-file compression algorithm needs to be marked in the on-disk superblock for initialization. However, syzkaller can generate inconsistent crafted images that use an unsupported algorithmtype for specific inodes, e.g. use MicroLZMA algorithmtype even it's not set in `sbi->available_compr_algs`. This can lead to an unexpected "BUG: kernel NULL pointer dereference" if the corresponding decompressor isn't built-in. Fix this by checking against `sbi->available_compr_algs` for each m_algorithmformat request. Incorrect !erofs_sb_has_compr_cfgs preset bitmap is now fixed together since it was harmless previously. Reported-by: <bugreport@ubisectech.com> Fixes:8f89926290
("erofs: get compression algorithms directly on mapping") Fixes:622ceaddb7
("erofs: lzma compression support") Reviewed-by: Yue Hu <huyue2@coolpad.com> Link: https://lore.kernel.org/r/20240113150602.1471050-1-hsiangkao@linux.alibaba.com Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com> Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com> Signed-off-by: Yue Hu <huyue2@coolpad.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
54407d9bc5
commit
47467e0481
|
@ -396,7 +396,7 @@ int z_erofs_parse_cfgs(struct super_block *sb, struct erofs_super_block *dsb)
|
||||||
int size, ret = 0;
|
int size, ret = 0;
|
||||||
|
|
||||||
if (!erofs_sb_has_compr_cfgs(sbi)) {
|
if (!erofs_sb_has_compr_cfgs(sbi)) {
|
||||||
sbi->available_compr_algs = Z_EROFS_COMPRESSION_LZ4;
|
sbi->available_compr_algs = 1 << Z_EROFS_COMPRESSION_LZ4;
|
||||||
return z_erofs_load_lz4_config(sb, dsb, NULL, 0);
|
return z_erofs_load_lz4_config(sb, dsb, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -610,7 +610,7 @@ static int z_erofs_do_map_blocks(struct inode *inode,
|
||||||
.map = map,
|
.map = map,
|
||||||
};
|
};
|
||||||
int err = 0;
|
int err = 0;
|
||||||
unsigned int lclusterbits, endoff;
|
unsigned int lclusterbits, endoff, afmt;
|
||||||
unsigned long initial_lcn;
|
unsigned long initial_lcn;
|
||||||
unsigned long long ofs, end;
|
unsigned long long ofs, end;
|
||||||
|
|
||||||
|
@ -700,17 +700,20 @@ static int z_erofs_do_map_blocks(struct inode *inode,
|
||||||
err = -EFSCORRUPTED;
|
err = -EFSCORRUPTED;
|
||||||
goto unmap_out;
|
goto unmap_out;
|
||||||
}
|
}
|
||||||
if (vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER)
|
afmt = vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER ?
|
||||||
map->m_algorithmformat =
|
Z_EROFS_COMPRESSION_INTERLACED :
|
||||||
Z_EROFS_COMPRESSION_INTERLACED;
|
|
||||||
else
|
|
||||||
map->m_algorithmformat =
|
|
||||||
Z_EROFS_COMPRESSION_SHIFTED;
|
Z_EROFS_COMPRESSION_SHIFTED;
|
||||||
} else if (m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_HEAD2) {
|
|
||||||
map->m_algorithmformat = vi->z_algorithmtype[1];
|
|
||||||
} else {
|
} else {
|
||||||
map->m_algorithmformat = vi->z_algorithmtype[0];
|
afmt = m.headtype == Z_EROFS_VLE_CLUSTER_TYPE_HEAD2 ?
|
||||||
|
vi->z_algorithmtype[1] : vi->z_algorithmtype[0];
|
||||||
|
if (!(EROFS_I_SB(inode)->available_compr_algs & (1 << afmt))) {
|
||||||
|
erofs_err(inode->i_sb, "inconsistent algorithmtype %u for nid %llu",
|
||||||
|
afmt, vi->nid);
|
||||||
|
err = -EFSCORRUPTED;
|
||||||
|
goto unmap_out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
map->m_algorithmformat = afmt;
|
||||||
|
|
||||||
if ((flags & EROFS_GET_BLOCKS_FIEMAP) ||
|
if ((flags & EROFS_GET_BLOCKS_FIEMAP) ||
|
||||||
((flags & EROFS_GET_BLOCKS_READMORE) &&
|
((flags & EROFS_GET_BLOCKS_READMORE) &&
|
||||||
|
|
Loading…
Reference in New Issue