From 13f26971eddf07919f1589c6955c43848aa369e1 Mon Sep 17 00:00:00 2001 From: "Bat.bat" <45396585+williambj1@users.noreply.github.com> Date: Sun, 13 Feb 2022 01:47:34 +1100 Subject: [PATCH] Fix NSRegularExpression memory leak --- HeliPort/NetworkManager.swift | 121 ++++++++++++++---- .../String+NonNullTerminated.swift | 26 ++-- 2 files changed, 114 insertions(+), 33 deletions(-) diff --git a/HeliPort/NetworkManager.swift b/HeliPort/NetworkManager.swift index 42e9862c..7d4f71ba 100644 --- a/HeliPort/NetworkManager.swift +++ b/HeliPort/NetworkManager.swift @@ -168,7 +168,12 @@ final class NetworkManager { return nil } let bsdData = Data(bsd.utf8) - var managementInfoBase = [CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, bsdIndex] + var managementInfoBase = [CTL_NET, + AF_ROUTE, + 0, + AF_LINK, + NET_RT_IFLIST, + bsdIndex] if sysctl(&managementInfoBase, 6, nil, &length, nil, 0) < 0 { Log.error("Could not determine length of info data structure") @@ -214,28 +219,7 @@ final class NetworkManager { } class func getRouterAddress(bsd: String) -> String? { - // from Goshin - let ipAddressRegex = #"\s([a-fA-F0-9\.:]+)(\s|%)"# // for ipv4 and ipv6 - - let routerCommand = ["-c", "netstat -rn", "|", "egrep -o", "default.*\(bsd)"] - guard let routerOutput = Commands.execute(executablePath: .shell, args: routerCommand).0 else { - return nil - } - - let regex = try? NSRegularExpression.init(pattern: ipAddressRegex, options: []) - let firstMatch = regex?.firstMatch(in: routerOutput, - options: [], - range: NSRange(location: 0, length: routerOutput.count)) - if let range = firstMatch?.range(at: 1) { - if let swiftRange = Range(range, in: routerOutput) { - let ipAddr = String(routerOutput[swiftRange]) - return ipAddr - } - } else { - Log.debug("Could not find router ip address") - } - - return nil + return getRouterAddressFromSysctl(bsd) ?? getRouterAddressFromNetstat(bsd) } // from https://stackoverflow.com/questions/30748480/swift-get-devices-wifi-ip-address/30754194#30754194 @@ -318,4 +302,95 @@ final class NetworkManager { } return ITL80211_SECURITY_UNKNOWN } + + private class func getRouterAddressFromNetstat(_ bsd: String) -> String? { + var ipAddr: String? + + autoreleasepool { + // from Goshin + let ipAddressRegex = #"\s([a-fA-F0-9\.:]+)(\s|%)"# // for ipv4 and ipv6 + + let routerCommand = ["-c", "netstat -rn", "|", "egrep -o", "default.*\(bsd)"] + guard let routerOutput = Commands.execute(executablePath: .shell, args: routerCommand).0 else { return } + let regex = try? NSRegularExpression.init(pattern: ipAddressRegex, options: []) + let firstMatch = regex?.firstMatch(in: routerOutput, + options: [], + range: NSRange(location: 0, length: routerOutput.count)) + if let range = firstMatch?.range(at: 1) { + if let swiftRange = Range(range, in: routerOutput) { + ipAddr = String(routerOutput[swiftRange]) + } + } else { + Log.debug("Could not find router ip address") + } + } + + return ipAddr + } + + // Modified from https://stackoverflow.com/a/67780630 to support ipv6 and bsd filtering + // See https://opensource.apple.com/source/network_cmds/network_cmds-606.40.2/netstat.tproj/route.c + private class func getRouterAddressFromSysctl(_ bsd: String) -> String? { + var mib: [Int32] = [CTL_NET, + PF_ROUTE, + 0, + 0, + NET_RT_DUMP2, + 0] + let mibSize = u_int(mib.count) + + var bufSize = 0 + sysctl(&mib, mibSize, nil, &bufSize, nil, 0) + + let buf = UnsafeMutablePointer.allocate(capacity: bufSize) + defer { buf.deallocate() } + buf.initialize(repeating: 0, count: bufSize) + + guard sysctl(&mib, mibSize, buf, &bufSize, nil, 0) == 0 else { return nil } + + // Routes + var next = buf + let lim = next.advanced(by: bufSize) + while next < lim { + let rtm = next.withMemoryRebound(to: rt_msghdr2.self, capacity: 1) { $0.pointee } + var ifname = [CChar](repeating: 0, count: Int(IFNAMSIZ + 1)) + if_indextoname(UInt32(rtm.rtm_index), &ifname) + + if String(cString: ifname) == bsd, let addr = getRouterAddressFromRTM(rtm, next) { + return addr + } + + next = next.advanced(by: Int(rtm.rtm_msglen)) + } + + return nil + } + + private class func getRouterAddressFromRTM(_ rtm: rt_msghdr2, + _ ptr: UnsafeMutablePointer) -> String? { + var rawAddr = ptr.advanced(by: MemoryLayout.stride) + + for idx in 0..) -> String { - var string = String(cString: cString).trimmingCharacters(in: .whitespacesAndNewlines) - .replacingOccurrences(of: "[\n,\r]*", with: "", options: .regularExpression) - if string.count > NWID_LEN { - let pointer = UnsafeRawPointer(cString) - let nsString = NSString(bytes: pointer, length: Int(NWID_LEN), encoding: Encoding.utf8.rawValue) - if let nsString = nsString { - string = nsString as String - } else { - string = "\(string.prefix(Int(NWID_LEN)))" + static func getSSIDFromCString(cString: UnsafePointer) -> String { + var string = String(cString: cString) + // Fixes memory leak, see https://stackoverflow.com/a/37584615 + autoreleasepool { + string = string.trimmingCharacters(in: .whitespacesAndNewlines) + .replacingOccurrences(of: "[\n,\r]*", + with: "", + options: .regularExpression) + if string.count > NWID_LEN { + let pointer = UnsafeRawPointer(cString) + let nsString = NSString(bytes: pointer, length: Int(NWID_LEN), encoding: Encoding.utf8.rawValue) + if let nsString = nsString { + string = nsString as String + } else { + string = "\(string.prefix(Int(NWID_LEN)))" + } } } return string