提交 9ca291d9 编写于 作者: G Guo Xuenan 提交者: Zheng Zengkai

ubi: fix slab-out-of-bounds in ubi_eba_get_ldesc+0xfb/0x130

hulk inclusion
category: bugfix
bugzilla: 182980 https://gitee.com/openeuler/kernel/issues/I4DDEL

-------------------------------------------------

When using ioctl interface to resize ubi volume, ubi_resize_volume will
resize eba table first, but not change vol->reserved_pebs in the same
atomic context which may cause concurrency access eba table.

For example, When user do shrink ubi volume A calling ubi_resize_volume,
while the other thread is writing (volume B) and triggering wear-leveling,
which may calling ubi_write_fastmap, under these circumstances, KASAN may
report: slab-out-of-bounds in ubi_eba_get_ldesc+0xfb/0x130.

The main work of this patch include:
1. fix races in ubi_resize_volume and ubi_update_fastmap, to avoid
   eba_tbl read out of bounds. first, we make eba_tbl and reserved_pebs
   updating under the protect of vol->volumes_lock. second, rollback
   volume in case of resize failure. Also mention that for volume
   shrinking failure, since part of volume has been shrunk and unmapped,
   there is no need to recover {rsvd/avail}_pebs.
2. fix some memleak in error path of ubi_resize_volume when destroy
   new_eba_tbl.

BUG: KASAN: slab-out-of-bounds in ubi_eba_get_ldesc+0xfb/0x130 [ubi]
Read of size 4 at addr ffff888013ff7170 by task kworker/u16:3/160
CPU: 6 PID: 160 Comm: kworker/u16:3 Not tainted
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS
1.14.0-1.fc33 04/01/2014
Workqueue: writeback wb_workfn (flush-ubifs_0_0)
Call Trace:
 dump_stack+0x9c/0xcf
 print_address_description.constprop.0+0x1c/0x220
 kasan_report.cold+0x1f/0x37
 ubi_eba_get_ldesc+0xfb/0x130 [ubi]
 ubi_update_fastmap.cold+0x6be/0xc6b [ubi]
 ubi_wl_get_peb+0x2a2/0x580 [ubi]
 try_write_vid_and_data+0x9a/0x4d0 [ubi]
 ubi_eba_write_leb+0x780/0x1890 [ubi]
 ubi_leb_map+0x197/0x2c0 [ubi]
 ubifs_leb_map+0x139/0x240 [ubifs]
 ubifs_add_bud_to_log+0xb02/0xea0 [ubifs]
 make_reservation+0x860/0xb40 [ubifs]
 ubifs_jnl_write_data+0x461/0x9b0 [ubifs]
 do_writepage+0x375/0x550 [ubifs]
 ubifs_writepage+0x3a4/0x670 [ubifs]
 __writepage+0x61/0x160
 write_cache_pages+0x433/0xb70
 do_writepages+0x1ad/0x260
 __writeback_single_inode+0xb3/0x810
 writeback_sb_inodes+0x4d9/0xcc0
 __writeback_inodes_wb+0xc1/0x260
 wb_writeback+0x585/0x760
 wb_workfn+0x751/0xdb0
 process_one_work+0x6e5/0xf10
 worker_thread+0x5e1/0x10c0
 kthread+0x335/0x400
 ret_from_fork+0x1f/0x30

Allocated by task 690:
 kasan_save_stack+0x1b/0x40
 __kasan_kmalloc.constprop.0+0xc2/0xd0
 ubi_eba_create_table+0x88/0x1a0 [ubi]
 ubi_resize_volume.cold+0x166/0xc75 [ubi]
 ubi_cdev_ioctl+0x57f/0x1aa0 [ubi]
 __x64_sys_ioctl+0x141/0x1a0
 do_syscall_64+0x2d/0x40
 entry_SYSCALL_64_after_hwframe+0x44/0xa9

