diff options
author | Zhizhou Zhang <zhizhouzhang@asrmicro.com> | 2018-11-20 22:01:43 -0500 |
---|---|---|
committer | Jens Wiklander <jens.wiklander@linaro.org> | 2018-12-11 08:38:21 -0500 |
commit | b2d102bd0146d9eb1fa630ca0cd19a15ef2f74c8 (patch) | |
tree | b72e8dbe029487643c5150e5360c04ebebeb4446 /drivers/tee | |
parent | 40e020c129cfc991e8ab4736d2665351ffd1468d (diff) |
tee: optee: avoid possible double list_del()
This bug occurs when:
- a new request arrives, one thread(let's call it A) is pending in
optee_supp_req() with req->busy is initial value false.
- tee-supplicant is killed, then optee_supp_release() is called, this
function calls list_del(&req->link), and set supp->ctx to NULL. And
it also wake up process A.
- process A continues, it firstly checks supp->ctx which is NULL,
then checks req->busy which is false, at last run list_del(&req->link).
This triggers double list_del() and results kernel panic.
For solve this problem, we rename req->busy to req->in_queue, and
associate it with state of whether req is linked to supp->reqs. So we
can just only check req->in_queue to make decision calling list_del()
or not.
Signed-off-by: Zhizhou Zhang <zhizhouzhang@asrmicro.com>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Diffstat (limited to 'drivers/tee')
-rw-r--r-- | drivers/tee/optee/supp.c | 13 |
1 files changed, 7 insertions, 6 deletions
diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index df35fc01fd3e..43626e15703a 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c | |||
@@ -19,7 +19,7 @@ | |||
19 | struct optee_supp_req { | 19 | struct optee_supp_req { |
20 | struct list_head link; | 20 | struct list_head link; |
21 | 21 | ||
22 | bool busy; | 22 | bool in_queue; |
23 | u32 func; | 23 | u32 func; |
24 | u32 ret; | 24 | u32 ret; |
25 | size_t num_params; | 25 | size_t num_params; |
@@ -54,7 +54,6 @@ void optee_supp_release(struct optee_supp *supp) | |||
54 | 54 | ||
55 | /* Abort all request retrieved by supplicant */ | 55 | /* Abort all request retrieved by supplicant */ |
56 | idr_for_each_entry(&supp->idr, req, id) { | 56 | idr_for_each_entry(&supp->idr, req, id) { |
57 | req->busy = false; | ||
58 | idr_remove(&supp->idr, id); | 57 | idr_remove(&supp->idr, id); |
59 | req->ret = TEEC_ERROR_COMMUNICATION; | 58 | req->ret = TEEC_ERROR_COMMUNICATION; |
60 | complete(&req->c); | 59 | complete(&req->c); |
@@ -63,6 +62,7 @@ void optee_supp_release(struct optee_supp *supp) | |||
63 | /* Abort all queued requests */ | 62 | /* Abort all queued requests */ |
64 | list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { | 63 | list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { |
65 | list_del(&req->link); | 64 | list_del(&req->link); |
65 | req->in_queue = false; | ||
66 | req->ret = TEEC_ERROR_COMMUNICATION; | 66 | req->ret = TEEC_ERROR_COMMUNICATION; |
67 | complete(&req->c); | 67 | complete(&req->c); |
68 | } | 68 | } |
@@ -103,6 +103,7 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, | |||
103 | /* Insert the request in the request list */ | 103 | /* Insert the request in the request list */ |
104 | mutex_lock(&supp->mutex); | 104 | mutex_lock(&supp->mutex); |
105 | list_add_tail(&req->link, &supp->reqs); | 105 | list_add_tail(&req->link, &supp->reqs); |
106 | req->in_queue = true; | ||
106 | mutex_unlock(&supp->mutex); | 107 | mutex_unlock(&supp->mutex); |
107 | 108 | ||
108 | /* Tell an eventual waiter there's a new request */ | 109 | /* Tell an eventual waiter there's a new request */ |
@@ -130,9 +131,10 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, | |||
130 | * will serve all requests in a timely manner and | 131 | * will serve all requests in a timely manner and |
131 | * interrupting then wouldn't make sense. | 132 | * interrupting then wouldn't make sense. |
132 | */ | 133 | */ |
133 | interruptable = !req->busy; | 134 | if (req->in_queue) { |
134 | if (!req->busy) | ||
135 | list_del(&req->link); | 135 | list_del(&req->link); |
136 | req->in_queue = false; | ||
137 | } | ||
136 | } | 138 | } |
137 | mutex_unlock(&supp->mutex); | 139 | mutex_unlock(&supp->mutex); |
138 | 140 | ||
@@ -176,7 +178,7 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, | |||
176 | return ERR_PTR(-ENOMEM); | 178 | return ERR_PTR(-ENOMEM); |
177 | 179 | ||
178 | list_del(&req->link); | 180 | list_del(&req->link); |
179 | req->busy = true; | 181 | req->in_queue = false; |
180 | 182 | ||
181 | return req; | 183 | return req; |
182 | } | 184 | } |
@@ -318,7 +320,6 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, | |||
318 | if ((num_params - nm) != req->num_params) | 320 | if ((num_params - nm) != req->num_params) |
319 | return ERR_PTR(-EINVAL); | 321 | return ERR_PTR(-EINVAL); |
320 | 322 | ||
321 | req->busy = false; | ||
322 | idr_remove(&supp->idr, id); | 323 | idr_remove(&supp->idr, id); |
323 | supp->req_id = -1; | 324 | supp->req_id = -1; |
324 | *num_meta = nm; | 325 | *num_meta = nm; |