Skip to content

Commit e7a9f66

Browse files
jhovoldgregkh
authored andcommitted
Revert "usb: typec: ucsi: Update UCSI structure to have message in and message out fields"
This reverts commit 3e08297. The new buffer management code has not been tested or reviewed properly and breaks boot of machines like the Lenovo ThinkPad X13s: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 CPU: 0 UID: 0 PID: 813 Comm: kworker/0:3 Not tainted 6.19.0-rc2 #26 PREEMPT Hardware name: LENOVO 21BYZ9SRUS/21BYZ9SRUS, BIOS N3HET87W (1.59 ) 12/05/2023 Workqueue: events ucsi_handle_connector_change [typec_ucsi] Call trace: ucsi_sync_control_common+0xe4/0x1ec [typec_ucsi] (P) ucsi_run_command+0xcc/0x194 [typec_ucsi] ucsi_send_command_common+0x84/0x2a0 [typec_ucsi] ucsi_get_connector_status+0x48/0x78 [typec_ucsi] ucsi_handle_connector_change+0x5c/0x4f4 [typec_ucsi] process_one_work+0x208/0x60c worker_thread+0x244/0x388 The new code completely ignores concurrency so that the message length can be updated while a transaction is ongoing. In the above case, the length ends up being modified by another thread while processing an ack so that the NULL cci pointer is dereferenced. Fixing this will require designing a proper interface for managing these transactions, something which most likely involves reverting most of the offending commit anyway. Revert the broken code to fix the regression and let Intel come up with a properly tested implementation for a later kernel. Fixes: 3e08297 ("usb: typec: ucsi: Update UCSI structure to have message in and message out fields") Signed-off-by: Johan Hovold <johan@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://patch.msgid.link/20251222152204.2846-5-johan@kernel.org
1 parent 2e46b4e commit e7a9f66

8 files changed

Lines changed: 71 additions & 112 deletions

File tree

drivers/usb/typec/ucsi/cros_ec_ucsi.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,13 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
105105
return 0;
106106
}
107107

