Chromium Code Reviews| Index: runtime/bin/secure_socket.cc |
| diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc |
| index f63d9d06f5aecd43b712c741f16e1df9113ab6e5..7d79dc60158e9d7b056afdf79b11bd59ea14812d 100644 |
| --- a/runtime/bin/secure_socket.cc |
| +++ b/runtime/bin/secure_socket.cc |
| @@ -29,6 +29,15 @@ |
| #include "include/dart_api.h" |
| +// Return the error from the containing function if handle is an error handle. |
| +#define RETURN_IF_ERROR(handle) \ |
| + { \ |
| + Dart_Handle __handle = handle; \ |
| + if (Dart_IsError((__handle))) { \ |
| + return __handle; \ |
| + } \ |
| + } |
| + |
| namespace dart { |
| namespace bin { |
| @@ -103,13 +112,30 @@ static SSLFilter* GetFilter(Dart_NativeArguments args) { |
| } |
| -static void SetFilter(Dart_NativeArguments args, SSLFilter* filter) { |
| - Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| +static void DeleteFilter( |
| + void* isolate_data, |
| + Dart_WeakPersistentHandle handle, |
| + void* context_pointer) { |
| + SSLFilter* filter = reinterpret_cast<SSLFilter*>(context_pointer); |
| + delete filter; |
| +} |
| + |
| + |
| +static Dart_Handle SetFilter(Dart_NativeArguments args, SSLFilter* filter) { |
| + ASSERT(filter != NULL); |
| + Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); |
| + RETURN_IF_ERROR(dart_this); |
| ASSERT(Dart_IsInstance(dart_this)); |
| - ThrowIfError(Dart_SetNativeInstanceField( |
| + Dart_Handle err = Dart_SetNativeInstanceField( |
| dart_this, |
| kSSLFilterNativeFieldIndex, |
| - reinterpret_cast<intptr_t>(filter))); |
| + reinterpret_cast<intptr_t>(filter)); |
| + RETURN_IF_ERROR(err); |
| + Dart_NewWeakPersistentHandle(dart_this, |
| + reinterpret_cast<void*>(filter), |
| + sizeof(*filter), |
| + DeleteFilter); |
| + return Dart_Null(); |
| } |
| @@ -134,19 +160,22 @@ static void FreeSecurityContext( |
| } |
| -static void SetSecurityContext(Dart_NativeArguments args, |
| - SSL_CTX* context) { |
| +static Dart_Handle SetSecurityContext(Dart_NativeArguments args, |
| + SSL_CTX* context) { |
| const int approximate_size_of_context = 1500; |
| - Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| + Dart_Handle dart_this = Dart_GetNativeArgument(args, 0); |
| + RETURN_IF_ERROR(dart_this); |
| ASSERT(Dart_IsInstance(dart_this)); |
| - ThrowIfError(Dart_SetNativeInstanceField( |
| + Dart_Handle err = Dart_SetNativeInstanceField( |
| dart_this, |
| kSecurityContextNativeFieldIndex, |
| - reinterpret_cast<intptr_t>(context))); |
| + reinterpret_cast<intptr_t>(context)); |
| + RETURN_IF_ERROR(err); |
| Dart_NewWeakPersistentHandle(dart_this, |
| context, |
| approximate_size_of_context, |
| FreeSecurityContext); |
| + return Dart_Null(); |
| } |
| @@ -171,9 +200,19 @@ static void SetAlpnProtocolList(Dart_Handle protocols_handle, |
| void FUNCTION_NAME(SecureSocket_Init)(Dart_NativeArguments args) { |
| Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| - SSLFilter* filter = new SSLFilter; |
| - SetFilter(args, filter); |
| - filter->Init(dart_this); |
| + SSLFilter* filter = new SSLFilter(); |
| + Dart_Handle err = SetFilter(args, filter); |
| + if (Dart_IsError(err)) { |
| + delete filter; |
| + Dart_PropagateError(err); |
| + } |
| + err = filter->Init(dart_this); |
| + if (Dart_IsError(err)) { |
| + // The finalizer was set up by SetFilter. It will delete `filter` if there |
| + // is an error. |
| + filter->Destroy(); |
| + Dart_PropagateError(err); |
| + } |
| } |
| @@ -215,9 +254,13 @@ void FUNCTION_NAME(SecureSocket_Connect)(Dart_NativeArguments args) { |
| void FUNCTION_NAME(SecureSocket_Destroy)(Dart_NativeArguments args) { |
| SSLFilter* filter = GetFilter(args); |
| - SetFilter(args, NULL); |
| + // The SSLFilter is deleted in the finalizer for the Dart object created by |
| + // SetFilter. There is no need to NULL-out the native field for the SSLFilter |
| + // here because the SSLFilter won't be deleted until the finalizer for the |
| + // Dart object runs while the Dart object is being GCd. This approach avoids a |
| + // leak if Destroy isn't called, and avoids a NULL-dereference if Destroy is |
| + // called more than once. |
|
Ivan Posva
2016/03/02 06:17:21
Thanks!
|
| filter->Destroy(); |
| - delete filter; |
| } |
| @@ -271,7 +314,8 @@ void FUNCTION_NAME(SecureSocket_RegisterBadCertificateCallback)( |
| void FUNCTION_NAME(SecureSocket_PeerCertificate) |
| (Dart_NativeArguments args) { |
| - Dart_SetReturnValue(args, GetFilter(args)->PeerCertificate()); |
| + Dart_Handle cert = ThrowIfError(GetFilter(args)->PeerCertificate()); |
| + Dart_SetReturnValue(args, cert); |
| } |
| @@ -281,19 +325,34 @@ void FUNCTION_NAME(SecureSocket_FilterPointer)(Dart_NativeArguments args) { |
| } |
| +static void ReleaseCertificate( |
| + void* isolate_data, |
| + Dart_WeakPersistentHandle handle, |
| + void* context_pointer) { |
| + X509* cert = reinterpret_cast<X509*>(context_pointer); |
| + X509_free(cert); |
| +} |
| + |
| + |
| +// Returns the handle for a Dart object wrapping the X509 certificate object. |
| +// The caller should own a reference to the X509 object whose reference count |
| +// won't drop to zero before the ReleaseCertificate finalizer runs. |
| static Dart_Handle WrappedX509Certificate(X509* certificate) { |
| + const intptr_t approximate_size_of_certificate = 1500; |
| if (certificate == NULL) { |
| return Dart_Null(); |
| } |
| Dart_Handle x509_type = |
| DartUtils::GetDartType(DartUtils::kIOLibURL, "X509Certificate"); |
| if (Dart_IsError(x509_type)) { |
| + X509_free(certificate); |
| return x509_type; |
| } |
| Dart_Handle arguments[] = { NULL }; |
| Dart_Handle result = |
| Dart_New(x509_type, DartUtils::NewString("_"), 0, arguments); |
| if (Dart_IsError(result)) { |
| + X509_free(certificate); |
| return result; |
| } |
| ASSERT(Dart_IsInstance(result)); |
| @@ -302,8 +361,13 @@ static Dart_Handle WrappedX509Certificate(X509* certificate) { |
| kX509NativeFieldIndex, |
| reinterpret_cast<intptr_t>(certificate)); |
| if (Dart_IsError(status)) { |
| + X509_free(certificate); |
| return status; |
| } |
| + Dart_NewWeakPersistentHandle(result, |
| + reinterpret_cast<void*>(certificate), |
| + approximate_size_of_certificate, |
| + ReleaseCertificate); |
| return result; |
| } |
| @@ -326,6 +390,11 @@ int CertificateCallback(int preverify_ok, X509_STORE_CTX* store_ctx) { |
| if (Dart_IsNull(callback)) { |
| return 0; |
| } |
| + |
| + // Upref since the Dart X509 object may outlive the SecurityContext. |
| + if (certificate != NULL) { |
| + X509_up_ref(certificate); |
| + } |
| Dart_Handle args[1]; |
| args[0] = WrappedX509Certificate(certificate); |
| if (Dart_IsError(args[0])) { |
| @@ -354,7 +423,11 @@ void FUNCTION_NAME(SecurityContext_Allocate)(Dart_NativeArguments args) { |
| SSL_CTX_set_min_version(context, TLS1_VERSION); |
| SSL_CTX_set_cipher_list(context, "HIGH:MEDIUM"); |
| SSL_CTX_set_cipher_list_tls11(context, "HIGH:MEDIUM"); |
| - SetSecurityContext(args, context); |
| + Dart_Handle err = SetSecurityContext(args, context); |
| + if (Dart_IsError(err)) { |
| + SSL_CTX_free(context); |
| + Dart_PropagateError(err); |
| + } |
| } |
| @@ -1154,7 +1227,7 @@ bool SSLFilter::ProcessAllBuffers(int starts[kNumBuffers], |
| } |
| -void SSLFilter::Init(Dart_Handle dart_this) { |
| +Dart_Handle SSLFilter::Init(Dart_Handle dart_this) { |
| if (!library_initialized_) { |
| InitializeLibrary(); |
| } |
| @@ -1168,24 +1241,40 @@ void SSLFilter::Init(Dart_Handle dart_this) { |
| bad_certificate_callback_ = Dart_NewPersistentHandle(Dart_Null()); |
| ASSERT(bad_certificate_callback_ != NULL); |
| - InitializeBuffers(dart_this); |
| + // Caller handles cleanup on an error. |
| + return InitializeBuffers(dart_this); |
| } |
| -void SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
| +Dart_Handle SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
| // Create SSLFilter buffers as ExternalUint8Array objects. |
| - Dart_Handle dart_buffers_object = ThrowIfError( |
| - Dart_GetField(dart_this, DartUtils::NewString("buffers"))); |
| - Dart_Handle secure_filter_impl_type = |
| - Dart_InstanceGetType(dart_this); |
| - Dart_Handle dart_buffer_size = ThrowIfError( |
| - Dart_GetField(secure_filter_impl_type, DartUtils::NewString("SIZE"))); |
| - int64_t buffer_size = DartUtils::GetIntegerValue(dart_buffer_size); |
| - Dart_Handle dart_encrypted_buffer_size = ThrowIfError( |
| - Dart_GetField(secure_filter_impl_type, |
| - DartUtils::NewString("ENCRYPTED_SIZE"))); |
| - int64_t encrypted_buffer_size = |
| - DartUtils::GetIntegerValue(dart_encrypted_buffer_size); |
| + Dart_Handle buffers_string = DartUtils::NewString("buffers"); |
| + RETURN_IF_ERROR(buffers_string); |
| + Dart_Handle dart_buffers_object = Dart_GetField(dart_this, buffers_string); |
| + RETURN_IF_ERROR(dart_buffers_object); |
| + Dart_Handle secure_filter_impl_type = Dart_InstanceGetType(dart_this); |
| + RETURN_IF_ERROR(secure_filter_impl_type); |
| + Dart_Handle size_string = DartUtils::NewString("SIZE"); |
| + RETURN_IF_ERROR(size_string); |
| + Dart_Handle dart_buffer_size = Dart_GetField( |
| + secure_filter_impl_type, size_string); |
| + RETURN_IF_ERROR(dart_buffer_size); |
| + |
| + int64_t buffer_size = 0; |
| + Dart_Handle err = Dart_IntegerToInt64(dart_buffer_size, &buffer_size); |
| + RETURN_IF_ERROR(err); |
| + |
| + Dart_Handle encrypted_size_string = DartUtils::NewString("ENCRYPTED_SIZE"); |
| + RETURN_IF_ERROR(encrypted_size_string); |
| + |
| + Dart_Handle dart_encrypted_buffer_size = Dart_GetField( |
| + secure_filter_impl_type, encrypted_size_string); |
| + RETURN_IF_ERROR(dart_encrypted_buffer_size); |
| + |
| + int64_t encrypted_buffer_size = 0; |
| + err = Dart_IntegerToInt64(dart_encrypted_buffer_size, &encrypted_buffer_size); |
| + RETURN_IF_ERROR(err); |
| + |
| if (buffer_size <= 0 || buffer_size > 1 * MB) { |
| FATAL("Invalid buffer size in _ExternalBuffer"); |
| } |
| @@ -1195,21 +1284,44 @@ void SSLFilter::InitializeBuffers(Dart_Handle dart_this) { |
| buffer_size_ = static_cast<int>(buffer_size); |
| encrypted_buffer_size_ = static_cast<int>(encrypted_buffer_size); |
| - |
| Dart_Handle data_identifier = DartUtils::NewString("data"); |
| + RETURN_IF_ERROR(data_identifier); |
| + |
| + for (int i = 0; i < kNumBuffers; i++) { |
| + int size = isBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
| + buffers_[i] = new uint8_t[size]; |
| + ASSERT(buffers_[i] != NULL); |
| + dart_buffer_objects_[i] = NULL; |
| + } |
| + |
| + Dart_Handle result = Dart_Null(); |
| for (int i = 0; i < kNumBuffers; ++i) { |
| int size = isBufferEncrypted(i) ? encrypted_buffer_size_ : buffer_size_; |
| - dart_buffer_objects_[i] = |
| - Dart_NewPersistentHandle(Dart_ListGetAt(dart_buffers_object, i)); |
| + result = Dart_ListGetAt(dart_buffers_object, i); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| + |
| + dart_buffer_objects_[i] = Dart_NewPersistentHandle(result); |
| ASSERT(dart_buffer_objects_[i] != NULL); |
| - buffers_[i] = new uint8_t[size]; |
| - Dart_Handle data = ThrowIfError( |
| - Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size)); |
| - ThrowIfError( |
| - Dart_SetField(Dart_HandleFromPersistent(dart_buffer_objects_[i]), |
| - data_identifier, |
| - data)); |
| + Dart_Handle data = |
| + Dart_NewExternalTypedData(Dart_TypedData_kUint8, buffers_[i], size); |
| + if (Dart_IsError(data)) { |
| + result = data; |
| + break; |
| + } |
| + result = Dart_HandleFromPersistent(dart_buffer_objects_[i]); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| + result = Dart_SetField(result, data_identifier, data); |
| + if (Dart_IsError(result)) { |
| + break; |
| + } |
| } |
| + |
| + // Caller handles cleanup on an error. |
| + return result; |
| } |
| @@ -1241,12 +1353,10 @@ void SSLFilter::InitializeLibrary() { |
| Dart_Handle SSLFilter::PeerCertificate() { |
| + // SSL_get_peer_certificate incs the refcount of certificate. X509_free is |
| + // called by the finalizer set up by WrappedX509Certificate. |
| X509* certificate = SSL_get_peer_certificate(ssl_); |
| - Dart_Handle x509_object = WrappedX509Certificate(certificate); |
| - if (Dart_IsError(x509_object)) { |
| - Dart_PropagateError(x509_object); |
| - } |
| - return x509_object; |
| + return WrappedX509Certificate(certificate); |
| } |
| @@ -1484,7 +1594,7 @@ void SSLFilter::Renegotiate(bool use_session_cache, |
| } |
| -void SSLFilter::Destroy() { |
| +SSLFilter::~SSLFilter() { |
| if (ssl_ != NULL) { |
| SSL_free(ssl_); |
| ssl_ = NULL; |
| @@ -1498,13 +1608,37 @@ void SSLFilter::Destroy() { |
| hostname_ = NULL; |
| } |
| for (int i = 0; i < kNumBuffers; ++i) { |
| - Dart_DeletePersistentHandle(dart_buffer_objects_[i]); |
| - delete[] buffers_[i]; |
| + if (buffers_[i] != NULL) { |
| + delete[] buffers_[i]; |
| + buffers_[i] = NULL; |
| + } |
| + } |
| +} |
| + |
| + |
| +void SSLFilter::Destroy() { |
| + for (int i = 0; i < kNumBuffers; ++i) { |
| + if (dart_buffer_objects_[i] != NULL) { |
| + Dart_DeletePersistentHandle(dart_buffer_objects_[i]); |
| + dart_buffer_objects_[i] = NULL; |
| + } |
| + } |
| + if (string_start_ != NULL) { |
| + Dart_DeletePersistentHandle(string_start_); |
| + string_start_ = NULL; |
| + } |
| + if (string_length_ != NULL) { |
| + Dart_DeletePersistentHandle(string_length_); |
| + string_length_ = NULL; |
| + } |
| + if (handshake_complete_ != NULL) { |
| + Dart_DeletePersistentHandle(handshake_complete_); |
| + handshake_complete_ = NULL; |
| + } |
| + if (bad_certificate_callback_ != NULL) { |
| + Dart_DeletePersistentHandle(bad_certificate_callback_); |
| + bad_certificate_callback_ = NULL; |
| } |
| - Dart_DeletePersistentHandle(string_start_); |
| - Dart_DeletePersistentHandle(string_length_); |
| - Dart_DeletePersistentHandle(handshake_complete_); |
| - Dart_DeletePersistentHandle(bad_certificate_callback_); |
| } |