Skip to content

Commit 6cb1c8f

Browse files
jianglizhoutholenstlukaszobernig
committed
8371864: GaloisCounterMode.implGCMCrypt0 AVX512/AVX2 intrinsics stubs cause AES-GCM encryption failure for certain payload sizes
Co-authored-by: Thomas Holenstein <[email protected]> Co-authored-by: Lukas Zobernig <[email protected]> Reviewed-by: shade, sviswanathan
1 parent 002fff3 commit 6cb1c8f

File tree

2 files changed

+150
-2
lines changed

2 files changed

+150
-2
lines changed

src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3524,10 +3524,10 @@ void StubGenerator::aesgcm_avx512(Register in, Register len, Register ct, Regist
35243524
false, true, false, false, false, ghashin_offset, aesout_offset, HashKey_32);
35253525

35263526
ghash16_avx512(false, true, false, false, true, in, pos, avx512_subkeyHtbl, AAD_HASHx, SHUF_MASK, stack_offset, 16 * 16, 0, HashKey_16);
3527+
__ addl(pos, 16 * 16);
35273528

35283529
__ bind(MESG_BELOW_32_BLKS);
35293530
__ subl(len, 16 * 16);
3530-
__ addl(pos, 16 * 16);
35313531
gcm_enc_dec_last_avx512(len, in, pos, AAD_HASHx, SHUF_MASK, avx512_subkeyHtbl, ghashin_offset, HashKey_16, true, true);
35323532

35333533
__ bind(GHASH_DONE);
@@ -4016,13 +4016,15 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register
40164016
const Register rounds = r10;
40174017
const XMMRegister ctr_blockx = xmm9;
40184018
const XMMRegister aad_hashx = xmm8;
4019-
Label encrypt_done, encrypt_by_8_new, encrypt_by_8;
4019+
Label encrypt_done, encrypt_by_8_new, encrypt_by_8, exit;
40204020

40214021
//This routine should be called only for message sizes of 128 bytes or more.
40224022
//Macro flow:
40234023
//process 8 16 byte blocks in initial_num_blocks.
40244024
//process 8 16 byte blocks at a time until all are done 'encrypt_by_8_new followed by ghash_last_8'
40254025
__ xorl(pos, pos);
4026+
__ cmpl(len, 128);
4027+
__ jcc(Assembler::less, exit);
40264028

40274029
//Generate 8 constants for htbl
40284030
generateHtbl_8_block_avx2(subkeyHtbl);
@@ -4090,6 +4092,7 @@ void StubGenerator::aesgcm_avx2(Register in, Register len, Register ct, Register
40904092
__ vpxor(xmm0, xmm0, xmm0, Assembler::AVX_128bit);
40914093
__ vpxor(xmm13, xmm13, xmm13, Assembler::AVX_128bit);
40924094

4095+
__ bind(exit);
40934096
}
40944097