108-
static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci)
108+
static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
109+
void *data, size_t size)
109110
{
110111
struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
111112
int ret;
112113

113-
ret = ucsi_sync_control_common(ucsi, cmd, cci);
114+
ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
114115
switch (ret) {
115116
case -EBUSY:
116117
/* EC may return -EBUSY if CCI.busy is set.

drivers/usb/typec/ucsi/debugfs.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ static int ucsi_cmd(void *data, u64 val)
3737
case UCSI_SET_USB:
3838
case UCSI_SET_POWER_LEVEL:
3939
case UCSI_READ_POWER_LEVEL:
40-
ucsi->message_in_size = 0;
41-
ret = ucsi_send_command(ucsi, val);
40+
ret = ucsi_send_command(ucsi, val, NULL, 0);
4241
break;
4342
case UCSI_GET_CAPABILITY:
4443
case UCSI_GET_CONNECTOR_CAPABILITY:
@@ -53,9 +52,9 @@ static int ucsi_cmd(void *data, u64 val)
5352
case UCSI_GET_ATTENTION_VDO:
5453
case UCSI_GET_CAM_CS:
5554
case UCSI_GET_LPM_PPM_INFO:
56-
ucsi->message_in_size = sizeof(ucsi->debugfs->response);
57-
ret = ucsi_send_command(ucsi, val);
58-
memcpy(&ucsi->debugfs->response, ucsi->message_in, sizeof(ucsi->debugfs->response));
55+
ret = ucsi_send_command(ucsi, val,
56+
&ucsi->debugfs->response,
57+
sizeof(ucsi->debugfs->response));
5958
break;
6059
default:
6160
ret = -EOPNOTSUPP;

drivers/usb/typec/ucsi/displayport.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,11 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
6767
}
6868

6969
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(dp->con->num);
70-
ucsi->message_in_size = sizeof(cur);
71-
ret = ucsi_send_command(ucsi, command);
70+
ret = ucsi_send_command(ucsi, command, &cur, sizeof(cur));
7271
if (ret < 0) {
7372
if (ucsi->version > 0x0100)
7473
goto err_unlock;
7574
cur = 0xff;
76-
} else {
77-
memcpy(&cur, ucsi->message_in, ucsi->message_in_size);
7875
}
7976

8077
if (cur != 0xff) {
@@ -129,8 +126,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
129126
}
130127

131128
command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0);
132-
dp->con->ucsi->message_in_size = 0;
133-
ret = ucsi_send_command(dp->con->ucsi, command);
129+
ret = ucsi_send_command(dp->con->ucsi, command, NULL, 0);
134130
if (ret < 0)
135131
goto out_unlock;
136132

@@ -197,8 +193,7 @@ static int ucsi_displayport_configure(struct ucsi_dp *dp)
197193

198194
command = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins);
199195

200-
dp->con->ucsi->message_in_size = 0;
201-
return ucsi_send_command(dp->con->ucsi, command);
196+
return ucsi_send_command(dp->con->ucsi, command, NULL, 0);
202197
}
203198

204199
static int ucsi_displayport_vdm(struct typec_altmode *alt,

drivers/usb/typec/ucsi/ucsi.c

Lines changed: 36 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
5555
}
5656
EXPORT_SYMBOL_GPL(ucsi_notify_common);
5757

58-
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci)
58+
int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
59+
void *data, size_t size)
5960
{
6061
bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
6162
int ret;
@@ -83,10 +84,9 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci)
8384
if (!ret && cci)
8485
ret = ucsi->ops->read_cci(ucsi, cci);
8586

86-
if (!ret && ucsi->message_in_size > 0 &&
87+
if (!ret && data &&
8788
(*cci & UCSI_CCI_COMMAND_COMPLETE))
88-
ret = ucsi->ops->read_message_in(ucsi, ucsi->message_in,
89-
ucsi->message_in_size);
89+
ret = ucsi->ops->read_message_in(ucsi, data, size);
9090

9191
return ret;
9292
}
@@ -103,25 +103,23 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
103103
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
104104
}
105105

106-
ucsi->message_in_size = 0;
107-
return ucsi->ops->sync_control(ucsi, ctrl, NULL);
106+
return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
108107
}
109108

110-
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, bool conn_ack)
109+
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
110+
void *data, size_t size, bool conn_ack)
111111
{
112112
int ret, err;
113113

114114
*cci = 0;
115115

116-
if (ucsi->message_in_size > UCSI_MAX_DATA_LENGTH(ucsi))
116+
if (size > UCSI_MAX_DATA_LENGTH(ucsi))
117117
return -EINVAL;
118118

119-
ret = ucsi->ops->sync_control(ucsi, command, cci);
119+
ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
120120

121-
if (*cci & UCSI_CCI_BUSY) {
122-
ucsi->message_in_size = 0;
123-
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, false) ?: -EBUSY;
124-
}
121+
if (*cci & UCSI_CCI_BUSY)
122+
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
125123
if (ret)
126124
return ret;
127125

@@ -153,13 +151,10 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
153151
int ret;
154152

155153
command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
156-
ucsi->message_in_size = sizeof(error);
157-
ret = ucsi_run_command(ucsi, command, &cci, false);
154+
ret = ucsi_run_command(ucsi, command, &cci, &error, sizeof(error), false);
158155
if (ret < 0)
159156
return ret;
160157

161-
memcpy(&error, ucsi->message_in, sizeof(error));
162-
163158
switch (error) {
164159
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
165160
return -EOPNOTSUPP;
@@ -205,7 +200,8 @@ static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
205200
return -EIO;
206201
}
207202

208-
static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack)
203+
static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
204+
void *data, size_t size, bool conn_ack)
209205
{
210206
u8 connector_num;
211207
u32 cci;
@@ -233,7 +229,7 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack)
233229

234230
mutex_lock(&ucsi->ppm_lock);
235231

236-
ret = ucsi_run_command(ucsi, cmd, &cci, conn_ack);
232+
ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
237233

238234
if (cci & UCSI_CCI_ERROR)
239235
ret = ucsi_read_error(ucsi, connector_num);
@@ -242,9 +238,10 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, bool conn_ack)
242238
return ret;
243239
}
244240

245-
int ucsi_send_command(struct ucsi *ucsi, u64 command)
241+
int ucsi_send_command(struct ucsi *ucsi, u64 command,
242+
void *data, size_t size)
246243
{
247-
return ucsi_send_command_common(ucsi, command, false);
244+
return ucsi_send_command_common(ucsi, command, data, size, false);
248245
}
249246
EXPORT_SYMBOL_GPL(ucsi_send_command);
250247

@@ -322,17 +319,14 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
322319
int i;
323320

324321
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
325-
con->ucsi->message_in_size = sizeof(cur);
326-
ret = ucsi_send_command(con->ucsi, command);
322+
ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
327323
if (ret < 0) {
328324
if (con->ucsi->version > 0x0100) {
329325
dev_err(con->ucsi->dev,
330326
"GET_CURRENT_CAM command failed\n");
331327
return;
332328
}
333329
cur = 0xff;
334-
} else {
335-
memcpy(&cur, con->ucsi->message_in, sizeof(cur));
336330
}
337331

338332
if (cur < UCSI_MAX_ALTMODES)
@@ -516,8 +510,7 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
516510
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
517511
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
518512
command |= UCSI_GET_ALTMODE_OFFSET(i);
519-
ucsi->message_in_size = sizeof(alt);
520-
len = ucsi_send_command(con->ucsi, command);
513+
len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt));
521514
/*
522515
* We are collecting all altmodes first and then registering.
523516
* Some type-C device will return zero length data beyond last
@@ -526,8 +519,6 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
526519
if (len < 0)
527520
return len;
528521

529-
memcpy(&alt, ucsi->message_in, sizeof(alt));
530-
531522
/* We got all altmodes, now break out and register them */
532523
if (!len || !alt.svid)
533524
break;
@@ -595,15 +586,12 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
595586
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
596587
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
597588
command |= UCSI_GET_ALTMODE_OFFSET(i);
598-
con->ucsi->message_in_size = sizeof(alt);
599-
len = ucsi_send_command(con->ucsi, command);
589+
len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt));
600590
if (len == -EBUSY)
601591
continue;
602592
if (len <= 0)
603593
return len;
604594

