Skip to content

Commit 2348699

Browse files
committed
JSSE: extract peek logic to internal function. Adds flag to allow
skipping TLS record peek if continuing with the same TLS record. Update bufferUnderflow guards.
1 parent 84ebbf7 commit 2348699

1 file changed

Lines changed: 73 additions & 33 deletions

File tree

src/java/com/wolfssl/provider/jsse/WolfSSLEngine.java

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ public class WolfSSLEngine extends SSLEngine {
110110

111111
/* session stored (WOLFSSL_SESSION), relevant on client side */
112112
private boolean sessionStored = false;
113+
private boolean contPartialRecord = false;
113114

114115
/* TLS 1.3 session ticket received (on client side) */
115116
private boolean sessionTicketReceived = false;
@@ -1289,6 +1290,47 @@ private byte[] getRecvAppDataBuf(int minSz) {
12891290
return this.recvAppDataBuf;
12901291
}
12911292

1293+
/**
1294+
* TLS-only: peek at the record header and
1295+
* return BUFFER_UNDERFLOW before calling into
1296+
* JNI when the full record is not yet present.
1297+
* Without this, native wolfSSL consumes partial
1298+
* record bytes via the I/O callback, violating
1299+
* the JSSE contract that BUFFER_UNDERFLOW must
1300+
* report bytesConsumed() == 0. DTLS still
1301+
* relies on the native WANT_READ path.
1302+
*
1303+
* @param in input buffer
1304+
* @param inRemaining bytes remaining in input buffer.
1305+
* @param handshaking whether the function is being called during
1306+
* handshake.
1307+
*
1308+
* @return true if buffer underflow detected, false otherwise
1309+
*/
1310+
private boolean peekTlsRecordHeader(ByteBuffer in, int inRemaining) {
1311+
boolean bufferUnderflow = false;
1312+
if (inRemaining > 0 && (this.ssl.dtls() == 0)) {
1313+
int pos = in.position();
1314+
if (inRemaining < TLS_RECORD_HEADER_LEN) {
1315+
/* Not enough for TLS record header */
1316+
bufferUnderflow = true;
1317+
} else {
1318+
/* Peek at record length from header
1319+
* bytes 3-4 (big-endian) */
1320+
int recLen =
1321+
((in.get(pos + TLS_RECORD_LEN_HI_OFF)
1322+
& 0xFF) << 8) |
1323+
(in.get(pos + TLS_RECORD_LEN_LO_OFF)
1324+
& 0xFF);
1325+
if (recLen <= WolfSSL.MAX_RECORD_SIZE &&
1326+
inRemaining < TLS_RECORD_HEADER_LEN + recLen) {
1327+
bufferUnderflow = true;
1328+
}
1329+
}
1330+
}
1331+
return bufferUnderflow;
1332+
}
1333+
12921334
@Override
12931335
public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer out)
12941336
throws SSLException {
@@ -1313,6 +1355,7 @@ public synchronized SSLEngineResult unwrap(ByteBuffer in, ByteBuffer[] out,
13131355
long dtlsPrevDropCount = 0;
13141356
long dtlsCurrDropCount = 0;
13151357
int prevSessionTicketCount = 0;
1358+
boolean bufferUnderflow = false;
13161359
final int tmpRet;
13171360

13181361
/* Set initial status for SSLEngineResult return */
@@ -1463,38 +1506,22 @@ else if (hs == SSLEngineResult.HandshakeStatus.NEED_WRAP &&
14631506
if (this.handshakeFinished == false) {
14641507
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
14651508
() -> "starting or continuing handshake");
1466-
ret = DoHandshake(false);
1509+
if (!this.contPartialRecord) {
1510+
bufferUnderflow =
1511+
this.getUseClientMode() &&
1512+
peekTlsRecordHeader(in, inRemaining);
1513+
}
1514+
if (bufferUnderflow) {
1515+
status = SSLEngineResult.Status.BUFFER_UNDERFLOW;
1516+
}
1517+
else {
1518+
ret = DoHandshake(false);
1519+
}
14671520
}
14681521
else {
1469-
/* TLS-only: peek at the record header and
1470-
* return BUFFER_UNDERFLOW before calling into
1471-
* JNI when the full record is not yet present.
1472-
* Without this, native wolfSSL consumes partial
1473-
* record bytes via the I/O callback, violating
1474-
* the JSSE contract that BUFFER_UNDERFLOW must
1475-
* report bytesConsumed() == 0. DTLS still
1476-
* relies on the native WANT_READ path. */
1477-
boolean bufferUnderflow = false;
1478-
if (inRemaining > 0 && (this.ssl.dtls() == 0)) {
1479-
synchronized (netDataLock) {
1480-
int pos = in.position();
1481-
if (inRemaining < TLS_RECORD_HEADER_LEN) {
1482-
/* Not enough for TLS record header */
1483-
bufferUnderflow = true;
1484-
} else {
1485-
/* Peek at record length from header
1486-
* bytes 3-4 (big-endian) */
1487-
int recLen =
1488-
((in.get(pos + TLS_RECORD_LEN_HI_OFF)
1489-
& 0xFF) << 8) |
1490-
(in.get(pos + TLS_RECORD_LEN_LO_OFF)
1491-
& 0xFF);
1492-
if (inRemaining <
1493-
TLS_RECORD_HEADER_LEN + recLen) {
1494-
bufferUnderflow = true;
1495-
}
1496-
}
1497-
}
1522+
if (!this.contPartialRecord) {
1523+
bufferUnderflow =
1524+
peekTlsRecordHeader(in, inRemaining);
14981525
}
14991526

15001527
/* Serve stashed data from previous
@@ -1629,8 +1656,8 @@ else if (inRemaining > 0 &&
16291656
}
16301657
} /* end DoHandshake() / RecvAppData() */
16311658

1632-
if (outBoundOpen == false || this.closeNotifySent ||
1633-
this.closeNotifyReceived) {
1659+
if ((outBoundOpen == false || this.closeNotifySent ||
1660+
this.closeNotifyReceived) && !bufferUnderflow) {
16341661
/* Mark SSLEngine status as CLOSED */
16351662
status = SSLEngineResult.Status.CLOSED;
16361663
/* Handshake has finished and SSLEngine is closed,
@@ -1697,7 +1724,8 @@ else if (ret < 0 &&
16971724
}
16981725
}
16991726
else if (!this.handshakeFinished && (ret == 0) &&
1700-
(err == 0 || err == WolfSSL.SSL_ERROR_ZERO_RETURN)) {
1727+
(err == 0 || err == WolfSSL.SSL_ERROR_ZERO_RETURN)
1728+
&& !bufferUnderflow) {
17011729

17021730
boolean gotCloseNotify = false;
17031731
synchronized (ioLock) {
@@ -2650,6 +2678,16 @@ protected synchronized int internalRecvCb(ByteBuffer toRead, int sz) {
26502678
if (this.ssl.dtls() == 1 && this.handshakeFinished) {
26512679
this.nativeWantsToRead = 1;
26522680
}
2681+
/* wolfSSL asked for bytes but we have none remaining
2682+
* (netData is valid but empty). Mark as mid-record so
2683+
* the next unwrap() skips the TLS header peek and
2684+
* passes the continuation bytes directly to DoHandshake.
2685+
* Only set when netData is non-null (netData==null means
2686+
* we are inside wrap(), not a mid-record scenario).
2687+
* Prevents stale state from being used. */
2688+
if (this.netData != null) {
2689+
this.contPartialRecord = true;
2690+
}
26532691
return WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ;
26542692
}
26552693

@@ -2676,6 +2714,8 @@ protected synchronized int internalRecvCb(ByteBuffer toRead, int sz) {
26762714
toRead.position(toReadPos);
26772715
}
26782716

2717+
this.contPartialRecord = (max < sz);
2718+
26792719
return max;
26802720
}
26812721
}

0 commit comments

Comments
 (0)