40954098
#undef __
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright (c) 2025, Google LLC. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8371864
27+
* @run main/othervm/timeout=600 TestGCMSplitBound
28+
* @requires (os.simpleArch == "x64" & (vm.cpu.features ~= ".*avx2.*" |
29+
* vm.cpu.features ~= ".*avx512.*"))
30+
* @summary Test GaloisCounterMode.implGCMCrypt0 AVX512/AVX2 intrinsics.
31+
*/
32+
33+
import java.security.SecureRandom;
34+
import java.security.spec.AlgorithmParameterSpec;
35+
import java.time.Duration;
36+
import java.util.Arrays;
37+
import javax.crypto.Cipher;
38+
import javax.crypto.SecretKey;
39+
import javax.crypto.spec.GCMParameterSpec;
40+
import javax.crypto.spec.SecretKeySpec;
41+
42+
public class TestGCMSplitBound {
43+
44+
static final SecureRandom SECURE_RANDOM = newDefaultSecureRandom();
45+
46+
private static SecureRandom newDefaultSecureRandom() {
47+
SecureRandom retval = new SecureRandom();
48+
retval.nextLong(); // force seeding
49+
return retval;
50+
}
51+
52+
private static byte[] randBytes(int size) {
53+
byte[] rand = new byte[size];
54+
SECURE_RANDOM.nextBytes(rand);
55+
return rand;
56+
}
57+
58+
private static final int IV_SIZE_IN_BYTES = 12;
59+
private static final int TAG_SIZE_IN_BYTES = 16;
60+
61+
private Cipher getCipher(final byte[] key, final byte[] aad,
62+
final byte[] nonce, int mode)
63+
throws Exception {
64+
SecretKey keySpec = new SecretKeySpec(key, "AES");
65+
AlgorithmParameterSpec params =
66+
new GCMParameterSpec(8 * TAG_SIZE_IN_BYTES, nonce, 0, nonce.length);
67+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
68+
cipher.init(mode, keySpec, params);
69+
if (aad != null && aad.length != 0) {
70+
cipher.updateAAD(aad);
71+
}
72+
return cipher;
73+
}
74+
75+
private byte[] gcmEncrypt(final byte[] key, final byte[] plaintext,
76+
final byte[] aad)
77+
throws Exception {
78+
byte[] nonce = randBytes(IV_SIZE_IN_BYTES);
79+
Cipher cipher = getCipher(key, aad, nonce, Cipher.ENCRYPT_MODE);
80+
int outputSize = cipher.getOutputSize(plaintext.length);
81+
int len = IV_SIZE_IN_BYTES + outputSize;
82+
byte[] output = new byte[len];
83+
System.arraycopy(nonce, 0, output, 0, IV_SIZE_IN_BYTES);
84+
cipher.doFinal(plaintext, 0, plaintext.length, output,
85+
IV_SIZE_IN_BYTES);
86+
return output;
87+
}
88+
89+
private byte[] gcmDecrypt(final byte[] key, final byte[] ciphertext,
90+
final byte[] aad)
91+
throws Exception {
92+
byte[] nonce = new byte[IV_SIZE_IN_BYTES];
93+
System.arraycopy(ciphertext, 0, nonce, 0, IV_SIZE_IN_BYTES);
94+
Cipher cipher = getCipher(key, aad, nonce, Cipher.DECRYPT_MODE);
95+
return cipher.doFinal(ciphertext, IV_SIZE_IN_BYTES,
96+
ciphertext.length - IV_SIZE_IN_BYTES);
97+
}
98+
99+
// x86-64 parallel intrinsic data size
100+
private static final int PARALLEL_LEN = 512;
101+
// max data size for x86-64 intrinsic
102+
private static final int SPLIT_LEN = 1048576; // 1MB
103+
104+
private void encryptAndDecrypt(byte[] key, byte[] aad, byte[] message,
105+
int messageSize)
106+
throws Exception {
107+
byte[] ciphertext = gcmEncrypt(key, message, aad);
108+
byte[] decrypted = gcmDecrypt(key, ciphertext, aad);
109+
if (ciphertext == null) {
110+
throw new RuntimeException("ciphertext is null");
111+
}
112+
if (Arrays.compare(decrypted, 0, messageSize,
113+
message, 0, messageSize) != 0) {
114+
throw new RuntimeException(
115+
"Decrypted message is different from the original message");
116+
}
117+
}
118+
119+
private void run() throws Exception {
120+
byte[] aad = randBytes(20);
121+
byte[] key = randBytes(16);
122+
// Force JIT.
123+
for (int i = 0; i < 100000; i++) {
124+
byte[] message = randBytes(PARALLEL_LEN);
125+
encryptAndDecrypt(key, aad, message, PARALLEL_LEN);
126+
}
127+
for (int messageSize = SPLIT_LEN - 300; messageSize <= SPLIT_LEN + 300;
128+
messageSize++) {
129+
byte[] message = randBytes(messageSize);
130+
try {
131+
encryptAndDecrypt(key, aad, message, messageSize);
132+
} catch (Exception e) {
133+
throw new RuntimeException("Failed for messageSize "
134+
+ Integer.toHexString(messageSize), e);
135+
}
136+
}
137+
}
138+
139+
public static void main(String[] args) throws Exception {
140+
TestGCMSplitBound test = new TestGCMSplitBound();
141+
for (int i = 0; i < 3; i++) {
142+
test.run();
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)