MachPort.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2022 - 2025 Apple Inc. and the Swift System project authors
  4. Licensed under Apache License v2.0 with Runtime Library Exception
  5. See https://swift.org/LICENSE.txt for license information
  6. */
  7. #if SYSTEM_PACKAGE_DARWIN
  8. #if canImport(Darwin, _version: 310)
  9. import Darwin.Mach
  10. #else
  11. @preconcurrency import Darwin.Mach
  12. #endif
  13. @available(System 1.4.0, *)
  14. public protocol MachPortRight {}
  15. @available(System 1.4.0, *)
  16. @inlinable
  17. internal func _machPrecondition(
  18. file: StaticString = #file,
  19. line: UInt = #line,
  20. _ body: @autoclosure () -> kern_return_t
  21. ) {
  22. let kr = body()
  23. let expected = KERN_SUCCESS
  24. precondition(kr == expected, file: file, line: line)
  25. }
  26. @available(System 1.4.0, *)
  27. @frozen
  28. public enum Mach {
  29. @available(System 1.4.0, *)
  30. public struct Port<RightType: MachPortRight>: ~Copyable {
  31. @usableFromInline
  32. internal var _name: mach_port_name_t
  33. @usableFromInline
  34. internal var _context: mach_port_context_t
  35. /// Transfer ownership of an existing unmanaged Mach port right into a
  36. /// `Mach.Port` by name.
  37. ///
  38. /// This initializer traps if `name` is `MACH_PORT_NULL`, or if `name` is
  39. /// `MACH_PORT_DEAD` and the `RightType` is `Mach.ReceiveRight`.
  40. ///
  41. /// If the type of the right does not match the `RightType` of the
  42. /// `Mach.Port` being constructed, behavior is undefined.
  43. ///
  44. /// The underlying port right will be automatically deallocated at the
  45. /// end of the `Mach.Port` instance's lifetime.
  46. ///
  47. /// This initializer makes a syscall to guard the right.
  48. public init(name: mach_port_name_t) {
  49. precondition(name != mach_port_name_t(MACH_PORT_NULL),
  50. "Mach.Port cannot be initialized with MACH_PORT_NULL")
  51. self._name = name
  52. if RightType.self == ReceiveRight.self {
  53. precondition(
  54. _name != (0xFFFFFFFF as mach_port_name_t) /* MACH_PORT_DEAD */,
  55. "Receive rights cannot be dead names"
  56. )
  57. let secret = mach_port_context_t(arc4random())
  58. _machPrecondition(mach_port_guard(mach_task_self_, name, secret, 0))
  59. self._context = secret
  60. }
  61. else {
  62. self._context = 0
  63. }
  64. }
  65. /// Borrow access to the port name in a block that can perform
  66. /// non-consuming operations.
  67. ///
  68. /// Take care when using this function; many operations consume rights,
  69. /// and send-once rights are easily consumed.
  70. ///
  71. /// If the right is consumed, behavior is undefined.
  72. ///
  73. /// The body block may optionally return something, which will then be
  74. /// returned to the caller of withBorrowedName.
  75. @inlinable
  76. public func withBorrowedName<ReturnType>(
  77. body: (mach_port_name_t) -> ReturnType
  78. ) -> ReturnType {
  79. return body(_name)
  80. }
  81. deinit {
  82. if RightType.self == ReceiveRight.self {
  83. precondition(
  84. _name != (0xFFFFFFFF as mach_port_name_t) /* MACH_PORT_DEAD */,
  85. "Receive rights cannot be dead names"
  86. )
  87. _machPrecondition(
  88. mach_port_destruct(mach_task_self_, _name, 0, _context)
  89. )
  90. } else {
  91. assert(
  92. RightType.self == SendRight.self ||
  93. RightType.self == SendOnceRight.self
  94. )
  95. _machPrecondition(mach_port_deallocate(mach_task_self_, _name))
  96. }
  97. }
  98. }
  99. /// Possible errors that can be thrown by Mach.Port operations.
  100. public enum PortRightError : Error {
  101. /// Returned when an operation cannot be completed, because the Mach
  102. /// port right has become a dead name. This is caused by deallocation of the
  103. /// receive right on the other end.
  104. case deadName
  105. }
  106. /// The MachPortRight type used to manage a receive right.
  107. @frozen
  108. public struct ReceiveRight: MachPortRight {}
  109. /// The MachPortRight type used to manage a send right.
  110. @frozen
  111. public struct SendRight: MachPortRight {}
  112. /// The MachPortRight type used to manage a send-once right.
  113. ///
  114. /// Send-once rights are the most restrictive type of Mach port rights.
  115. /// They cannot create other rights, and are consumed upon use.
  116. ///
  117. /// Upon destruction a send-once notification will be sent to the
  118. /// receiving end.
  119. @frozen
  120. public struct SendOnceRight: MachPortRight {}
  121. }
  122. @available(System 1.4.0, *)
  123. extension Mach.Port where RightType == Mach.ReceiveRight {
  124. /// Transfer ownership of an existing, unmanaged, but already guarded,
  125. /// Mach port right into a Mach.Port by name.
  126. ///
  127. /// This initializer aborts if name is MACH_PORT_NULL.
  128. ///
  129. /// If the type of the right does not match the type T of Mach.Port<T>
  130. /// being constructed, the behavior is undefined.
  131. ///
  132. /// The underlying port right will be automatically deallocated when
  133. /// the Mach.Port object is destroyed.
  134. @available(System 1.4.0, *)
  135. public init(name: mach_port_name_t, context: mach_port_context_t) {
  136. precondition(name != mach_port_name_t(MACH_PORT_NULL),
  137. "Mach.Port cannot be initialized with MACH_PORT_NULL")
  138. self._name = name
  139. self._context = context
  140. }
  141. /// Allocate a new Mach port with a receive right, creating a
  142. /// Mach.Port<Mach.ReceiveRight> to manage it.
  143. ///
  144. /// This initializer will abort if the right could not be created.
  145. /// Callers may assert that a valid right is always returned.
  146. @inlinable
  147. @available(System 1.4.0, *)
  148. public init() {
  149. var storage: mach_port_name_t = mach_port_name_t(MACH_PORT_NULL)
  150. _machPrecondition(
  151. mach_port_allocate(mach_task_self_, MACH_PORT_RIGHT_RECEIVE, &storage)
  152. )
  153. // name-only init will guard ReceiveRights
  154. self.init(name: storage)
  155. }
  156. /// Transfer ownership of the underlying port right to the caller.
  157. ///
  158. /// Returns a tuple containing the Mach port name representing the right,
  159. /// and the context value used to guard the right.
  160. ///
  161. /// This operation liberates the right from management by the Mach.Port,
  162. /// and the underlying right will no longer be automatically deallocated.
  163. ///
  164. /// After this function completes, the Mach.Port is destroyed and no longer
  165. /// usable.
  166. @available(System 1.4.0, *)
  167. public consuming func relinquish(
  168. ) -> (name: mach_port_name_t, context: mach_port_context_t) {
  169. let destructured = (name: _name, context: _context)
  170. discard self
  171. return destructured
  172. }
  173. /// Remove guard and transfer ownership of the underlying port right to
  174. /// the caller.
  175. ///
  176. /// Returns the Mach port name representing the right.
  177. ///
  178. /// This operation liberates the right from management by the Mach.Port,
  179. /// and the underlying right will no longer be automatically deallocated.
  180. ///
  181. /// After this function completes, the Mach.Port is destroyed and no longer
  182. /// usable.
  183. ///
  184. /// This function makes a syscall to remove the guard from
  185. /// Mach.ReceiveRights. Use relinquish() to avoid the syscall and extract
  186. /// the context value along with the port name.
  187. @inlinable
  188. @available(System 1.4.0, *)
  189. public consuming func unguardAndRelinquish() -> mach_port_name_t {
  190. let (name, context) = self.relinquish()
  191. _machPrecondition(mach_port_unguard(mach_task_self_, name, context))
  192. return name
  193. }
  194. /// Borrow access to the port name in a block that can perform
  195. /// non-consuming operations.
  196. ///
  197. /// Take care when using this function; many operations consume rights.
  198. ///
  199. /// If the right is consumed, behavior is undefined.
  200. ///
  201. /// The body block may optionally return something, which will then be
  202. /// returned to the caller of withBorrowedName.
  203. @inlinable
  204. @available(System 1.4.0, *)
  205. public func withBorrowedName<ReturnType>(
  206. body: (mach_port_name_t, mach_port_context_t) -> ReturnType
  207. ) -> ReturnType {
  208. return body(_name, _context)
  209. }
  210. /// Create a send-once right for a given receive right.
  211. ///
  212. /// This does not affect the makeSendCount of the receive right.
  213. ///
  214. /// This function will abort if the right could not be created.
  215. /// Callers may assert that a valid right is always returned.
  216. @inlinable
  217. @available(System 1.4.0, *)
  218. public func makeSendOnceRight() -> Mach.Port<Mach.SendOnceRight> {
  219. // send once rights do not coalesce
  220. var newRight: mach_port_name_t = mach_port_name_t(MACH_PORT_NULL)
  221. var newRightType: mach_port_type_t = MACH_PORT_TYPE_NONE
  222. _machPrecondition(
  223. mach_port_extract_right(
  224. mach_task_self_,
  225. _name,
  226. mach_msg_type_name_t(MACH_MSG_TYPE_MAKE_SEND_ONCE),
  227. &newRight,
  228. &newRightType
  229. )
  230. )
  231. // The value of newRight is validated by the Mach.Port initializer
  232. precondition(newRightType == MACH_MSG_TYPE_MOVE_SEND_ONCE)
  233. return Mach.Port(name: newRight)
  234. }
  235. /// Create a send right for a given receive right.
  236. ///
  237. /// This increments the makeSendCount of the receive right.
  238. ///
  239. /// This function will abort if the right could not be created.
  240. /// Callers may assert that a valid right is always returned.
  241. @inlinable
  242. @available(System 1.4.0, *)
  243. public func makeSendRight() -> Mach.Port<Mach.SendRight> {
  244. let how = MACH_MSG_TYPE_MAKE_SEND
  245. // name is the same because send and recv rights are coalesced
  246. _machPrecondition(
  247. mach_port_insert_right(
  248. mach_task_self_, _name, _name, mach_msg_type_name_t(how)
  249. )
  250. )
  251. return Mach.Port(name: _name)
  252. }
  253. /// Access the make-send count.
  254. ///
  255. /// Each get/set of this property makes a syscall.
  256. @inlinable
  257. @available(System 1.4.0, *)
  258. public var makeSendCount: mach_port_mscount_t {
  259. get {
  260. var status: mach_port_status = mach_port_status()
  261. var size = mach_msg_type_number_t(
  262. MemoryLayout<mach_port_status>.size / MemoryLayout<natural_t>.size
  263. )
  264. withUnsafeMutablePointer(to: &status) {
  265. [ _name = self._name ] in
  266. let status = UnsafeMutableBufferPointer(start: $0, count: 1)
  267. status.withMemoryRebound(to: integer_t.self) {
  268. let info = $0.baseAddress
  269. _machPrecondition(
  270. mach_port_get_attributes(
  271. mach_task_self_, _name, MACH_PORT_RECEIVE_STATUS, info, &size
  272. )
  273. )
  274. }
  275. }
  276. return status.mps_mscount
  277. }
  278. set {
  279. _machPrecondition(mach_port_set_mscount(mach_task_self_, _name, newValue))
  280. }
  281. }
  282. }
  283. @available(System 1.4.0, *)
  284. extension Mach.Port where RightType == Mach.SendRight {
  285. /// Transfer ownership of the underlying port right to the caller.
  286. ///
  287. /// Returns the Mach port name representing the right.
  288. ///
  289. /// This operation liberates the right from management by the Mach.Port,
  290. /// and the underlying right will no longer be automatically deallocated.
  291. ///
  292. /// After this function completes, the Mach.Port is destroyed and no longer
  293. /// usable.
  294. @available(System 1.4.0, *)
  295. public consuming func relinquish() -> mach_port_name_t {
  296. let name = _name
  297. discard self
  298. return name
  299. }
  300. /// Create another send right from a given send right.
  301. ///
  302. /// This does not affect the makeSendCount of the receive right.
  303. ///
  304. /// If the send right being copied has become a dead name, meaning the
  305. /// receiving side has been deallocated, then copySendRight() will throw
  306. /// a Mach.PortRightError.deadName error.
  307. @inlinable
  308. @available(System 1.4.0, *)
  309. public func copySendRight() throws -> Mach.Port<Mach.SendRight> {
  310. let how = MACH_MSG_TYPE_COPY_SEND
  311. // name is the same because send rights are coalesced
  312. let kr = mach_port_insert_right(
  313. mach_task_self_, _name, _name, mach_msg_type_name_t(how)
  314. )
  315. if kr == KERN_INVALID_NAME || kr == KERN_INVALID_CAPABILITY {
  316. throw Mach.PortRightError.deadName
  317. }
  318. _machPrecondition(kr)
  319. return Mach.Port(name: _name)
  320. }
  321. }
  322. @available(System 1.4.0, *)
  323. extension Mach.Port where RightType == Mach.SendOnceRight {
  324. /// Transfer ownership of the underlying port right to the caller.
  325. ///
  326. /// Returns the Mach port name representing the right.
  327. ///
  328. /// This operation liberates the right from management by the Mach.Port,
  329. /// and the underlying right will no longer be automatically deallocated.
  330. ///
  331. /// After this function completes, the Mach.Port is destroyed and no longer
  332. /// usable.
  333. @available(System 1.4.0, *)
  334. public consuming func relinquish() -> mach_port_name_t {
  335. let name = _name
  336. discard self
  337. return name
  338. }
  339. }
  340. #endif