crypto: s390/phmac - Do not modify the req->nbytes value
Subsystems (3)
Harald Freudenberger
maintainerPATCH 1/1 crypto: s390/phmac - Do not modify the req->nbytes value
There was a failure reported by the phmac only in combination with dm-crypt where the phmac is used as the integrity check mechanism. A pseudo phmac which was implemented just as an asynchronous wrapper around the synchronous hmac algorithm did not show this failure. After some debugging the reason is clear:
The crypto aead layer obvious uses the req->nbytes value after the verification algorithm is through and finished with the request. If the req->nbytes value has been modified the aead layer will react with -EBADMSG to the caller (dm-crypt).
Unfortunately the phmac implementation used the req->nbytes field on combined operations (finup, digest) to track the
state: with req->nbytes > 0 the update needs to be processed, while req->nbytes == 0 means to do the final operation. For this purpose the req->nbytes field was set to 0 after successful update operation. This worked fine and all tests succeeded but only failed with aead use as dm-crypt with verify uses it.
Fixed by a slight rework on the phmac implementation. There is now a new field async_op in the request context which tracks the (asynch) operation to process. So the 'state' via req->nbytes is not needed any more and now this field is untouched and may be evaluated even after a request is processed by the phmac implementation.
--- arch/s390/crypto/phmac_s390.c | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-)
arch/s390/crypto/phmac_s390.c +34 -18
@@ -169,11 +169,18 @@ struct kmac_sha2_ctx {u64 buflen[2 ]; }; +enum async_op { +OP_NOP =0 , +OP_UPDATE , +OP_FINAL , +OP_FINUP , +}; +/* phmac request context */ struct phmac_req_ctx {struct hash_walk_helper hwh;struct kmac_sha2_ctx kmac_ctx; -bool final; +int async_op; };/*
@@ -610,6 +617,7 @@ static int phmac_update(struct ahash_request *req)* using engine to serialize requests. */ if (rc ==0 || rc == -EKEYEXPIRED ) { + req_ctx->async_op =OP_UPDATE ;atomic_inc (&tfm_ctx->via_engine_ctr); rc =crypto_transfer_hash_request_to_engine (phmac_crypto_engine, req);if (rc != -EINPROGRESS )
@@ -647,8 +655,7 @@ static int phmac_final(struct ahash_request *req)* using engine to serialize requests. */ if (rc ==0 || rc == -EKEYEXPIRED ) { - req->nbytes =0 ; - req_ctx->final = true; + req_ctx->async_op =OP_FINAL ;atomic_inc (&tfm_ctx->via_engine_ctr); rc =crypto_transfer_hash_request_to_engine (phmac_crypto_engine, req);if (rc != -EINPROGRESS )
@@ -676,13 +683,16 @@ static int phmac_finup(struct ahash_request *req)if (rc) goto out; + req_ctx->async_op =OP_FINUP ; +/* Try synchronous operations if no active engine usage */ if (!atomic_read (&tfm_ctx->via_engine_ctr)) { rc =phmac_kmac_update (req, false);if (rc ==0 ) - req->nbytes =0 ; + req_ctx->async_op =OP_FINAL ; } -if (!rc && !req->nbytes && !atomic_read (&tfm_ctx->via_engine_ctr)) { +if (!rc && req_ctx->async_op ==OP_FINAL && + !atomic_read (&tfm_ctx->via_engine_ctr)) { rc =phmac_kmac_final (req, false);if (rc ==0 ) goto out;
@@ -694,7 +704,7 @@ static int phmac_finup(struct ahash_request *req)* using engine to serialize requests. */ if (rc ==0 || rc == -EKEYEXPIRED ) { - req_ctx->final = true; +/* req->async_op has been set to either OP_FINUP or OP_FINAL */ atomic_inc (&tfm_ctx->via_engine_ctr); rc =crypto_transfer_hash_request_to_engine (phmac_crypto_engine, req);if (rc != -EINPROGRESS )
@@ -855,15 +865,16 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)/* * Three kinds of requests come in here: -* update when req->nbytes > 0 and req_ctx->final is false -* final when req->nbytes = 0 and req_ctx->final is true -* finup when req->nbytes > 0 and req_ctx->final is true -* For update and finup the hwh walk needs to be prepared and -* up to date but the actual nr of bytes in req->nbytes may be -* any non zero number. For final there is no hwh walk needed. +* 1. req->async_op == OP_UPDATE with req->nbytes > 0 +* 2. req->async_op == OP_FINUP with req->nbytes > 0 +* 3. req->async_op == OP_FINAL +* For update and finup the hwh walk has already been prepared +* by the caller. For final there is no hwh walk needed. */ -if (req->nbytes) { +switch (req_ctx->async_op) { +case OP_UPDATE : +case OP_FINUP : rc =phmac_kmac_update (req, true);if (rc == -EKEYEXPIRED ) {/*
@@ -880,10 +891,11 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)hwh_advance (hwh, rc); goto out; } - req->nbytes =0 ; - } - -if (req_ctx->final) { +if (req_ctx->async_op ==OP_UPDATE ) +break ; + req_ctx->async_op =OP_FINAL ; + fallthrough; +case OP_FINAL : rc =phmac_kmac_final (req, true);if (rc == -EKEYEXPIRED ) {/*
@@ -897,10 +909,14 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)cond_resched ();return -ENOSPC ; } +break ; +default : +/* unknown/unsupported/unimplemented asynch op */ +return -EOPNOTSUPP ; } out: -if (rc || req_ctx->final) +if (rc || req_ctx->async_op ==OP_FINAL )memzero_explicit (kmac_ctx,sizeof (*kmac_ctx));pr_debug ("request complete with rc=%d\n" , rc);local_bh_disable ();
Signature · 1 line
2.43.0
Quoted 25 lines · Harald Freudenberger
There was a failure reported by the phmac only in combination with dm-crypt where the phmac is used as the integrity check mechanism. A pseudo phmac which was implemented just as an asynchronous wrapper around the synchronous hmac algorithm did not show this failure. After some debugging the reason is clear:
The crypto aead layer obvious uses thereq->nbytesvalue after the verification algorithm is through and finished with the request. If thereq->nbytesvalue has been modified the aead layer will react with-EBADMSGto the caller (dm-crypt).Unfortunately the phmac implementation used the
req->nbytesfield on combined operations (finup, digest) to track the
state: withreq->nbytes> 0 the update needs to be processed, whilereq->nbytes== 0 means to do the final operation. For this purpose thereq->nbytesfield was set to 0 after successful update operation. This worked fine and all tests succeeded but only failed with aead use as dm-crypt with verify uses it.Fixed by a slight rework on the phmac implementation. There is now a new field
async_opin the request context which tracks the (asynch) operation to process. So the 'state' viareq->nbytesis not needed any more and now this field is untouched and may be evaluated even after a request is processed by the phmac implementation.
Quoted 6 lines
Fixes cbbc675506cc ("crypto: s390 - New s390 specific protected key hash phmac")Reported-by Ingo Franzki <[email protected]>Signed-off-by Harald Freudenberger <[email protected]>--- arch/s390/crypto/phmac_s390.c | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-)
I can confirm that this fixes the failures reported at https://lore.kernel.org/dm-devel/[email protected]/T/#u
Also, having the operation in the request context makes the code even more clear IMHO.
Signature · 9 lines
Ingo Franzki eMail: [email protected] Linux on IBM Z Development, Schoenaicher Str. 220, 71032 Boeblingen, Germany IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Wolfgang Wendt Geschäftsführung: David Faller Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 243294 IBM DATA Privacy Statement: https://www.ibm.com/privacy/us/en/
Quoted 10 lines · Ingo Franzki
Quoted 9 lines · Harald Freudenberger
There was a failure reported by the phmac only in combination with dm-crypt where the phmac is used as the integrity check mechanism. A pseudo phmac which was implemented just as an asynchronous wrapper around the synchronous hmac algorithm did not show this failure. After some debugging the reason is clear:
The crypto aead layer obvious uses thereq->nbytesvalue after the verification algorithm is through and finished with the request. If thereq->nbytesvalue has been modified the aead layer will react with-EBADMSGto the caller (dm-crypt).
I guess the place where nbytes is used after the verification is in crypto_authenc_decrypt_tail(). This is called either directly by crypto_authenc_decrypt() if crypto_ahash_digest() completed synchronously, or in case or async completion, via the authenc_verify_ahash_done() callback.
crypto_authenc_decrypt_tail() then does:
scatterwalk_map_and_copy(ihash, req->src, ahreq->nbytes, authsize, 0); if (crypto_memneq(ihash, ahreq->result, authsize)) return -EBADMSG;
Where the scatterwalk_map_and_copy() uses the ahreq->nbytes value to know the amount of bytes to copy. If this is zero then the copied ihash value is wrong and thus it returns -EBADMSG.
Not sure if the number of bytes could be saved elsewhere, to allow that the requests's nbytes can be modified by the digest (if this is desired to be allowed)? Otherwise, a comment in the header file that nbytes must not be modified might be useful.
Quoted 30 lines
Quoted 16 lines
Unfortunately the phmac implementation used the
req->nbytesfield on combined operations (finup, digest) to track the
state: withreq->nbytes> 0 the update needs to be processed, whilereq->nbytes== 0 means to do the final operation. For this purpose thereq->nbytesfield was set to 0 after successful update operation. This worked fine and all tests succeeded but only failed with aead use as dm-crypt with verify uses it.Fixed by a slight rework on the phmac implementation. There is now a new field
async_opin the request context which tracks the (asynch) operation to process. So the 'state' viareq->nbytesis not needed any more and now this field is untouched and may be evaluated even after a request is processed by the phmac implementation.Quoted 6 lines
Fixes cbbc675506cc ("crypto: s390 - New s390 specific protected key hash phmac")Reported-by Ingo Franzki <[email protected]>Signed-off-by Harald Freudenberger <[email protected]>--- arch/s390/crypto/phmac_s390.c | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-)I can confirm that this fixes the failures reported at https://lore.kernel.org/dm-devel/[email protected]/T/#u
Also, having the operation in the request context makes the code even more clear IMHO.Tested-by Ingo Franzki <[email protected]>Reviewed-by Ingo Franzki <[email protected]>
Signature · 9 lines
Ingo Franzki eMail: [email protected] Linux on IBM Z Development, Schoenaicher Str. 220, 71032 Boeblingen, Germany IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Wolfgang Wendt Geschäftsführung: David Faller Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 243294 IBM DATA Privacy Statement: https://www.ibm.com/privacy/us/en/
Holger Dengler
maintainerQuoted 5 lines · Harald Freudenberger
There was a failure reported by the phmac only in combination with dm-crypt where the phmac is used as the integrity check mechanism. A pseudo phmac which was implemented just as an asynchronous wrapper around the synchronous hmac algorithm did not show this failure. After some debugging the reason is clear:
In my opinion, the information up to here should not be part of the commit message. If you want to keep it, I would suggest to move it to the cover letter.
Quoted 20 lines
The crypto aead layer obvious uses the
req->nbytesvalue after the verification algorithm is through and finished with the request. If thereq->nbytesvalue has been modified the aead layer will react with-EBADMSGto the caller (dm-crypt).Unfortunately the phmac implementation used the
req->nbytesfield on combined operations (finup, digest) to track the
state: withreq->nbytes> 0 the update needs to be processed, whilereq->nbytes== 0 means to do the final operation. For this purpose thereq->nbytesfield was set to 0 after successful update operation. This worked fine and all tests succeeded but only failed with aead use as dm-crypt with verify uses it.Fixed by a slight rework on the phmac implementation. There is now a new field
async_opin the request context which tracks the (asynch) operation to process. So the 'state' viareq->nbytesis not needed any more and now this field is untouched and may be evaluated even after a request is processed by the phmac implementation.
Quoted 3 lines
Fixes cbbc675506cc ("crypto: s390 - New s390 specific protected key hash phmac")Reported-by Ingo Franzki <[email protected]>Signed-off-by Harald Freudenberger <[email protected]>
See my comments below.
Quoted 9 lines
--- arch/s390/crypto/phmac_s390.c | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-)arch/s390/crypto/phmac_s390.c +0
-0
arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 173
Quoted 5 lines
arch/s390/crypto/phmac_s390.c +2
-0@@ -169,11 +169,18 @@ struct kmac_sha2_ctx {u64 buflen[2 ]; }; +enum async_op { +OP_NOP =0 ,
The async_op element in struct phmac_req_ctx is implicitly initialized with OP_NOP. Only the functions update, final and finup will set another (valid) operation. Can it ever happen, that do_one_request() is called *before* any of update, final or finup is called? If this is a valid case, the OP_NOP should be handled correctly in do_one_request(), otherwise we get a -ENOTSUPP (see my comment to phmac_do_one_request()).
If do_one_request() is never called before update/finup/final(), no change is required.
arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 183
Quoted 11 lines
arch/s390/crypto/phmac_s390.c +6
-1+OP_UPDATE , +OP_FINAL , + OP_FINUP, +}; +/* phmac request context */ struct phmac_req_ctx {struct hash_walk_helper hwh;struct kmac_sha2_ctx kmac_ctx; -bool final; +int async_op;
I know, that the compiler is happy with an int. But I would prefer to use the enum for the element.
enum async_op async_op;
arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 186
Quoted 3 lines
arch/s390/crypto/phmac_s390.c +0
-0};/*
[...]> @@ -855,15 +865,16 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)
Quoted 40 lines
arch/s390/crypto/phmac_s390.c +13
-11/* * Three kinds of requests come in here: -* update when req->nbytes > 0 and req_ctx->final is false -* final when req->nbytes = 0 and req_ctx->final is true -* finup when req->nbytes > 0 and req_ctx->final is true -* For update and finup the hwh walk needs to be prepared and -* up to date but the actual nr of bytes in req->nbytes may be -* any non zero number. For final there is no hwh walk needed. +* 1. req->async_op == OP_UPDATE with req->nbytes > 0 +* 2. req->async_op == OP_FINUP with req->nbytes > 0 +* 3. req->async_op == OP_FINAL +* For update and finup the hwh walk has already been prepared +* by the caller. For final there is no hwh walk needed. */ -if (req->nbytes) { +switch (req_ctx->async_op) { +case OP_UPDATE : +case OP_FINUP : rc =phmac_kmac_update (req, true);if (rc == -EKEYEXPIRED ) {/* @@ -880,10 +891,11 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)hwh_advance (hwh, rc); goto out; } - req->nbytes =0 ; - } - -if (req_ctx->final) { +if (req_ctx->async_op ==OP_UPDATE ) +break ; + req_ctx->async_op =OP_FINAL ; + fallthrough; +case OP_FINAL : rc =phmac_kmac_final (req, true);if (rc == -EKEYEXPIRED ) {/*
arch/s390/crypto/phmac_s390.c static int phmac_do_one_request(struct crypto_engine *engine, void *areq) · line 915
Quoted 7 lines
arch/s390/crypto/phmac_s390.c +4
-0@@ -897,10 +909,14 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)cond_resched ();return -ENOSPC ; } +break ; +default : +/* unknown/unsupported/unimplemented asynch op */ +return -EOPNOTSUPP ;
If it is a valid case, that do_one_request() is called before update(), final() or finup() is called, we should handle OP_NOP here and not return with an error. If do_one_request() is never called before update/finup/final(), no change is required.
[...]
Signature · 5 lines
Mit freundlichen Grüßen / Kind regards Holger Dengler -- IBM Systems, Linux on IBM Z Development [email protected]
Harald Freudenberger
maintainerQuoted 11 lines · Holger Dengler
Quoted 5 lines · Harald Freudenberger
There was a failure reported by the phmac only in combination with dm-crypt where the phmac is used as the integrity check mechanism. A pseudo phmac which was implemented just as an asynchronous wrapper around the synchronous hmac algorithm did not show this failure. After some debugging the reason is clear:
In my opinion, the information up to here should not be part of the commit message. If you want to keep it, I would suggest to move it to the cover letter.
Ok, will remove this.
Quoted 50 lines
Quoted 20 lines
The crypto aead layer obvious uses the
req->nbytesvalue after the verification algorithm is through and finished with the request. If thereq->nbytesvalue has been modified the aead layer will react with-EBADMSGto the caller (dm-crypt).Unfortunately the phmac implementation used the
req->nbytesfield on combined operations (finup, digest) to track the
state: withreq->nbytes> 0 the update needs to be processed, whilereq->nbytes== 0 means to do the final operation. For this purpose thereq->nbytesfield was set to 0 after successful update operation. This worked fine and all tests succeeded but only failed with aead use as dm-crypt with verify uses it.Fixed by a slight rework on the phmac implementation. There is now a new field
async_opin the request context which tracks the (asynch) operation to process. So the 'state' viareq->nbytesis not needed any more and now this field is untouched and may be evaluated even after a request is processed by the phmac implementation.Quoted 3 lines
Fixes cbbc675506cc ("crypto: s390 - New s390 specific protected key hash phmac")Reported-by Ingo Franzki <[email protected]>Signed-off-by Harald Freudenberger <[email protected]>See my comments below.
Quoted 10 lines
--- arch/s390/crypto/phmac_s390.c | 52 +++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 18 deletions(-)arch/s390/crypto/phmac_s390.c +0
-0arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 173
Quoted 5 lines
arch/s390/crypto/phmac_s390.c +2
-0@@ -169,11 +169,18 @@ struct kmac_sha2_ctx {u64 buflen[2 ]; }; +enum async_op { +OP_NOP =0 ,The
async_opelement in structphmac_req_ctxis implicitly initialized withOP_NOP. Only the functions update, final and finup will set another (valid) operation. Can it ever happen, thatdo_one_request()is called *before* any of update, final or finup is called? If this is a valid case, theOP_NOPshould be handled correctly indo_one_request(), otherwise we get a-ENOTSUPP(see my comment tophmac_do_one_request()).
Quoted 3 lines
If
do_one_request()is never called before update/finup/final(), no change is required.
Well, do_one_request() is only called via a postponed request pushed from one of the "front" functions (init/update/final/finup/digest) to the engine instance. So a request is always first seen by these functions and these have a chance to update the async_op field.
Quoted 15 lines
arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 183
Quoted 11 lines
arch/s390/crypto/phmac_s390.c +6
-1+OP_UPDATE , +OP_FINAL , + OP_FINUP, +}; +/* phmac request context */ struct phmac_req_ctx {struct hash_walk_helper hwh;struct kmac_sha2_ctx kmac_ctx; -bool final; +int async_op;I know, that the compiler is happy with an int. But I would prefer to use the enum for the element.
Quoted 2 lines
enum
async_opasync_op;
Catched - my first experiences with C where at a time where enums where not supported. So I am still not familiar with this kind of stuff :-)
Quoted 52 lines
arch/s390/crypto/phmac_s390.c struct kmac_sha2_ctx { · line 186
Quoted 3 lines
arch/s390/crypto/phmac_s390.c +0
-0};/* [...]> @@ -855,15 +865,16 @@ static int
phmac_do_one_request(struct crypto_engine *engine, void *areq)Quoted 47 lines
arch/s390/crypto/phmac_s390.c +13
-11/* * Three kinds of requests come in here: -* update when req->nbytes > 0 and req_ctx->final is false -* final when req->nbytes = 0 and req_ctx->final is true -* finup when req->nbytes > 0 and req_ctx->final is true -* For update and finup the hwh walk needs to be prepared and -* up to date but the actual nr of bytes in req->nbytes may be -* any non zero number. For final there is no hwh walk needed. +* 1. req->async_op == OP_UPDATE with req->nbytes > 0 +* 2. req->async_op == OP_FINUP with req->nbytes > 0 +* 3. req->async_op == OP_FINAL +* For update and finup the hwh walk has already been prepared +* by the caller. For final there is no hwh walk needed. */ -if (req->nbytes) { +switch (req_ctx->async_op) { +case OP_UPDATE : +case OP_FINUP : rc =phmac_kmac_update (req, true);if (rc == -EKEYEXPIRED ) {/* @@ -880,10 +891,11 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)hwh_advance (hwh, rc); goto out; } - req->nbytes =0 ; - } - -if (req_ctx->final) { +if (req_ctx->async_op ==OP_UPDATE ) +break ; + req_ctx->async_op =OP_FINAL ; + fallthrough; +case OP_FINAL : rc =phmac_kmac_final (req, true);if (rc == -EKEYEXPIRED ) {arch/s390/crypto/phmac_s390.c +2
-0/* @@ -897,10 +909,14 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)cond_resched ();return -ENOSPC ; } +break ; +default :
Quoted 9 lines
arch/s390/crypto/phmac_s390.c static int phmac_do_one_request(struct crypto_engine *engine, void *areq) · line 915
Quoted 2 lines
arch/s390/crypto/phmac_s390.c +2
-0+/* unknown/unsupported/unimplemented asynch op */ +return -EOPNOTSUPP ;If it is a valid case, that
do_one_request()is called beforeupdate(),final()orfinup()is called, we should handleOP_NOPhere and not return with an error.
Ifdo_one_request()is never called before update/finup/final(), no change is required.
As wrote above the "front" functions always see a request first before it is postponed to the engine and appears here. So the OP_NOP case must not happen here and thus is only covered with the default arm.
Quoted 1 line
[...]
Holger Dengler
maintainerQuoted 2 lines · Harald Freudenberger
Quoted 1 line · Holger Dengler
On 09/10/2025 18:01, Harald Freudenberger wrote:
[...]>>> +enum async_op {
Quoted 10 lines
Quoted 10 lines
Quoted 1 line
+
OP_NOP= 0,The
async_opelement in structphmac_req_ctxis implicitly initialized withOP_NOP. Only the functions update, final and finup will set another (valid) operation. Can it ever happen, thatdo_one_request()is called *before* any of update, final or finup is called? If this is a valid case, theOP_NOPshould be handled correctly indo_one_request(), otherwise we get a-ENOTSUPP(see my comment tophmac_do_one_request()).
Quoted 8 lines
Quoted 3 lines
If
do_one_request()is never called before update/finup/final(), no change is required.Well,
do_one_request()is only called via a postponed request pushed from one of the "front" functions (init/update/final/finup/digest) to the engine instance. So a request is always first seen by these functions and these have a chance to update theasync_opfield.
Ok, then you can leave it that way.
[...]
Signature · 5 lines
Mit freundlichen Grüßen / Kind regards Holger Dengler -- IBM Systems, Linux on IBM Z Development [email protected]