Skip to content

Commit 230fb3a

Browse files
committed
Merge tag 'erofs-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs
Pull erofs updates from Gao Xiang: - Validate xattr h_shared_count to report -EFSCORRUPTED explicitly for crafted images - Verify metadata accesses for file-backed mounts via rw_verify_area() - Fix FS_IOC_GETFSLABEL to include the trailing NUL byte, consistent with ext4 and xfs - Properly handle 48-bit on-disk blocks/uniaddr for extra devices - Fix an index underflow in the LZ4 in-place decompression that can cause out-of-bounds accesses with crafted images - Minor fixes and cleanups * tag 'erofs-for-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs: erofs: error out obviously illegal extents in advance erofs: clean up encoded map flags erofs: fix unsigned underflow in z_erofs_lz4_handle_overlap() erofs: handle 48-bit blocks/uniaddr for extra devices erofs: include the trailing NUL in FS_IOC_GETFSLABEL erofs: ensure all folios are managed in erofs_try_to_free_all_cached_folios() erofs: verify metadata accesses for file-backed mounts erofs: harden h_shared_count in erofs_init_inode_xattrs()
2 parents a62fe21 + a5242d3 commit 230fb3a

9 files changed

Lines changed: 80 additions & 51 deletions

File tree

fs/erofs/data.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ void *erofs_bread(struct erofs_buf *buf, erofs_off_t offset, bool need_kmap)
3030
{
3131
pgoff_t index = (buf->off + offset) >> PAGE_SHIFT;
3232
struct folio *folio = NULL;
33+
loff_t fpos;
34+
int err;
35+
36+
/*
37+
* Metadata access for file-backed mounts reuses page cache of backing
38+
* fs inodes (only folio data will be needed) to prevent double caching.
39+
* However, the data access range must be verified here in advance.
40+
*/
41+
if (buf->file) {
42+
fpos = index << PAGE_SHIFT;
43+
err = rw_verify_area(READ, buf->file, &fpos, PAGE_SIZE);
44+
if (err < 0)
45+
return ERR_PTR(err);
46+
}
3347

3448
if (buf->page) {
3549
folio = page_folio(buf->page);

fs/erofs/erofs_fs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ struct erofs_deviceslot {
4444
u8 tag[64]; /* digest(sha256), etc. */
4545
__le32 blocks_lo; /* total blocks count of this device */
4646
__le32 uniaddr_lo; /* unified starting block of this device */
47-
__le32 blocks_hi; /* total blocks count MSB */
47+
__le16 blocks_hi; /* total blocks count MSB */
4848
__le16 uniaddr_hi; /* unified starting block MSB */
49-
u8 reserved[50];
49+
u8 reserved[52];
5050
};
5151
#define EROFS_DEVT_SLOT_SIZE sizeof(struct erofs_deviceslot)
5252

fs/erofs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ static int erofs_ioctl_get_volume_label(struct inode *inode, void __user *arg)
351351
ret = clear_user(arg, 1);
352352
else
353353
ret = copy_to_user(arg, sbi->volume_name,
354-
strlen(sbi->volume_name));
354+
strlen(sbi->volume_name) + 1);
355355
return ret ? -EFAULT : 0;
356356
}
357357

fs/erofs/internal.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -360,20 +360,19 @@ static inline struct folio *erofs_grab_folio_nowait(struct address_space *as,
360360
readahead_gfp_mask(as) & ~__GFP_RECLAIM);
361361
}
362362

363-
/* Has a disk mapping */
364-
#define EROFS_MAP_MAPPED 0x0001
363+
/* Allocated on disk at @m_pa (e.g. NOT a fragment extent) */
364+
#define EROFS_MAP_MAPPED 0x0001
365365
/* Located in metadata (could be copied from bd_inode) */
366-
#define EROFS_MAP_META 0x0002
367-
/* The extent is encoded */
368-
#define EROFS_MAP_ENCODED 0x0004
369-
/* The length of extent is full */
370-
#define EROFS_MAP_FULL_MAPPED 0x0008
366+
#define EROFS_MAP_META 0x0002
367+
/* @m_llen may be truncated by the runtime compared to the on-disk record */
368+
#define EROFS_MAP_PARTIAL_MAPPED 0x0004
369+
/* The on-disk @m_llen may cover only part of the encoded data */
370+
#define EROFS_MAP_PARTIAL_REF 0x0008
371371
/* Located in the special packed inode */
372-
#define __EROFS_MAP_FRAGMENT 0x0010
373-
/* The extent refers to partial decompressed data */
374-
#define EROFS_MAP_PARTIAL_REF 0x0020
375-
376-
#define EROFS_MAP_FRAGMENT (EROFS_MAP_MAPPED | __EROFS_MAP_FRAGMENT)
372+
#define EROFS_MAP_FRAGMENT 0x0010
373+
/* The encoded on-disk data will be fully handled (decompressed) */
374+
#define EROFS_MAP_FULL(f) (!((f) & (EROFS_MAP_PARTIAL_MAPPED | \
375+
EROFS_MAP_PARTIAL_REF)))
377376

