diff --git a/core/src/jni/okhttp_transport_adapter.cpp b/core/src/jni/okhttp_transport_adapter.cpp index 15526036a7..4ed36c5e8a 100644 --- a/core/src/jni/okhttp_transport_adapter.cpp +++ b/core/src/jni/okhttp_transport_adapter.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -182,54 +183,137 @@ class ScopedJniEnv { bool did_attach_ = false; }; -// Helper: turn a std::string pair list into a jobjectArray for +rac_result_t build_request_strings(JNIEnv* env, const rac_http_request_t* req, + jstring* out_method, jstring* out_url) { + if (env == nullptr || req == nullptr || req->method == nullptr || req->url == nullptr || + out_method == nullptr || out_url == nullptr) { + return RAC_ERROR_INVALID_ARGUMENT; + } + *out_method = nullptr; + *out_url = nullptr; + + jstring method = env->NewStringUTF(req->method); + if (method == nullptr || env->ExceptionCheck() == JNI_TRUE) { + env->ExceptionClear(); + if (method != nullptr) + env->DeleteLocalRef(method); + return RAC_ERROR_OUT_OF_MEMORY; + } + + jstring url = env->NewStringUTF(req->url); + if (url == nullptr || env->ExceptionCheck() == JNI_TRUE) { + env->ExceptionClear(); + env->DeleteLocalRef(method); + if (url != nullptr) + env->DeleteLocalRef(url); + return RAC_ERROR_OUT_OF_MEMORY; + } + + *out_method = method; + *out_url = url; + return RAC_SUCCESS; +} + +// Helper: turn a string pair list into a jobjectArray for // `OkHttpTransport.executeRequest(headersFlat=[k1,v1,...])`. -jobjectArray build_headers_flat(JNIEnv* env, const rac_http_header_kv_t* headers, - size_t header_count) { +rac_result_t build_headers_flat(JNIEnv* env, const rac_http_header_kv_t* headers, + size_t header_count, jobjectArray* out_headers) { + if (env == nullptr || out_headers == nullptr || (header_count > 0 && headers == nullptr)) { + return RAC_ERROR_INVALID_ARGUMENT; + } + *out_headers = nullptr; + + constexpr size_t kMaxHeaderCount = + static_cast(std::numeric_limits::max()) / 2; + if (header_count > kMaxHeaderCount) { + return RAC_ERROR_INVALID_ARGUMENT; + } + jclass strCls = env->FindClass("java/lang/String"); - if (strCls == nullptr) - return nullptr; + if (strCls == nullptr) { + if (env->ExceptionCheck() == JNI_TRUE) + env->ExceptionClear(); + return RAC_ERROR_INTERNAL; + } - jsize total = static_cast(header_count * 2); + const jsize total = static_cast(header_count * 2); jobjectArray arr = env->NewObjectArray(total, strCls, nullptr); if (arr == nullptr) { + if (env->ExceptionCheck() == JNI_TRUE) + env->ExceptionClear(); env->DeleteLocalRef(strCls); - return nullptr; + return RAC_ERROR_OUT_OF_MEMORY; } for (size_t i = 0; i < header_count; ++i) { jstring k = env->NewStringUTF(headers[i].name ? headers[i].name : ""); + if (k == nullptr) { + if (env->ExceptionCheck() == JNI_TRUE) + env->ExceptionClear(); + env->DeleteLocalRef(arr); + env->DeleteLocalRef(strCls); + return RAC_ERROR_OUT_OF_MEMORY; + } jstring v = env->NewStringUTF(headers[i].value ? headers[i].value : ""); - env->SetObjectArrayElement(arr, static_cast(i * 2), k); - env->SetObjectArrayElement(arr, static_cast(i * 2 + 1), v); + if (v == nullptr) { + if (env->ExceptionCheck() == JNI_TRUE) + env->ExceptionClear(); + env->DeleteLocalRef(k); + env->DeleteLocalRef(arr); + env->DeleteLocalRef(strCls); + return RAC_ERROR_OUT_OF_MEMORY; + } + + const jsize key_index = static_cast(i * 2); + env->SetObjectArrayElement(arr, key_index, k); + env->SetObjectArrayElement(arr, key_index + 1, v); + if (env->ExceptionCheck() == JNI_TRUE) { + env->ExceptionClear(); + env->DeleteLocalRef(k); + env->DeleteLocalRef(v); + env->DeleteLocalRef(arr); + env->DeleteLocalRef(strCls); + return RAC_ERROR_INTERNAL; + } env->DeleteLocalRef(k); env->DeleteLocalRef(v); } env->DeleteLocalRef(strCls); - return arr; + *out_headers = arr; + return RAC_SUCCESS; } -// Guard the empty-headers fallback. FindClass can fail under -// JVM shutdown / classloader pressure, so never hand a null class to -// NewObjectArray (which throws NPE and would ship a malformed request). -rac_result_t ensure_headers_array(JNIEnv* env, jobjectArray* headers) { - if (*headers != nullptr) - return RAC_SUCCESS; +rac_result_t build_request_body(JNIEnv* env, const rac_http_request_t* req, + jbyteArray* out_body) { + if (env == nullptr || req == nullptr || out_body == nullptr) { + return RAC_ERROR_INVALID_ARGUMENT; + } + *out_body = nullptr; - jclass strCls = env->FindClass("java/lang/String"); - if (strCls == nullptr) { - if (env->ExceptionCheck() == JNI_TRUE) - env->ExceptionClear(); - return RAC_ERROR_INTERNAL; + if (req->body_len == 0) { + return RAC_SUCCESS; + } + if (req->body_bytes == nullptr || + req->body_len > static_cast(std::numeric_limits::max())) { + return RAC_ERROR_INVALID_ARGUMENT; } - *headers = env->NewObjectArray(0, strCls, nullptr); - env->DeleteLocalRef(strCls); - if (*headers == nullptr) { + const jsize body_len = static_cast(req->body_len); + jbyteArray body = env->NewByteArray(body_len); + if (body == nullptr) { if (env->ExceptionCheck() == JNI_TRUE) env->ExceptionClear(); return RAC_ERROR_OUT_OF_MEMORY; } + env->SetByteArrayRegion(body, 0, body_len, + reinterpret_cast(req->body_bytes)); + if (env->ExceptionCheck() == JNI_TRUE) { + env->ExceptionClear(); + env->DeleteLocalRef(body); + return RAC_ERROR_INTERNAL; + } + + *out_body = body; return RAC_SUCCESS; } @@ -365,10 +449,14 @@ rac_result_t okhttp_request_send(void* /*user_data*/, const rac_http_request_t* // Build jstring / jobjectArray / jbyteArray args. Always pass a // non-null headers array so Kotlin's executeRequest(headersFlat) loop // can do a plain `headersFlat.size` check. - jstring j_method = env->NewStringUTF(req->method); - jstring j_url = env->NewStringUTF(req->url); - jobjectArray j_headers = build_headers_flat(env, req->headers, req->header_count); - rac_result_t headers_rc = ensure_headers_array(env, &j_headers); + jstring j_method = nullptr; + jstring j_url = nullptr; + rac_result_t strings_rc = build_request_strings(env, req, &j_method, &j_url); + if (strings_rc != RAC_SUCCESS) + return strings_rc; + jobjectArray j_headers = nullptr; + rac_result_t headers_rc = + build_headers_flat(env, req->headers, req->header_count, &j_headers); if (headers_rc != RAC_SUCCESS) { if (j_method) env->DeleteLocalRef(j_method); @@ -378,12 +466,12 @@ rac_result_t okhttp_request_send(void* /*user_data*/, const rac_http_request_t* } jbyteArray j_body = nullptr; - if (req->body_bytes != nullptr && req->body_len > 0) { - j_body = env->NewByteArray(static_cast(req->body_len)); - if (j_body != nullptr) { - env->SetByteArrayRegion(j_body, 0, static_cast(req->body_len), - reinterpret_cast(req->body_bytes)); - } + rac_result_t body_rc = build_request_body(env, req, &j_body); + if (body_rc != RAC_SUCCESS) { + env->DeleteLocalRef(j_method); + env->DeleteLocalRef(j_url); + env->DeleteLocalRef(j_headers); + return body_rc; } jlong j_timeout_ms = static_cast(req->timeout_ms); @@ -525,10 +613,14 @@ rac_result_t okhttp_request_stream(void* /*user_data*/, const rac_http_request_t return RAC_ERROR_INTERNAL; } - jstring j_method = env->NewStringUTF(req->method); - jstring j_url = env->NewStringUTF(req->url); - jobjectArray j_headers = build_headers_flat(env, req->headers, req->header_count); - rac_result_t headers_rc = ensure_headers_array(env, &j_headers); + jstring j_method = nullptr; + jstring j_url = nullptr; + rac_result_t strings_rc = build_request_strings(env, req, &j_method, &j_url); + if (strings_rc != RAC_SUCCESS) + return strings_rc; + jobjectArray j_headers = nullptr; + rac_result_t headers_rc = + build_headers_flat(env, req->headers, req->header_count, &j_headers); if (headers_rc != RAC_SUCCESS) { if (j_method) env->DeleteLocalRef(j_method); @@ -538,12 +630,12 @@ rac_result_t okhttp_request_stream(void* /*user_data*/, const rac_http_request_t } jbyteArray j_body = nullptr; - if (req->body_bytes != nullptr && req->body_len > 0) { - j_body = env->NewByteArray(static_cast(req->body_len)); - if (j_body != nullptr) { - env->SetByteArrayRegion(j_body, 0, static_cast(req->body_len), - reinterpret_cast(req->body_bytes)); - } + rac_result_t body_rc = build_request_body(env, req, &j_body); + if (body_rc != RAC_SUCCESS) { + env->DeleteLocalRef(j_method); + env->DeleteLocalRef(j_url); + env->DeleteLocalRef(j_headers); + return body_rc; } jlong j_timeout_ms = static_cast(req->timeout_ms); @@ -670,10 +762,14 @@ rac_result_t okhttp_request_resume(void* /*user_data*/, const rac_http_request_t return RAC_ERROR_INTERNAL; } - jstring j_method = env->NewStringUTF(req->method); - jstring j_url = env->NewStringUTF(req->url); - jobjectArray j_headers = build_headers_flat(env, req->headers, req->header_count); - rac_result_t headers_rc = ensure_headers_array(env, &j_headers); + jstring j_method = nullptr; + jstring j_url = nullptr; + rac_result_t strings_rc = build_request_strings(env, req, &j_method, &j_url); + if (strings_rc != RAC_SUCCESS) + return strings_rc; + jobjectArray j_headers = nullptr; + rac_result_t headers_rc = + build_headers_flat(env, req->headers, req->header_count, &j_headers); if (headers_rc != RAC_SUCCESS) { if (j_method) env->DeleteLocalRef(j_method); @@ -683,12 +779,12 @@ rac_result_t okhttp_request_resume(void* /*user_data*/, const rac_http_request_t } jbyteArray j_body = nullptr; - if (req->body_bytes != nullptr && req->body_len > 0) { - j_body = env->NewByteArray(static_cast(req->body_len)); - if (j_body != nullptr) { - env->SetByteArrayRegion(j_body, 0, static_cast(req->body_len), - reinterpret_cast(req->body_bytes)); - } + rac_result_t body_rc = build_request_body(env, req, &j_body); + if (body_rc != RAC_SUCCESS) { + env->DeleteLocalRef(j_method); + env->DeleteLocalRef(j_url); + env->DeleteLocalRef(j_headers); + return body_rc; } jlong j_timeout_ms = static_cast(req->timeout_ms);