Help rid of warning with UnsafeMutablePointer<> (with Array.withUnsafeMutablePointer)

I'm getting warning with this line:

        // Initialization of 'UnsafeMutablePointer<thread_act_t>' (aka 'UnsafeMutablePointer<UInt32>') results in a dangling pointer
        // 1. Implicit argument conversion from '[thread_act_t]' (aka 'Array<UInt32>') to 'UnsafePointer<thread_act_t>' (aka 'UnsafePointer<UInt32>') produces a pointer valid only for the duration of the call to 'init(mutating:)'
        // 2. Use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope
        var thread_list: thread_act_array_t? = UnsafeMutablePointer(mutating: [thread_act_t]())

So it's asking to enclose the whole access scope of thread_list inside Array.withUnsafeBufferPointer closure.

But I'm not able to make the change compile.

Here is the whole thing:

import SwiftUI

struct CPUUsageView: View {
    var body: some View {
        Text("\(Self.cpuUsage())")
    }


    // Source for cpuUsage(): https://stackoverflow.com/a/44134397/7786555
    static func cpuUsage() -> Double {
        var kr: kern_return_t
        var task_info_count: mach_msg_type_number_t

        task_info_count = mach_msg_type_number_t(TASK_INFO_MAX)
        var tinfo = [integer_t](repeating: 0, count: Int(task_info_count))

        kr = task_info(mach_task_self_, task_flavor_t(TASK_BASIC_INFO), &tinfo, &task_info_count)
        if kr != KERN_SUCCESS {
            return -1
        }

        // Initialization of 'UnsafeMutablePointer<thread_act_t>' (aka 'UnsafeMutablePointer<UInt32>') results in a dangling pointer
        // 1. Implicit argument conversion from '[thread_act_t]' (aka 'Array<UInt32>') to 'UnsafePointer<thread_act_t>' (aka 'UnsafePointer<UInt32>') produces a pointer valid only for the duration of the call to 'init(mutating:)'
        // 2. Use the 'withUnsafeBufferPointer' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope
        var thread_list: thread_act_array_t? = UnsafeMutablePointer(mutating: [thread_act_t]())
        var thread_count: mach_msg_type_number_t = 0
        defer {
            if let thread_list = thread_list {
                vm_deallocate(mach_task_self_, vm_address_t(bitPattern: thread_list), vm_size_t(Int(thread_count) * MemoryLayout<thread_t>.stride) )
            }
        }

        kr = task_threads(mach_task_self_, &thread_list, &thread_count)

        if kr != KERN_SUCCESS {
            return -1
        }

        var tot_cpu: Double = 0

        if let thread_list = thread_list {

            for j in 0 ..< Int(thread_count) {
                var thread_info_count = mach_msg_type_number_t(THREAD_INFO_MAX)
                var thinfo = [integer_t](repeating: 0, count: Int(thread_info_count))
                kr = thread_info(thread_list[j], thread_flavor_t(THREAD_BASIC_INFO),
                                 &thinfo, &thread_info_count)
                if kr != KERN_SUCCESS {
                    return -1
                }

                let threadBasicInfo = convertThreadInfoToThreadBasicInfo(thinfo)

                if threadBasicInfo.flags != TH_FLAGS_IDLE {
                    tot_cpu += (Double(threadBasicInfo.cpu_usage) / Double(TH_USAGE_SCALE)) * 100.0
                }
            } // for each thread
        }

        return tot_cpu
    }

    

    static func convertThreadInfoToThreadBasicInfo(_ threadInfo: [integer_t]) -> thread_basic_info {
        var result = thread_basic_info()

        result.user_time = time_value_t(seconds: threadInfo[0], microseconds: threadInfo[1])
        result.system_time = time_value_t(seconds: threadInfo[2], microseconds: threadInfo[3])
        result.cpu_usage = threadInfo[4]
        result.policy = threadInfo[5]
        result.run_state = threadInfo[6]
        result.flags = threadInfo[7]
        result.suspend_count = threadInfo[8]
        result.sleep_time = threadInfo[9]

        return result
    }

}

struct CPUUsageView_Previews: PreviewProvider {
    static var previews: some View {
        CPUUsageView()
    }
}
UnsafeMutablePointer(mutating: [thread_act_t]())

is wrong for (at least) two reasons:

  • UnsafeMutablePointer(mutating:) takes a pointer argument. Passing an array results in passing the address of the element storage to the initializer. This address is only valid during the function call.
  • This pointer is then converted to a mutable pointer referencing the same element storage. That might not matter here because the array has no elements, but generally it is undefined behavior.

Apparently that construction is only meant to “somehow initialize” the thread_list variable. In your case the solution is much simpler: You can just initialize the pointer to nil (which is also the implicit initial value for an optional):

var thread_list: thread_act_array_t? // = nil
var thread_count: mach_msg_type_number_t = 0
defer {
   // Release the allocated memory
}
kr = task_threads(mach_task_self_, &thread_list, &thread_count)

task_threads() then allocates (and fills) memory and assings the adress of that memory to thread_list on return.

1 Like

Here this matters a great deal: the Array is immediately freed, so this pointer is instantly dangling. Any attempt to use that pointer is immediately going to lead to an exploitable security vulnerability.

Hence the new warning: this is never safe.

1 Like

Do you know why there is no warning in Xcode Playground for this?

Nope. I recommend using feedback assistant to file a bug.

Close and re-open the playground project, it's showing warning.