The following steps can used to reproduce:
Process 1: write and trigger ubi wear-leveling
    ubimkvol /dev/ubi0 -s 5000MiB -N v1
    ubimkvol /dev/ubi0 -s 2000MiB -N v2
    ubimkvol /dev/ubi0 -s 10MiB -N v3
    mount -t ubifs /dev/ubi0_0 /mnt/ubifs
    while true;
    do
        filename=/mnt/ubifs/$((RANDOM))
        dd if=/dev/random of=${filename} bs=1M count=$((RANDOM % 1000))
        rm -rf ${filename}
        sync /mnt/ubifs/
    done

Process 2: do random resize
    struct ubi_rsvol_req req;
    req.vol_id = 1;
    req.bytes = (rand() % 50) * 512KB;
    ioctl(fd, UBI_IOCRSVOL, &req);
Reported-by: NHulk Robot <hulkci@huawei.com>
Signed-off-by: NGuo Xuenan <guoxuenan@huawei.com>
Reviewed-by: NZhang Xiaoxu <zhangxiaoxu5@huawei.com>
Reviewed-by: NZhang Yi <yi.zhang@huawei.com>
Signed-off-by: NChen Jun <chenjun102@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 e90ee8dc
......@@ -409,6 +409,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
struct ubi_device *ubi = vol->ubi;
struct ubi_vtbl_record vtbl_rec;
struct ubi_eba_table *new_eba_tbl = NULL;
struct ubi_eba_table *old_eba_tbl = NULL;
int vol_id = vol->vol_id;
if (ubi->ro_mode)
......@@ -454,10 +455,13 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
err = -ENOSPC;
goto out_free;
}
ubi->avail_pebs -= pebs;
ubi->rsvd_pebs += pebs;
ubi_eba_copy_table(vol, new_eba_tbl, vol->reserved_pebs);
ubi_eba_replace_table(vol, new_eba_tbl);
old_eba_tbl = vol->eba_tbl;
vol->eba_tbl = new_eba_tbl;
vol->reserved_pebs = reserved_pebs;
spin_unlock(&ubi->volumes_lock);
}
......@@ -465,14 +469,16 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
for (i = 0; i < -pebs; i++) {
err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i);
if (err)
goto out_acc;
goto out_free;
}
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs += pebs;
ubi->avail_pebs -= pebs;
ubi_update_reserved(ubi);
ubi_eba_copy_table(vol, new_eba_tbl, reserved_pebs);
ubi_eba_replace_table(vol, new_eba_tbl);
old_eba_tbl = vol->eba_tbl;
vol->eba_tbl = new_eba_tbl;
vol->reserved_pebs = reserved_pebs;
spin_unlock(&ubi->volumes_lock);
}
......@@ -494,27 +500,32 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs)
if (err)
goto out_acc;
vol->reserved_pebs = reserved_pebs;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
vol->used_ebs = reserved_pebs;
vol->last_eb_bytes = vol->usable_leb_size;
vol->used_bytes =
(long long)vol->used_ebs * vol->usable_leb_size;
}
/* destroy old table */
ubi_eba_destroy_table(old_eba_tbl);
ubi_volume_notify(ubi, vol, UBI_VOLUME_RESIZED);
self_check_volumes(ubi);
return err;
out_acc:
spin_lock(&ubi->volumes_lock);
vol->reserved_pebs = reserved_pebs - pebs;
if (pebs > 0) {
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= pebs;
ubi->avail_pebs += pebs;
spin_unlock(&ubi->volumes_lock);
ubi_eba_copy_table(vol, old_eba_tbl, vol->reserved_pebs);
} else {
ubi_eba_copy_table(vol, old_eba_tbl, reserved_pebs);
}
vol->eba_tbl = old_eba_tbl;
spin_unlock(&ubi->volumes_lock);
out_free:
kfree(new_eba_tbl);
ubi_eba_destroy_table(new_eba_tbl);
return err;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册