mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-24 14:33:38 +00:00
dm: mei: vmei_proc_tx check buffer length before accessing the memory
Prevent memory and information leaks by checking boundaries of the incoming buffers from the hypervisor. 1. We check that the buffer is sufficent to hold a valid header. 2. We that each hbm message has valid size vmei_hbm_handler() now returns -EINVAL if the size too small. 3. hdr->length < data_length. Tracked-On: #5451 Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Acked-by: Wang, Yu1 <yu1.wang@intel.com>
This commit is contained in:
parent
14ea52c42f
commit
9ab1110f80
@ -1149,78 +1149,84 @@ vmei_hbm_version(struct virtio_mei *vmei,
|
||||
return vmei_hbm_response(vmei, &ver_res, sizeof(ver_res));
|
||||
}
|
||||
|
||||
static void
|
||||
vmei_hbm_handler(struct virtio_mei *vmei, const void *data)
|
||||
static int
|
||||
vmei_hbm_handler(struct virtio_mei *vmei, const void *data, size_t len)
|
||||
{
|
||||
struct mei_hbm_cmd *hbm_cmd = (struct mei_hbm_cmd *)data;
|
||||
struct mei_hbm_cmd *hbm_cmd = NULL;
|
||||
struct vmei_me_client *mclient = NULL;
|
||||
struct vmei_host_client *hclient = NULL;
|
||||
|
||||
uint8_t status;
|
||||
|
||||
struct mei_hbm_host_enum_req *enum_req;
|
||||
struct mei_hbm_host_enum_res enum_res;
|
||||
struct mei_hbm_host_client_prop_req *client_prop_req;
|
||||
struct mei_hbm_host_client_prop_res client_prop_res;
|
||||
struct mei_hbm_client_connect_req *connect_req;
|
||||
struct mei_hbm_client_connect_res connect_res;
|
||||
struct mei_hbm_client_disconnect_req *disconnect_req;
|
||||
struct mei_hbm_client_disconnect_res disconnect_res;
|
||||
struct mei_hbm_flow_ctl *flow_ctl_req;
|
||||
struct mei_hbm_power_gate pgi_res;
|
||||
struct mei_hbm_notification_req *notif_req;
|
||||
struct mei_hbm_notification_res notif_res;
|
||||
if (len < sizeof(*hbm_cmd)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hbm_cmd = (struct mei_hbm_cmd *)data;
|
||||
DPRINTF("HBM cmd[0x%X] is handling\n", hbm_cmd->cmd);
|
||||
|
||||
switch (hbm_cmd->cmd) {
|
||||
case MEI_HBM_HOST_START:
|
||||
vmei_hbm_version(vmei, data);
|
||||
case MEI_HBM_HOST_START: {
|
||||
const struct mei_hbm_host_ver_req *req = data;
|
||||
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
vmei_hbm_version(vmei, req);
|
||||
}
|
||||
break;
|
||||
case MEI_HBM_HOST_ENUM: {
|
||||
const struct mei_hbm_host_enum_req *req = data;
|
||||
struct mei_hbm_host_enum_res res = {};
|
||||
|
||||
case MEI_HBM_HOST_ENUM:
|
||||
enum_req = (struct mei_hbm_host_enum_req *)data;
|
||||
|
||||
enum_res.hbm_cmd = enum_req->hbm_cmd;
|
||||
enum_res.hbm_cmd.is_response = 1;
|
||||
/* copy valid_addresses for hbm enum request */
|
||||
memcpy(enum_res.valid_addresses, &vmei->me_clients_map,
|
||||
sizeof(enum_res.valid_addresses));
|
||||
vmei_hbm_response(vmei, &enum_res, sizeof(enum_res));
|
||||
|
||||
break;
|
||||
|
||||
case MEI_HBM_HOST_CLIENT_PROP:
|
||||
client_prop_req = (struct mei_hbm_host_client_prop_req *)data;
|
||||
|
||||
client_prop_res.hbm_cmd = client_prop_req->hbm_cmd;
|
||||
client_prop_res.hbm_cmd.is_response = 1;
|
||||
client_prop_res.address = client_prop_req->address;
|
||||
|
||||
mclient = vmei_find_me_client(vmei, client_prop_req->address);
|
||||
if (mclient) {
|
||||
memcpy(&client_prop_res.props, &mclient->props,
|
||||
sizeof(client_prop_res.props));
|
||||
client_prop_res.status = MEI_HBM_SUCCESS;
|
||||
} else {
|
||||
client_prop_res.status = MEI_HBM_CLIENT_NOT_FOUND;
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vmei_dbg_client_properties(&client_prop_res.props);
|
||||
|
||||
vmei_hbm_response(vmei,
|
||||
&client_prop_res,
|
||||
sizeof(client_prop_res));
|
||||
res.hbm_cmd = req->hbm_cmd;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
/* copy valid_addresses for hbm enum request */
|
||||
memcpy(res.valid_addresses, &vmei->me_clients_map,
|
||||
sizeof(res.valid_addresses));
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_FLOW_CONTROL:
|
||||
case MEI_HBM_HOST_CLIENT_PROP: {
|
||||
const struct mei_hbm_host_client_prop_req *req = data;
|
||||
struct mei_hbm_host_client_prop_res res;
|
||||
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res.hbm_cmd = req->hbm_cmd;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
res.address = req->address;
|
||||
|
||||
mclient = vmei_find_me_client(vmei, req->address);
|
||||
if (mclient) {
|
||||
memcpy(&res.props, &mclient->props, sizeof(res.props));
|
||||
res.status = MEI_HBM_SUCCESS;
|
||||
} else {
|
||||
res.status = MEI_HBM_CLIENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
vmei_dbg_client_properties(&res.props);
|
||||
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_FLOW_CONTROL: {
|
||||
/*
|
||||
* FE client is ready, we can send message
|
||||
*/
|
||||
flow_ctl_req = (struct mei_hbm_flow_ctl *)data;
|
||||
hclient = vmei_find_host_client(vmei,
|
||||
flow_ctl_req->me_addr,
|
||||
flow_ctl_req->host_addr);
|
||||
const struct mei_hbm_flow_ctl *req = data;
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hclient = vmei_find_host_client(vmei, req->me_addr, req->host_addr);
|
||||
if (hclient) {
|
||||
hclient->recv_creds++;
|
||||
mevent_enable(hclient->rx_mevp);
|
||||
@ -1233,51 +1239,53 @@ vmei_hbm_handler(struct virtio_mei *vmei, const void *data)
|
||||
} else {
|
||||
DPRINTF("client has been released.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_CLIENT_CONNECT:
|
||||
case MEI_HBM_CLIENT_CONNECT: {
|
||||
|
||||
connect_req = (struct mei_hbm_client_connect_req *)data;
|
||||
const struct mei_hbm_client_connect_req *req = data;
|
||||
struct mei_hbm_client_connect_res res;
|
||||
|
||||
connect_res = *(struct mei_hbm_client_connect_res *)connect_req;
|
||||
connect_res.hbm_cmd.is_response = 1;
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mclient = vmei_find_me_client(vmei, connect_req->me_addr);
|
||||
res = *(struct mei_hbm_client_connect_res *)req;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
|
||||
mclient = vmei_find_me_client(vmei, req->me_addr);
|
||||
if (!mclient) {
|
||||
/* NOT FOUND */
|
||||
DPRINTF("client with me address %d was not found\n",
|
||||
connect_req->me_addr);
|
||||
connect_res.status = MEI_HBM_CLIENT_NOT_FOUND;
|
||||
vmei_hbm_response(vmei, &connect_res,
|
||||
sizeof(connect_res));
|
||||
req->me_addr);
|
||||
res.status = MEI_HBM_CLIENT_NOT_FOUND;
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
break;
|
||||
}
|
||||
|
||||
hclient = vmei_me_client_get_host_client(mclient,
|
||||
connect_req->host_addr);
|
||||
req->host_addr);
|
||||
if (hclient) {
|
||||
DPRINTF("client alread exists return BUSY!\n");
|
||||
connect_res.status = MEI_HBM_ALREADY_EXISTS;
|
||||
res.status = MEI_HBM_ALREADY_EXISTS;
|
||||
vmei_host_client_put(hclient);
|
||||
hclient = NULL;
|
||||
vmei_hbm_response(vmei, &connect_res,
|
||||
sizeof(connect_res));
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
break;
|
||||
}
|
||||
|
||||
/* create a new host client and add ito the list */
|
||||
hclient = vmei_host_client_create(mclient,
|
||||
connect_req->host_addr);
|
||||
hclient = vmei_host_client_create(mclient, req->host_addr);
|
||||
if (!hclient) {
|
||||
connect_res.status = MEI_HBM_REJECTED;
|
||||
vmei_hbm_response(vmei, &connect_res,
|
||||
sizeof(connect_res));
|
||||
res.status = MEI_HBM_REJECTED;
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
break;
|
||||
}
|
||||
|
||||
status = vmei_host_client_native_connect(hclient);
|
||||
connect_res.status = status;
|
||||
vmei_hbm_response(vmei, &connect_res, sizeof(connect_res));
|
||||
res.status = status;
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
|
||||
if (status) {
|
||||
vmei_host_client_put(hclient);
|
||||
@ -1286,14 +1294,19 @@ vmei_hbm_handler(struct virtio_mei *vmei, const void *data)
|
||||
}
|
||||
|
||||
vmei_hbm_flow_ctl_req(hclient);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_CLIENT_DISCONNECT:
|
||||
case MEI_HBM_CLIENT_DISCONNECT: {
|
||||
|
||||
disconnect_req = (struct mei_hbm_client_disconnect_req *)data;
|
||||
hclient = vmei_find_host_client(vmei, disconnect_req->me_addr,
|
||||
disconnect_req->host_addr);
|
||||
const struct mei_hbm_client_disconnect_req *req = data;
|
||||
struct mei_hbm_client_disconnect_res res;
|
||||
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hclient = vmei_find_host_client(vmei, req->me_addr, req->host_addr);
|
||||
if (hclient) {
|
||||
vmei_host_client_put(hclient);
|
||||
vmei_host_client_put(hclient);
|
||||
@ -1306,45 +1319,52 @@ vmei_hbm_handler(struct virtio_mei *vmei, const void *data)
|
||||
if (hbm_cmd->is_response)
|
||||
break;
|
||||
|
||||
memset(&disconnect_res, 0, sizeof(disconnect_res));
|
||||
disconnect_res.hbm_cmd.cmd = MEI_HBM_CLIENT_DISCONNECT;
|
||||
disconnect_res.hbm_cmd.is_response = 1;
|
||||
disconnect_res.me_addr = disconnect_req->me_addr;
|
||||
disconnect_res.host_addr = disconnect_req->host_addr;
|
||||
disconnect_res.status = status;
|
||||
vmei_hbm_response(vmei, &disconnect_res,
|
||||
sizeof(disconnect_res));
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.hbm_cmd.cmd = MEI_HBM_CLIENT_DISCONNECT;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
res.me_addr = req->me_addr;
|
||||
res.host_addr = req->host_addr;
|
||||
res.status = status;
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_PG_ISOLATION_ENTRY:
|
||||
case MEI_HBM_PG_ISOLATION_ENTRY: {
|
||||
|
||||
memset(&pgi_res, 0, sizeof(pgi_res));
|
||||
pgi_res.hbm_cmd.cmd = MEI_HBM_PG_ISOLATION_ENTRY;
|
||||
pgi_res.hbm_cmd.is_response = 1;
|
||||
vmei_hbm_response(vmei, &pgi_res, sizeof(pgi_res));
|
||||
struct mei_hbm_power_gate res;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.hbm_cmd.cmd = MEI_HBM_PG_ISOLATION_ENTRY;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
}
|
||||
break;
|
||||
|
||||
case MEI_HBM_NOTIFY:
|
||||
case MEI_HBM_NOTIFY: {
|
||||
|
||||
notif_req = (struct mei_hbm_notification_req *)data;
|
||||
memset(¬if_res, 0, sizeof(notif_res));
|
||||
notif_res.hbm_cmd.cmd = MEI_HBM_NOTIFY;
|
||||
notif_res.hbm_cmd.is_response = 1;
|
||||
notif_res.start = notif_req->start;
|
||||
const struct mei_hbm_notification_req *req = data;
|
||||
struct mei_hbm_notification_res res;
|
||||
|
||||
hclient = vmei_find_host_client(vmei, notif_req->me_addr,
|
||||
notif_req->host_addr);
|
||||
if (sizeof(*req) < len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.hbm_cmd.cmd = MEI_HBM_NOTIFY;
|
||||
res.hbm_cmd.is_response = 1;
|
||||
res.start = req->start;
|
||||
|
||||
hclient = vmei_find_host_client(vmei, req->me_addr,
|
||||
req->host_addr);
|
||||
if (hclient) {
|
||||
/* The notify is not supported now */
|
||||
notif_res.status = MEI_HBM_REJECTED;
|
||||
res.status = MEI_HBM_REJECTED;
|
||||
vmei_host_client_put(hclient);
|
||||
} else {
|
||||
notif_res.status = MEI_HBM_INVALID_PARAMETER;
|
||||
res.status = MEI_HBM_INVALID_PARAMETER;
|
||||
}
|
||||
vmei_hbm_response(vmei, &res, sizeof(res));
|
||||
}
|
||||
vmei_hbm_response(vmei, ¬if_res, sizeof(notif_res));
|
||||
|
||||
break;
|
||||
|
||||
case MEI_HBM_HOST_STOP:
|
||||
@ -1358,6 +1378,8 @@ vmei_hbm_handler(struct virtio_mei *vmei, const void *data)
|
||||
default:
|
||||
DPRINTF("HBM cmd[0x%X] unhandled\n", hbm_cmd->cmd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vmei_fixed_client_connect(struct vmei_me_client *mclient)
|
||||
@ -1480,6 +1502,7 @@ vmei_proc_tx(struct virtio_mei *vmei, struct virtio_vq_info *vq)
|
||||
|
||||
struct mei_msg_hdr *hdr;
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
uint8_t i_idx;
|
||||
struct vmei_circular_iobufs *bufs;
|
||||
|
||||
@ -1500,19 +1523,34 @@ vmei_proc_tx(struct virtio_mei *vmei, struct virtio_vq_info *vq)
|
||||
return;
|
||||
}
|
||||
|
||||
tlen = iov[0].iov_len + iov[1].iov_len;
|
||||
|
||||
if (iov[0].iov_len < sizeof(*hdr)) {
|
||||
pr_err("%s: supplied buffer has invalid size");
|
||||
goto failed;
|
||||
}
|
||||
hdr = (struct mei_msg_hdr *)iov[0].iov_base;
|
||||
data = (uint8_t *)iov[1].iov_base;
|
||||
data_len = iov[1].iov_len;
|
||||
|
||||
tlen = iov[0].iov_len + iov[1].iov_len;
|
||||
|
||||
DPRINTF("TX: UOS->DM, hdr[h=%02d me=%02d comp=%1d] length[%d]\n",
|
||||
hdr->host_addr, hdr->me_addr, hdr->msg_complete, hdr->length);
|
||||
vmei_dbg_print_hex("TX: UOS->DM", iov[1].iov_base, iov[1].iov_len);
|
||||
vmei_dbg_print_hex("TX: UOS->DM", data, data_len);
|
||||
|
||||
if (hdr->length < data_len) {
|
||||
pr_err("%s: supplied buffer has invalid size");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (hdr_is_hbm(hdr)) {
|
||||
vmei_hbm_handler(vmei, data);
|
||||
if (vmei_hbm_handler(vmei, data, data_len)) {
|
||||
pr_err("%s: supplied buffer has invalid size");
|
||||
goto failed;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* general client client
|
||||
* must be in active_clients list.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user