diff options
author | David Howells <dhowells@redhat.com> | 2017-01-05 10:38:36 +0000 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2017-01-09 11:10:02 +0000 |
commit | 341f741f04beceebcb30daa12ae2e5e52e64e532 (patch) | |
tree | 4c49b19c44fd13d26a2a84264aa062b07b911498 /fs/afs/cmservice.c | |
parent | 210f035316f545e6f507e7d61e191495ba983e27 (diff) |
afs: Refcount the afs_call struct
A static checker warning occurs in the AFS filesystem:
fs/afs/cmservice.c:155 SRXAFSCB_CallBack()
error: dereferencing freed memory 'call'
due to the reply being sent before we access the server it points to. The
act of sending the reply causes the call to be freed if an error occurs
(but not if it doesn't).
On top of this, the lifetime handling of afs_call structs is fragile
because they get passed around through workqueues without any sort of
refcounting.
Deal with the issues by:
(1) Fix the maybe/maybe not nature of the reply sending functions with
regards to whether they release the call struct.
(2) Refcount the afs_call struct and sort out places that need to get/put
references.
(3) Pass a ref through the work queue and release (or pass on) that ref in
the work function. Care has to be taken because a work queue may
already own a ref to the call.
(4) Do the cleaning up in the put function only.
(5) Simplify module cleanup by always incrementing afs_outstanding_calls
whenever a call is allocated.
(6) Set the backlog to 0 with kernel_listen() at the beginning of the
process of closing the socket to prevent new incoming calls from
occurring and to remove the contribution of preallocated calls from
afs_outstanding_calls before we wait on it.
A tracepoint is also added to monitor the afs_call refcount and lifetime.
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Fixes: 08e0e7c82eea: "[AF_RXRPC]: Make the in-kernel AFS filesystem use AF_RXRPC."
Diffstat (limited to 'fs/afs/cmservice.c')
-rw-r--r-- | fs/afs/cmservice.c | 41 |
1 files changed, 22 insertions, 19 deletions
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c index a2e1e02005f6..e349a3316303 100644 --- a/fs/afs/cmservice.c +++ b/fs/afs/cmservice.c @@ -24,6 +24,11 @@ static int afs_deliver_cb_callback(struct afs_call *); static int afs_deliver_cb_probe_uuid(struct afs_call *); static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *); static void afs_cm_destructor(struct afs_call *); +static void SRXAFSCB_CallBack(struct work_struct *); +static void SRXAFSCB_InitCallBackState(struct work_struct *); +static void SRXAFSCB_Probe(struct work_struct *); +static void SRXAFSCB_ProbeUuid(struct work_struct *); +static void SRXAFSCB_TellMeAboutYourself(struct work_struct *); #define CM_NAME(name) \ const char afs_SRXCB##name##_name[] __tracepoint_string = \ @@ -38,6 +43,7 @@ static const struct afs_call_type afs_SRXCBCallBack = { .deliver = afs_deliver_cb_callback, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_CallBack, }; /* @@ -49,6 +55,7 @@ static const struct afs_call_type afs_SRXCBInitCallBackState = { .deliver = afs_deliver_cb_init_call_back_state, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_InitCallBackState, }; /* @@ -60,6 +67,7 @@ static const struct afs_call_type afs_SRXCBInitCallBackState3 = { .deliver = afs_deliver_cb_init_call_back_state3, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_InitCallBackState, }; /* @@ -71,6 +79,7 @@ static const struct afs_call_type afs_SRXCBProbe = { .deliver = afs_deliver_cb_probe, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_Probe, }; /* @@ -82,6 +91,7 @@ static const struct afs_call_type afs_SRXCBProbeUuid = { .deliver = afs_deliver_cb_probe_uuid, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_ProbeUuid, }; /* @@ -93,6 +103,7 @@ static const struct afs_call_type afs_SRXCBTellMeAboutYourself = { .deliver = afs_deliver_cb_tell_me_about_yourself, .abort_to_error = afs_abort_to_error, .destructor = afs_cm_destructor, + .work = SRXAFSCB_TellMeAboutYourself, }; /* @@ -163,6 +174,7 @@ static void SRXAFSCB_CallBack(struct work_struct *work) afs_send_empty_reply(call); afs_break_callbacks(call->server, call->count, call->request); + afs_put_call(call); _leave(""); } @@ -284,9 +296,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) return -ENOTCONN; call->server = server; - INIT_WORK(&call->work, SRXAFSCB_CallBack); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } /* @@ -300,6 +310,7 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work) afs_init_callback_state(call->server); afs_send_empty_reply(call); + afs_put_call(call); _leave(""); } @@ -330,9 +341,7 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call) return -ENOTCONN; call->server = server; - INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } /* @@ -404,9 +413,7 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call) return -ENOTCONN; call->server = server; - INIT_WORK(&call->work, SRXAFSCB_InitCallBackState); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } /* @@ -418,6 +425,7 @@ static void SRXAFSCB_Probe(struct work_struct *work) _enter(""); afs_send_empty_reply(call); + afs_put_call(call); _leave(""); } @@ -437,9 +445,7 @@ static int afs_deliver_cb_probe(struct afs_call *call) /* no unmarshalling required */ call->state = AFS_CALL_REPLYING; - INIT_WORK(&call->work, SRXAFSCB_Probe); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } /* @@ -462,6 +468,7 @@ static void SRXAFSCB_ProbeUuid(struct work_struct *work) reply.match = htonl(1); afs_send_simple_reply(call, &reply, sizeof(reply)); + afs_put_call(call); _leave(""); } @@ -520,9 +527,7 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) call->state = AFS_CALL_REPLYING; - INIT_WORK(&call->work, SRXAFSCB_ProbeUuid); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } /* @@ -584,7 +589,7 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) reply.cap.capcount = htonl(1); reply.cap.caps[0] = htonl(AFS_CAP_ERROR_TRANSLATION); afs_send_simple_reply(call, &reply, sizeof(reply)); - + afs_put_call(call); _leave(""); } @@ -604,7 +609,5 @@ static int afs_deliver_cb_tell_me_about_yourself(struct afs_call *call) /* no unmarshalling required */ call->state = AFS_CALL_REPLYING; - INIT_WORK(&call->work, SRXAFSCB_TellMeAboutYourself); - queue_work(afs_wq, &call->work); - return 0; + return afs_queue_call_work(call); } |