605-
memcpy(&alt, con->ucsi->message_in, sizeof(alt));
606-
607595
/*
608596
* This code is requesting one alt mode at a time, but some PPMs
609597
* may still return two. If that happens both alt modes need be
@@ -671,9 +659,7 @@ static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack)
671659
UCSI_MAX_DATA_LENGTH(con->ucsi));
672660
int ret;
673661

674-
con->ucsi->message_in_size = size;
675-
ret = ucsi_send_command_common(con->ucsi, command, conn_ack);
676-
memcpy(&con->status, con->ucsi->message_in, size);
662+
ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack);
677663

678664
return ret < 0 ? ret : 0;
679665
}
@@ -696,9 +682,8 @@ static int ucsi_read_pdos(struct ucsi_connector *con,
696682
command |= UCSI_GET_PDOS_PDO_OFFSET(offset);
697683
command |= UCSI_GET_PDOS_NUM_PDOS(num_pdos - 1);
698684
command |= is_source(role) ? UCSI_GET_PDOS_SRC_PDOS : 0;
699-
ucsi->message_in_size = num_pdos * sizeof(u32);
700-
ret = ucsi_send_command(ucsi, command);
701-
memcpy(pdos + offset, ucsi->message_in, num_pdos * sizeof(u32));
685+
ret = ucsi_send_command(ucsi, command, pdos + offset,
686+
num_pdos * sizeof(u32));
702687
if (ret < 0 && ret != -ETIMEDOUT)
703688
dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
704689

@@ -785,9 +770,7 @@ static int ucsi_get_pd_message(struct ucsi_connector *con, u8 recipient,
785770
command |= UCSI_GET_PD_MESSAGE_BYTES(len);
786771
command |= UCSI_GET_PD_MESSAGE_TYPE(type);
787772

788-
con->ucsi->message_in_size = len;
789-
ret = ucsi_send_command(con->ucsi, command);
790-
memcpy(data + offset, con->ucsi->message_in, len);
773+
ret = ucsi_send_command(con->ucsi, command, data + offset, len);
791774
if (ret < 0)
792775
return ret;
793776
}
@@ -952,9 +935,7 @@ static int ucsi_register_cable(struct ucsi_connector *con)
952935
int ret;
953936

954937
command = UCSI_GET_CABLE_PROPERTY | UCSI_CONNECTOR_NUMBER(con->num);
955-
con->ucsi->message_in_size = sizeof(cable_prop);
956-
ret = ucsi_send_command(con->ucsi, command);
957-
memcpy(&cable_prop, con->ucsi->message_in, sizeof(cable_prop));
938+
ret = ucsi_send_command(con->ucsi, command, &cable_prop, sizeof(cable_prop));
958939
if (ret < 0) {
959940
dev_err(con->ucsi->dev, "GET_CABLE_PROPERTY failed (%d)\n", ret);
960941
return ret;
@@ -1015,9 +996,7 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con)
1015996
return 0;
1016997

1017998
command = UCSI_GET_CONNECTOR_CAPABILITY | UCSI_CONNECTOR_NUMBER(con->num);
1018-
con->ucsi->message_in_size = sizeof(con->cap);
1019-
ret = ucsi_send_command(con->ucsi, command);
1020-
memcpy(&con->cap, con->ucsi->message_in, sizeof(con->cap));
999+
ret = ucsi_send_command(con->ucsi, command, &con->cap, sizeof(con->cap));
10211000
if (ret < 0) {
10221001
dev_err(con->ucsi->dev, "GET_CONNECTOR_CAPABILITY failed (%d)\n", ret);
10231002
return ret;
@@ -1401,8 +1380,7 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard)
14011380
else if (con->ucsi->version >= UCSI_VERSION_2_0)
14021381
command |= hard ? 0 : UCSI_CONNECTOR_RESET_DATA_VER_2_0;
14031382

1404-
con->ucsi->message_in_size = 0;
1405-
return ucsi_send_command(con->ucsi, command);
1383+
return ucsi_send_command(con->ucsi, command, NULL, 0);
14061384
}
14071385

14081386
static int ucsi_reset_ppm(struct ucsi *ucsi)
@@ -1483,17 +1461,15 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
14831461
{
14841462
int ret;
14851463

1486-
con->ucsi->message_in_size = 0;
1487-
ret = ucsi_send_command(con->ucsi, command);
1464+
ret = ucsi_send_command(con->ucsi, command, NULL, 0);
14881465
if (ret == -ETIMEDOUT) {
14891466
u64 c;
14901467

14911468
/* PPM most likely stopped responding. Resetting everything. */
14921469
ucsi_reset_ppm(con->ucsi);
14931470

