Skip to content

Commit 915d75d

Browse files
committed
fix: prevent use-after-free crash in Redis plugin redisFree
1 parent 53ad63e commit 915d75d

File tree

1 file changed

+25
-34
lines changed

1 file changed

+25
-34
lines changed

Plugins/RedisDriverPlugin/RedisPluginConnection.swift

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -212,20 +212,16 @@ final class RedisPluginConnection: @unchecked Sendable {
212212
redisSetTimeout(ctx, commandTimeout)
213213
redisEnableKeepAliveWithInterval(ctx, 60)
214214

215+
stateLock.lock()
215216
self.context = ctx
217+
stateLock.unlock()
216218

217-
if sslConfig.isEnabled {
218-
do {
219+
do {
220+
if sslConfig.isEnabled {
219221
try connectSSL(ctx)
220-
} catch {
221-
redisFree(ctx)
222-
self.context = nil
223-
throw error
224222
}
225-
}
226223

227-
if let password = password, !password.isEmpty {
228-
do {
224+
if let password = password, !password.isEmpty {
229225
let authArgs: [String]
230226
if let username = username, !username.isEmpty {
231227
authArgs = ["AUTH", username, password]
@@ -234,42 +230,30 @@ final class RedisPluginConnection: @unchecked Sendable {
234230
}
235231
let reply = try executeCommandSync(authArgs)
236232
if case .error(let msg) = reply {
237-
redisFree(ctx)
238-
self.context = nil
239233
throw RedisPluginError(code: 1, message: "AUTH failed: \(msg)")
240234
}
241-
} catch {
242-
redisFree(ctx)
243-
self.context = nil
244-
throw error
245235
}
246-
}
247236

248-
if database != 0 {
249-
do {
237+
if database != 0 {
250238
let reply = try executeCommandSync(["SELECT", String(database)])
251239
if case .error(let msg) = reply {
252-
redisFree(ctx)
253-
self.context = nil
254240
throw RedisPluginError(code: 2, message: "SELECT \(database) failed: \(msg)")
255241
}
256-
} catch {
257-
redisFree(ctx)
258-
self.context = nil
259-
throw error
260242
}
261-
}
262243

263-
do {
264-
let reply = try executeCommandSync(["PING"])
265-
if case .error(let msg) = reply {
266-
redisFree(ctx)
267-
self.context = nil
244+
let pingReply = try executeCommandSync(["PING"])
245+
if case .error(let msg) = pingReply {
268246
throw RedisPluginError(code: 3, message: "PING failed: \(msg)")
269247
}
270248
} catch {
271-
redisFree(ctx)
249+
stateLock.lock()
250+
let handle = self.context
272251
self.context = nil
252+
let ssl = self.sslContext
253+
self.sslContext = nil
254+
stateLock.unlock()
255+
if let handle { redisFree(handle) }
256+
if let ssl { redisFreeSSLContext(ssl) }
273257
throw error
274258
}
275259

@@ -529,11 +513,12 @@ private extension RedisPluginConnection {
529513
if redisGetReply(ctx, &discard) != REDIS_OK { break }
530514
if let d = discard { freeReplyObject(d) }
531515
}
516+
let errCode = Int(ctx.pointee.err)
532517
let errMsg = withUnsafePointer(to: &ctx.pointee.errstr) { ptr in
533518
ptr.withMemoryRebound(to: CChar.self, capacity: 128) { String(cString: $0) }
534519
}
535520
markDisconnected()
536-
throw RedisPluginError(code: Int(ctx.pointee.err), message: errMsg)
521+
throw RedisPluginError(code: errCode, message: errMsg)
537522
}
538523
}
539524
appendedCount += 1
@@ -545,6 +530,7 @@ private extension RedisPluginConnection {
545530
var rawReply: UnsafeMutableRawPointer?
546531
let status = redisGetReply(ctx, &rawReply)
547532
guard status == REDIS_OK, let reply = rawReply else {
533+
let errCode = Int(ctx.pointee.err)
548534
let errMsg = withUnsafePointer(to: &ctx.pointee.errstr) { ptr in
549535
ptr.withMemoryRebound(to: CChar.self, capacity: 128) { String(cString: $0) }
550536
}
@@ -555,7 +541,7 @@ private extension RedisPluginConnection {
555541
}
556542
}
557543
markDisconnected()
558-
throw RedisPluginError(code: Int(ctx.pointee.err), message: errMsg)
544+
throw RedisPluginError(code: errCode, message: errMsg)
559545
}
560546
let replyPtr = reply.assumingMemoryBound(to: redisReply.self)
561547
let parsed = parseReply(replyPtr)
@@ -572,7 +558,12 @@ private extension RedisPluginConnection {
572558
_isConnected = false
573559
stateLock.unlock()
574560
#if canImport(CRedis)
575-
if let handle { redisFree(handle) }
561+
if let handle {
562+
let cleanupQueue = queue
563+
cleanupQueue.async {
564+
redisFree(handle)
565+
}
566+
}
576567
#endif
577568
}
578569

0 commit comments

Comments
 (0)