Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

RDMA/srpt: Fix a use-after-free

Change the LIO port members inside struct srpt_port from regular members
into pointers. Allocate the LIO port data structures from inside
srpt_make_tport() and free these from inside srpt_make_tport(). Keep
struct srpt_device as long as either an RDMA port or a LIO target port is
associated with it. This patch decouples the lifetime of struct srpt_port
(controlled by the RDMA core) and struct srpt_port_id (controlled by LIO).
This patch fixes the following KASAN complaint:

BUG: KASAN: use-after-free in srpt_enable_tpg+0x31/0x70 [ib_srpt]
Read of size 8 at addr ffff888141cc34b8 by task check/5093

Call Trace:
<TASK>
show_stack+0x4e/0x53
dump_stack_lvl+0x51/0x66
print_address_description.constprop.0.cold+0xea/0x41e
print_report.cold+0x90/0x205
kasan_report+0xb9/0xf0
__asan_load8+0x69/0x90
srpt_enable_tpg+0x31/0x70 [ib_srpt]
target_fabric_tpg_base_enable_store+0xe2/0x140 [target_core_mod]
configfs_write_iter+0x18b/0x210
new_sync_write+0x1f2/0x2f0
vfs_write+0x3e3/0x540
ksys_write+0xbb/0x140
__x64_sys_write+0x42/0x50
do_syscall_64+0x34/0x80
entry_SYSCALL_64_after_hwframe+0x46/0xb0
</TASK>

Link: https://lore.kernel.org/r/20220727193415.1583860-4-bvanassche@acm.org
Reported-by: Li Zhijian <lizhijian@fujitsu.com>
Tested-by: Li Zhijian <lizhijian@fujitsu.com>
Fixes: a42d985bd5b2 ("ib_srpt: Initial SRP Target merge for v3.3-rc1")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

authored by

Bart Van Assche and committed by
Jason Gunthorpe
b5605148 aa7dfbb4

