Skip to content

Commit 12eae28

Browse files
committed
JNI/JSSE: optimize out array creation in WolfSSLEngine RecvAppData(), pass ByteBuffer down to JNI directly
1 parent 9db7ff1 commit 12eae28

4 files changed

Lines changed: 377 additions & 86 deletions

File tree

native/com_wolfssl_WolfSSLSession.c

Lines changed: 250 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,16 +1002,104 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_write
10021002
}
10031003
}
10041004

1005-
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read
1006-
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jbyteArray raw, jint offset,
1007-
jint length, jint timeout)
1005+
/**
1006+
* Read len bytes from wolfSSL_read() back into provided output buffer.
1007+
*
1008+
* Internal function called by WolfSSLSession.read() calls.
1009+
*
1010+
* If wolfSSL_get_fd(ssl) returns a socket descriptor, try to wait for
1011+
* data with select()/poll() up to provided timeout.
1012+
*
1013+
* Returns number of bytes read on success, or negative on error.
1014+
*/
1015+
static int SSLReadNonblockingWithSelectPoll(WOLFSSL* ssl, byte* out,
1016+
int length, int timeout)
10081017
{
1009-
byte* data = NULL;
1010-
int size = 0, ret, err, sockfd;
1018+
int size, ret, err, sockfd;
10111019
int pollRx = 0;
10121020
int pollTx = 0;
10131021
wolfSSL_Mutex* jniSessLock = NULL;
10141022
SSLAppData* appData = NULL;
1023+
1024+
if (ssl == NULL || out == NULL) {
1025+
return BAD_FUNC_ARG;
1026+
}
1027+
1028+
/* get session mutex from SSL app data */
1029+
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
1030+
if (appData == NULL) {
1031+
return WOLFSSL_FAILURE;
1032+
}
1033+
1034+
jniSessLock = appData->jniSessLock;
1035+
if (jniSessLock == NULL) {
1036+
return WOLFSSL_FAILURE;
1037+
}
1038+
1039+
do {
1040+
/* lock mutex around session I/O before read attempt */
1041+
if (wc_LockMutex(jniSessLock) != 0) {
1042+
size = WOLFSSL_FAILURE;
1043+
break;
1044+
}
1045+
1046+
size = wolfSSL_read(ssl, out, length);
1047+
err = wolfSSL_get_error(ssl, size);
1048+
1049+
/* unlock mutex around session I/O after read attempt */
1050+
if (wc_UnLockMutex(jniSessLock) != 0) {
1051+
size = WOLFSSL_FAILURE;
1052+
break;
1053+
}
1054+
1055+
if (size < 0 &&
1056+
((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE))) {
1057+
1058+
sockfd = wolfSSL_get_fd(ssl);
1059+
if (sockfd == -1) {
1060+
/* For I/O that does not use sockets, sockfd may be -1,
1061+
* skip try to call select() */
1062+
break;
1063+
}
1064+
1065+
if (err == SSL_ERROR_WANT_READ) {
1066+
pollRx = 1;
1067+
}
1068+
else if (err == SSL_ERROR_WANT_WRITE) {
1069+
pollTx = 1;
1070+
}
1071+
1072+
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
1073+
ret = socketSelect(sockfd, timeout, pollRx);
1074+
#else
1075+
ret = socketPoll(sockfd, timeout, pollRx, pollTx);
1076+
#endif
1077+
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
1078+
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
1079+
/* loop around and try wolfSSL_read() again */
1080+
continue;
1081+
} else {
1082+
/* Java will throw SocketTimeoutException or
1083+
* SocketException if ret equals
1084+
* WOLFJNI_IO_EVENT_TIMEOUT, WOLFJNI_IO_EVENT_FD_CLOSED
1085+
* WOLFJNI_IO_EVENT_ERROR, WOLFJNI_IO_EVENT_POLLHUP or
1086+
* WOLFJNI_IO_EVENT_FAIL */
1087+
size = ret;
1088+
break;
1089+
}
1090+
}
1091+
1092+
} while (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ);
1093+
1094+
return size;
1095+
}
1096+
1097+
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__J_3BIII
1098+
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jbyteArray raw, jint offset,
1099+
jint length, jint timeout)
1100+
{
1101+
int size = 0;
1102+
byte* data = NULL;
10151103
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
10161104
(void)jcl;
10171105

@@ -1027,79 +1115,178 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read
10271115
return SSL_FAILURE;
10281116
}
10291117

1030-
/* get session mutex from SSL app data */
1031-
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
1032-
if (appData == NULL) {
1118+
size = SSLReadNonblockingWithSelectPoll(ssl, data + offset,
1119+
(int)length, (int)timeout);
1120+
1121+
if (size < 0) {
10331122
(*jenv)->ReleaseByteArrayElements(jenv, raw, (jbyte*)data,
1034-
JNI_ABORT);
1035-
return WOLFSSL_FAILURE;
1123+
JNI_ABORT);
10361124
}
1125+
else {
1126+
/* JNI_COMMIT commits the data but does not free the local array
1127+
* 0 is used here to both commit and free */
1128+
(*jenv)->ReleaseByteArrayElements(jenv, raw, (jbyte*)data, 0);
1129+
}
1130+
}
10371131

