Skip to content

Commit 955f3d2

Browse files
authored
Manage early-exit of ByteBufferBIO in tests. (#286)
Motivation: Nightly Swift's improvement to the copy propagation pass has caused ByteBufferBIOTests to start misbehaving, as they now deallocate some ByteBufferBIOs very early in the test case. This is because the backing BIO pointer does not have a strong reference to the ByteBufferBIO. Modifications: - Give the backing BIO a strong ref that can be broken by the ByteBufferBIO calling close(). - Update the tests to call close() Result: No leaking BIOs.
1 parent c4f0dbe commit 955f3d2

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

Sources/NIOSSL/ByteBufferBIO.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,21 +217,36 @@ final class ByteBufferBIO {
217217
preconditionFailure("Unable to initialize custom BIO")
218218
}
219219

220-
// We now need to complete the BIO initialization. The BIO does not have an owned pointer
221-
// to us, as that would create an annoying-to-break reference cycle.
220+
// We now need to complete the BIO initialization. The BIO takes an owned reference to self here,
221+
// which is broken on close().
222222
self.bioPtr = bio
223-
CNIOBoringSSL_BIO_set_data(self.bioPtr, Unmanaged.passUnretained(self).toOpaque())
223+
CNIOBoringSSL_BIO_set_data(self.bioPtr, Unmanaged.passRetained(self).toOpaque())
224224
CNIOBoringSSL_BIO_set_init(self.bioPtr, 1)
225225
CNIOBoringSSL_BIO_set_shutdown(self.bioPtr, 1)
226226
}
227227

228228
deinit {
229-
// On deinit we need to drop our reference to the BIO, and also ensure that it doesn't hold any
230-
// pointers to this object anymore.
231-
CNIOBoringSSL_BIO_set_data(self.bioPtr, nil)
229+
// In debug mode we assert that we've been closed.
230+
assert(CNIOBoringSSL_BIO_get_data(self.bioPtr) == nil, "must call close() on ByteBufferBIO before deinit")
231+
232+
// On deinit we need to drop our reference to the BIO.
232233
CNIOBoringSSL_BIO_free(self.bioPtr)
233234
}
234235

236+
/// Shuts down the BIO, rendering it unable to be used.
237+
///
238+
/// This method is idempotent: it is safe to call more than once.
239+
internal func close() {
240+
guard let selfRef = CNIOBoringSSL_BIO_get_data(self.bioPtr) else {
241+
// Shutdown is safe to call more than once.
242+
return
243+
}
244+
245+
// We consume the original retain of self, and then nil out the ref in the BIO so that this can't happen again.
246+
Unmanaged<ByteBufferBIO>.fromOpaque(selfRef).release()
247+
CNIOBoringSSL_BIO_set_data(self.bioPtr, nil)
248+
}
249+
235250
/// Obtain an owned pointer to the backing BoringSSL BIO object.
236251
///
237252
/// This pointer is safe to use elsewhere, as it has increased the reference to the backing

Sources/NIOSSL/SSLConnection.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,9 @@ internal final class SSLConnection {
403403

404404
// Also drop the reference to the parent channel handler, which is a trivial reference cycle.
405405
self.parentHandler = nil
406+
407+
// And finally drop the data stored by the bytebuffer BIO
408+
self.bio?.close()
406409
}
407410

408411
/// Retrieves any inbound data that has not been processed by BoringSSL.

Tests/NIOSSLTests/ByteBufferBIOTest.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ final class ByteBufferBIOTest: XCTestCase {
2929
}
3030
}
3131

32+
/// This leaks on purpose!
3233
private func retainedBIO() -> UnsafeMutablePointer<BIO> {
3334
let swiftBIO = ByteBufferBIO(allocator: ByteBufferAllocator())
35+
swiftBIO.close()
3436
return swiftBIO.retainedBIO()
3537
}
3638

