From 8b195d1d2124f18f795d9d6fa8a44bb28c930361 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Tue, 25 Jun 2024 07:01:10 +1000 Subject: [PATCH] #3693: Compile natives as C instead of C++, check malloc/calloc return values for null --- native/compile-native.sh | 19 ++++++++-- ...ativeCipherImpl.cpp => NativeCipherImpl.c} | 27 +++++++------- ...eCompressImpl.cpp => NativeCompressImpl.c} | 34 +++++++++++------- native/src/main/c/shared.c | 15 ++++++++ native/src/main/c/shared.h | 10 ++++++ .../md_5/bungee/jni/NativeCodeException.java | 2 +- .../bungee/jni/zlib/NativeCompressImpl.java | 7 ++++ .../net/md_5/bungee/jni/zlib/NativeZlib.java | 10 +++++- native/src/main/resources/native-cipher.so | Bin 39784 -> 40016 bytes native/src/main/resources/native-compress.so | Bin 117056 -> 117320 bytes .../java/net/md_5/bungee/NativeZlibTest.java | 29 +++++++++++++++ 11 files changed, 122 insertions(+), 31 deletions(-) rename native/src/main/c/{NativeCipherImpl.cpp => NativeCipherImpl.c} (70%) rename native/src/main/c/{NativeCompressImpl.cpp => NativeCompressImpl.c} (69%) create mode 100644 native/src/main/c/shared.c create mode 100644 native/src/main/c/shared.h diff --git a/native/compile-native.sh b/native/compile-native.sh index c342e782..5b6e54c4 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -8,7 +8,20 @@ echo "Compiling mbedtls" echo "Compiling zlib" (cd zlib && CFLAGS=-fPIC ./configure --static && make) -CXX="g++ -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +CC="gcc" +CFLAGS="-c -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +LDFLAGS="-shared" -$CXX -Imbedtls/include src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so mbedtls/library/libmbedcrypto.a -$CXX -Izlib src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so zlib/libz.a +echo "Compiling bungee" +$CC $CFLAGS -o shared.o src/main/c/shared.c +$CC $CFLAGS -Imbedtls/include -o NativeCipherImpl.o src/main/c/NativeCipherImpl.c +$CC $CFLAGS -Izlib -o NativeCompressImpl.o src/main/c/NativeCompressImpl.c + +echo "Linking native-cipher.so" +$CC $LDFLAGS -o src/main/resources/native-cipher.so shared.o NativeCipherImpl.o mbedtls/library/libmbedcrypto.a + +echo "Linking native-compress.so" +$CC $LDFLAGS -o src/main/resources/native-compress.so shared.o NativeCompressImpl.o zlib/libz.a + +echo "Cleaning up" +rm shared.o NativeCipherImpl.o NativeCompressImpl.o diff --git a/native/src/main/c/NativeCipherImpl.cpp b/native/src/main/c/NativeCipherImpl.c similarity index 70% rename from native/src/main/c/NativeCipherImpl.cpp rename to native/src/main/c/NativeCipherImpl.c index cfe5f089..727a2f1f 100644 --- a/native/src/main/c/NativeCipherImpl.cpp +++ b/native/src/main/c/NativeCipherImpl.c @@ -2,31 +2,33 @@ #include #include +#include "shared.h" #include "net_md_5_bungee_jni_cipher_NativeCipherImpl.h" typedef unsigned char byte; -struct crypto_context { +typedef struct crypto_context { int mode; mbedtls_aes_context cipher; - byte *key; -}; + byte key[]; +} crypto_context; jlong JNICALL Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_init(JNIEnv* env, jobject obj, jboolean forEncryption, jbyteArray key) { - jsize keyLen = env->GetArrayLength(key); - jbyte *keyBytes = env->GetByteArrayElements(key, NULL); + jsize keyLen = (*env)->GetArrayLength(env, key); + + crypto_context *crypto = (crypto_context*) malloc(sizeof (crypto_context) + (size_t) keyLen); + if (!crypto) { + throwOutOfMemoryError(env, "Failed to malloc new crypto_context"); + return 0; + } + + (*env)->GetByteArrayRegion(env, key, 0, keyLen, (jbyte*) &crypto->key); - crypto_context *crypto = (crypto_context*) malloc(sizeof (crypto_context)); mbedtls_aes_init(&crypto->cipher); - - mbedtls_aes_setkey_enc(&crypto->cipher, (byte*) keyBytes, keyLen * 8); - - crypto->key = (byte*) malloc(keyLen); - memcpy(crypto->key, keyBytes, keyLen); + mbedtls_aes_setkey_enc(&crypto->cipher, (byte*) &crypto->key, keyLen * 8); crypto->mode = (forEncryption) ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; - env->ReleaseByteArrayElements(key, keyBytes, JNI_ABORT); return (jlong) crypto; } @@ -34,7 +36,6 @@ void Java_net_md_15_bungee_jni_cipher_NativeCipherImpl_free(JNIEnv* env, jobject crypto_context *crypto = (crypto_context*) ctx; mbedtls_aes_free(&crypto->cipher); - free(crypto->key); free(crypto); } diff --git a/native/src/main/c/NativeCompressImpl.cpp b/native/src/main/c/NativeCompressImpl.c similarity index 69% rename from native/src/main/c/NativeCompressImpl.cpp rename to native/src/main/c/NativeCompressImpl.c index 4b93a56a..7fb8e6b9 100644 --- a/native/src/main/c/NativeCompressImpl.cpp +++ b/native/src/main/c/NativeCompressImpl.c @@ -2,28 +2,28 @@ #include #include +#include "shared.h" #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" typedef unsigned char byte; +static jclass classID; static jfieldID consumedID; static jfieldID finishedID; +static jmethodID makeExceptionID; void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields(JNIEnv* env, jclass clazz) { - // We trust that these fields will be there - consumedID = env->GetFieldID(clazz, "consumed", "I"); - finishedID = env->GetFieldID(clazz, "finished", "Z"); + classID = clazz; + // We trust that these will be there + consumedID = (*env)->GetFieldID(env, clazz, "consumed", "I"); + finishedID = (*env)->GetFieldID(env, clazz, "finished", "Z"); + makeExceptionID = (*env)->GetMethodID(env, clazz, "makeException", "(Ljava/lang/String;I)Lnet/md_5/bungee/jni/NativeCodeException;"); } jint throwException(JNIEnv *env, const char* message, int err) { - // These can't be static for some unknown reason - jclass exceptionClass = env->FindClass("net/md_5/bungee/jni/NativeCodeException"); - jmethodID exceptionInitID = env->GetMethodID(exceptionClass, "", "(Ljava/lang/String;I)V"); - - jstring jMessage = env->NewStringUTF(message); - - jthrowable throwable = (jthrowable) env->NewObject(exceptionClass, exceptionInitID, jMessage, err); - return env->Throw(throwable); + jstring jMessage = (*env)->NewStringUTF(env, message); + jthrowable throwable = (jthrowable) (*env)->CallStaticObjectMethod(env, classID, makeExceptionID, jMessage, err); + return (*env)->Throw(env, throwable); } void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { @@ -48,10 +48,17 @@ void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_end(JNIEnv* env, jlong JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_init(JNIEnv* env, jobject obj, jboolean compress, jint level) { z_stream* stream = (z_stream*) calloc(1, sizeof (z_stream)); + if (!stream) { + throwOutOfMemoryError(env, "Failed to calloc new z_stream"); + return 0; + } + int ret = (compress) ? deflateInit(stream, level) : inflateInit(stream); if (ret != Z_OK) { + free(stream); throwException(env, "Could not init z_stream", ret); + return 0; } return (jlong) stream; @@ -70,15 +77,16 @@ jint JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_process(JNIEnv* e switch (ret) { case Z_STREAM_END: - env->SetBooleanField(obj, finishedID, true); + (*env)->SetBooleanField(env, obj, finishedID, JNI_TRUE); break; case Z_OK: break; default: throwException(env, "Unknown z_stream return code", ret); + return -1; } - env->SetIntField(obj, consumedID, inLength - stream->avail_in); + (*env)->SetIntField(env, obj, consumedID, inLength - stream->avail_in); return outLength - stream->avail_out; } diff --git a/native/src/main/c/shared.c b/native/src/main/c/shared.c new file mode 100644 index 00000000..8b35bc6b --- /dev/null +++ b/native/src/main/c/shared.c @@ -0,0 +1,15 @@ +#include "shared.h" +#include +#include + +void throwOutOfMemoryError(JNIEnv* env, const char* msg) { + jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + if (!exceptionClass) { + // If the proxy ran out of memory, loading this class may fail + fprintf(stderr, "OUT OF MEMORY: %s\n", msg); + fprintf(stderr, "Could not load class java.lang.OutOfMemoryError!\n"); + exit(-1); + return; + } + (*env)->ThrowNew(env, exceptionClass, msg); +} diff --git a/native/src/main/c/shared.h b/native/src/main/c/shared.h new file mode 100644 index 00000000..e3ad9c86 --- /dev/null +++ b/native/src/main/c/shared.h @@ -0,0 +1,10 @@ +// This header contains functions to be shared between both native libraries + +#include + +#ifndef _INCLUDE_SHARED_H +#define _INCLUDE_SHARED_H + +void throwOutOfMemoryError(JNIEnv* env, const char* msg); + +#endif diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java index 0020c54b..1ac8f590 100644 --- a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java @@ -1,6 +1,6 @@ package net.md_5.bungee.jni; -public class NativeCodeException extends Exception +public class NativeCodeException extends RuntimeException { public NativeCodeException(String message, int reason) diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java index 3bcc6ddb..f78caf18 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeCompressImpl.java @@ -1,5 +1,7 @@ package net.md_5.bungee.jni.zlib; +import net.md_5.bungee.jni.NativeCodeException; + public class NativeCompressImpl { @@ -20,4 +22,9 @@ public class NativeCompressImpl native long init(boolean compress, int compressionLevel); native int process(long ctx, long in, int inLength, long out, int outLength, boolean compress); + + NativeCodeException makeException(String message, int err) + { + return new NativeCodeException( message, err ); + } } diff --git a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java index f1f3b1e3..e9fe82b8 100644 --- a/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java +++ b/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import io.netty.buffer.ByteBuf; import java.util.zip.DataFormatException; import lombok.Getter; +import net.md_5.bungee.jni.NativeCodeException; public class NativeZlib implements BungeeZlib { @@ -48,7 +49,14 @@ public class NativeZlib implements BungeeZlib { out.ensureWritable( 8192 ); - int processed = nativeCompress.process( ctx, in.memoryAddress() + in.readerIndex(), in.readableBytes(), out.memoryAddress() + out.writerIndex(), out.writableBytes(), compress ); + int processed; + try + { + processed = nativeCompress.process( ctx, in.memoryAddress() + in.readerIndex(), in.readableBytes(), out.memoryAddress() + out.writerIndex(), out.writableBytes(), compress ); + } catch ( NativeCodeException exception ) + { + throw (DataFormatException) new DataFormatException( "Failed to decompress via Zlib!" ).initCause( exception ); + } in.readerIndex( in.readerIndex() + nativeCompress.consumed ); out.writerIndex( out.writerIndex() + processed ); diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so index 843f7f7a2ddcb36b92aee155b564f1e8b219fe3e..87cc1756fed8437a69adc88fae7f1d9da625760d 100755 GIT binary patch delta 8882 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"cwLXN^4d*36Xb-E=;&N4Xm%&j^aNk}q3N{uG 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 aZZ9J(YgFv!4)4<&aFE9wq4iFYX8#9~+S!`` delta 15840 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 ZVeCGcp%1@`48GYFGTDsrYe!hi{{tq2LU#ZF diff --git a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java index 030fae28..70b2b475 100644 --- a/native/src/test/java/net/md_5/bungee/NativeZlibTest.java +++ b/native/src/test/java/net/md_5/bungee/NativeZlibTest.java @@ -28,6 +28,17 @@ public class NativeZlibTest test( new JavaZlib() ); } + @Test + public void testException() throws DataFormatException + { + if ( NativeCode.isSupported() ) + { + assertTrue( factory.load(), "Native code failed to load!" ); + testExceptionImpl( factory.newInstance() ); + } + testExceptionImpl( new JavaZlib() ); + } + private void test(BungeeZlib zlib) throws DataFormatException { System.out.println( "Testing: " + zlib ); @@ -66,4 +77,22 @@ public class NativeZlibTest assertTrue( Arrays.equals( dataBuf, check ), "Results do not match" ); } + + private void testExceptionImpl(BungeeZlib zlib) throws DataFormatException + { + System.out.println( "Testing Exception: " + zlib ); + long start = System.currentTimeMillis(); + + byte[] dataBuf = new byte[ 1 << 12 ]; // 4096 random bytes + new Random().nextBytes( dataBuf ); + + zlib.init( false, 0 ); + + ByteBuf originalBuf = Unpooled.directBuffer(); + originalBuf.writeBytes( dataBuf ); + + ByteBuf decompressed = Unpooled.directBuffer(); + + assertThrows( DataFormatException.class, () -> zlib.process( originalBuf, decompressed ), "Decompressing random bytes did not result in a DataFormatException!" ); + } }