Skip to content

Commit ec416cd

Browse files
committed
Fix critical stability issues from technical audit
- AccountStateManager: Add 3s execution timeout budget for replayFinalState to prevent Watchdog 0x8BADF00D terminations. - MTWebSocketConnectionInterface: Add 5s cooldown on fallback socket redials to prevent OOMs from connection storms. - FetchV2: Guard knownSize against shrinking on premature stream closures. - WebProxyManager: Introduce carrierRebuildGeneration to fix race conditions during foreground resumes. - AppDelegate: Route OS memory warnings to clear temporary Account caches to mitigate background Jetsam terminations.
1 parent 916ed50 commit ec416cd

6 files changed

Lines changed: 60 additions & 9 deletions

File tree

‎submodules/TelegramCore/Sources/Network/FetchV2.swift‎

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,16 +1062,30 @@ private final class FetchImpl {
10621062

10631063
if actualLength < requestedLength {
10641064
let resultingSize = fetchRange.lowerBound + actualLength
1065-
if let currentKnownSize = self.knownSize {
1066-
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): setting known size to min(\(currentKnownSize), \(resultingSize)) = \(min(currentKnownSize, resultingSize))")
1067-
self.knownSize = min(currentKnownSize, resultingSize)
1065+
let maxCompleted = state.completedRanges.ranges.last?.upperBound ?? 0
1066+
1067+
if resultingSize < maxCompleted {
1068+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): ignoring false EOF at \(resultingSize) (already fetched up to \(maxCompleted))")
10681069
} else {
1069-
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): setting known size to \(resultingSize)")
1070-
self.knownSize = resultingSize
1070+
if let currentKnownSize = self.knownSize {
1071+
if resultingSize > 0 && resultingSize < currentKnownSize && actualLength == 0 {
1072+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): ignoring 0-byte chunk as EOF because current known size is \(currentKnownSize)")
1073+
} else {
1074+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): setting known size to min(\(currentKnownSize), \(resultingSize)) = \(min(currentKnownSize, resultingSize))")
1075+
self.knownSize = min(currentKnownSize, resultingSize)
1076+
}
1077+
} else {
1078+
if actualLength == 0 && fetchRange.lowerBound > 0 {
1079+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): ignoring 0-byte chunk as EOF for unknown size at \(fetchRange.lowerBound)")
1080+
} else {
1081+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): setting known size to \(resultingSize)")
1082+
self.knownSize = resultingSize
1083+
}
1084+
}
1085+
let reportedSize = self.knownSize ?? resultingSize
1086+
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): reporting resource size \(reportedSize)")
1087+
self.onNext(.resourceSizeUpdated(reportedSize))
10711088
}
1072-
let reportedSize = self.knownSize ?? resultingSize
1073-
Logger.shared.log("FetchV2", "\(self.loggingIdentifier): reporting resource size \(reportedSize)")
1074-
self.onNext(.resourceSizeUpdated(reportedSize))
10751089
}
10761090

10771091
state.completedRanges.formUnion(RangeSet<Int64>(partRange))