@@ -39,6 +41,7 @@ final class ByteBufferBIOTest: XCTestCase {
3941
let cBIO = swiftBIO.retainedBIO()
4042
defer {
4143
CNIOBoringSSL_BIO_free(cBIO)
44+
swiftBIO.close()
4245
}
4346

4447
XCTAssertNil(swiftBIO.outboundCiphertext())
@@ -60,6 +63,7 @@ final class ByteBufferBIOTest: XCTestCase {
6063
let cBIO = swiftBIO.retainedBIO()
6164
defer {
6265
CNIOBoringSSL_BIO_free(cBIO)
66+
swiftBIO.close()
6367
}
6468

6569
XCTAssertNil(swiftBIO.outboundCiphertext())
@@ -85,6 +89,7 @@ final class ByteBufferBIOTest: XCTestCase {
8589
let cBIO = swiftBIO.retainedBIO()
8690
defer {
8791
CNIOBoringSSL_BIO_free(cBIO)
92+
swiftBIO.close()
8893
}
8994

9095
var targetBuffer = [UInt8](repeating: 0, count: 512)
@@ -100,6 +105,7 @@ final class ByteBufferBIOTest: XCTestCase {
100105
let cBIO = swiftBIO.retainedBIO()
101106
defer {
102107
CNIOBoringSSL_BIO_free(cBIO)
108+
swiftBIO.close()
103109
}
104110

105111
var inboundBytes = ByteBufferAllocator().buffer(capacity: 1024)
@@ -129,6 +135,7 @@ final class ByteBufferBIOTest: XCTestCase {
129135
let cBIO = swiftBIO.retainedBIO()
130136
defer {
131137
CNIOBoringSSL_BIO_free(cBIO)
138+
swiftBIO.close()
132139
}
133140

134141
var inboundBytes = ByteBufferAllocator().buffer(capacity: 1024)
@@ -181,6 +188,7 @@ final class ByteBufferBIOTest: XCTestCase {
181188
let cBIO = swiftBIO.retainedBIO()
182189
defer {
183190
CNIOBoringSSL_BIO_free(cBIO)
191+
swiftBIO.close()
184192
}
185193

186194
var targetBuffer = [UInt8](repeating: 0, count: 512)
@@ -194,6 +202,7 @@ final class ByteBufferBIOTest: XCTestCase {
194202
let cBIO = swiftBIO.retainedBIO()
195203
defer {
196204
CNIOBoringSSL_BIO_free(cBIO)
205+
swiftBIO.close()
197206
}
198207

199208
var bytesToWrite: [UInt8] = [1, 2, 3, 4, 5]
@@ -227,6 +236,7 @@ final class ByteBufferBIOTest: XCTestCase {
227236
let cBIO = swiftBIO.retainedBIO()
228237
defer {
229238
CNIOBoringSSL_BIO_free(cBIO)
239+
swiftBIO.close()
230240
}
231241

232242
let firstAddress = writeAddress(swiftBIO: swiftBIO, cBIO: cBIO)
@@ -244,6 +254,7 @@ final class ByteBufferBIOTest: XCTestCase {
244254
let cBIO = swiftBIO.retainedBIO()
245255
defer {
246256
CNIOBoringSSL_BIO_free(cBIO)
257+
swiftBIO.close()
247258
}
248259

249260
var bytesToWrite: [UInt8] = [1, 2, 3, 4, 5]
@@ -266,6 +277,7 @@ final class ByteBufferBIOTest: XCTestCase {
266277
let cBIO = swiftBIO.retainedBIO()
267278
defer {
268279
CNIOBoringSSL_BIO_free(cBIO)
280+
swiftBIO.close()
269281
}
270282

271283
XCTAssertNil(swiftBIO.outboundCiphertext())
@@ -286,6 +298,7 @@ final class ByteBufferBIOTest: XCTestCase {
286298
let cBIO = swiftBIO.retainedBIO()
287299
defer {
288300
CNIOBoringSSL_BIO_free(cBIO)
301+
swiftBIO.close()
289302
}
290303

291304
var buffer = ByteBufferAllocator().buffer(capacity: 1024)
@@ -306,6 +319,7 @@ final class ByteBufferBIOTest: XCTestCase {
306319
let cBIO = swiftBIO.retainedBIO()
307320
defer {
308321
CNIOBoringSSL_BIO_free(cBIO)
322+
swiftBIO.close()
309323
}
310324

311325
let originalShutdown = CNIOBoringSSL_BIO_ctrl(cBIO, BIO_CTRL_GET_CLOSE, 0, nil)

0 commit comments

Comments
 (0)