xref: /xnu-12377.81.4/osfmk/kern/host_notify.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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