14941471
c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy;
1495-
con->ucsi->message_in_size = 0;
1496-
ucsi_send_command(con->ucsi, c);
1472+
ucsi_send_command(con->ucsi, c, NULL, 0);
14971473

14981474
ucsi_reset_connector(con, true);
14991475
}
@@ -1646,13 +1622,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
16461622
/* Get connector capability */
16471623
command = UCSI_GET_CONNECTOR_CAPABILITY;
16481624
command |= UCSI_CONNECTOR_NUMBER(con->num);
1649-
ucsi->message_in_size = sizeof(con->cap);
1650-
ret = ucsi_send_command(ucsi, command);
1625+
ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
16511626
if (ret < 0)
16521627
goto out_unlock;
16531628

1654-
memcpy(&con->cap, ucsi->message_in, sizeof(con->cap));
1655-
16561629
if (UCSI_CONCAP(con, OPMODE_DRP))
16571630
cap->data = TYPEC_PORT_DRD;
16581631
else if (UCSI_CONCAP(con, OPMODE_DFP))
@@ -1849,20 +1822,17 @@ static int ucsi_init(struct ucsi *ucsi)
18491822
/* Enable basic notifications */
18501823
ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
18511824
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
1852-
ucsi->message_in_size = 0;
1853-
ret = ucsi_send_command(ucsi, command);
1825+
ret = ucsi_send_command(ucsi, command, NULL, 0);
18541826
if (ret < 0)
18551827
goto err_reset;
18561828

18571829
/* Get PPM capabilities */
18581830
command = UCSI_GET_CAPABILITY;
1859-
ucsi->message_in_size = BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE);
1860-
ret = ucsi_send_command(ucsi, command);
1831+
ret = ucsi_send_command(ucsi, command, &ucsi->cap,
1832+
BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE));
18611833
if (ret < 0)
18621834
goto err_reset;
18631835

1864-
memcpy(&ucsi->cap, ucsi->message_in, BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE));
1865-
18661836
if (!ucsi->cap.num_connectors) {
18671837
ret = -ENODEV;
18681838
goto err_reset;
@@ -1892,8 +1862,7 @@ static int ucsi_init(struct ucsi *ucsi)
18921862
/* Enable all supported notifications */
18931863
ntfy = ucsi_get_supported_notifications(ucsi);
18941864
command = UCSI_SET_NOTIFICATION_ENABLE | ntfy;
1895-
ucsi->message_in_size = 0;
1896-
ret = ucsi_send_command(ucsi, command);
1865+
ret = ucsi_send_command(ucsi, command, NULL, 0);
18971866
if (ret < 0)
18981867
goto err_unregister;
18991868

@@ -1944,8 +1913,7 @@ static void ucsi_resume_work(struct work_struct *work)
19441913

19451914
/* Restore UCSI notification enable mask after system resume */
19461915
command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
1947-
ucsi->message_in_size = 0;
1948-
ret = ucsi_send_command(ucsi, command);
1916+
ret = ucsi_send_command(ucsi, command, NULL, 0);
19491917
if (ret < 0) {
19501918
dev_err(ucsi->dev, "failed to re-enable notifications (%d)\n", ret);
19511919
return;

0 commit comments

Comments
 (0)