Skip to content

Commit 499bcb4

Browse files
committed
Use workaround to fix double free error
1 parent 7a9aa72 commit 499bcb4

File tree

9 files changed

+41
-26
lines changed

9 files changed

+41
-26
lines changed

Firestore/Example/Firestore.xcodeproj/xcshareddata/xcschemes/Firestore_IntegrationTests_iOS.xcscheme

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
2929
shouldUseLaunchSchemeArgsEnv = "YES"
30+
enableAddressSanitizer = "YES"
3031
enableASanStackUseAfterReturn = "YES">
3132
<MacroExpansion>
3233
<BuildableReference
@@ -37,6 +38,13 @@
3738
ReferencedContainer = "container:Firestore.xcodeproj">
3839
</BuildableReference>
3940
</MacroExpansion>
41+
<AdditionalOptions>
42+
<AdditionalOption
43+
key = "NSZombieEnabled"
44+
value = "YES"
45+
isEnabled = "YES">
46+
</AdditionalOption>
47+
</AdditionalOptions>
4048
<Testables>
4149
<TestableReference
4250
skipped = "NO">

Firestore/Source/API/FIRCallbackWrapper.mm

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,26 @@
3434

3535
@implementation FIRCallbackWrapper
3636

37+
// In public Swift documentation for integrating Swift and C++, using raw pointers in C++ is
38+
// generally considered unsafe. However, during an experiment where the result was passed as a value
39+
// instead of a pointer, a double free error occurred. This issue could not be traced effectively
40+
// because the implementation resides within the Swift-C++ transition layer. In this specific use
41+
// case, the C++ OnEvent() scope is destroyed after the Swift callback has been destroyed. Due to
42+
// this ordering, using a raw pointer is a safe workaround for now.
3743
+ (PipelineSnapshotListener)wrapPipelineCallback:(std::shared_ptr<api::Firestore>)firestore
38-
completion:(void (^)(PipelineResultVector result,
44+
completion:(void (^)(CppPipelineResult *_Nullable result,
3945
NSError *_Nullable error))completion {
40-
class Converter : public EventListener<std::vector<PipelineResult>> {
46+
class Converter : public EventListener<CppPipelineResult> {
4147
public:
4248
explicit Converter(std::shared_ptr<api::Firestore> firestore, PipelineBlock completion)
4349
: firestore_(firestore), completion_(completion) {
4450
}
4551

46-
void OnEvent(StatusOr<std::vector<PipelineResult>> maybe_snapshot) override {
52+
void OnEvent(StatusOr<CppPipelineResult> maybe_snapshot) override {
4753
if (maybe_snapshot.ok()) {
48-
completion_(
49-
std::initializer_list<PipelineResult>{PipelineResult::GetTestResult(firestore_)},
50-
nullptr);
54+
completion_(&maybe_snapshot.ValueOrDie(), nullptr);
5155
} else {
52-
completion_(std::initializer_list<PipelineResult>{}, MakeNSError(maybe_snapshot.status()));
56+
completion_(nullptr, MakeNSError(maybe_snapshot.status()));
5357
}
5458
}
5559

@@ -58,7 +62,7 @@ void OnEvent(StatusOr<std::vector<PipelineResult>> maybe_snapshot) override {
5862
PipelineBlock completion_;
5963
};
6064

61-
return absl::make_unique<Converter>(firestore, completion);
65+
return std::make_shared<Converter>(firestore, completion);
6266
}
6367

6468
@end

Firestore/Source/Public/FirebaseFirestore/FIRCallbackWrapper.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ namespace core = firebase::firestore::core;
3838

3939
NS_ASSUME_NONNULL_BEGIN
4040

41-
typedef void (^PipelineBlock)(std::vector<api::PipelineResult> result, NSError *_Nullable error)
42-
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
41+
typedef api::PipelineResult CppPipelineResult;
4342

44-
typedef std::vector<api::PipelineResult> PipelineResultVector;
43+
typedef void (^PipelineBlock)(CppPipelineResult *_Nullable result, NSError *_Nullable error)
44+
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
4545

4646
NS_SWIFT_SENDABLE
4747
NS_SWIFT_NAME(CallbackWrapper)
@@ -51,9 +51,9 @@ NS_SWIFT_NAME(CallbackWrapper)
5151
// are invoked on a different thread than the one they were originally defined in. If this callback
5252
// is expected to be called on a different thread, it should be marked as `Sendable` to ensure
5353
// thread safety.
54-
+ (std::shared_ptr<core::EventListener<std::vector<api::PipelineResult>>>)
54+
+ (std::shared_ptr<core::EventListener<api::PipelineResult>>)
5555
wrapPipelineCallback:(std::shared_ptr<api::Firestore>)firestore
56-
completion:(void (^NS_SWIFT_SENDABLE)(PipelineResultVector result,
56+
completion:(void (^NS_SWIFT_SENDABLE)(CppPipelineResult *_Nullable result,
5757
NSError *_Nullable error))completion
5858
NS_SWIFT_NAME(wrapPipelineCallback(firestore:completion:));
5959

Firestore/Swift/Source/SwiftAPI/Pipeline.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@ public struct Pipeline {
2929
}
3030

3131
@discardableResult
32-
public func GetPipelineResult() async throws -> [PipelineResult] {
32+
public func GetPipelineResult() async throws -> PipelineResult {
3333
return try await withCheckedThrowingContinuation { continuation in
3434
let listener = CallbackWrapper.wrapPipelineCallback(firestore: cppObj.GetFirestore()) {
3535
result, error in
3636
if let error {
3737
continuation.resume(throwing: error)
3838
} else {
39-
// Our callbacks guarantee that we either return an error or a progress event.
40-
continuation.resume(returning: PipelineResult.convertToArrayFromCppVector(result))
39+
continuation.resume(returning: PipelineResult(result!.pointee))
4140
}
4241
}
4342
cppObj.GetPipelineResult(listener)

Firestore/Swift/Source/SwiftAPI/PipelineResult.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ public struct PipelineResult {
2424
cppObj = cppSource
2525
}
2626

27-
static func convertToArrayFromCppVector(_ vector: PipelineResultVector)
27+
static func convertToArrayFromCppVector(_ vector: CppPipelineResult)
2828
-> [PipelineResult] {
2929
// Create a Swift array and populate it by iterating over the C++ vector
3030
var swiftArray: [PipelineResult] = []
3131

32-
for index in vector.indices {
33-
let cppResult = vector[index]
34-
swiftArray.append(PipelineResult(cppResult))
35-
}
32+
// for index in vector.indices {
33+
// let cppResult = vector[index]
34+
// swiftArray.append(PipelineResult(cppResult))
35+
// }
3636

3737
return swiftArray
3838
}

Firestore/Swift/Tests/Integration/PipelineTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ final class PipelineTests: FSTIntegrationTestCase {
2121
func testCreatePipeline() async throws {
2222
let pipelineSource: PipelineSource = db.pipeline()
2323
let pipeline: Pipeline = pipelineSource.GetCollection("path")
24-
let _: [PipelineResult] = try await pipeline.GetPipelineResult()
24+
let _ = try await pipeline.GetPipelineResult()
2525
}
2626
}

Firestore/core/interfaceForSwift/api/pipeline.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ void Pipeline::GetPipelineResult(PipelineSnapshotListener callback) const {
4545
/*include_document_metadata_changes=*/true,
4646
/*wait_for_sync_when_online=*/true);
4747

48-
callback->OnEvent(StatusOr<std::vector<PipelineResult>>(
49-
{(PipelineResult::GetTestResult(firestore_))}));
48+
PipelineResult sample = PipelineResult::GetTestResult(firestore_);
49+
50+
StatusOr<PipelineResult> res(sample);
51+
callback->OnEvent(res);
5052

5153
// class ListenOnce : public EventListener<std::vector<PipelineResult>> {
5254
// public:

Firestore/core/interfaceForSwift/api/pipeline.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Firestore;
3535
class PipelineResult;
3636

3737
using PipelineSnapshotListener =
38-
std::shared_ptr<core::EventListener<std::vector<PipelineResult>>>;
38+
std::shared_ptr<core::EventListener<PipelineResult>>;
3939

4040
class Pipeline {
4141
public:

Firestore/core/interfaceForSwift/api/pipeline_result.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#include "Firestore/core/interfaceForSwift/api/pipeline_result.h"
15+
#include <iostream>
16+
1617
#include "Firestore/core/include/firebase/firestore/timestamp.h"
18+
#include "Firestore/core/interfaceForSwift/api/pipeline_result.h"
1719

1820
namespace firebase {
1921
namespace firestore {

0 commit comments

Comments
 (0)