OP-TEE fix for v7.1
Prevent possible use after free in supplicant communication. -----BEGIN PGP SIGNATURE----- iQJOBAABCgA4FiEE0qerISgy2SKkqO79Wr/6JGat8H4FAmoPEQQaHGplbnMud2lr bGFuZGVyQGxpbmFyby5vcmcACgkQWr/6JGat8H6i7g/+IKeDvegjBiud2okBgEq3 5yMYhmHP/ycgnAaQkpqvtZP41I4ar0LAbresNWEvmuAR71rirWSCDKRHbm0xv8Ww UJ1xyDxnIdNQe8sBBqrYe4QBq80l9gKpX5Tmi8eQkFGfDX+DWet0fdYBW/pKoSaI M/ciBORdbwYFPPb7hGBpuZ/tUuFNlRo4tcW14mM9v64m5pOe94RR92Dshf8fCwWk +4HmMBldBCUHz1j4hjzwEunq3aMVFHobUozssk3A0eLIpEPla0OqeO4vT1UpskfL nXiJCvioKH/X6/xKzhuumPN7Bvkq1eMZAQy+4N4wNJShzJJtqokAjDBUjc42576l 7xKoNNa+mSNfrpu9RABC0hdpcTRxIbcgE70tViyeUIPOfhryAAlhaUe/iN1O4HYf dfwqWVGwosGUbXb8vZUvggR1GmLSLrN5H/PhXg7hb6RDveDD2eSgHWsh/+CGDrYq quWOamOeodblIrjOnRKsBVLuBLXJ3muIsED9HrCfChlS/EMGQYSWqr7KP+MIBQuQ Q9TzrBZ0j7m2MOwEzmLssa5nmbIxB7bnVOO/46xqQv4ktRC335/IYVpsQrD0AJ2B XM4raWikXLNIxNAwkzzDzP2Tu67zKxNtiPi1z4S0UeMY90NSJZ5Z4E7AEe8iSL1P Z3oXjhxDyv7aJFzYnxBApgI= =aOSI -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmoYQvUACgkQmmx57+YA GNkRcxAAjiVXyEFwLErRTJlQ9Tq00axugkt0YJW5LxDixuqXjfmPp6TpsATm1Np7 Xx2eplkEhMZiIgP0nDQkT/31kFlLUd3nOQiJv9Cz8ZwwZulCWMlMeTPZ773MwlmO rIuo+cBaQhkYp3e3mglWLuAXR4MtW4GQzZ42jRXYvOz+3HrG3Zb8C/CfOQfMlFU0 17M+HMBq6FAGjJEhH1sZEb19WEE87ZiH2aee1+HQ+FgzfDtW0QD+9bhlxpIsFkak NRKoD58ixobSy1SuMSPQ4liK76VEdP6UQDZ2R4IyhO6/QM1ZE5JRpAf9RWAy4Mqi 4gcHrBXdME2hJUrXLtV6PdQMXPq9+/yphs+JWXwRfns+uL9ltDYwxk14Tc+hshGk jM0qcfPvF/s/6Med+oCnSGQn8CYYOblwi/qhpWLqyl6Rrpag1kkTqVXmOsJ0J71m J59fIEjqcYkv8y+46Pj3ZxNXVgXlhREpsp/gg/FAkJ5bNpKxWqdqPba4gy/rhZFh x2TOpfzMHMQS9MrfbLtlu4HshbwDqEyiThTgUc5c1njYwb3Mao//QxwmjLBQesWY sZSfl+26rF/fyPcaHne5nZ3JSKE0FGxnHot+jPDS6OURAhF8AwhKFjLnzIFnmWhy kqsrVQCrDySP1rzm23A9pFcpx7mlR4tZM4q4xRb4DujPHiIYK9w= =jbl0 -----END PGP SIGNATURE----- Merge tag 'optee-fix-for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/jenswi/linux-tee into arm/fixes OP-TEE fix for v7.1 Prevent possible use after free in supplicant communication. * tag 'optee-fix-for-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/jenswi/linux-tee: tee: optee: prevent use-after-free when the client exits before the supplicant Signed-off-by: Arnd Bergmann <arnd@arndb.de>master^2
commit
56d5829720
|
|
@ -10,7 +10,11 @@
|
|||
struct optee_supp_req {
|
||||
struct list_head link;
|
||||
|
||||
int id;
|
||||
|
||||
bool in_queue;
|
||||
bool processed;
|
||||
|
||||
u32 func;
|
||||
u32 ret;
|
||||
size_t num_params;
|
||||
|
|
@ -19,6 +23,9 @@ struct optee_supp_req {
|
|||
struct completion c;
|
||||
};
|
||||
|
||||
/* It is temporary request used for revoked pending request in supp->idr. */
|
||||
#define INVALID_REQ_PTR ((struct optee_supp_req *)ERR_PTR(-EBADF))
|
||||
|
||||
void optee_supp_init(struct optee_supp *supp)
|
||||
{
|
||||
memset(supp, 0, sizeof(*supp));
|
||||
|
|
@ -39,21 +46,23 @@ void optee_supp_release(struct optee_supp *supp)
|
|||
{
|
||||
int id;
|
||||
struct optee_supp_req *req;
|
||||
struct optee_supp_req *req_tmp;
|
||||
|
||||
mutex_lock(&supp->mutex);
|
||||
|
||||
/* Abort all request retrieved by supplicant */
|
||||
/* Abort all request */
|
||||
idr_for_each_entry(&supp->idr, req, id) {
|
||||
idr_remove(&supp->idr, id);
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
complete(&req->c);
|
||||
}
|
||||
/* Skip if request was already marked invalid */
|
||||
if (IS_ERR(req))
|
||||
continue;
|
||||
|
||||
/* Abort all queued requests */
|
||||
list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) {
|
||||
list_del(&req->link);
|
||||
req->in_queue = false;
|
||||
/* For queued requests where supplicant has not seen it */
|
||||
if (req->in_queue) {
|
||||
list_del(&req->link);
|
||||
req->in_queue = false;
|
||||
}
|
||||
|
||||
req->processed = true;
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
complete(&req->c);
|
||||
}
|
||||
|
|
@ -100,8 +109,16 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
|
|||
|
||||
/* Insert the request in the request list */
|
||||
mutex_lock(&supp->mutex);
|
||||
req->id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
|
||||
if (req->id < 0) {
|
||||
mutex_unlock(&supp->mutex);
|
||||
kfree(req);
|
||||
return TEEC_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
list_add_tail(&req->link, &supp->reqs);
|
||||
req->in_queue = true;
|
||||
req->processed = false;
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
/* Tell an eventual waiter there's a new request */
|
||||
|
|
@ -117,21 +134,43 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
|
|||
if (wait_for_completion_killable(&req->c)) {
|
||||
mutex_lock(&supp->mutex);
|
||||
if (req->in_queue) {
|
||||
/* Supplicant has not seen this request yet. */
|
||||
idr_remove(&supp->idr, req->id);
|
||||
list_del(&req->link);
|
||||
req->in_queue = false;
|
||||
|
||||
ret = TEEC_ERROR_COMMUNICATION;
|
||||
} else if (req->processed) {
|
||||
/*
|
||||
* Supplicant has processed this request. Ignore the
|
||||
* kill signal for now and submit the result. req is not
|
||||
* in supp->reqs (removed by supp_pop_entry()) nor in
|
||||
* supp->idr (removed by supp_pop_req()).
|
||||
*/
|
||||
ret = req->ret;
|
||||
} else {
|
||||
/*
|
||||
* Supplicant is in the middle of processing this
|
||||
* request. Replace req with INVALID_REQ_PTR so that
|
||||
* the ID remains busy, causing optee_supp_send() to
|
||||
* fail on the next call to supp_pop_req() with this ID.
|
||||
*/
|
||||
idr_replace(&supp->idr, INVALID_REQ_PTR, req->id);
|
||||
ret = TEEC_ERROR_COMMUNICATION;
|
||||
}
|
||||
|
||||
mutex_unlock(&supp->mutex);
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
} else {
|
||||
ret = req->ret;
|
||||
}
|
||||
|
||||
ret = req->ret;
|
||||
kfree(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp,
|
||||
int num_params, int *id)
|
||||
int num_params)
|
||||
{
|
||||
struct optee_supp_req *req;
|
||||
|
||||
|
|
@ -153,10 +192,6 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp,
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
*id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
|
||||
if (*id < 0)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
list_del(&req->link);
|
||||
req->in_queue = false;
|
||||
|
||||
|
|
@ -214,7 +249,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
struct optee *optee = tee_get_drvdata(teedev);
|
||||
struct optee_supp *supp = &optee->supp;
|
||||
struct optee_supp_req *req = NULL;
|
||||
int id;
|
||||
size_t num_meta;
|
||||
int rc;
|
||||
|
||||
|
|
@ -224,15 +258,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
|
||||
while (true) {
|
||||
mutex_lock(&supp->mutex);
|
||||
req = supp_pop_entry(supp, *num_params - num_meta, &id);
|
||||
req = supp_pop_entry(supp, *num_params - num_meta);
|
||||
if (req)
|
||||
break; /* Keep mutex held. */
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
if (req) {
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't get a request we'll block in
|
||||
* wait_for_completion() to avoid needless spinning.
|
||||
|
|
@ -245,6 +275,13 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
/* supp->mutex held and req != NULL. */
|
||||
|
||||
if (IS_ERR(req)) {
|
||||
mutex_unlock(&supp->mutex);
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
if (num_meta) {
|
||||
/*
|
||||
* tee-supplicant support meta parameters -> requsts can be
|
||||
|
|
@ -252,13 +289,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
*/
|
||||
param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
|
||||
TEE_IOCTL_PARAM_ATTR_META;
|
||||
param->u.value.a = id;
|
||||
param->u.value.a = req->id;
|
||||
param->u.value.b = 0;
|
||||
param->u.value.c = 0;
|
||||
} else {
|
||||
mutex_lock(&supp->mutex);
|
||||
supp->req_id = id;
|
||||
mutex_unlock(&supp->mutex);
|
||||
supp->req_id = req->id;
|
||||
}
|
||||
|
||||
*func = req->func;
|
||||
|
|
@ -266,6 +301,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
memcpy(param + num_meta, req->param,
|
||||
sizeof(struct tee_param) * req->num_params);
|
||||
|
||||
mutex_unlock(&supp->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -297,12 +333,17 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp,
|
|||
if (!req)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* optee_supp_thrd_req() already returned to optee. */
|
||||
if (IS_ERR(req))
|
||||
goto failed_req;
|
||||
|
||||
if ((num_params - nm) != req->num_params)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*num_meta = nm;
|
||||
failed_req:
|
||||
idr_remove(&supp->idr, id);
|
||||
supp->req_id = -1;
|
||||
*num_meta = nm;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
|
@ -328,10 +369,9 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
|
|||
|
||||
mutex_lock(&supp->mutex);
|
||||
req = supp_pop_req(supp, num_params, param, &num_meta);
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
if (IS_ERR(req)) {
|
||||
/* Something is wrong, let supplicant restart. */
|
||||
mutex_unlock(&supp->mutex);
|
||||
/* Something is wrong, let supplicant handel it. */
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
|
|
@ -355,9 +395,10 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
|
|||
}
|
||||
}
|
||||
req->ret = ret;
|
||||
|
||||
req->processed = true;
|
||||
/* Let the requesting thread continue */
|
||||
complete(&req->c);
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue