1 /*
2 * Copyright (c) 2003-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <mach/mach_types.h>
30 #include <mach/mach_host.h>
31
32 #include <ipc/ipc_policy.h>
33
34 #include <kern/kern_types.h>
35 #include <kern/ipc_kobject.h>
36 #include <kern/host_notify.h>
37
38 #include <kern/queue.h>
39
40 #include "mach/host_notify_reply.h"
41
42 struct host_notify_entry {
43 queue_chain_t entries;
44 ipc_port_t port;
45 ipc_port_request_index_t index;
46 };
47
48 LCK_GRP_DECLARE(host_notify_lock_grp, "host_notify");
49 LCK_MTX_DECLARE(host_notify_lock, &host_notify_lock_grp);
50
51 static KALLOC_TYPE_DEFINE(host_notify_zone,
52 struct host_notify_entry, KT_DEFAULT);
53
54 static queue_head_t host_notify_queue[HOST_NOTIFY_TYPE_MAX + 1] = {
55 QUEUE_HEAD_INITIALIZER(host_notify_queue[HOST_NOTIFY_CALENDAR_CHANGE]),
56 QUEUE_HEAD_INITIALIZER(host_notify_queue[HOST_NOTIFY_CALENDAR_SET]),
57 };
58
59 static mach_msg_id_t host_notify_replyid[HOST_NOTIFY_TYPE_MAX + 1] = {
60 HOST_CALENDAR_CHANGED_REPLYID,
61 HOST_CALENDAR_SET_REPLYID,
62 };
63
64 kern_return_t
host_request_notification(host_t host,host_flavor_t notify_type,ipc_port_t port)65 host_request_notification(
66 host_t host,
67 host_flavor_t notify_type,
68 ipc_port_t port)
69 {
70 host_notify_t entry;
71 kern_return_t kr;
72
73 if (host == HOST_NULL) {
74 return KERN_INVALID_ARGUMENT;
75 }
76
77 if (!IP_VALID(port)) {
78 return KERN_INVALID_CAPABILITY;
79 }
80
81 if (notify_type > HOST_NOTIFY_TYPE_MAX || notify_type < 0) {
82 return KERN_INVALID_ARGUMENT;
83 }
84
85 entry = zalloc_flags(host_notify_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
86 entry->port = port;
87
88 again:
89 lck_mtx_lock(&host_notify_lock);
90
91 ip_mq_lock(port);
92 if (ip_active(port)) {
93 kr = ipc_port_request_hnotify_alloc(port, entry, &entry->index);
94 } else {
95 kr = KERN_INVALID_CAPABILITY;
96 }
97
98 if (kr == KERN_SUCCESS) {
99 /*
100 * Preserve original ABI of host-notify ports being immovable
101 * as a side effect of being a kobject.
102 */
103 if (!ip_is_immovable_receive(port)) {
104 ipc_object_label_t label = ip_label_get(port);
105
106 ipc_release_assert(label.io_state == IO_STATE_IN_SPACE);
107 label.io_state = IO_STATE_IN_SPACE_IMMOVABLE;
108 io_label_set_and_put(&port->ip_object, &label);
109 }
110 enqueue_tail(&host_notify_queue[notify_type], &entry->entries);
111 }
112
113 lck_mtx_unlock(&host_notify_lock);
114
115 if (kr == KERN_NO_SPACE) {
116 kr = ipc_port_request_grow(port);
117 /* port unlocked */
118 if (kr == KERN_SUCCESS) {
119 goto again;
120 }
121 } else {
122 ip_mq_unlock(port);
123 }
124
125 if (kr != KERN_SUCCESS) {
126 zfree(host_notify_zone, entry);
127 }
128
129 return kr;
130 }
131
132 void
host_notify_cancel(host_notify_t entry)133 host_notify_cancel(host_notify_t entry)
134 {
135 ipc_port_t port;
136
137 lck_mtx_lock(&host_notify_lock);
138 remqueue((queue_entry_t)entry);
139 port = entry->port;
140 lck_mtx_unlock(&host_notify_lock);
141
142 zfree(host_notify_zone, entry);
143 ipc_port_release_sonce(port);
144 }
145
146 static void
host_notify_all(host_flavor_t notify_type,mach_msg_header_t * msg,mach_msg_size_t msg_size)147 host_notify_all(
148 host_flavor_t notify_type,
149 mach_msg_header_t *msg,
150 mach_msg_size_t msg_size)
151 {
152 queue_head_t send_queue = QUEUE_HEAD_INITIALIZER(send_queue);
153 queue_entry_t e;
154 host_notify_t entry;
155 ipc_port_t port;
156
157 lck_mtx_lock(&host_notify_lock);
158
159 qe_foreach_safe(e, &host_notify_queue[notify_type]) {
160 entry = (host_notify_t)e;
161 port = entry->port;
162
163 ip_mq_lock(port);
164 if (ip_active(port)) {
165 ipc_port_request_cancel(port, IPR_HOST_NOTIFY,
166 entry->index);
167 remqueue(e);
168 enqueue_tail(&send_queue, e);
169 } else {
170 /*
171 * leave the entry in place,
172 * we're racing with ipc_port_dnnotify()
173 * which will call host_notify_cancel().
174 */
175 }
176 ip_mq_unlock(port);
177 }
178
179 lck_mtx_unlock(&host_notify_lock);
180
181 if (queue_empty(&send_queue)) {
182 return;
183 }
184
185 msg->msgh_bits =
186 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0, 0, 0);
187 msg->msgh_local_port = MACH_PORT_NULL;
188 msg->msgh_voucher_port = MACH_PORT_NULL;
189 msg->msgh_id = host_notify_replyid[notify_type];
190
191 qe_foreach_safe(e, &send_queue) {
192 entry = (host_notify_t)e;
193 port = entry->port;
194
195 zfree(host_notify_zone, entry);
196
197 msg->msgh_remote_port = port;
198 (void)mach_msg_send_from_kernel(msg, msg_size);
199 }
200 }
201
202 void
host_notify_calendar_change(void)203 host_notify_calendar_change(void)
204 {
205 __Request__host_calendar_changed_t msg;
206
207 host_notify_all(HOST_NOTIFY_CALENDAR_CHANGE, &msg.Head, sizeof(msg));
208 }
209
210 void
host_notify_calendar_set(void)211 host_notify_calendar_set(void)
212 {
213 __Request__host_calendar_set_t msg;
214
215 host_notify_all(HOST_NOTIFY_CALENDAR_SET, &msg.Head, sizeof(msg));
216 }
217