mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 01:32:21 +02:00
ksmbd: fix outstanding credit leak on abort and error paths
smb2_validate_credit_charge() adds the request's CreditCharge to
conn->outstanding_credits when an SMB2 PDU is received, and
smb2_set_rsp_credits() subtracts it again when the response is built.
However smb2_set_rsp_credits() only runs on the normal response path:
- __process_request() returning SERVER_HANDLER_ABORT (unimplemented
command, command index out of range, signature check failure, or a
handler that sets send_no_response such as a cancelled blocking
lock) breaks out of the processing loop before set_rsp_credits() is
called;
- smb2_set_rsp_credits() itself returns early with -EINVAL (total
credit overflow or insufficient credits) before the subtraction.
On all of these paths the charge added at receive time is never
returned, so conn->outstanding_credits only grows. Because a client can
repeatedly trigger them (e.g. by sending unimplemented commands or by
issuing and cancelling blocking locks), outstanding_credits eventually
reaches total_credits and smb2_validate_credit_charge() then rejects
every subsequent request, wedging the connection.
Record the charge that was added in work->credit_charge and release any
charge still pending at the single send. exit point of
__handle_ksmbd_work(), which all abort and error paths fall through to.
smb2_set_rsp_credits() clears work->credit_charge once it has returned
the charge so the response path is unchanged and the credit is never
released twice. Paths that never charged a credit (no multi-credit
support, validation failure) leave work->credit_charge at zero and are
unaffected.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
284dc80ff5
commit
4a0b782661
|
|
@ -67,6 +67,13 @@ struct ksmbd_work {
|
|||
/* Number of granted credits */
|
||||
unsigned int credits_granted;
|
||||
|
||||
/*
|
||||
* Credit charge added to conn->outstanding_credits at receive time
|
||||
* for the SMB2 PDU currently being processed, pending release. Zero
|
||||
* once the charge has been returned (on the response or error path).
|
||||
*/
|
||||
unsigned short credit_charge;
|
||||
|
||||
/* response smb header size */
|
||||
unsigned int response_sz;
|
||||
|
||||
|
|
|
|||
|
|
@ -242,6 +242,20 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
|
|||
} while (is_chained == true);
|
||||
|
||||
send:
|
||||
/*
|
||||
* Release any credit charge still outstanding for this request. On
|
||||
* the normal path smb2_set_rsp_credits() already returned it, but the
|
||||
* abort, error and send-no-response paths skip that call, so the
|
||||
* charge would otherwise leak and eventually exhaust the connection's
|
||||
* outstanding credit window.
|
||||
*/
|
||||
if (work->credit_charge) {
|
||||
spin_lock(&conn->credits_lock);
|
||||
conn->outstanding_credits -= work->credit_charge;
|
||||
work->credit_charge = 0;
|
||||
spin_unlock(&conn->credits_lock);
|
||||
}
|
||||
|
||||
if (work->tcon)
|
||||
ksmbd_tree_connect_put(work->tcon);
|
||||
smb3_preauth_hash_rsp(work);
|
||||
|
|
|
|||
|
|
@ -301,9 +301,10 @@ static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
|
|||
le32_to_cpu(h->MaxOutputResponse);
|
||||
}
|
||||
|
||||
static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
|
||||
static int smb2_validate_credit_charge(struct ksmbd_work *work,
|
||||
struct smb2_hdr *hdr)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
unsigned int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
|
||||
unsigned short credit_charge = le16_to_cpu(hdr->CreditCharge);
|
||||
void *__hdr = hdr;
|
||||
|
|
@ -361,8 +362,10 @@ static int smb2_validate_credit_charge(struct ksmbd_conn *conn,
|
|||
ksmbd_debug(SMB, "Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u\n",
|
||||
credit_charge, conn->outstanding_credits);
|
||||
ret = 1;
|
||||
} else
|
||||
} else {
|
||||
conn->outstanding_credits += credit_charge;
|
||||
work->credit_charge = credit_charge;
|
||||
}
|
||||
|
||||
spin_unlock(&conn->credits_lock);
|
||||
|
||||
|
|
@ -465,7 +468,7 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
|
|||
|
||||
validate_credit:
|
||||
if ((work->conn->vals->req_capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
|
||||
smb2_validate_credit_charge(work->conn, hdr))
|
||||
smb2_validate_credit_charge(work, hdr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ int smb2_set_rsp_credits(struct ksmbd_work *work)
|
|||
|
||||
conn->total_credits -= credit_charge;
|
||||
conn->outstanding_credits -= credit_charge;
|
||||
work->credit_charge = 0;
|
||||
credits_requested = max_t(unsigned short,
|
||||
le16_to_cpu(req_hdr->CreditRequest), 1);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user