1038-
jniSessLock = appData->jniSessLock;
1039-
if (jniSessLock == NULL) {
1040-
(*jenv)->ReleaseByteArrayElements(jenv, raw, (jbyte*)data,
1041-
JNI_ABORT);
1042-
return WOLFSSL_FAILURE;
1132+
return size;
1133+
}
1134+
1135+
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__JLjava_nio_ByteBuffer_2II
1136+
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jobject buf, jint length, jint timeout)
1137+
{
1138+
int size = 0;
1139+
int maxOutputSz;
1140+
int outSz = length;
1141+
byte* data = NULL;
1142+
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
1143+
1144+
jclass excClass;
1145+
jclass buffClass;
1146+
jmethodID positionMeth;
1147+
jmethodID limitMeth;
1148+
jmethodID hasArrayMeth;
1149+
jmethodID arrayMeth;
1150+
jmethodID setPositionMeth;
1151+
1152+
jint position;
1153+
jint limit;
1154+
jboolean hasArray;
1155+
jbyteArray bufArr;
1156+
1157+
(void)jcl;
1158+
1159+
if (jenv == NULL || ssl == NULL || buf == NULL) {
1160+
return BAD_FUNC_ARG;
1161+
}
1162+
1163+
if (length > 0) {
1164+
/* Get WolfSSLException class */
1165+
excClass = (*jenv)->FindClass(jenv, "com/wolfssl/WolfSSLException");
1166+
if ((*jenv)->ExceptionOccurred(jenv)) {
1167+
(*jenv)->ExceptionDescribe(jenv);
1168+
(*jenv)->ExceptionClear(jenv);
1169+
return -1;
10431170
}
10441171

1045-
do {
1046-
/* lock mutex around session I/O before read attempt */
1047-
if (wc_LockMutex(jniSessLock) != 0) {
1048-
size = WOLFSSL_FAILURE;
1049-
break;
1050-
}
1172+
/* Get ByteBuffer class */
1173+
buffClass = (*jenv)->GetObjectClass(jenv, buf);
1174+
if (buffClass == NULL) {
1175+
(*jenv)->ThrowNew(jenv, excClass,
1176+
"Failed to find ByteBuffer class in native read()");
1177+
return -1;
1178+
}
10511179

1052-
size = wolfSSL_read(ssl, data + offset, length);
1053-
err = wolfSSL_get_error(ssl, size);
1180+
/* Get ByteBuffer position */
1181+
positionMeth = (*jenv)->GetMethodID(jenv, buffClass, "position", "()I");
1182+
if (positionMeth == NULL) {
1183+
if ((*jenv)->ExceptionOccurred(jenv)) {
1184+
(*jenv)->ExceptionDescribe(jenv);
1185+
(*jenv)->ExceptionClear(jenv);
1186+
}
1187+
(*jenv)->ThrowNew(jenv, excClass,
1188+
"Failed to find ByteBuffer position() method in native read()");
1189+
return -1;
1190+
}
1191+
position = (*jenv)->CallIntMethod(jenv, buf, positionMeth);
10541192

1055-
/* unlock mutex around session I/O after read attempt */
1056-
if (wc_UnLockMutex(jniSessLock) != 0) {
1057-
size = WOLFSSL_FAILURE;
1058-
break;
1193+
/* Get ByteBuffer limit */
1194+
limitMeth = (*jenv)->GetMethodID(jenv, buffClass, "limit", "()I");
1195+
if (limitMeth == NULL) {
1196+
if ((*jenv)->ExceptionOccurred(jenv)) {
1197+
(*jenv)->ExceptionDescribe(jenv);
1198+
(*jenv)->ExceptionClear(jenv);
10591199
}
1200+
(*jenv)->ThrowNew(jenv, excClass,
1201+
"Failed to find ByteBuffer limit() method in native read()");
1202+
return -1;
1203+
}
1204+
limit = (*jenv)->CallIntMethod(jenv, buf, limitMeth);
10601205

1061-
if (size < 0 && ((err == SSL_ERROR_WANT_READ) || \
1062-
(err == SSL_ERROR_WANT_WRITE))) {
1206+
/* Get and call ByteBuffer.hasArray() before calling array() */
1207+
hasArrayMeth = (*jenv)->GetMethodID(jenv, buffClass, "hasArray", "()Z");
1208+
if (hasArrayMeth == NULL) {
1209+
if ((*jenv)->ExceptionOccurred(jenv)) {
1210+
(*jenv)->ExceptionDescribe(jenv);
1211+
(*jenv)->ExceptionClear(jenv);
1212+
}
1213+
(*jenv)->ThrowNew(jenv, excClass,
1214+
"Failed to find ByteBuffer hasArray() method in native read()");
1215+
return -1;
1216+
}
10631217

1064-
sockfd = wolfSSL_get_fd(ssl);
1065-
if (sockfd == -1) {
1066-
/* For I/O that does not use sockets, sockfd may be -1,
1067-
* skip try to call select() */
1068-
break;
1069-
}
1218+
/* ByteBuffer.hasArray() does not throw any exceptions */
1219+
hasArray = (*jenv)->CallBooleanMethod(jenv, buf, hasArrayMeth);
1220+
if (!hasArray) {
1221+
(*jenv)->ThrowNew(jenv, excClass,
1222+
"ByteBuffer.hasArray() is false in native read()");
1223+
return BAD_FUNC_ARG;
1224+
}
10701225

1071-
if (err == SSL_ERROR_WANT_READ) {
1072-
pollRx = 1;
1073-
}
1074-
else if (err == SSL_ERROR_WANT_WRITE) {
1075-
pollTx = 1;
1076-
}
1226+
/* Only read up to maximum space we have in this ByteBuffer */
1227+
maxOutputSz = (limit - position);
1228+
if (outSz > maxOutputSz) {
1229+
outSz = maxOutputSz;
1230+
}
10771231

1078-
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
1079-
ret = socketSelect(sockfd, (int)timeout, pollRx);
1080-
#else
1081-
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
1082-
#endif
1083-
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
1084-
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
1085-
/* loop around and try wolfSSL_read() again */
1086-
continue;
1087-
} else {
1088-
/* Java will throw SocketTimeoutException or
1089-
* SocketException if ret equals
1090-
* WOLFJNI_IO_EVENT_TIMEOUT, WOLFJNI_IO_EVENT_FD_CLOSED
1091-
* WOLFJNI_IO_EVENT_ERROR, WOLFJNI_IO_EVENT_POLLHUP or
1092-
* WOLFJNI_IO_EVENT_FAIL */
1093-
size = ret;
1094-
break;
1095-
}
1232+
/* Get reference to underlying byte[] from ByteBuffer */
1233+
arrayMeth = (*jenv)->GetMethodID(jenv, buffClass, "array", "()[B");
1234+
if (arrayMeth == NULL) {
1235+
if ((*jenv)->ExceptionOccurred(jenv)) {
1236+
(*jenv)->ExceptionDescribe(jenv);
1237+
(*jenv)->ExceptionClear(jenv);
10961238
}
1239+
(*jenv)->ThrowNew(jenv, excClass,
1240+
"Failed to find ByteBuffer array() method in native read()");
1241+
return -1;
1242+
}
1243+
bufArr = (jbyteArray)(*jenv)->CallObjectMethod(jenv, buf, arrayMeth);
10971244

1098-
} while (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ);
1245+
/* Get array elements */
1246+
data = (byte*)(*jenv)->GetByteArrayElements(jenv, bufArr, NULL);
1247+
if ((*jenv)->ExceptionOccurred(jenv)) {
1248+
(*jenv)->ExceptionDescribe(jenv);
1249+
(*jenv)->ExceptionClear(jenv);
1250+
(*jenv)->ThrowNew(jenv, excClass,
1251+
"Exception when calling ByteBuffer.array() in native read()");
1252+
return -1;
1253+
}
1254+
1255+
1256+
if (data != NULL) {
1257+
size = SSLReadNonblockingWithSelectPoll(ssl, data + position,
1258+
maxOutputSz, (int)timeout);
10991259

1100-
/* JNI_COMMIT commits the data but does not free the local array
1101-
* 0 is used here to both commit and free */
1102-
(*jenv)->ReleaseByteArrayElements(jenv, raw, (jbyte*)data, 0);
1260+
/* Relase array elements */
1261+
if (size < 0) {
1262+
(*jenv)->ReleaseByteArrayElements(jenv, bufArr, (jbyte*)data,
1263+
JNI_ABORT);
1264+
}
1265+
else {
1266+
/* JNI_COMMIT commits the data but does not free the local array
1267+
* 0 is used here to both commit and free */
1268+
(*jenv)->ReleaseByteArrayElements(jenv, bufArr,
1269+
(jbyte*)data, 0);
1270+
1271+
/* Update ByteBuffer position() based on bytes written */
1272+
setPositionMeth = (*jenv)->GetMethodID(jenv, buffClass,
1273+
"position", "(I)Ljava/nio/Buffer;");
1274+
if (setPositionMeth == NULL) {
1275+
if ((*jenv)->ExceptionOccurred(jenv)) {
1276+
(*jenv)->ExceptionDescribe(jenv);
1277+
(*jenv)->ExceptionClear(jenv);
1278+
}
1279+
(*jenv)->ThrowNew(jenv, excClass,
1280+
"Failed to set ByteBuffer position() from "
1281+
"native read()");
1282+
size = -1;
1283+
}
1284+
else {
1285+
(*jenv)->CallVoidMethod(jenv, buf, setPositionMeth,
1286+
position + size);
1287+
}
1288+
}
1289+
}
11031290
}
11041291

11051292
return size;

native/com_wolfssl_WolfSSLSession.h

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)