378377
struct erofs_map_blocks {
379378
struct erofs_buf buf;

fs/erofs/super.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
129129
struct erofs_fscache *fscache;
130130
struct erofs_deviceslot *dis;
131131
struct file *file;
132+
bool _48bit;
132133

133134
dis = erofs_read_metabuf(buf, sb, *pos, false);
134135
if (IS_ERR(dis))
@@ -175,8 +176,11 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,
175176
dif->file = file;
176177
}
177178

178-
dif->blocks = le32_to_cpu(dis->blocks_lo);
179-
dif->uniaddr = le32_to_cpu(dis->uniaddr_lo);
179+
_48bit = erofs_sb_has_48bit(sbi);
180+
dif->blocks = le32_to_cpu(dis->blocks_lo) |
181+
(_48bit ? (u64)le16_to_cpu(dis->blocks_hi) << 32 : 0);
182+
dif->uniaddr = le32_to_cpu(dis->uniaddr_lo) |
183+
(_48bit ? (u64)le16_to_cpu(dis->uniaddr_hi) << 32 : 0);
180184
sbi->total_blocks += dif->blocks;
181185
*pos += EROFS_DEVT_SLOT_SIZE;
182186
return 0;

fs/erofs/xattr.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ static int erofs_init_inode_xattrs(struct inode *inode)
8585
}
8686
vi->xattr_name_filter = le32_to_cpu(ih->h_name_filter);
8787
vi->xattr_shared_count = ih->h_shared_count;
88+
if ((u32)vi->xattr_shared_count * sizeof(__le32) >
89+
vi->xattr_isize - sizeof(struct erofs_xattr_ibody_header)) {
90+
erofs_err(sb, "invalid h_shared_count %u @ nid %llu",
91+
vi->xattr_shared_count, vi->nid);
92+
erofs_put_metabuf(&buf);
93+
ret = -EFSCORRUPTED;
94+
goto out_unlock;
95+
}
8896
vi->xattr_shared_xattrs = kmalloc_objs(uint, vi->xattr_shared_count);
8997
if (!vi->xattr_shared_xattrs) {
9098
erofs_put_metabuf(&buf);

fs/erofs/zdata.c

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ static bool z_erofs_should_alloc_cache(struct z_erofs_frontend *fe)
520520
if (cachestrategy <= EROFS_ZIP_CACHE_DISABLED)
521521
return false;
522522

523-
if (!(fe->map.m_flags & EROFS_MAP_FULL_MAPPED))
523+
if (fe->map.m_flags & EROFS_MAP_PARTIAL_MAPPED)
524524
return true;
525525

526526
if (cachestrategy >= EROFS_ZIP_CACHE_READAROUND &&
@@ -605,8 +605,7 @@ static int erofs_try_to_free_all_cached_folios(struct erofs_sb_info *sbi,
605605
if (!folio_trylock(folio))
606606
return -EBUSY;
607607

608-
if (!erofs_folio_is_managed(sbi, folio))
609-
continue;
608+
DBG_BUGON(!erofs_folio_is_managed(sbi, folio));
610609
pcl->compressed_bvecs[i].page = NULL;
611610
folio_detach_private(folio);
612611
folio_unlock(folio);
@@ -1034,10 +1033,7 @@ static int z_erofs_scan_folio(struct z_erofs_frontend *f,
10341033
/* bump split parts first to avoid several separate cases */
10351034
++split;
10361035

1037-
if (!(map->m_flags & EROFS_MAP_MAPPED)) {
1038-
folio_zero_segment(folio, cur, end);
1039-
tight = false;
1040-
} else if (map->m_flags & __EROFS_MAP_FRAGMENT) {
1036+
if (map->m_flags & EROFS_MAP_FRAGMENT) {
10411037
erofs_off_t fpos = offset + cur - map->m_la;
10421038

10431039
err = z_erofs_read_fragment(inode->i_sb, folio, cur,
@@ -1046,6 +1042,9 @@ static int z_erofs_scan_folio(struct z_erofs_frontend *f,
10461042
if (err)
10471043
break;
10481044
tight = false;
1045+
} else if (!(map->m_flags & EROFS_MAP_MAPPED)) {
1046+
folio_zero_segment(folio, cur, end);
1047+
tight = false;
10491048
} else {
10501049
if (!f->pcl) {
10511050
err = z_erofs_pcluster_begin(f);
@@ -1081,14 +1080,13 @@ static int z_erofs_scan_folio(struct z_erofs_frontend *f,
10811080
f->pcl->length = offset + end - map->m_la;
10821081
f->pcl->pageofs_out = map->m_la & ~PAGE_MASK;
10831082
}
1084-
if ((map->m_flags & EROFS_MAP_FULL_MAPPED) &&
1085-
!(map->m_flags & EROFS_MAP_PARTIAL_REF) &&
1083+
if (EROFS_MAP_FULL(map->m_flags) &&
10861084
f->pcl->length == map->m_llen)
10871085
f->pcl->partial = false;
10881086
}
10891087
/* shorten the remaining extent to update progress */
10901088
map->m_llen = offset + cur - map->m_la;
1091-
map->m_flags &= ~EROFS_MAP_FULL_MAPPED;
1089+
map->m_flags |= EROFS_MAP_PARTIAL_MAPPED;
10921090
if (cur <= pgs) {
10931091
split = cur < pgs;
10941092
tight = (bs == PAGE_SIZE);
@@ -1842,7 +1840,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f,
18421840
map->m_la = end;
18431841
err = z_erofs_map_blocks_iter(inode, map,
18441842
EROFS_GET_BLOCKS_READMORE);
1845-
if (err || !(map->m_flags & EROFS_MAP_ENCODED))
1843+
if (err || !(map->m_flags & EROFS_MAP_MAPPED))
18461844
return;
18471845

18481846
/* expand ra for the trailing edge if readahead */
@@ -1854,7 +1852,7 @@ static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f,
18541852
end = round_up(end, PAGE_SIZE);
18551853
} else {
18561854
end = round_up(map->m_la, PAGE_SIZE);
1857-
if (!(map->m_flags & EROFS_MAP_ENCODED) || !map->m_llen)
1855+
if (!(map->m_flags & EROFS_MAP_MAPPED) || !map->m_llen)
18581856
return;
18591857
}
18601858

fs/erofs/zmap.c

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
419419

420420
if ((flags & EROFS_GET_BLOCKS_FINDTAIL) && ztailpacking)
421421
vi->z_fragmentoff = m.nextpackoff;
422-
map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_ENCODED;
422+
map->m_flags = EROFS_MAP_MAPPED | EROFS_MAP_PARTIAL_MAPPED;
423423
end = (m.lcn + 1ULL) << lclusterbits;
424424

425425
if (m.type != Z_EROFS_LCLUSTER_TYPE_NONHEAD && endoff >= m.clusterofs) {
@@ -435,7 +435,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
435435
} else {
436436
if (m.type != Z_EROFS_LCLUSTER_TYPE_NONHEAD) {
437437
end = (m.lcn << lclusterbits) | m.clusterofs;
438-
map->m_flags |= EROFS_MAP_FULL_MAPPED;
438+
map->m_flags &= ~EROFS_MAP_PARTIAL_MAPPED;
439439
m.delta[0] = 1;
440440
}
441441
/* get the corresponding first chunk */
@@ -473,11 +473,6 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
473473
}
474474

475475
if (m.headtype == Z_EROFS_LCLUSTER_TYPE_PLAIN) {
476-
if (map->m_llen > map->m_plen) {
477-
DBG_BUGON(1);
478-
err = -EFSCORRUPTED;
479-
goto unmap_out;
480-
}
481476
if (vi->z_advise & Z_EROFS_ADVISE_INTERLACED_PCLUSTER)
482477
map->m_algorithmformat = Z_EROFS_COMPRESSION_INTERLACED;
483478
else
@@ -496,7 +491,7 @@ static int z_erofs_map_blocks_fo(struct inode *inode,
496491
map->m_llen >= i_blocksize(inode))) {
497492
err = z_erofs_get_extent_decompressedlen(&m);
498493
if (!err)
499-
map->m_flags |= EROFS_MAP_FULL_MAPPED;
494+
map->m_flags &= ~EROFS_MAP_PARTIAL_MAPPED;
500495
}
501496

502497
unmap_out:
@@ -594,8 +589,7 @@ static int z_erofs_map_blocks_ext(struct inode *inode,
594589
if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
595590
vi->z_fragmentoff |= map->m_pa << 32;
596591
} else if (map->m_plen & Z_EROFS_EXTENT_PLEN_MASK) {
597-
map->m_flags |= EROFS_MAP_MAPPED |
598-
EROFS_MAP_FULL_MAPPED | EROFS_MAP_ENCODED;
592+
map->m_flags |= EROFS_MAP_MAPPED;
599593
fmt = map->m_plen >> Z_EROFS_EXTENT_PLEN_FMT_BIT;
600594
if (map->m_plen & Z_EROFS_EXTENT_PLEN_PARTIAL)
601595
map->m_flags |= EROFS_MAP_PARTIAL_REF;
@@ -714,17 +708,28 @@ static int z_erofs_map_sanity_check(struct inode *inode,
714708
struct erofs_sb_info *sbi = EROFS_I_SB(inode);
715709
u64 pend;
716710

717-
if (!(map->m_flags & EROFS_MAP_ENCODED))
711+
if (!(map->m_flags & EROFS_MAP_MAPPED))
718712
return 0;
719713
if (unlikely(map->m_algorithmformat >= Z_EROFS_COMPRESSION_RUNTIME_MAX)) {
720714
erofs_err(inode->i_sb, "unknown algorithm %d @ pos %llu for nid %llu, please upgrade kernel",
721715
map->m_algorithmformat, map->m_la, EROFS_I(inode)->nid);
722716
return -EOPNOTSUPP;
723717
}
724-
if (unlikely(map->m_algorithmformat < Z_EROFS_COMPRESSION_MAX &&
725-
!(sbi->available_compr_algs & (1 << map->m_algorithmformat)))) {
726-
erofs_err(inode->i_sb, "inconsistent algorithmtype %u for nid %llu",
727-
map->m_algorithmformat, EROFS_I(inode)->nid);
718+
719+
if (map->m_algorithmformat < Z_EROFS_COMPRESSION_MAX) {
720+
if (sbi->available_compr_algs ^ BIT(map->m_algorithmformat)) {
721+
erofs_err(inode->i_sb, "inconsistent algorithmtype %u for nid %llu",
722+
map->m_algorithmformat, EROFS_I(inode)->nid);
723+
return -EFSCORRUPTED;
724+
}
725+
if (EROFS_MAP_FULL(map->m_flags) && map->m_llen < map->m_plen) {
726+
erofs_err(inode->i_sb, "too much compressed data @ la %llu of nid %llu",
727+
map->m_la, EROFS_I(inode)->nid);
728+
return -EFSCORRUPTED;
729+
}
730+
} else if (map->m_llen > map->m_plen) {
731+
erofs_err(inode->i_sb, "not enough plain data on disk @ la %llu of nid %llu",
732+
map->m_la, EROFS_I(inode)->nid);
728733
return -EFSCORRUPTED;
729734
}
730735
if (unlikely(map->m_plen > Z_EROFS_PCLUSTER_MAX_SIZE ||
@@ -781,10 +786,12 @@ static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
781786
iomap->bdev = inode->i_sb->s_bdev;
782787
iomap->offset = map.m_la;
783788
iomap->length = map.m_llen;
784-
if (map.m_flags & EROFS_MAP_MAPPED) {
789+
if (map.m_flags & EROFS_MAP_FRAGMENT) {
790+
iomap->type = IOMAP_MAPPED;
791+
iomap->addr = IOMAP_NULL_ADDR;
792+
} else if (map.m_flags & EROFS_MAP_MAPPED) {
785793
iomap->type = IOMAP_MAPPED;
786-
iomap->addr = map.m_flags & __EROFS_MAP_FRAGMENT ?
787-
IOMAP_NULL_ADDR : map.m_pa;
794+
iomap->addr = map.m_pa;
788795
} else {
789796
iomap->type = IOMAP_HOLE;
790797
iomap->addr = IOMAP_NULL_ADDR;

include/trace/events/erofs.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ struct erofs_map_blocks;
2626
#define show_mflags(flags) __print_flags(flags, "", \
2727
{ EROFS_MAP_MAPPED, "M" }, \
2828
{ EROFS_MAP_META, "I" }, \
29-
{ EROFS_MAP_ENCODED, "E" }, \
30-
{ EROFS_MAP_FULL_MAPPED, "F" }, \
31-
{ EROFS_MAP_FRAGMENT, "R" }, \
32-
{ EROFS_MAP_PARTIAL_REF, "P" })
29+
{ EROFS_MAP_PARTIAL_MAPPED, "T" }, \
30+
{ EROFS_MAP_PARTIAL_REF, "P" }, \
31+
{ EROFS_MAP_FRAGMENT, "R" })
3332

3433
TRACE_EVENT(erofs_lookup,
3534

0 commit comments

Comments
 (0)