+94 -46
+89 -41
drivers/infiniband/ulp/srpt/ib_srpt.c
··· 565 565 if (ret) 566 566 return ret; 567 567 568 - sport->port_guid_id.wwn.priv = sport; 569 568 srpt_format_guid(sport->guid_name, ARRAY_SIZE(sport->guid_name), 570 569 &sport->gid.global.interface_id); 571 - memcpy(sport->port_guid_id.name, sport->guid_name, 572 - ARRAY_SIZE(sport->guid_name)); 573 - sport->port_gid_id.wwn.priv = sport; 574 570 snprintf(sport->gid_name, ARRAY_SIZE(sport->gid_name), 575 571 "0x%016llx%016llx", 576 572 be64_to_cpu(sport->gid.global.subnet_prefix), 577 573 be64_to_cpu(sport->gid.global.interface_id)); 578 - memcpy(sport->port_gid_id.name, sport->gid_name, 579 - ARRAY_SIZE(sport->gid_name)); 580 574 581 575 if (rdma_protocol_iwarp(sport->sdev->device, sport->port)) 582 576 return 0; ··· 2311 2317 tag_num = ch->rq_size; 2312 2318 tag_size = 1; /* ib_srpt does not use se_sess->sess_cmd_map */ 2313 2319 2314 - mutex_lock(&sport->port_guid_id.mutex); 2315 - list_for_each_entry(stpg, &sport->port_guid_id.tpg_list, entry) { 2316 - if (!IS_ERR_OR_NULL(ch->sess)) 2317 - break; 2318 - ch->sess = target_setup_session(&stpg->tpg, tag_num, 2320 + if (sport->guid_id) { 2321 + mutex_lock(&sport->guid_id->mutex); 2322 + list_for_each_entry(stpg, &sport->guid_id->tpg_list, entry) { 2323 + if (!IS_ERR_OR_NULL(ch->sess)) 2324 + break; 2325 + ch->sess = target_setup_session(&stpg->tpg, tag_num, 2319 2326 tag_size, TARGET_PROT_NORMAL, 2320 2327 ch->sess_name, ch, NULL); 2328 + } 2329 + mutex_unlock(&sport->guid_id->mutex); 2321 2330 } 2322 - mutex_unlock(&sport->port_guid_id.mutex); 2323 2331 2324 - mutex_lock(&sport->port_gid_id.mutex); 2325 - list_for_each_entry(stpg, &sport->port_gid_id.tpg_list, entry) { 2326 - if (!IS_ERR_OR_NULL(ch->sess)) 2327 - break; 2328 - ch->sess = target_setup_session(&stpg->tpg, tag_num, 2332 + if (sport->gid_id) { 2333 + mutex_lock(&sport->gid_id->mutex); 2334 + list_for_each_entry(stpg, &sport->gid_id->tpg_list, entry) { 2335 + if (!IS_ERR_OR_NULL(ch->sess)) 2336 + break; 2337 + ch->sess = target_setup_session(&stpg->tpg, tag_num, 2329 2338 tag_size, TARGET_PROT_NORMAL, i_port_id, 2330 2339 ch, NULL); 2331 - if (!IS_ERR_OR_NULL(ch->sess)) 2332 - break; 2333 - /* Retry without leading "0x" */ 2334 - ch->sess = target_setup_session(&stpg->tpg, tag_num, 2340 + if (!IS_ERR_OR_NULL(ch->sess)) 2341 + break; 2342 + /* Retry without leading "0x" */ 2343 + ch->sess = target_setup_session(&stpg->tpg, tag_num, 2335 2344 tag_size, TARGET_PROT_NORMAL, 2336 2345 i_port_id + 2, ch, NULL); 2346 + } 2347 + mutex_unlock(&sport->gid_id->mutex); 2337 2348 } 2338 - mutex_unlock(&sport->port_gid_id.mutex); 2339 2349 2340 2350 if (IS_ERR_OR_NULL(ch->sess)) { 2341 2351 WARN_ON_ONCE(ch->sess == NULL); ··· 2984 2986 return 0; 2985 2987 } 2986 2988 2987 - static struct se_wwn *__srpt_lookup_wwn(const char *name) 2989 + struct port_and_port_id { 2990 + struct srpt_port *sport; 2991 + struct srpt_port_id **port_id; 2992 + }; 2993 + 2994 + static struct port_and_port_id __srpt_lookup_port(const char *name) 2988 2995 { 2989 2996 struct ib_device *dev; 2990 2997 struct srpt_device *sdev; ··· 3004 3001 for (i = 0; i < dev->phys_port_cnt; i++) { 3005 3002 sport = &sdev->port[i]; 3006 3003 3007 - if (strcmp(sport->port_guid_id.name, name) == 0) 3008 - return &sport->port_guid_id.wwn; 3009 - if (strcmp(sport->port_gid_id.name, name) == 0) 3010 - return &sport->port_gid_id.wwn; 3004 + if (strcmp(sport->guid_name, name) == 0) { 3005 + kref_get(&sdev->refcnt); 3006 + return (struct port_and_port_id){ 3007 + sport, &sport->guid_id}; 3008 + } 3009 + if (strcmp(sport->gid_name, name) == 0) { 3010 + kref_get(&sdev->refcnt); 3011 + return (struct port_and_port_id){ 3012 + sport, &sport->gid_id}; 3013 + } 3011 3014 } 3012 3015 } 3013 3016 3014 - return NULL; 3017 + return (struct port_and_port_id){}; 3015 3018 } 3016 3019 3017 - static struct se_wwn *srpt_lookup_wwn(const char *name) 3020 + /** 3021 + * srpt_lookup_port() - Look up an RDMA port by name 3022 + * @name: ASCII port name 3023 + * 3024 + * Increments the RDMA port reference count if an RDMA port pointer is returned. 3025 + * The caller must drop that reference count by calling srpt_port_put_ref(). 3026 + */ 3027 + static struct port_and_port_id srpt_lookup_port(const char *name) 3018 3028 { 3019 - struct se_wwn *wwn; 3029 + struct port_and_port_id papi; 3020 3030 3021 3031 spin_lock(&srpt_dev_lock); 3022 - wwn = __srpt_lookup_wwn(name); 3032 + papi = __srpt_lookup_port(name); 3023 3033 spin_unlock(&srpt_dev_lock); 3024 3034 3025 - return wwn; 3035 + return papi; 3026 3036 } 3027 3037 3028 3038 static void srpt_free_srq(struct srpt_device *sdev) ··· 3214 3198 sport->port_attrib.srp_sq_size = DEF_SRPT_SQ_SIZE; 3215 3199 sport->port_attrib.use_srq = false; 3216 3200 INIT_WORK(&sport->work, srpt_refresh_port_work); 3217 - mutex_init(&sport->port_guid_id.mutex); 3218 - INIT_LIST_HEAD(&sport->port_guid_id.tpg_list); 3219 - mutex_init(&sport->port_gid_id.mutex); 3220 - INIT_LIST_HEAD(&sport->port_gid_id.tpg_list); 3221 3201 3222 3202 ret = srpt_refresh_port(sport); 3223 3203 if (ret) { ··· 3314 3302 { 3315 3303 struct srpt_port *sport = wwn->priv; 3316 3304 3317 - if (wwn == &sport->port_guid_id.wwn) 3318 - return &sport->port_guid_id; 3319 - if (wwn == &sport->port_gid_id.wwn) 3320 - return &sport->port_gid_id; 3305 + if (sport->guid_id && &sport->guid_id->wwn == wwn) 3306 + return sport->guid_id; 3307 + if (sport->gid_id && &sport->gid_id->wwn == wwn) 3308 + return sport->gid_id; 3321 3309 WARN_ON_ONCE(true); 3322 3310 return NULL; 3323 3311 } ··· 3802 3790 struct config_group *group, 3803 3791 const char *name) 3804 3792 { 3805 - return srpt_lookup_wwn(name) ? : ERR_PTR(-EINVAL); 3793 + struct port_and_port_id papi = srpt_lookup_port(name); 3794 + struct srpt_port *sport = papi.sport; 3795 + struct srpt_port_id *port_id; 3796 + 3797 + if (!papi.port_id) 3798 + return ERR_PTR(-EINVAL); 3799 + if (*papi.port_id) { 3800 + /* Attempt to create a directory that already exists. */ 3801 + WARN_ON_ONCE(true); 3802 + return &(*papi.port_id)->wwn; 3803 + } 3804 + port_id = kzalloc(sizeof(*port_id), GFP_KERNEL); 3805 + if (!port_id) { 3806 + srpt_sdev_put(sport->sdev); 3807 + return ERR_PTR(-ENOMEM); 3808 + } 3809 + mutex_init(&port_id->mutex); 3810 + INIT_LIST_HEAD(&port_id->tpg_list); 3811 + port_id->wwn.priv = sport; 3812 + memcpy(port_id->name, port_id == sport->guid_id ? sport->guid_name : 3813 + sport->gid_name, ARRAY_SIZE(port_id->name)); 3814 + 3815 + *papi.port_id = port_id; 3816 + 3817 + return &port_id->wwn; 3806 3818 } 3807 3819 3808 3820 /** ··· 3835 3799 */ 3836 3800 static void srpt_drop_tport(struct se_wwn *wwn) 3837 3801 { 3802 + struct srpt_port_id *port_id = container_of(wwn, typeof(*port_id), wwn); 3803 + struct srpt_port *sport = wwn->priv; 3804 + 3805 + if (sport->guid_id == port_id) 3806 + sport->guid_id = NULL; 3807 + else if (sport->gid_id == port_id) 3808 + sport->gid_id = NULL; 3809 + else 3810 + WARN_ON_ONCE(true); 3811 + 3812 + srpt_sdev_put(sport->sdev); 3813 + kfree(port_id); 3838 3814 } 3839 3815 3840 3816 static ssize_t srpt_wwn_version_show(struct config_item *item, char *buf)
+5 -5
drivers/infiniband/ulp/srpt/ib_srpt.h
··· 393 393 }; 394 394 395 395 /** 396 - * struct srpt_port - information associated by SRPT with a single IB port 396 + * struct srpt_port - SRPT RDMA port information 397 397 * @sdev: backpointer to the HCA information. 398 398 * @mad_agent: per-port management datagram processing information. 399 399 * @enabled: Whether or not this target port is enabled. ··· 403 403 * @gid: cached value of the port's gid. 404 404 * @work: work structure for refreshing the aforementioned cached values. 405 405 * @guid_name: port name in GUID format. 406 - * @port_guid_id: LIO target port information for the port name in GUID format. 406 + * @guid_id: LIO target port information for the port name in GUID format. 407 407 * @gid_name: port name in GID format. 408 - * @port_gid_id: LIO target port information for the port name in GID format. 408 + * @gid_id: LIO target port information for the port name in GID format. 409 409 * @port_attrib: Port attributes that can be accessed through configfs. 410 410 * @refcount: Number of objects associated with this port. 411 411 * @freed_channels: Completion that will be signaled once @refcount becomes 0. ··· 422 422 union ib_gid gid; 423 423 struct work_struct work; 424 424 char guid_name[64]; 425 - struct srpt_port_id port_guid_id; 425 + struct srpt_port_id *guid_id; 426 426 char gid_name[64]; 427 - struct srpt_port_id port_gid_id; 427 + struct srpt_port_id *gid_id; 428 428 struct srpt_port_attrib port_attrib; 429 429 atomic_t refcount; 430 430 struct completion *freed_channels;