Skip to content

Commit 8a7f690

Browse files
simplify tunnel activation race protection
1 parent df0edaf commit 8a7f690

File tree

5 files changed

+33
-33
lines changed

5 files changed

+33
-33
lines changed

apple/client/OsStatus.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,12 @@ class OsStatus: Encodable {
1414
var updaterStatus = UpdaterStatus()
1515
var debugBundleStatus = DebugBundleStatus()
1616

17-
init(
18-
strictLeakPrevention: Bool,
19-
osVpnStatus: NEVPNStatus,
20-
) {
17+
init(strictLeakPrevention: Bool, osVpnStatus: NEVPNStatus) {
2118
self.osVpnStatus = osVpnStatus
2219
self.strictLeakPrevention = strictLeakPrevention
2320
}
2421

25-
static func watchable(
26-
manager: NEVPNManager,
27-
) -> WatchableValue<OsStatus> {
22+
static func watchable(manager: NEVPNManager) -> WatchableValue<OsStatus> {
2823
var lastIncludeAllNetworks = switch manager.protocolConfiguration {
2924
case let .some(proto): proto.includeAllNetworks
3025
case nil: false // Report safe default.

apple/shared/NetworkExtensionIpc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum NeManagerCmd: Codable {
1515
case getStatus(knownVersion: UUID?)
1616
case getTrafficStats
1717
case ping
18-
case setTunnelArgs(args: TunnelArgs?)
18+
case setTunnelArgs(args: TunnelArgs?, allowActivation: Bool = false)
1919
}
2020

2121
struct TunnelArgs: Codable {

apple/system-network-extension/PacketTunnelProvider.swift

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
7878

7979
let networkConfig = NetworkConfig(ipv4: "10.75.76.77", dns: ["10.64.0.99"], ipv6: "fc00:bbbb:bbbb:bb01::c:4c4d/128")
8080
try await self.setTunnelNetworkSettings(NEPacketTunnelNetworkSettings.build(networkConfig))
81-
let _: Empty = try await runManagerCmd(.setTunnelArgs(args: tunnelArgs))
81+
let _: Empty = try await runManagerCmd(.setTunnelArgs(args: tunnelArgs, allowActivation: true))
8282

8383
logger.log("set tunnel active flag \(self.providerId, privacy: .public)")
8484
isActiveGuard.value = true
@@ -104,7 +104,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
104104

105105
logger.log("stopping tunnel \(self.providerId, privacy: .public)")
106106
do {
107-
let _: Empty = try await runManagerCmd(.setTunnelArgs(args: .none))
107+
let _: Empty = try await runManagerCmd(.setTunnelArgs(args: .none, allowActivation: false))
108108
} catch {
109109
logger.critical("setting empty tunnel args failed: \(error, privacy: .public)")
110110
}
@@ -123,23 +123,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
123123
logger.error("received app message without completion handler")
124124
return
125125
}
126-
guard let isSetTunnelArgs = try? [String: Empty].self.init(json: msg).keys.contains("setTunnelArgs") else {
127-
logger.error("received invalid app message, expected JSON encoded manager command")
128-
let json_error = try! NeManagerCmdResult.error(errorCodeOther).json().data(using: .utf8)
129-
completionHandler(json_error)
130-
return
131-
}
132126
Task {
133-
let isActiveGuard = isSetTunnelArgs ? await self.isActive.lock() : nil
134-
defer { withExtendedLifetime(isActiveGuard) {}}
135-
if isActiveGuard?.value == .some(false) {
136-
// Prevent accidental tunnel starts due to a "setTunnelArgs" command that lost a race with "stopTunnel".
137-
let json_error = try! NeManagerCmdResult.error(errorCodeTunnelInactive).json().data(using: .utf8)
138-
completionHandler(json_error)
139-
} else {
140-
let json_result = try! await ffiJsonManagerCmd(msg).json()
141-
completionHandler(json_result.data(using: .utf8))
142-
}
127+
let json_result = try! await ffiJsonManagerCmd(msg).json()
128+
completionHandler(json_result.data(using: .utf8))
143129
}
144130
}
145131

rustlib/src/manager.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,26 @@ impl Manager {
148148
self.status_watch.subscribe()
149149
}
150150

151-
pub fn set_target_state(&self, target_args: Option<TunnelArgs>) {
152-
_ = self.target_tunnel_args.send(target_args)
151+
pub fn set_target_state(&self, new_target_args: Option<TunnelArgs>, allow_activation: bool) -> Result<(), ()> {
152+
let mut ret = Ok(());
153+
let ret_ref = &mut ret;
154+
_ = self.target_tunnel_args.send_if_modified(move |target_args| {
155+
if target_args == &new_target_args {
156+
tracing::warn!(
157+
message_id = "oqQ8GZEE",
158+
"not setting target state, because the new one is identical to the current one"
159+
);
160+
return false;
161+
}
162+
if target_args.is_none() && new_target_args.is_some() && !allow_activation {
163+
*ret_ref = Err(());
164+
tracing::warn!(message_id = "juurJ3bm", "not setting target state, because activation is not allowed");
165+
return false;
166+
}
167+
*target_args = new_target_args;
168+
true
169+
});
170+
ret
153171
}
154172

155173
pub fn send_packet(&self, packet: &[u8]) {

rustlib/src/manager_cmd.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub enum ManagerCmdErrorCode {
3131
ApiRateLimitExceeded,
3232
ApiSignupLimitExceeded,
3333
ConfigSaveError,
34+
TunnelInactive,
3435
Other,
3536
}
3637

@@ -84,7 +85,7 @@ pub enum ManagerCmd {
8485
SetApiUrl { url: Option<String> },
8586
SetInNewAccountFlow { value: bool },
8687
SetPinnedExits { exits: Vec<PinnedLocation> },
87-
SetTunnelArgs { args: Option<TunnelArgs> },
88+
SetTunnelArgs { args: Option<TunnelArgs>, allow_activation: bool },
8889
RotateWgKey {},
8990
}
9091

@@ -139,10 +140,10 @@ impl ManagerCmd {
139140
Ok(()) => Ok(ManagerCmdOk::Empty),
140141
Err(err) => Err((&err).into()),
141142
},
142-
ManagerCmd::SetTunnelArgs { args } => {
143-
manager.set_target_state(args);
144-
Ok(ManagerCmdOk::Empty)
145-
}
143+
ManagerCmd::SetTunnelArgs { args, allow_activation } => match manager.set_target_state(args, allow_activation) {
144+
Ok(()) => Ok(ManagerCmdOk::Empty),
145+
Err(()) => Err(ManagerCmdErrorCode::TunnelInactive),
146+
},
146147
ManagerCmd::RotateWgKey {} => match manager.rotate_wg_key() {
147148
Ok(()) => Ok(ManagerCmdOk::Empty),
148149
Err(err) => Err((&err).into()),

0 commit comments

Comments
 (0)