‎submodules/TelegramCore/Sources/Network/MTWebSocketConnectionInterface.swift‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ final class MTWebSocketConnectionInterface: NSObject, MTTcpConnectionInterface {
182182
/// completes. Buffer outbound bytes until then instead of dropping them.
183183
private static let maximumPendingWriteBytes = 256 * 1024
184184

185+
private static var lastFallbackTime: Double = 0.0
186+
185187
private enum HandshakeState {
186188
case tcpConnecting
187189
case sentUpgradeRequest
@@ -755,6 +757,16 @@ final class MTWebSocketConnectionInterface: NSObject, MTTcpConnectionInterface {
755757
}
756758

757759
if let _ = self.endpointSelector.advance() {
760+
let currentTime = CFAbsoluteTimeGetCurrent()
761+
let timeSinceLastFallback = currentTime - Impl.lastFallbackTime
762+
763+
if timeSinceLastFallback < 5.0 {
764+
Logger.shared.log("MTWebSocket", "[WS] fallback cooldown active (\(timeSinceLastFallback)s), delaying secondary endpoint probe")
765+
self.cancelWithError(error: error)
766+
return
767+
}
768+
Impl.lastFallbackTime = currentTime
769+
758770
Logger.shared.log("MTWebSocket", "[WS] trying secondary endpoint with jitter")
759771

760772
let delay = Double.random(in: 0.1...1.5)

‎submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3946,6 +3946,8 @@ func replayFinalState(
39463946
}
39473947
}
39483948

3949+
let replayStartTime = CFAbsoluteTimeGetCurrent()
3950+
39493951
var peerIdsWithAddedSecretMessages = Set<PeerId>()
39503952

39513953
var updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:]
@@ -4177,6 +4179,11 @@ func replayFinalState(
41774179
var isPremiumUpdated = false
41784180

41794181
for operation in optimizedOperations(finalState.state.operations) {
4182+
if CFAbsoluteTimeGetCurrent() - replayStartTime > 3.0 {
4183+
Logger.shared.log("State", "replayFinalState taking too long (> 3.0s), aborting to trigger state reset")
4184+
return nil
4185+
}
4186+
41804187
switch operation {
41814188
case let .AddMessages(messages, location):
41824189
if case .UpperHistoryBlock = location {

‎submodules/TelegramCore/Sources/State/AccountStateManager.swift‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,8 @@ public final class AccountStateManager {
915915

916916
return (difference, replayedState, false, false)
917917
} else {
918-
return (nil, nil, false, false)
918+
Logger.shared.log("State", "replayFinalState returned nil, triggering state reset")
919+
return (nil, nil, true, true)
919920
}
920921
}
921922
}

‎submodules/TelegramUI/Sources/AppDelegate.swift‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
21242124
|> deliverOnMainQueue).start(next: { activeAccounts in
21252125
for (_, context, _) in activeAccounts.accounts {
21262126
context.account.postbox.clearCaches()
2127+
context.account.resetCachedData()
21272128
}
21282129
Queue.mainQueue().after(1.0, {
21292130
let after = ForkPerformanceTelemetry.mallocHeap()

‎submodules/WebProxyTransport/Sources/WebProxyManager.swift‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public final class WebProxyManager {
6666
private var lastFailureTime: Double = 0.0
6767
private var consecutiveFailureCount: Int = 0
6868
private var sidecarReadySince: Double = 0.0
69+
private var carrierRebuildGeneration: UInt64 = 0
6970

7071
/// Set from `applicationDidEnterBackground`. The WEB carrier is foreground-only.
7172
private var enteredBackgroundAt: Double = 0.0
@@ -356,13 +357,28 @@ public final class WebProxyManager {
356357
}
357358

358359
private func performInPlaceCarrierResume(sidecar: WebProxySidecar, configuration: WebProxyConfiguration) {
360+
self.startLock.lock()
361+
self.carrierRebuildGeneration &+= 1
362+
let generation = self.carrierRebuildGeneration
363+
self.startLock.unlock()
364+
359365
// Rebuild the carrier behind the listener that is already published. On failure the
360366
// sidecar has already torn itself down, so there is nothing left to salvage and the
361367
// port has to change after all.
362368
sidecar.reconnectTransport { [weak self] result in
363369
guard let self else {
364370
return
365371
}
372+
373+
self.startLock.lock()
374+
let isCurrent = self.carrierRebuildGeneration == generation
375+
self.startLock.unlock()
376+
377+
guard isCurrent else {
378+
WebProxyLog.log("resume transport reconnect completed but generation is stale, ignoring")
379+
return
380+
}
381+
366382
switch result {
367383
case .success:
368384
WebProxyLog.log("resume transport reconnect succeeded, notifying networks")

0 commit comments

Comments
 (0)