diff --git a/debian/changelog b/debian/changelog index 305e5a40e..5709b3b0b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -249,6 +249,7 @@ linux (4.9.22-1) UNRELEASED; urgency=medium - rtmutex: Provide rt_mutex_lock_state() - rtmutex: Provide locked slowpath - rwsem/rt: Lift single reader restriction + * crypto: ahash - Fix EINPROGRESS notification callback (CVE-2017-7618) [ Salvatore Bonaccorso ] * ping: implement proper locking (CVE-2017-2671) diff --git a/debian/patches/bugfix/all/crypto-ahash-fix-einprogress-notification-callback.patch b/debian/patches/bugfix/all/crypto-ahash-fix-einprogress-notification-callback.patch new file mode 100644 index 000000000..1f69ee5a1 --- /dev/null +++ b/debian/patches/bugfix/all/crypto-ahash-fix-einprogress-notification-callback.patch @@ -0,0 +1,226 @@ +From: Herbert Xu +Date: Mon, 10 Apr 2017 17:27:57 +0800 +Subject: crypto: ahash - Fix EINPROGRESS notification callback +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git/commit?id=ef0579b64e93188710d48667cb5e014926af9f1b +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2017-7618 + +The ahash API modifies the request's callback function in order +to clean up after itself in some corner cases (unaligned final +and missing finup). + +When the request is complete ahash will restore the original +callback and everything is fine. However, when the request gets +an EBUSY on a full queue, an EINPROGRESS callback is made while +the request is still ongoing. + +In this case the ahash API will incorrectly call its own callback. + +This patch fixes the problem by creating a temporary request +object on the stack which is used to relay EINPROGRESS back to +the original completion function. + +This patch also adds code to preserve the original flags value. + +Fixes: ab6bf4e5e5e4 ("crypto: hash - Fix the pointer voodoo in...") +Cc: +Reported-by: Sabrina Dubroca +Tested-by: Sabrina Dubroca +Signed-off-by: Herbert Xu +--- + crypto/ahash.c | 79 ++++++++++++++++++++++++++---------------- + include/crypto/internal/hash.h | 10 ++++++ + 2 files changed, 60 insertions(+), 29 deletions(-) + +--- a/crypto/ahash.c ++++ b/crypto/ahash.c +@@ -31,6 +31,7 @@ struct ahash_request_priv { + crypto_completion_t complete; + void *data; + u8 *result; ++ u32 flags; + void *ubuf[] CRYPTO_MINALIGN_ATTR; + }; + +@@ -252,6 +253,8 @@ static int ahash_save_req(struct ahash_r + priv->result = req->result; + priv->complete = req->base.complete; + priv->data = req->base.data; ++ priv->flags = req->base.flags; ++ + /* + * WARNING: We do not backup req->priv here! The req->priv + * is for internal use of the Crypto API and the +@@ -266,38 +269,44 @@ static int ahash_save_req(struct ahash_r + return 0; + } + +-static void ahash_restore_req(struct ahash_request *req) ++static void ahash_restore_req(struct ahash_request *req, int err) + { + struct ahash_request_priv *priv = req->priv; + ++ if (!err) ++ memcpy(priv->result, req->result, ++ crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); ++ + /* Restore the original crypto request. */ + req->result = priv->result; +- req->base.complete = priv->complete; +- req->base.data = priv->data; ++ ++ ahash_request_set_callback(req, priv->flags, ++ priv->complete, priv->data); + req->priv = NULL; + + /* Free the req->priv.priv from the ADJUSTED request. */ + kzfree(priv); + } + +-static void ahash_op_unaligned_finish(struct ahash_request *req, int err) ++static void ahash_notify_einprogress(struct ahash_request *req) + { + struct ahash_request_priv *priv = req->priv; ++ struct crypto_async_request oreq; + +- if (err == -EINPROGRESS) +- return; +- +- if (!err) +- memcpy(priv->result, req->result, +- crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); ++ oreq.data = priv->data; + +- ahash_restore_req(req); ++ priv->complete(&oreq, -EINPROGRESS); + } + + static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) + { + struct ahash_request *areq = req->data; + ++ if (err == -EINPROGRESS) { ++ ahash_notify_einprogress(areq); ++ return; ++ } ++ + /* + * Restore the original request, see ahash_op_unaligned() for what + * goes where. +@@ -308,7 +317,7 @@ static void ahash_op_unaligned_done(stru + */ + + /* First copy req->result into req->priv.result */ +- ahash_op_unaligned_finish(areq, err); ++ ahash_restore_req(areq, err); + + /* Complete the ORIGINAL request. */ + areq->base.complete(&areq->base, err); +@@ -324,7 +333,12 @@ static int ahash_op_unaligned(struct aha + return err; + + err = op(req); +- ahash_op_unaligned_finish(req, err); ++ if (err == -EINPROGRESS || ++ (err == -EBUSY && (ahash_request_flags(req) & ++ CRYPTO_TFM_REQ_MAY_BACKLOG))) ++ return err; ++ ++ ahash_restore_req(req, err); + + return err; + } +@@ -359,25 +373,14 @@ int crypto_ahash_digest(struct ahash_req + } + EXPORT_SYMBOL_GPL(crypto_ahash_digest); + +-static void ahash_def_finup_finish2(struct ahash_request *req, int err) ++static void ahash_def_finup_done2(struct crypto_async_request *req, int err) + { +- struct ahash_request_priv *priv = req->priv; ++ struct ahash_request *areq = req->data; + + if (err == -EINPROGRESS) + return; + +- if (!err) +- memcpy(priv->result, req->result, +- crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); +- +- ahash_restore_req(req); +-} +- +-static void ahash_def_finup_done2(struct crypto_async_request *req, int err) +-{ +- struct ahash_request *areq = req->data; +- +- ahash_def_finup_finish2(areq, err); ++ ahash_restore_req(areq, err); + + areq->base.complete(&areq->base, err); + } +@@ -388,11 +391,15 @@ static int ahash_def_finup_finish1(struc + goto out; + + req->base.complete = ahash_def_finup_done2; +- req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ + err = crypto_ahash_reqtfm(req)->final(req); ++ if (err == -EINPROGRESS || ++ (err == -EBUSY && (ahash_request_flags(req) & ++ CRYPTO_TFM_REQ_MAY_BACKLOG))) ++ return err; + + out: +- ahash_def_finup_finish2(req, err); ++ ahash_restore_req(req, err); + return err; + } + +@@ -400,7 +407,16 @@ static void ahash_def_finup_done1(struct + { + struct ahash_request *areq = req->data; + ++ if (err == -EINPROGRESS) { ++ ahash_notify_einprogress(areq); ++ return; ++ } ++ ++ areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; ++ + err = ahash_def_finup_finish1(areq, err); ++ if (areq->priv) ++ return; + + areq->base.complete(&areq->base, err); + } +@@ -415,6 +431,11 @@ static int ahash_def_finup(struct ahash_ + return err; + + err = tfm->update(req); ++ if (err == -EINPROGRESS || ++ (err == -EBUSY && (ahash_request_flags(req) & ++ CRYPTO_TFM_REQ_MAY_BACKLOG))) ++ return err; ++ + return ahash_def_finup_finish1(req, err); + } + +--- a/include/crypto/internal/hash.h ++++ b/include/crypto/internal/hash.h +@@ -166,6 +166,16 @@ static inline struct ahash_instance *aha + return crypto_alloc_instance2(name, alg, ahash_instance_headroom()); + } + ++static inline void ahash_request_complete(struct ahash_request *req, int err) ++{ ++ req->base.complete(&req->base, err); ++} ++ ++static inline u32 ahash_request_flags(struct ahash_request *req) ++{ ++ return req->base.flags; ++} ++ + static inline struct crypto_ahash *crypto_spawn_ahash( + struct crypto_ahash_spawn *spawn) + { diff --git a/debian/patches/series b/debian/patches/series index 99a6cc7d4..bcee17e63 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -126,6 +126,7 @@ bugfix/all/net-packet-fix-overflow-in-check-for-priv-area-size.patch bugfix/all/net-packet-fix-overflow-in-check-for-tp_frame_nr.patch bugfix/all/net-packet-fix-overflow-in-check-for-tp_reserve.patch bugfix/all/ping-implement-proper-locking.patch +bugfix/all/crypto-ahash-fix-einprogress-notification-callback.patch # Fix exported symbol versions bugfix/ia64/revert-ia64-move-exports-to-definitions.patch