-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8371864: GaloisCounterMode.implGCMCrypt0 AVX512/AVX2 intrinsics stubs cause AES-GCM encryption failure for certain payload sizes #28363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…tubs cause AES-GCM encryption failure for certain payload sizes
|
👋 Welcome back jiangli! A progress list of the required criteria for merging this PR into |
|
/contributor [email protected] |
|
❗ This change is not yet ready to be integrated. |
|
@jianglizhou Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
|
@jianglizhou Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
|
@jianglizhou The following labels will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command. |
|
/contributor Thomas Holenstein [email protected] |
|
@jianglizhou Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
|
@jianglizhou Syntax:
User names can only be used for users in the census associated with this repository. For other contributors you need to supply the full name and email address. |
|
/contributor add Thomas Holenstein [email protected] |
|
@jianglizhou |
|
@jianglizhou |
Webrevs
|
|
|
||
| public class TestAesGcmIntrinsic { | ||
|
|
||
| static final SecureRandom SECURE_RANDOM = newDefaultSecureRandom(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive-by comment: Java code should use 4x whitespace indentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TobiHartmann, thanks! Fixed.
shipilev
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Some stylistic comments for the product fix, and suggestions for the test.
|
|
||
| __ bind(MESG_BELOW_32_BLKS); | ||
| __ subl(len, 16 * 16); | ||
| __ cmpl(len, 256); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the stylistic logic, this should be written as 16 * 16, to match the surrounding subl and addl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed review, @shipilev! Fixed.
| public static void main(String[] args) throws Exception { | ||
| TestAesGcmIntrinsic test = new TestAesGcmIntrinsic(); | ||
| long startTime = System.currentTimeMillis(); | ||
| while (System.currentTimeMillis() - startTime < 60 * 1000) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get that you want a stress test. But time-limiting puts the test into weird condition: it can have different number of iterations, depending on auxiliary load on the machine. These tests are running in parallel with lots of other tests, so it is not uncommon. Do you even need to repeat jitFunc() call multiple times? Looks like it traverses the interesting configurations in one go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some testing today. For 200 runs, removing the time-limited loop, there is 89 runs out of 200 fail. So I changed to use an iteration of three runs, all 200 runs fail without the fix.
| for (int messageSize = SPLIT_LEN; messageSize < SPLIT_LEN + 300; messageSize++) { | ||
| byte[] message = randBytes(messageSize); | ||
| try { | ||
| byte[] ciphertext = gcmEncrypt(key, message, aad); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it makes sense to check that round-trip is successful, e.g. that decrypt(encrypt(message)) == message. Currently we implicitly rely on exceptions being thrown from the incorrectly executing code, which is IMO too weak -- in the boundary conditions like these, there might be bugs that do not manifest in visible exceptions, and just the encryption is subtly broken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea. I added decrypt part and the check as suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the changes, there were more common parts in the test. I moved common code into helper methods.
| import javax.crypto.spec.GCMParameterSpec; | ||
| import javax.crypto.spec.SecretKeySpec; | ||
|
|
||
| public class TestAesGcmIntrinsic { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds like TestGCMSplitBound or some such; it is not a generic test for AES/GCM intrinsic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed to TestAesGcmIntrinsic name, when converting the original test into the jtreg test. TestGCMSplitBound SGTM. Changed.
| throw new RuntimeException("ciphertext is null"); | ||
| } | ||
| } | ||
| for (int messageSize = SPLIT_LEN; messageSize < SPLIT_LEN + 300; messageSize++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[SPLIT_LEN - 300; SPLIT_LEN + 300] for completeness, perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
|
||
| public class TestAesGcmIntrinsic { | ||
|
|
||
| static final SecureRandom SECURE_RANDOM = newDefaultSecureRandom(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really need a SecureRandom here? Random RANDOM = Utils.getRandomInstance(); gets you the pre-seeded random instance, which can be used to repeatably reproduce failures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept the SecureRandom without changing. I think that could be more related to what the original reproducible.
- Rename test to TestGCMSplitBound.java - Change test range to [SPLIT_LEN - 300; SPLIT_LEN + 300].
- Replace time-bound loop with an iteration of three runs. - Add encrypt part and check to make sure the encrypted message is the same as the original.
| byte[] nonce = randBytes(IV_SIZE_IN_BYTES); | ||
| System.arraycopy(ciphertext, 0, nonce, 0, IV_SIZE_IN_BYTES); | ||
| Cipher cipher = getCipher(key, aad, nonce); | ||
| return cipher.doFinal(ciphertext, IV_SIZE_IN_BYTES, ciphertext.length - IV_SIZE_IN_BYTES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indenting is still 2-space here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Fixed, thanks.
test/jdk/com/sun/crypto/provider/Cipher/AES/TestGCMSplitBound.java
Outdated
Show resolved
Hide resolved
| AlgorithmParameterSpec params = | ||
| new GCMParameterSpec(8 * TAG_SIZE_IN_BYTES, nonce, 0, nonce.length); | ||
| Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); | ||
| cipher.init(Cipher.ENCRYPT_MODE, keySpec, params); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er. This is used from gcmDecrypt? How does it work without Cipher.DECRYPT_MODE?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Interestingly the test passed for me on my local machine. Fixed to use Cipher.DECRYPT_MODE when doing gcmDecrypt.
Also an interesting new finding, with the decrypted message verification, I see there are 2 failures out of 200 runs with AVX512. I'm filing a new issue on the specifically, so it can be investigated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…java Applied, thanks. Co-authored-by: Aleksey Shipilëv <[email protected]>
|
|
…dom data in gcmDecrypt. Suggested by AI.
|
|
||
| __ bind(MESG_BELOW_32_BLKS); | ||
| __ subl(len, 16 * 16); | ||
| __ cmpl(len, 16 * 16); | ||
| __ jcc(Assembler::lessEqual, ENC_DEC_DONE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the fix should instead be to just move the addl to pos before the MESG_BELOW_32_BLKS, as below:
+ __ addl(pos, 16 * 16);
__ bind(MESG_BELOW_32_BLKS);
__ subl(len, 16 * 16);
- __ addl(pos, 16 * 16);
This is because on fall through path addl is needed but not while coming from line 3479 via jcc. For the latter, the addl has already been done on line 3477.
Please review the fix in StubGenerator::aesgcm_avx512 and StubGenerator::aesgcm_avx2 to handle some edge cases with input sizes that are not multiple of the block size.
Thanks to Thomas Holenstein and Lukas Zobernig for analyzing the issue and providing the test case!
Progress
Issue
Contributors
<[email protected]><[email protected]>Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/28363/head:pull/28363$ git checkout pull/28363Update a local copy of the PR:
$ git checkout pull/28363$ git pull https://git.openjdk.org/jdk.git pull/28363/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 28363View PR using the GUI difftool:
$ git pr show -t 28363Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/28363.diff
Using Webrev
Link to Webrev Comment