Skip to content

Commit b560514

Browse files
bvanasschejgunthorpe
authored andcommitted
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/[email protected] Reported-by: Li Zhijian <[email protected]> Tested-by: Li Zhijian <[email protected]> Fixes: a42d985 ("ib_srpt: Initial SRP Target merge for v3.3-rc1") Signed-off-by: Bart Van Assche <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent aa7dfbb commit b560514

File tree

2 files changed

+94
-46
lines changed

2 files changed

+94
-46
lines changed

drivers/infiniband/ulp/srpt/ib_srpt.c

Lines changed: 89 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -565,18 +565,12 @@ static int srpt_refresh_port(struct srpt_port *sport)
565565
if (ret)
566566
return ret;
567567

568-
sport->port_guid_id.wwn.priv = sport;
569568
srpt_format_guid(sport->guid_name, ARRAY_SIZE(sport->guid_name),
570569
&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;
574570
snprintf(sport->gid_name, ARRAY_SIZE(sport->gid_name),
575571
"0x%016llx%016llx",
576572
be64_to_cpu(sport->gid.global.subnet_prefix),
577573
be64_to_cpu(sport->gid.global.interface_id));
578-
memcpy(sport->port_gid_id.name, sport->gid_name,
579-
ARRAY_SIZE(sport->gid_name));
580574

581575
if (rdma_protocol_iwarp(sport->sdev->device, sport->port))
582576
return 0;
@@ -2317,31 +2311,35 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev,
23172311
tag_num = ch->rq_size;
23182312
tag_size = 1; /* ib_srpt does not use se_sess->sess_cmd_map */
23192313

2320-
mutex_lock(&sport->port_guid_id.mutex);
2321-
list_for_each_entry(stpg, &sport->port_guid_id.tpg_list, entry) {
2322-
if (!IS_ERR_OR_NULL(ch->sess))
2323-
break;
2324-
ch->sess = target_setup_session(&stpg->tpg, tag_num,
2314+
if (sport->guid_id) {
2315+
mutex_lock(&sport->guid_id->mutex);
2316+
list_for_each_entry(stpg, &sport->guid_id->tpg_list, entry) {
2317+
if (!IS_ERR_OR_NULL(ch->sess))
2318+
break;
2319+
ch->sess = target_setup_session(&stpg->tpg, tag_num,
23252320
tag_size, TARGET_PROT_NORMAL,
23262321
ch->sess_name, ch, NULL);
2322+
}
2323+
mutex_unlock(&sport->guid_id->mutex);
23272324
}
2328-
mutex_unlock(&sport->port_guid_id.mutex);
23292325

2330-
mutex_lock(&sport->port_gid_id.mutex);
2331-
list_for_each_entry(stpg, &sport->port_gid_id.tpg_list, entry) {
2332-
if (!IS_ERR_OR_NULL(ch->sess))
2333-
break;
2334-
ch->sess = target_setup_session(&stpg->tpg, tag_num,
2326+
if (sport->gid_id) {
2327+
mutex_lock(&sport->gid_id->mutex);
2328+
list_for_each_entry(stpg, &sport->gid_id->tpg_list, entry) {
2329+
if (!IS_ERR_OR_NULL(ch->sess))
2330+
break;
2331+
ch->sess = target_setup_session(&stpg->tpg, tag_num,
23352332
tag_size, TARGET_PROT_NORMAL, i_port_id,
23362333
ch, NULL);
2337-
if (!IS_ERR_OR_NULL(ch->sess))
2338-
break;
2339-
/* Retry without leading "0x" */
2340-
ch->sess = target_setup_session(&stpg->tpg, tag_num,
2334+
if (!IS_ERR_OR_NULL(ch->sess))
2335+
break;
2336+
/* Retry without leading "0x" */
2337+
ch->sess = target_setup_session(&stpg->tpg, tag_num,
23412338
tag_size, TARGET_PROT_NORMAL,
23422339
i_port_id + 2, ch, NULL);
2340+
}
2341+
mutex_unlock(&sport->gid_id->mutex);
23432342
}
2344-
mutex_unlock(&sport->port_gid_id.mutex);
23452343

23462344
if (IS_ERR_OR_NULL(ch->sess)) {
23472345
WARN_ON_ONCE(ch->sess == NULL);
@@ -2986,7 +2984,12 @@ static int srpt_release_sport(struct srpt_port *sport)
29862984
return 0;
29872985
}
29882986

2989-
static struct se_wwn *__srpt_lookup_wwn(const char *name)
2987+
struct port_and_port_id {
2988+
struct srpt_port *sport;
2989+
struct srpt_port_id **port_id;
2990+
};
2991+
2992+
static struct port_and_port_id __srpt_lookup_port(const char *name)
29902993
{
29912994
struct ib_device *dev;
29922995
struct srpt_device *sdev;
@@ -3001,25 +3004,38 @@ static struct se_wwn *__srpt_lookup_wwn(const char *name)
30013004
for (i = 0; i < dev->phys_port_cnt; i++) {
30023005
sport = &sdev->port[i];
30033006

3004-
if (strcmp(sport->port_guid_id.name, name) == 0)
3005-
return &sport->port_guid_id.wwn;
3006-
if (strcmp(sport->port_gid_id.name, name) == 0)
3007-
return &sport->port_gid_id.wwn;
3007+
if (strcmp(sport->guid_name, name) == 0) {
3008+
kref_get(&sdev->refcnt);
3009+
return (struct port_and_port_id){
3010+
sport, &sport->guid_id};
3011+
}
3012+
if (strcmp(sport->gid_name, name) == 0) {
3013+
kref_get(&sdev->refcnt);
3014+
return (struct port_and_port_id){
3015+
sport, &sport->gid_id};
3016+
}
30083017
}
30093018
}
30103019

3011-
return NULL;
3020+
return (struct port_and_port_id){};
30123021
}
30133022

3014-
static struct se_wwn *srpt_lookup_wwn(const char *name)
3023+
/**
3024+
* srpt_lookup_port() - Look up an RDMA port by name
3025+
* @name: ASCII port name
3026+
*
3027+
* Increments the RDMA port reference count if an RDMA port pointer is returned.
3028+
* The caller must drop that reference count by calling srpt_port_put_ref().
3029+
*/
3030+
static struct port_and_port_id srpt_lookup_port(const char *name)
30153031
{
3016-
struct se_wwn *wwn;
3032+
struct port_and_port_id papi;
30173033

30183034
spin_lock(&srpt_dev_lock);
3019-
wwn = __srpt_lookup_wwn(name);
3035+
papi = __srpt_lookup_port(name);
30203036
spin_unlock(&srpt_dev_lock);
30213037

3022-
return wwn;
3038+
return papi;
30233039
}
30243040

30253041
static void srpt_free_srq(struct srpt_device *sdev)
@@ -3198,10 +3214,6 @@ static int srpt_add_one(struct ib_device *device)
31983214
sport->port_attrib.srp_sq_size = DEF_SRPT_SQ_SIZE;
31993215
sport->port_attrib.use_srq = false;
32003216
INIT_WORK(&sport->work, srpt_refresh_port_work);
3201-
mutex_init(&sport->port_guid_id.mutex);
3202-
INIT_LIST_HEAD(&sport->port_guid_id.tpg_list);
3203-
mutex_init(&sport->port_gid_id.mutex);
3204-
INIT_LIST_HEAD(&sport->port_gid_id.tpg_list);
32053217

32063218
ret = srpt_refresh_port(sport);
32073219
if (ret) {
@@ -3302,10 +3314,10 @@ static struct srpt_port_id *srpt_wwn_to_sport_id(struct se_wwn *wwn)
33023314
{
33033315
struct srpt_port *sport = wwn->priv;
33043316

3305-
if (wwn == &sport->port_guid_id.wwn)
3306-
return &sport->port_guid_id;
3307-
if (wwn == &sport->port_gid_id.wwn)
3308-
return &sport->port_gid_id;
3317+
if (sport->guid_id && &sport->guid_id->wwn == wwn)
3318+
return sport->guid_id;
3319+
if (sport->gid_id && &sport->gid_id->wwn == wwn)
3320+
return sport->gid_id;
33093321
WARN_ON_ONCE(true);
33103322
return NULL;
33113323
}
@@ -3790,7 +3802,31 @@ static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf,
37903802
struct config_group *group,
37913803
const char *name)
37923804
{
3793-
return srpt_lookup_wwn(name) ? : ERR_PTR(-EINVAL);
3805+
struct port_and_port_id papi = srpt_lookup_port(name);
3806+
struct srpt_port *sport = papi.sport;
3807+
struct srpt_port_id *port_id;
3808+
3809+
if (!papi.port_id)
3810+
return ERR_PTR(-EINVAL);
3811+
if (*papi.port_id) {
3812+
/* Attempt to create a directory that already exists. */
3813+
WARN_ON_ONCE(true);
3814+
return &(*papi.port_id)->wwn;
3815+
}
3816+
port_id = kzalloc(sizeof(*port_id), GFP_KERNEL);
3817+
if (!port_id) {
3818+
srpt_sdev_put(sport->sdev);
3819+
return ERR_PTR(-ENOMEM);
3820+
}
3821+
mutex_init(&port_id->mutex);
3822+
INIT_LIST_HEAD(&port_id->tpg_list);
3823+
port_id->wwn.priv = sport;
3824+
memcpy(port_id->name, port_id == sport->guid_id ? sport->guid_name :
3825+
sport->gid_name, ARRAY_SIZE(port_id->name));
3826+
3827+
*papi.port_id = port_id;
3828+
3829+
return &port_id->wwn;
37943830
}
37953831

37963832
/**
@@ -3799,6 +3835,18 @@ static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf,
37993835
*/
38003836
static void srpt_drop_tport(struct se_wwn *wwn)
38013837
{
3838+
struct srpt_port_id *port_id = container_of(wwn, typeof(*port_id), wwn);
3839+
struct srpt_port *sport = wwn->priv;
3840+
3841+
if (sport->guid_id == port_id)
3842+
sport->guid_id = NULL;
3843+
else if (sport->gid_id == port_id)
3844+
sport->gid_id = NULL;
3845+
else
3846+
WARN_ON_ONCE(true);
3847+
3848+
srpt_sdev_put(sport->sdev);
3849+
kfree(port_id);
38023850
}
38033851

38043852
static ssize_t srpt_wwn_version_show(struct config_item *item, char *buf)

drivers/infiniband/ulp/srpt/ib_srpt.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ struct srpt_port_id {
393393
};
394394

395395
/**
396-
* struct srpt_port - information associated by SRPT with a single IB port
396+
* struct srpt_port - SRPT RDMA port information
397397
* @sdev: backpointer to the HCA information.
398398
* @mad_agent: per-port management datagram processing information.
399399
* @enabled: Whether or not this target port is enabled.
@@ -403,9 +403,9 @@ struct srpt_port_id {
403403
* @gid: cached value of the port's gid.
404404
* @work: work structure for refreshing the aforementioned cached values.
405405
* @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.
407407
* @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.
409409
* @port_attrib: Port attributes that can be accessed through configfs.
410410
* @refcount: Number of objects associated with this port.
411411
* @freed_channels: Completion that will be signaled once @refcount becomes 0.
@@ -422,9 +422,9 @@ struct srpt_port {
422422
union ib_gid gid;
423423
struct work_struct work;
424424
char guid_name[64];
425-
struct srpt_port_id port_guid_id;
425+
struct srpt_port_id *guid_id;
426426
char gid_name[64];
427-
struct srpt_port_id port_gid_id;
427+
struct srpt_port_id *gid_id;
428428
struct srpt_port_attrib port_attrib;
429429
atomic_t refcount;
430430
struct completion *freed_channels;

0 commit comments

Comments
 (0)