2019-01-25 02:31:34

by Yang Yingliang

[permalink] [raw]
Subject: [PATCH] ipmi_si: fix use-after-free of resource->name

When we excute the following commands, we got oops
rmmod ipmi_si
cat /proc/ioports

[ 1623.482380] Unable to handle kernel paging request at virtual address ffff00000901d478
[ 1623.482382] Mem abort info:
[ 1623.482383] ESR = 0x96000007
[ 1623.482385] Exception class = DABT (current EL), IL = 32 bits
[ 1623.482386] SET = 0, FnV = 0
[ 1623.482387] EA = 0, S1PTW = 0
[ 1623.482388] Data abort info:
[ 1623.482389] ISV = 0, ISS = 0x00000007
[ 1623.482390] CM = 0, WnR = 0
[ 1623.482393] swapper pgtable: 4k pages, 48-bit VAs, pgdp = 00000000d7d94a66
[ 1623.482395] [ffff00000901d478] pgd=000000dffbfff003, pud=000000dffbffe003, pmd=0000003f5d06e003, pte=0000000000000000
[ 1623.482399] Internal error: Oops: 96000007 [#1] SMP
[ 1623.487407] Modules linked in: ipmi_si(E) nls_utf8 isofs rpcrdma ib_iser ib_srpt target_core_mod ib_srp scsi_transport_srp ib_ipoib rdma_ucm ib_umad rdma_cm ib_cm dm_mirror dm_region_hash dm_log iw_cm dm_mod aes_ce_blk crypto_simd cryptd aes_ce_cipher ses ghash_ce sha2_ce enclosure sha256_arm64 sg sha1_ce hisi_sas_v2_hw hibmc_drm sbsa_gwdt hisi_sas_main ip_tables mlx5_ib ib_uverbs marvell ib_core mlx5_core ixgbe mdio hns_dsaf ipmi_devintf hns_enet_drv ipmi_msghandler hns_mdio [last unloaded: ipmi_si]
[ 1623.532410] CPU: 30 PID: 11438 Comm: cat Kdump: loaded Tainted: G E 5.0.0-rc3+ #168
[ 1623.541498] Hardware name: Huawei TaiShan 2280 /BC11SPCD, BIOS 1.37 11/21/2017
[ 1623.548822] pstate: a0000005 (NzCv daif -PAN -UAO)
[ 1623.553684] pc : string+0x28/0x98
[ 1623.557040] lr : vsnprintf+0x368/0x5e8
[ 1623.560837] sp : ffff000013213a80
[ 1623.564191] x29: ffff000013213a80 x28: ffff00001138abb5
[ 1623.569577] x27: ffff000013213c18 x26: ffff805f67d06049
[ 1623.574963] x25: 0000000000000000 x24: ffff00001138abb5
[ 1623.580349] x23: 0000000000000fb7 x22: ffff0000117ed000
[ 1623.585734] x21: ffff000011188fd8 x20: ffff805f67d07000
[ 1623.591119] x19: ffff805f67d06061 x18: ffffffffffffffff
[ 1623.596505] x17: 0000000000000200 x16: 0000000000000000
[ 1623.601890] x15: ffff0000117ed748 x14: ffff805f67d07000
[ 1623.607276] x13: ffff805f67d0605e x12: 0000000000000000
[ 1623.612661] x11: 0000000000000000 x10: 0000000000000000
[ 1623.618046] x9 : 0000000000000000 x8 : 000000000000000f
[ 1623.623432] x7 : ffff805f67d06061 x6 : fffffffffffffffe
[ 1623.628817] x5 : 0000000000000012 x4 : ffff00000901d478
[ 1623.634203] x3 : ffff0a00ffffff04 x2 : ffff805f67d07000
[ 1623.639588] x1 : ffff805f67d07000 x0 : ffffffffffffffff
[ 1623.644974] Process cat (pid: 11438, stack limit = 0x000000008d4cbc10)
[ 1623.651592] Call trace:
[ 1623.654068] string+0x28/0x98
[ 1623.657071] vsnprintf+0x368/0x5e8
[ 1623.660517] seq_vprintf+0x70/0x98
[ 1623.668009] seq_printf+0x7c/0xa0
[ 1623.675530] r_show+0xc8/0xf8
[ 1623.682558] seq_read+0x330/0x440
[ 1623.689877] proc_reg_read+0x78/0xd0
[ 1623.697346] __vfs_read+0x60/0x1a0
[ 1623.704564] vfs_read+0x94/0x150
[ 1623.711339] ksys_read+0x6c/0xd8
[ 1623.717939] __arm64_sys_read+0x24/0x30
[ 1623.725077] el0_svc_common+0x120/0x148
[ 1623.732035] el0_svc_handler+0x30/0x40
[ 1623.738757] el0_svc+0x8/0xc
[ 1623.744520] Code: d1000406 aa0103e2 54000149 b4000080 (39400085)
[ 1623.753441] ---[ end trace f91b6a4937de9835 ]---
[ 1623.760871] Kernel panic - not syncing: Fatal exception
[ 1623.768935] SMP: stopping secondary CPUs
[ 1623.775718] Kernel Offset: disabled
[ 1623.781998] CPU features: 0x002,21006008
[ 1623.788777] Memory Limit: none
[ 1623.798329] Starting crashdump kernel...
[ 1623.805202] Bye!

If io_setup is called successful in try_smi_init() but try_smi_init()
goes out_err before calling ipmi_register_smi(), so ipmi_unregister_smi()
will not be called while removing module. It leads to the resource that
allocated in io_setup() can not be freed, but the name(DEVICE_NAME) of
resource is freed while removing the module. It causes use-after-free
when cat /proc/ioports.

Fix this by calling shutdown_smi() while removing the module and don't
call release_region() if request_region() is not called to avoid error
prints.

Fixes: 93c303d2045b ("ipmi_si: Clean up shutdown a bit")
Cc: [email protected]
Reported-by: NuoHan Qiao <[email protected]>
Signed-off-by: Yang Yingliang <[email protected]>
---
drivers/char/ipmi/ipmi_si_intf.c | 2 ++
drivers/char/ipmi/ipmi_si_port_io.c | 3 +++
2 files changed, 5 insertions(+)

diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index dc8603d..635e98a 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -2235,6 +2235,8 @@ static void cleanup_one_si(struct smi_info *smi_info)

if (smi_info->intf)
ipmi_unregister_smi(smi_info->intf);
+ else
+ shutdown_smi(smi_info);

if (smi_info->pdev) {
if (smi_info->pdev_registered)
diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
index ef6dffc..0c46a3f 100644
--- a/drivers/char/ipmi/ipmi_si_port_io.c
+++ b/drivers/char/ipmi/ipmi_si_port_io.c
@@ -53,6 +53,9 @@ static void port_cleanup(struct si_sm_io *io)
unsigned int addr = io->addr_data;
int idx;

+ if (io->regsize != 1 && io->regsize != 2 && io->regsize != 4)
+ return;
+
if (addr) {
for (idx = 0; idx < io->io_size; idx++)
release_region(addr + idx * io->regspacing,
--
1.8.3




2019-01-25 20:36:26

by Corey Minyard

[permalink] [raw]
Subject: Re: [PATCH] ipmi_si: fix use-after-free of resource->name

On Fri, Jan 25, 2019 at 10:30:59AM +0800, Yang Yingliang wrote:
> When we excute the following commands, we got oops
> rmmod ipmi_si
> cat /proc/ioports
>

snip...

>
> If io_setup is called successful in try_smi_init() but try_smi_init()
> goes out_err before calling ipmi_register_smi(), so ipmi_unregister_smi()
> will not be called while removing module. It leads to the resource that
> allocated in io_setup() can not be freed, but the name(DEVICE_NAME) of
> resource is freed while removing the module. It causes use-after-free
> when cat /proc/ioports.
>
> Fix this by calling shutdown_smi() while removing the module and don't
> call release_region() if request_region() is not called to avoid error
> prints.
>
> Fixes: 93c303d2045b ("ipmi_si: Clean up shutdown a bit")
> Cc: [email protected]
> Reported-by: NuoHan Qiao <[email protected]>
> Signed-off-by: Yang Yingliang <[email protected]>
> ---
> drivers/char/ipmi/ipmi_si_intf.c | 2 ++
> drivers/char/ipmi/ipmi_si_port_io.c | 3 +++
> 2 files changed, 5 insertions(+)
>
> diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
> index dc8603d..635e98a 100644
> --- a/drivers/char/ipmi/ipmi_si_intf.c
> +++ b/drivers/char/ipmi/ipmi_si_intf.c
> @@ -2235,6 +2235,8 @@ static void cleanup_one_si(struct smi_info *smi_info)
>
> if (smi_info->intf)
> ipmi_unregister_smi(smi_info->intf);
> + else
> + shutdown_smi(smi_info);

This is completely the wrong way to fix this. The general principle is
that a function cleans up for itself if it returns an error. If you
add hacks other places for a function failing you end up with a mess.

I think the right way to fix this is to add something like:

if (rv && new_smi->io.io_size && smi_info->io.io_cleanup) {
smi_info->io.io_cleanup(&smi_info->io);
smi_info->io.io_cleanup = NULL;
}

at the end of try_smi_init().

-corey

>
> if (smi_info->pdev) {
> if (smi_info->pdev_registered)
> diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
> index ef6dffc..0c46a3f 100644
> --- a/drivers/char/ipmi/ipmi_si_port_io.c
> +++ b/drivers/char/ipmi/ipmi_si_port_io.c
> @@ -53,6 +53,9 @@ static void port_cleanup(struct si_sm_io *io)
> unsigned int addr = io->addr_data;
> int idx;
>
> + if (io->regsize != 1 && io->regsize != 2 && io->regsize != 4)
> + return;
> +
> if (addr) {
> for (idx = 0; idx < io->io_size; idx++)
> release_region(addr + idx * io->regspacing,
> --
> 1.8.3
>
>

2019-01-26 01:42:28

by Yang Yingliang

[permalink] [raw]
Subject: Re: [PATCH] ipmi_si: fix use-after-free of resource->name



On 2019/1/26 4:35, Corey Minyard wrote:
> On Fri, Jan 25, 2019 at 10:30:59AM +0800, Yang Yingliang wrote:
>> When we excute the following commands, we got oops
>> rmmod ipmi_si
>> cat /proc/ioports
>>
> snip...
>
>> If io_setup is called successful in try_smi_init() but try_smi_init()
>> goes out_err before calling ipmi_register_smi(), so ipmi_unregister_smi()
>> will not be called while removing module. It leads to the resource that
>> allocated in io_setup() can not be freed, but the name(DEVICE_NAME) of
>> resource is freed while removing the module. It causes use-after-free
>> when cat /proc/ioports.
>>
>> Fix this by calling shutdown_smi() while removing the module and don't
>> call release_region() if request_region() is not called to avoid error
>> prints.
>>
>> Fixes: 93c303d2045b ("ipmi_si: Clean up shutdown a bit")
>> Cc: [email protected]
>> Reported-by: NuoHan Qiao <[email protected]>
>> Signed-off-by: Yang Yingliang <[email protected]>
>> ---
>> drivers/char/ipmi/ipmi_si_intf.c | 2 ++
>> drivers/char/ipmi/ipmi_si_port_io.c | 3 +++
>> 2 files changed, 5 insertions(+)
>>
>> diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
>> index dc8603d..635e98a 100644
>> --- a/drivers/char/ipmi/ipmi_si_intf.c
>> +++ b/drivers/char/ipmi/ipmi_si_intf.c
>> @@ -2235,6 +2235,8 @@ static void cleanup_one_si(struct smi_info *smi_info)
>>
>> if (smi_info->intf)
>> ipmi_unregister_smi(smi_info->intf);
>> + else
>> + shutdown_smi(smi_info);
> This is completely the wrong way to fix this. The general principle is
> that a function cleans up for itself if it returns an error. If you
> add hacks other places for a function failing you end up with a mess.
>
> I think the right way to fix this is to add something like:
>
> if (rv && new_smi->io.io_size && smi_info->io.io_cleanup) {
> smi_info->io.io_cleanup(&smi_info->io);
> smi_info->io.io_cleanup = NULL;
> }
>
> at the end of try_smi_init().
>
> -corey
OK, I will do some test, and send v2 later.

Thanks,
Yang
>>
>> if (smi_info->pdev) {
>> if (smi_info->pdev_registered)
>> diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
>> index ef6dffc..0c46a3f 100644
>> --- a/drivers/char/ipmi/ipmi_si_port_io.c
>> +++ b/drivers/char/ipmi/ipmi_si_port_io.c
>> @@ -53,6 +53,9 @@ static void port_cleanup(struct si_sm_io *io)
>> unsigned int addr = io->addr_data;
>> int idx;
>>
>> + if (io->regsize != 1 && io->regsize != 2 && io->regsize != 4)
>> + return;
>> +
>> if (addr) {
>> for (idx = 0; idx < io->io_size; idx++)
>> release_region(addr + idx * io->regspacing,
>> --
>> 1.8.3
>>
>>
> .
>