diff --git a/StikDebug/Device/JITEnableContext.swift b/StikDebug/Device/JITEnableContext.swift index 7a2d8a3..b217279 100644 --- a/StikDebug/Device/JITEnableContext.swift +++ b/StikDebug/Device/JITEnableContext.swift @@ -17,6 +17,37 @@ typealias SyslogErrorHandler = (NSError?) -> Void final class JITEnableContext { static let shared = JITEnableContext() + private static func withCStringArray( + _ strings: [String], _ body: (UnsafePointer?>?, UInt) -> R + ) -> R { + if strings.isEmpty { + return body(nil, 0) + } + var cStrings: [UnsafeMutablePointer?] = strings.map { strdup($0) } + defer { cStrings.forEach { free($0) } } + return cStrings.withUnsafeBufferPointer { buffer in + buffer.baseAddress!.withMemoryRebound(to: UnsafePointer?.self, capacity: buffer.count) { rebound in + body(rebound, UInt(buffer.count)) + } + } + } + + private static func mallocDebugEnvVars() -> [String] { + let defaults = UserDefaults.standard + let enableAll = defaults.bool(forKey: UserDefaults.Keys.mallocDebug) + let guardEdges = enableAll || defaults.bool(forKey: UserDefaults.Keys.mallocGuardEdges) + let scribble = enableAll || defaults.bool(forKey: UserDefaults.Keys.mallocScribble) + + var envVars: [String] = [] + if guardEdges { + envVars.append("MallocGuardEdges=1") + } + if scribble { + envVars.append("MallocScribble=1") + } + return envVars + } + private struct TunnelHandles { var adapter: OpaquePointer? var handshake: OpaquePointer? @@ -603,7 +634,9 @@ final class JITEnableContext { try withProcessControl(remoteServer: remoteServer) { processControl in var pid: UInt64 = 0 let ffiError = bundleID.withCString { bundleID in - process_control_launch_app(processControl, bundleID, nil, 0, nil, 0, true, false, &pid) + Self.withCStringArray(Self.mallocDebugEnvVars()) { envPtr, envCount in + process_control_launch_app(processControl, bundleID, envPtr, envCount, nil, 0, true, false, &pid) + } } if let ffiError { diff --git a/StikDebug/Support/UserDefaults+Keys.swift b/StikDebug/Support/UserDefaults+Keys.swift index 85487e0..6f95a66 100644 --- a/StikDebug/Support/UserDefaults+Keys.swift +++ b/StikDebug/Support/UserDefaults+Keys.swift @@ -15,5 +15,9 @@ extension UserDefaults { static let defaultScriptName = "DefaultScriptName" static let defaultScriptNameValue = "" static let targetDeviceIP = "TunnelDeviceIP" + /// Attaches processes with MallocGuardEdges and MallocScribble. + static let mallocDebug = "enableMallocDebug" + static let mallocGuardEdges = "enableMallocGuardEdges" + static let mallocScribble = "enableMallocScribble" } } diff --git a/StikDebug/Views/SettingsView.swift b/StikDebug/Views/SettingsView.swift index c876037..adac8ac 100644 --- a/StikDebug/Views/SettingsView.swift +++ b/StikDebug/Views/SettingsView.swift @@ -19,6 +19,7 @@ struct SettingsView: View { @AppStorage("keepAliveAudio") private var keepAliveAudio = true @AppStorage("keepAliveLocation") private var keepAliveLocation = true @AppStorage(UserDefaults.Keys.targetDeviceIP) private var targetDeviceIP = DeviceConnectionContext.defaultTargetIPAddress + @AppStorage(UserDefaults.Keys.mallocDebug) private var mallocDebug = false @State private var isShowingPairingFilePicker = false @State private var isImportingFile = false @@ -131,6 +132,16 @@ struct SettingsView: View { } } + Section("Debugging") { + Toggle(isOn: $mallocDebug) { + VStack(alignment: .leading, spacing: 2) { + Text("MallocGuardEdges / MallocScribble") + Text("Attaches processes with MallocGuardEdges and MallocScribble to detect memory corruption.") + .font(.caption).foregroundStyle(.secondary) + } + } + } + Section("Advanced") { HStack { Text("Target Device IP")