diff options
| -rw-r--r-- | ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift | 16 | ||||
| -rw-r--r-- | ios/MullvadRustRuntime/include/mullvad_rust_runtime.h | 11 | ||||
| -rw-r--r-- | mullvad-ios/src/api_client/address_cache_provider.rs | 26 |
3 files changed, 37 insertions, 16 deletions
diff --git a/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift b/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift index 4c731eaf3a..5de81c733e 100644 --- a/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift +++ b/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift @@ -15,17 +15,13 @@ public func iniSwiftAddressCacheWrapper(provider: DefaultAddressCacheProvider) - } @_cdecl("swift_get_cached_endpoint") -func getCacheEndpoint(rawAddressCacheProvider: UnsafeMutableRawPointer) -> UnsafePointer<CChar>! { +func getCacheEndpoint(rawAddressCacheProvider: UnsafeMutableRawPointer) -> LateStringDeallocator { let addressCacheProvider = Unmanaged<DefaultAddressCacheProvider>.fromOpaque(rawAddressCacheProvider) .takeUnretainedValue() let cStr = addressCacheProvider.getCurrentEndpoint().description.toCStringPointer() - /** - `cStr` needs to shortly outlive the return of this function in order to get transformed into a `SocketAddr` - This is the simplest way to guarantee that the pointer returned does not get deallocated immediately - Or that no memory is leaked every time this function gets called - **/ - DispatchQueue(label: "com.MullvadRustRuntime.DecallocateQueue").async { - cStr?.deallocate() - } - return cStr + return LateStringDeallocator(ptr: cStr, deallocate_ptr: deallocate_pointer(pointer:)) +} + +func deallocate_pointer(pointer: UnsafePointer<CChar>?) { + pointer?.deallocate() } diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h index 70c1428098..08bb177116 100644 --- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h +++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h @@ -77,6 +77,15 @@ typedef struct SwiftRetryStrategy { struct RetryStrategy *_0; } SwiftRetryStrategy; +/** + * A struct used to deallocate a pointer to a C String later than when the pointer's control is relinquished from Swift. + * Use the `deallocate_ptr` function on `ptr` to call the custom deallocator provided by Swift. + */ +typedef struct LateStringDeallocator { + const char *ptr; + void (*deallocate_ptr)(const char*); +} LateStringDeallocator; + typedef struct SwiftMullvadApiResponse { uint8_t *body; uintptr_t body_size; @@ -316,7 +325,7 @@ struct SwiftCancelHandle mullvad_ios_delete_account(struct SwiftApiContext api_c * `rawAddressCacheProvider` **must** be provided by a call to `init_swift_address_cache_wrapper` * It is okay to persist it, and use it accross multiple threads. */ -extern const char *swift_get_cached_endpoint(const void *rawAddressCacheProvider); +extern struct LateStringDeallocator swift_get_cached_endpoint(const void *rawAddressCacheProvider); /** * Called by the Swift side in order to provide an object to rust that provides API addresses in a UTF-8 string form diff --git a/mullvad-ios/src/api_client/address_cache_provider.rs b/mullvad-ios/src/api_client/address_cache_provider.rs index ff9f4a13e8..f813683445 100644 --- a/mullvad-ios/src/api_client/address_cache_provider.rs +++ b/mullvad-ios/src/api_client/address_cache_provider.rs @@ -8,7 +8,9 @@ extern "C" { /// # SAFETY /// `rawAddressCacheProvider` **must** be provided by a call to `init_swift_address_cache_wrapper` /// It is okay to persist it, and use it accross multiple threads. - pub fn swift_get_cached_endpoint(rawAddressCacheProvider: *const c_void) -> *const c_char; + pub fn swift_get_cached_endpoint( + rawAddressCacheProvider: *const c_void, + ) -> LateStringDeallocator; } #[derive(Debug)] @@ -40,16 +42,30 @@ pub struct SwiftAddressCacheProviderContext { address_cache: *const c_void, } +/// A struct used to deallocate a pointer to a C String later than when the pointer's control is relinquished from Swift. +/// Use the `deallocate_ptr` function on `ptr` to call the custom deallocator provided by Swift. +#[repr(C)] +pub struct LateStringDeallocator { + ptr: *const c_char, + deallocate_ptr: unsafe extern "C" fn(*const c_char), +} + impl SwiftAddressCacheProviderContext { pub fn get_addrs(&self) -> SocketAddr { // SAFETY: See notice for `swift_get_cached_endpoint` - let raw_address = unsafe { swift_get_cached_endpoint(self.address_cache) }; + let deallocator = unsafe { swift_get_cached_endpoint(self.address_cache) }; - // SAFETY: The pointer returned by `swift_get_cached_endpoint` is guaranteed to point to a valid UTF-8 String + // SAFETY: The pointer contained in the late deallocator returned by `swift_get_cached_endpoint` + // is guaranteed to point to a valid UTF-8 String // It is also guaranteed to be a valid representation of either an IPv4 or IPv6 address - unsafe { convert_c_string(raw_address) } + let cached_address = unsafe { convert_c_string(deallocator.ptr) } .parse() - .expect("Invalid socket address in cache") + .expect("Invalid socket address in cache"); + + // SAFETY: The pointer in `deallocator.ptr` must not be used after `deallocate_ptr` has been called. + // `deallocate_ptr` must be called only once + unsafe { (deallocator.deallocate_ptr)(deallocator.ptr) }; + cached_address } } |
