xref: /xnu-10063.101.15/osfmk/kern/kern_cdata.c (revision 94d3b452840153a99b38a3a9659680b2a006908e)
1 /*
2  * Copyright (c) 2015 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 <kern/assert.h>
30 #include <mach/mach_types.h>
31 #include <mach/boolean.h>
32 #include <mach/vm_param.h>
33 #include <kern/kern_types.h>
34 #include <kern/mach_param.h>
35 #include <kern/thread.h>
36 #include <kern/task.h>
37 #include <kern/kern_cdata.h>
38 #include <kern/kalloc.h>
39 #include <kern/ipc_kobject.h>
40 #include <mach/mach_vm.h>
41 
42 static kern_return_t kcdata_get_memory_addr_with_flavor(kcdata_descriptor_t data, uint32_t type, uint32_t size, uint64_t flags, mach_vm_address_t *user_addr);
43 static size_t kcdata_get_memory_size_for_data(uint32_t size);
44 static kern_return_t kcdata_compress_chunk_with_flags(kcdata_descriptor_t data, uint32_t type, const void *input_data, uint32_t input_size, uint64_t flags);
45 static kern_return_t kcdata_compress_chunk(kcdata_descriptor_t data, uint32_t type, const void *input_data, uint32_t input_size);
46 static kern_return_t kcdata_write_compression_stats(kcdata_descriptor_t data);
47 static kern_return_t kcdata_get_compression_stats(kcdata_descriptor_t data, uint64_t *totalout, uint64_t *totalin);
48 static void kcdata_object_no_senders(ipc_port_t port, mach_port_mscount_t mscount);
49 
50 #ifndef ROUNDUP
51 #define ROUNDUP(x, y)            ((((x)+(y)-1)/(y))*(y))
52 #endif
53 
54 /*
55  * zlib will need to store its metadata and this value is indifferent from the
56  * window bits and other zlib internals
57  */
58 #define ZLIB_METADATA_SIZE 1440
59 
60 /* #define kcdata_debug_printf printf */
61 #define kcdata_debug_printf(...) ;
62 
63 #pragma pack(push, 4)
64 
65 /* Internal structs for convenience */
66 struct _uint64_with_description_data {
67 	char desc[KCDATA_DESC_MAXLEN];
68 	uint64_t data;
69 };
70 
71 struct _uint32_with_description_data {
72 	char     desc[KCDATA_DESC_MAXLEN];
73 	uint32_t data;
74 };
75 
76 #pragma pack(pop)
77 
78 int _Atomic lw_corpse_obj_cnt = 0;
79 
80 IPC_KOBJECT_DEFINE(IKOT_KCDATA,
81     .iko_op_stable     = true,
82     .iko_op_no_senders = kcdata_object_no_senders);
83 
84 KALLOC_TYPE_DEFINE(KCDATA_OBJECT, struct kcdata_object, KT_DEFAULT);
85 
86 os_refgrp_decl(static, kcdata_object_refgrp, "kcdata_object", NULL);
87 
88 /* Grab a throttle slot for rate-limited kcdata object type(s) */
89 kern_return_t
kcdata_object_throttle_get(kcdata_obj_flags_t flags)90 kcdata_object_throttle_get(
91 	kcdata_obj_flags_t flags)
92 {
93 	int oval, nval;
94 
95 	/* Currently only lightweight corpse is rate-limited */
96 	assert(flags & KCDATA_OBJECT_TYPE_LW_CORPSE);
97 	if (flags & KCDATA_OBJECT_TYPE_LW_CORPSE) {
98 		os_atomic_rmw_loop(&lw_corpse_obj_cnt, oval, nval, relaxed, {
99 			if (oval >= MAX_INFLIGHT_KCOBJECT_LW_CORPSE) {
100 			        printf("Too many lightweight corpse in flight: %d\n", oval);
101 			        os_atomic_rmw_loop_give_up(return KERN_RESOURCE_SHORTAGE);
102 			}
103 			nval = oval + 1;
104 		});
105 	}
106 
107 	return KERN_SUCCESS;
108 }
109 
110 /* Release a throttle slot for rate-limited kcdata object type(s) */
111 void
kcdata_object_throttle_release(kcdata_obj_flags_t flags)112 kcdata_object_throttle_release(
113 	kcdata_obj_flags_t flags)
114 {
115 	int oval, nval;
116 
117 	/* Currently only lightweight corpse is rate-limited */
118 	assert(flags & KCDATA_OBJECT_TYPE_LW_CORPSE);
119 	if (flags & KCDATA_OBJECT_TYPE_LW_CORPSE) {
120 		os_atomic_rmw_loop(&lw_corpse_obj_cnt, oval, nval, relaxed, {
121 			nval = oval - 1;
122 			if (__improbable(nval < 0)) {
123 			        os_atomic_rmw_loop_give_up(panic("Lightweight corpse kcdata object over-released"));
124 			}
125 		});
126 	}
127 }
128 
129 /*
130  * Create an object representation for the given kcdata.
131  *
132  * Captures kcdata descripter ref in object. If the object creation
133  * should be rate-limited, kcdata_object_throttle_get() must be called
134  * manually before invoking kcdata_create_object(), so as to save
135  * work (of creating the enclosed kcdata blob) if a throttled reference
136  * cannot be obtained in the first place.
137  */
138 kern_return_t
kcdata_create_object(kcdata_descriptor_t data,kcdata_obj_flags_t flags,uint32_t size,kcdata_object_t * objp)139 kcdata_create_object(
140 	kcdata_descriptor_t data,
141 	kcdata_obj_flags_t flags,
142 	uint32_t        size,
143 	kcdata_object_t *objp)
144 {
145 	kcdata_object_t obj;
146 
147 	if (data == NULL) {
148 		return KERN_INVALID_ARGUMENT;
149 	}
150 
151 	obj = zalloc_flags(KCDATA_OBJECT,
152 	    Z_ZERO | Z_WAITOK | Z_NOFAIL | Z_SET_NOTSHARED);
153 
154 	obj->ko_data = data;
155 	obj->ko_flags = flags;
156 	obj->ko_alloc_size = size;
157 	obj->ko_port = IP_NULL;
158 
159 	os_ref_init_count(&obj->ko_refs, &kcdata_object_refgrp, 1);
160 
161 	*objp = obj;
162 
163 	return KERN_SUCCESS;
164 }
165 
166 void
kcdata_object_reference(kcdata_object_t obj)167 kcdata_object_reference(kcdata_object_t obj)
168 {
169 	if (obj == KCDATA_OBJECT_NULL) {
170 		return;
171 	}
172 
173 	os_ref_retain(&obj->ko_refs);
174 }
175 
176 static void
kcdata_object_destroy(kcdata_object_t obj)177 kcdata_object_destroy(kcdata_object_t obj)
178 {
179 	void *begin_addr;
180 	ipc_port_t port;
181 	kcdata_obj_flags_t flags;
182 
183 	if (obj == KCDATA_OBJECT_NULL) {
184 		return;
185 	}
186 
187 	port = obj->ko_port;
188 	flags = obj->ko_flags;
189 
190 	/* Release the port */
191 	if (IP_VALID(port)) {
192 		ipc_kobject_dealloc_port(port, 0, IKOT_KCDATA);
193 	}
194 
195 	/* Release the ref for rate-limited kcdata object type(s) */
196 	kcdata_object_throttle_release(flags);
197 
198 	/* Destroy the kcdata backing captured in the object */
199 	begin_addr = kcdata_memory_get_begin_addr(obj->ko_data);
200 	kfree_data(begin_addr, obj->ko_alloc_size);
201 	kcdata_memory_destroy(obj->ko_data);
202 
203 	/* Free the object */
204 	zfree(KCDATA_OBJECT, obj);
205 }
206 
207 void
kcdata_object_release(kcdata_object_t obj)208 kcdata_object_release(kcdata_object_t obj)
209 {
210 	if (obj == KCDATA_OBJECT_NULL) {
211 		return;
212 	}
213 
214 	if (os_ref_release(&obj->ko_refs) > 0) {
215 		return;
216 	}
217 	/* last ref */
218 
219 	kcdata_object_destroy(obj);
220 }
221 
222 /* Produces kcdata object ref */
223 kcdata_object_t
convert_port_to_kcdata_object(ipc_port_t port)224 convert_port_to_kcdata_object(ipc_port_t port)
225 {
226 	kcdata_object_t obj = KCDATA_OBJECT_NULL;
227 
228 	if (IP_VALID(port)) {
229 		obj = ipc_kobject_get_stable(port, IKOT_KCDATA);
230 		if (obj != KCDATA_OBJECT_NULL) {
231 			zone_require(KCDATA_OBJECT->kt_zv.zv_zone, obj);
232 			kcdata_object_reference(obj);
233 		}
234 	}
235 
236 	return obj;
237 }
238 
239 /* Consumes kcdata object ref */
240 ipc_port_t
convert_kcdata_object_to_port(kcdata_object_t obj)241 convert_kcdata_object_to_port(kcdata_object_t obj)
242 {
243 	if (obj == KCDATA_OBJECT_NULL) {
244 		return IP_NULL;
245 	}
246 
247 	zone_require(KCDATA_OBJECT->kt_zv.zv_zone, obj);
248 
249 	if (!ipc_kobject_make_send_lazy_alloc_port(&obj->ko_port,
250 	    obj, IKOT_KCDATA, IPC_KOBJECT_ALLOC_NONE)) {
251 		kcdata_object_release(obj);
252 	}
253 	/* object ref consumed */
254 
255 	return obj->ko_port;
256 }
257 
258 static void
kcdata_object_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)259 kcdata_object_no_senders(
260 	ipc_port_t port,
261 	__unused mach_port_mscount_t mscount)
262 {
263 	kcdata_object_t obj;
264 
265 	obj = ipc_kobject_get_stable(port, IKOT_KCDATA);
266 	assert(obj != KCDATA_OBJECT_NULL);
267 
268 	/* release the ref given by no-senders notification */
269 	kcdata_object_release(obj);
270 }
271 
272 /*
273  * Estimates how large of a buffer that should be allocated for a buffer that will contain
274  * num_items items of known types with overall length payload_size.
275  *
276  * NOTE: This function will not give an accurate estimate for buffers that will
277  *       contain unknown types (those with string descriptions).
278  */
279 uint32_t
kcdata_estimate_required_buffer_size(uint32_t num_items,uint32_t payload_size)280 kcdata_estimate_required_buffer_size(uint32_t num_items, uint32_t payload_size)
281 {
282 	/*
283 	 * In the worst case each item will need (KCDATA_ALIGNMENT_SIZE - 1) padding
284 	 */
285 	uint32_t max_padding_bytes = 0;
286 	uint32_t max_padding_with_item_description_bytes = 0;
287 	uint32_t estimated_required_buffer_size = 0;
288 	const uint32_t begin_and_end_marker_bytes = 2 * sizeof(struct kcdata_item);
289 
290 	if (os_mul_overflow(num_items, KCDATA_ALIGNMENT_SIZE - 1, &max_padding_bytes)) {
291 		panic("%s: Overflow in required buffer size estimate", __func__);
292 	}
293 
294 	if (os_mul_and_add_overflow(num_items, sizeof(struct kcdata_item), max_padding_bytes, &max_padding_with_item_description_bytes)) {
295 		panic("%s: Overflow in required buffer size estimate", __func__);
296 	}
297 
298 	if (os_add3_overflow(max_padding_with_item_description_bytes, begin_and_end_marker_bytes, payload_size, &estimated_required_buffer_size)) {
299 		panic("%s: Overflow in required buffer size estimate", __func__);
300 	}
301 
302 	return estimated_required_buffer_size;
303 }
304 
305 kcdata_descriptor_t
kcdata_memory_alloc_init(mach_vm_address_t buffer_addr_p,unsigned data_type,unsigned size,unsigned flags)306 kcdata_memory_alloc_init(mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
307 {
308 	kcdata_descriptor_t data = NULL;
309 	mach_vm_address_t user_addr = 0;
310 	uint16_t clamped_flags = (uint16_t) flags;
311 
312 	data = kalloc_type(struct kcdata_descriptor, Z_WAITOK | Z_ZERO | Z_NOFAIL);
313 	data->kcd_addr_begin = buffer_addr_p;
314 	data->kcd_addr_end = buffer_addr_p;
315 	data->kcd_flags = (clamped_flags & KCFLAG_USE_COPYOUT) ? clamped_flags : clamped_flags | KCFLAG_USE_MEMCOPY;
316 	data->kcd_length = size;
317 	data->kcd_endalloced = 0;
318 
319 	/* Initialize the BEGIN header */
320 	if (KERN_SUCCESS != kcdata_get_memory_addr(data, data_type, 0, &user_addr)) {
321 		kcdata_memory_destroy(data);
322 		return NULL;
323 	}
324 
325 	return data;
326 }
327 
328 kern_return_t
kcdata_memory_static_init(kcdata_descriptor_t data,mach_vm_address_t buffer_addr_p,unsigned data_type,unsigned size,unsigned flags)329 kcdata_memory_static_init(kcdata_descriptor_t data, mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
330 {
331 	mach_vm_address_t user_addr = 0;
332 	uint16_t clamped_flags = (uint16_t) flags;
333 
334 	if (data == NULL) {
335 		return KERN_INVALID_ARGUMENT;
336 	}
337 	bzero(data, sizeof(struct kcdata_descriptor));
338 	data->kcd_addr_begin = buffer_addr_p;
339 	data->kcd_addr_end = buffer_addr_p;
340 	data->kcd_flags = (clamped_flags & KCFLAG_USE_COPYOUT) ? clamped_flags : clamped_flags | KCFLAG_USE_MEMCOPY;
341 	data->kcd_length = size;
342 	data->kcd_endalloced = 0;
343 
344 	/* Initialize the BEGIN header */
345 	return kcdata_get_memory_addr(data, data_type, 0, &user_addr);
346 }
347 
348 void *
kcdata_endalloc(kcdata_descriptor_t data,size_t length)349 kcdata_endalloc(kcdata_descriptor_t data, size_t length)
350 {
351 	mach_vm_address_t curend = data->kcd_addr_begin + data->kcd_length;
352 	/* round up allocation and ensure return value is uint64-aligned */
353 	size_t toalloc = ROUNDUP(length, sizeof(uint64_t)) + (curend % sizeof(uint64_t));
354 	/* an arbitrary limit: make sure we don't allocate more then 1/4th of the remaining buffer. */
355 	if (data->kcd_length / 4 <= toalloc) {
356 		return NULL;
357 	}
358 	data->kcd_length -= toalloc;
359 	data->kcd_endalloced += toalloc;
360 	return (void *)(curend - toalloc);
361 }
362 
363 /* Zeros and releases data allocated from the end of the buffer */
364 static void
kcdata_release_endallocs(kcdata_descriptor_t data)365 kcdata_release_endallocs(kcdata_descriptor_t data)
366 {
367 	mach_vm_address_t curend = data->kcd_addr_begin + data->kcd_length;
368 	size_t endalloced = data->kcd_endalloced;
369 	if (endalloced > 0) {
370 		bzero((void *)curend, endalloced);
371 		data->kcd_length += endalloced;
372 		data->kcd_endalloced = 0;
373 	}
374 }
375 
376 void *
kcdata_memory_get_begin_addr(kcdata_descriptor_t data)377 kcdata_memory_get_begin_addr(kcdata_descriptor_t data)
378 {
379 	if (data == NULL) {
380 		return NULL;
381 	}
382 
383 	return (void *)data->kcd_addr_begin;
384 }
385 
386 uint64_t
kcdata_memory_get_used_bytes(kcdata_descriptor_t kcd)387 kcdata_memory_get_used_bytes(kcdata_descriptor_t kcd)
388 {
389 	assert(kcd != NULL);
390 	return ((uint64_t)kcd->kcd_addr_end - (uint64_t)kcd->kcd_addr_begin) + sizeof(struct kcdata_item);
391 }
392 
393 uint64_t
kcdata_memory_get_uncompressed_bytes(kcdata_descriptor_t kcd)394 kcdata_memory_get_uncompressed_bytes(kcdata_descriptor_t kcd)
395 {
396 	kern_return_t kr;
397 
398 	assert(kcd != NULL);
399 	if (kcd->kcd_flags & KCFLAG_USE_COMPRESSION) {
400 		uint64_t totalout, totalin;
401 
402 		kr = kcdata_get_compression_stats(kcd, &totalout, &totalin);
403 		if (kr == KERN_SUCCESS) {
404 			return totalin;
405 		} else {
406 			return 0;
407 		}
408 	} else {
409 		/* If compression wasn't used, get the number of bytes used  */
410 		return kcdata_memory_get_used_bytes(kcd);
411 	}
412 }
413 
414 /*
415  * Free up the memory associated with kcdata
416  */
417 kern_return_t
kcdata_memory_destroy(kcdata_descriptor_t data)418 kcdata_memory_destroy(kcdata_descriptor_t data)
419 {
420 	if (!data) {
421 		return KERN_INVALID_ARGUMENT;
422 	}
423 
424 	/*
425 	 * data->kcd_addr_begin points to memory in not tracked by
426 	 * kcdata lib. So not clearing that here.
427 	 */
428 	kfree_type(struct kcdata_descriptor, data);
429 	return KERN_SUCCESS;
430 }
431 
432 /* Used by zlib to allocate space in its metadata section */
433 static void *
kcdata_compress_zalloc(void * opaque,u_int items,u_int size)434 kcdata_compress_zalloc(void *opaque, u_int items, u_int size)
435 {
436 	void *result;
437 	struct kcdata_compress_descriptor *cd = opaque;
438 	int alloc_size = ~31L & (31 + (items * size));
439 
440 	result = (void *)((uintptr_t)cd->kcd_cd_base + cd->kcd_cd_offset);
441 	if ((uintptr_t) result + alloc_size > (uintptr_t) cd->kcd_cd_base + cd->kcd_cd_maxoffset) {
442 		result = Z_NULL;
443 	} else {
444 		cd->kcd_cd_offset += alloc_size;
445 	}
446 
447 	kcdata_debug_printf("%s: %d * %d = %d  => %p\n", __func__, items, size, items * size, result);
448 
449 	return result;
450 }
451 
452 /* Used by zlib to free previously allocated space in its metadata section */
453 static void
kcdata_compress_zfree(void * opaque,void * ptr)454 kcdata_compress_zfree(void *opaque, void *ptr)
455 {
456 	(void) opaque;
457 	(void) ptr;
458 
459 	kcdata_debug_printf("%s: ptr %p\n", __func__, ptr);
460 
461 	/*
462 	 * Since the buffers we are using are temporary, we don't worry about
463 	 * freeing memory for now. Besides, testing has shown that zlib only calls
464 	 * this at the end, near deflateEnd() or a Z_FINISH deflate() call.
465 	 */
466 }
467 
468 /* Used to initialize the selected compression algorithm's internal state (if any) */
469 static kern_return_t
kcdata_init_compress_state(kcdata_descriptor_t data,void (* memcpy_f)(void *,const void *,size_t),uint64_t type,mach_vm_address_t totalout_addr,mach_vm_address_t totalin_addr)470 kcdata_init_compress_state(kcdata_descriptor_t data, void (*memcpy_f)(void *, const void *, size_t), uint64_t type, mach_vm_address_t totalout_addr, mach_vm_address_t totalin_addr)
471 {
472 	kern_return_t ret = KERN_SUCCESS;
473 	size_t size;
474 	int wbits = 12, memlevel = 3;
475 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
476 
477 	cd->kcd_cd_memcpy_f = memcpy_f;
478 	cd->kcd_cd_compression_type = type;
479 	cd->kcd_cd_totalout_addr = totalout_addr;
480 	cd->kcd_cd_totalin_addr = totalin_addr;
481 
482 	switch (type) {
483 	case KCDCT_ZLIB:
484 		/* allocate space for the metadata used by zlib */
485 		size = round_page(ZLIB_METADATA_SIZE + zlib_deflate_memory_size(wbits, memlevel));
486 		kcdata_debug_printf("%s: size = %zu kcd_length: %d\n", __func__, size, data->kcd_length);
487 		kcdata_debug_printf("%s: kcd buffer [%p - %p]\n", __func__, (void *) data->kcd_addr_begin, (void *) data->kcd_addr_begin + data->kcd_length);
488 		void *buf = kcdata_endalloc(data, size);
489 		if (buf == NULL) {
490 			return KERN_INSUFFICIENT_BUFFER_SIZE;
491 		}
492 
493 		cd->kcd_cd_zs.avail_in = 0;
494 		cd->kcd_cd_zs.next_in = NULL;
495 		cd->kcd_cd_zs.avail_out = 0;
496 		cd->kcd_cd_zs.next_out = NULL;
497 		cd->kcd_cd_zs.opaque = cd;
498 		cd->kcd_cd_zs.zalloc = kcdata_compress_zalloc;
499 		cd->kcd_cd_zs.zfree = kcdata_compress_zfree;
500 		cd->kcd_cd_base = (void *)(data->kcd_addr_begin + data->kcd_length - size);
501 		data->kcd_length -= size;
502 		cd->kcd_cd_offset = 0;
503 		cd->kcd_cd_maxoffset = size;
504 		cd->kcd_cd_flags = 0;
505 
506 		kcdata_debug_printf("%s: buffer [%p - %p]\n", __func__, cd->kcd_cd_base, cd->kcd_cd_base + size);
507 
508 		if (deflateInit2(&cd->kcd_cd_zs, Z_BEST_SPEED, Z_DEFLATED, wbits, memlevel, Z_DEFAULT_STRATEGY) != Z_OK) {
509 			kcdata_debug_printf("EMERGENCY: deflateInit2 failed!\n");
510 			ret = KERN_INVALID_ARGUMENT;
511 		}
512 		break;
513 	default:
514 		panic("kcdata_init_compress_state: invalid compression type: %d", (int) type);
515 	}
516 
517 	return ret;
518 }
519 
520 
521 /*
522  * Turn on the compression logic for kcdata
523  */
524 kern_return_t
kcdata_init_compress(kcdata_descriptor_t data,int hdr_tag,void (* memcpy_f)(void *,const void *,size_t),uint64_t type)525 kcdata_init_compress(kcdata_descriptor_t data, int hdr_tag, void (*memcpy_f)(void *, const void *, size_t), uint64_t type)
526 {
527 	kern_return_t kr;
528 	mach_vm_address_t user_addr, totalout_addr, totalin_addr;
529 	struct _uint64_with_description_data save_data;
530 	const uint64_t size_req = sizeof(save_data);
531 
532 	assert(data && (data->kcd_flags & KCFLAG_USE_COMPRESSION) == 0);
533 
534 	/* reset the compression descriptor */
535 	bzero(&data->kcd_comp_d, sizeof(struct kcdata_compress_descriptor));
536 
537 	/* add the header information */
538 	kcdata_add_uint64_with_description(data, type, "kcd_c_type");
539 
540 	/* reserve space to write total out */
541 	bzero(&save_data, size_req);
542 	strlcpy(&(save_data.desc[0]), "kcd_c_totalout", sizeof(save_data.desc));
543 	kr = kcdata_get_memory_addr(data, KCDATA_TYPE_UINT64_DESC, size_req, &totalout_addr);
544 	if (kr != KERN_SUCCESS) {
545 		return kr;
546 	}
547 	memcpy((void *)totalout_addr, &save_data, size_req);
548 
549 	/* space for total in */
550 	bzero(&save_data, size_req);
551 	strlcpy(&(save_data.desc[0]), "kcd_c_totalin", sizeof(save_data.desc));
552 	kr = kcdata_get_memory_addr(data, KCDATA_TYPE_UINT64_DESC, size_req, &totalin_addr);
553 	if (kr != KERN_SUCCESS) {
554 		return kr;
555 	}
556 	memcpy((void *)totalin_addr, &save_data, size_req);
557 
558 	/* add the inner buffer */
559 	kcdata_get_memory_addr(data, hdr_tag, 0, &user_addr);
560 
561 	/* save the flag */
562 	data->kcd_flags |= KCFLAG_USE_COMPRESSION;
563 
564 	/* initialize algorithm specific state */
565 	kr = kcdata_init_compress_state(data, memcpy_f, type, totalout_addr + offsetof(struct _uint64_with_description_data, data), totalin_addr + offsetof(struct _uint64_with_description_data, data));
566 	if (kr != KERN_SUCCESS) {
567 		kcdata_debug_printf("%s: failed to initialize compression state!\n", __func__);
568 		return kr;
569 	}
570 
571 	return KERN_SUCCESS;
572 }
573 
574 static inline
575 int
kcdata_zlib_translate_kcd_cf_flag(enum kcdata_compression_flush flush)576 kcdata_zlib_translate_kcd_cf_flag(enum kcdata_compression_flush flush)
577 {
578 	switch (flush) {
579 	case KCDCF_NO_FLUSH: return Z_NO_FLUSH;
580 	case KCDCF_SYNC_FLUSH: return Z_SYNC_FLUSH;
581 	case KCDCF_FINISH: return Z_FINISH;
582 	default: panic("invalid kcdata_zlib_translate_kcd_cf_flag flag");
583 	}
584 }
585 
586 static inline
587 int
kcdata_zlib_translate_kcd_cf_expected_ret(enum kcdata_compression_flush flush)588 kcdata_zlib_translate_kcd_cf_expected_ret(enum kcdata_compression_flush flush)
589 {
590 	switch (flush) {
591 	case KCDCF_NO_FLUSH:         /* fall through */
592 	case KCDCF_SYNC_FLUSH: return Z_OK;
593 	case KCDCF_FINISH: return Z_STREAM_END;
594 	default: panic("invalid kcdata_zlib_translate_kcd_cf_expected_ret flag");
595 	}
596 }
597 
598 /* Called by kcdata_do_compress() when the configured compression algorithm is zlib */
599 static kern_return_t
kcdata_do_compress_zlib(kcdata_descriptor_t data,void * inbuffer,size_t insize,void * outbuffer,size_t outsize,size_t * wrote,enum kcdata_compression_flush flush)600 kcdata_do_compress_zlib(kcdata_descriptor_t data, void *inbuffer,
601     size_t insize, void *outbuffer, size_t outsize, size_t *wrote,
602     enum kcdata_compression_flush flush)
603 {
604 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
605 	z_stream *zs = &cd->kcd_cd_zs;
606 	int expected_ret, ret;
607 
608 	zs->next_out = outbuffer;
609 	zs->avail_out = (unsigned int) outsize;
610 	zs->next_in = inbuffer;
611 	zs->avail_in = (unsigned int) insize;
612 	ret = deflate(zs, kcdata_zlib_translate_kcd_cf_flag(flush));
613 	if (zs->avail_in != 0 || zs->avail_out <= 0) {
614 		return KERN_INSUFFICIENT_BUFFER_SIZE;
615 	}
616 
617 	expected_ret = kcdata_zlib_translate_kcd_cf_expected_ret(flush);
618 	if (ret != expected_ret) {
619 		/*
620 		 * Should only fail with catastrophic, unrecoverable cases (i.e.,
621 		 * corrupted z_stream, or incorrect configuration)
622 		 */
623 		panic("zlib kcdata compression ret = %d", ret);
624 	}
625 
626 	kcdata_debug_printf("%s: %p (%zu) <- %p (%zu); flush: %d; ret = %ld\n",
627 	    __func__, outbuffer, outsize, inbuffer, insize, flush, outsize - zs->avail_out);
628 	if (wrote) {
629 		*wrote = outsize - zs->avail_out;
630 	}
631 	return KERN_SUCCESS;
632 }
633 
634 /*
635  * Compress the buffer at @inbuffer (of size @insize) into the kcdata buffer
636  * @outbuffer (of size @outsize). Flush based on the @flush parameter.
637  *
638  * Returns KERN_SUCCESS on success, or KERN_INSUFFICIENT_BUFFER_SIZE if
639  * @outsize isn't sufficient. Also, writes the number of bytes written in the
640  * @outbuffer to @wrote.
641  */
642 static kern_return_t
kcdata_do_compress(kcdata_descriptor_t data,void * inbuffer,size_t insize,void * outbuffer,size_t outsize,size_t * wrote,enum kcdata_compression_flush flush)643 kcdata_do_compress(kcdata_descriptor_t data, void *inbuffer, size_t insize,
644     void *outbuffer, size_t outsize, size_t *wrote, enum kcdata_compression_flush flush)
645 {
646 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
647 
648 	assert(data->kcd_flags & KCFLAG_USE_COMPRESSION);
649 
650 	kcdata_debug_printf("%s: %p (%zu) <- %p (%zu); flush: %d\n",
651 	    __func__, outbuffer, outsize, inbuffer, insize, flush);
652 
653 	/* don't compress if we are in a window */
654 	if (cd->kcd_cd_flags & KCD_CD_FLAG_IN_MARK || data->kcd_comp_d.kcd_cd_compression_type == KCDCT_NONE) {
655 		assert(cd->kcd_cd_memcpy_f);
656 		if (outsize >= insize) {
657 			cd->kcd_cd_memcpy_f(outbuffer, inbuffer, insize);
658 			if (wrote) {
659 				*wrote = insize;
660 			}
661 			return KERN_SUCCESS;
662 		} else {
663 			return KERN_INSUFFICIENT_BUFFER_SIZE;
664 		}
665 	}
666 
667 	switch (data->kcd_comp_d.kcd_cd_compression_type) {
668 	case KCDCT_ZLIB:
669 		return kcdata_do_compress_zlib(data, inbuffer, insize, outbuffer, outsize, wrote, flush);
670 	default:
671 		panic("invalid compression type 0x%llx in kcdata_do_compress", data->kcd_comp_d.kcd_cd_compression_type);
672 	}
673 }
674 
675 static size_t
kcdata_compression_bound_zlib(kcdata_descriptor_t data,size_t size)676 kcdata_compression_bound_zlib(kcdata_descriptor_t data, size_t size)
677 {
678 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
679 	z_stream *zs = &cd->kcd_cd_zs;
680 
681 	return (size_t) deflateBound(zs, (unsigned long) size);
682 }
683 
684 
685 /*
686  * returns the worst-case, maximum length of the compressed data when
687  * compressing a buffer of size @size using the configured algorithm.
688  */
689 static size_t
kcdata_compression_bound(kcdata_descriptor_t data,size_t size)690 kcdata_compression_bound(kcdata_descriptor_t data, size_t size)
691 {
692 	switch (data->kcd_comp_d.kcd_cd_compression_type) {
693 	case KCDCT_ZLIB:
694 		return kcdata_compression_bound_zlib(data, size);
695 	case KCDCT_NONE:
696 		return size;
697 	default:
698 		panic("%s: unknown compression method", __func__);
699 	}
700 }
701 
702 /*
703  * kcdata_compress_chunk_with_flags:
704  *		Compress buffer found at @input_data (length @input_size) to the kcdata
705  *		buffer described by @data. This method will construct the kcdata_item_t
706  *		required by parsers using the type information @type and flags @flags.
707  *
708  *	Returns KERN_SUCCESS when successful. Currently, asserts on failure.
709  */
710 kern_return_t
kcdata_compress_chunk_with_flags(kcdata_descriptor_t data,uint32_t type,const void * input_data,uint32_t input_size,uint64_t kcdata_flags)711 kcdata_compress_chunk_with_flags(kcdata_descriptor_t data, uint32_t type, const void *input_data, uint32_t input_size, uint64_t kcdata_flags)
712 {
713 	assert(data);
714 	assert((data->kcd_flags & KCFLAG_USE_COMPRESSION));
715 	assert(input_data);
716 	struct kcdata_item info;
717 	char padding_data[16] = {0};
718 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
719 	size_t wrote = 0;
720 	kern_return_t kr;
721 
722 	kcdata_debug_printf("%s: type: %d input_data: %p (%d) kcdata_flags: 0x%llx\n",
723 	    __func__, type, input_data, input_size, kcdata_flags);
724 
725 	/*
726 	 * first, get memory space. The uncompressed size must fit in the remained
727 	 * of the kcdata buffer, in case the compression algorithm doesn't actually
728 	 * compress the data at all.
729 	 */
730 	size_t total_uncompressed_size = kcdata_compression_bound(data, (size_t) kcdata_get_memory_size_for_data(input_size));
731 	if (total_uncompressed_size > data->kcd_length ||
732 	    data->kcd_length - total_uncompressed_size < data->kcd_addr_end - data->kcd_addr_begin) {
733 		kcdata_debug_printf("%s: insufficient buffer size: kcd_length => %d e-b=> %lld our size: %zu\n",
734 		    __func__, data->kcd_length, data->kcd_addr_end - data->kcd_addr_begin, total_uncompressed_size);
735 		return KERN_INSUFFICIENT_BUFFER_SIZE;
736 	}
737 	uint32_t padding = kcdata_calc_padding(input_size);
738 	assert(padding < sizeof(padding_data));
739 
740 	void *space_start = (void *) data->kcd_addr_end;
741 	void *space_ptr = space_start;
742 
743 	/* create the output stream */
744 	size_t total_uncompressed_space_remaining = total_uncompressed_size;
745 
746 	/* create the info data */
747 	bzero(&info, sizeof(info));
748 	info.type = type;
749 	info.size = input_size + padding;
750 	info.flags = kcdata_flags;
751 
752 	/*
753 	 * The next possibly three compresses are needed separately because of the
754 	 * scatter-gather nature of this operation. The kcdata item header (info)
755 	 * and padding are on the stack, while the actual data is somewhere else.
756 	 * */
757 
758 	/* create the input stream for info & compress */
759 	enum kcdata_compression_flush flush = (padding || input_size) ? KCDCF_NO_FLUSH :
760 	    cd->kcd_cd_flags & KCD_CD_FLAG_FINALIZE ? KCDCF_FINISH :
761 	    KCDCF_SYNC_FLUSH;
762 	kr = kcdata_do_compress(data, &info, sizeof(info), space_ptr, total_uncompressed_space_remaining, &wrote, flush);
763 	if (kr != KERN_SUCCESS) {
764 		return kr;
765 	}
766 	kcdata_debug_printf("%s: first wrote = %zu\n", __func__, wrote);
767 	space_ptr = (void *)((uintptr_t)space_ptr + wrote);
768 	total_uncompressed_space_remaining -= wrote;
769 
770 	/* If there is input provided, compress that here */
771 	if (input_size) {
772 		flush = padding ? KCDCF_NO_FLUSH :
773 		    cd->kcd_cd_flags & KCD_CD_FLAG_FINALIZE ? KCDCF_FINISH :
774 		    KCDCF_SYNC_FLUSH;
775 		kr = kcdata_do_compress(data, (void *) (uintptr_t) input_data, input_size, space_ptr, total_uncompressed_space_remaining, &wrote, flush);
776 		if (kr != KERN_SUCCESS) {
777 			return kr;
778 		}
779 		kcdata_debug_printf("%s: 2nd wrote = %zu\n", __func__, wrote);
780 		space_ptr = (void *)((uintptr_t)space_ptr + wrote);
781 		total_uncompressed_space_remaining -= wrote;
782 	}
783 
784 	/* If the item and its data require padding to maintain alignment,
785 	 * "compress" that into the output buffer. */
786 	if (padding) {
787 		/* write the padding */
788 		kr = kcdata_do_compress(data, padding_data, padding, space_ptr, total_uncompressed_space_remaining, &wrote,
789 		    cd->kcd_cd_flags & KCD_CD_FLAG_FINALIZE ? KCDCF_FINISH : KCDCF_SYNC_FLUSH);
790 		if (kr != KERN_SUCCESS) {
791 			return kr;
792 		}
793 		kcdata_debug_printf("%s: 3rd wrote = %zu\n", __func__, wrote);
794 		if (wrote == 0) {
795 			return KERN_FAILURE;
796 		}
797 		space_ptr = (void *)((uintptr_t)space_ptr + wrote);
798 		total_uncompressed_space_remaining -= wrote;
799 	}
800 
801 	assert((size_t)((uintptr_t)space_ptr - (uintptr_t)space_start) <= total_uncompressed_size);
802 
803 	/* move the end marker forward */
804 	data->kcd_addr_end = (mach_vm_address_t) space_start + (total_uncompressed_size - total_uncompressed_space_remaining);
805 
806 	return KERN_SUCCESS;
807 }
808 
809 /*
810  * kcdata_compress_chunk:
811  *		Like kcdata_compress_chunk_with_flags(), but uses the default set of kcdata flags,
812  *		i.e. padding and also saves the amount of padding bytes.
813  *
814  * Returns are the same as in kcdata_compress_chunk_with_flags()
815  */
816 kern_return_t
kcdata_compress_chunk(kcdata_descriptor_t data,uint32_t type,const void * input_data,uint32_t input_size)817 kcdata_compress_chunk(kcdata_descriptor_t data, uint32_t type, const void *input_data, uint32_t input_size)
818 {
819 	/* these flags are for kcdata - store that the struct is padded and store the amount of padding bytes */
820 	uint64_t flags = (KCDATA_FLAGS_STRUCT_PADDING_MASK & kcdata_calc_padding(input_size)) | KCDATA_FLAGS_STRUCT_HAS_PADDING;
821 	return kcdata_compress_chunk_with_flags(data, type, input_data, input_size, flags);
822 }
823 
824 kern_return_t
kcdata_push_data(kcdata_descriptor_t data,uint32_t type,uint32_t size,const void * input_data)825 kcdata_push_data(kcdata_descriptor_t data, uint32_t type, uint32_t size, const void *input_data)
826 {
827 	if (data->kcd_flags & KCFLAG_USE_COMPRESSION) {
828 		return kcdata_compress_chunk(data, type, input_data, size);
829 	} else {
830 		kern_return_t ret;
831 		mach_vm_address_t uaddr = 0;
832 		ret = kcdata_get_memory_addr(data, type, size, &uaddr);
833 		if (ret != KERN_SUCCESS) {
834 			return ret;
835 		}
836 
837 		kcdata_memcpy(data, uaddr, input_data, size);
838 		return KERN_SUCCESS;
839 	}
840 }
841 
842 kern_return_t
kcdata_push_array(kcdata_descriptor_t data,uint32_t type_of_element,uint32_t size_of_element,uint32_t count,const void * input_data)843 kcdata_push_array(kcdata_descriptor_t data, uint32_t type_of_element, uint32_t size_of_element, uint32_t count, const void *input_data)
844 {
845 	uint64_t flags      = type_of_element;
846 	flags               = (flags << 32) | count;
847 	uint32_t total_size = count * size_of_element;
848 	uint32_t pad        = kcdata_calc_padding(total_size);
849 
850 	if (data->kcd_flags & KCFLAG_USE_COMPRESSION) {
851 		return kcdata_compress_chunk_with_flags(data, KCDATA_TYPE_ARRAY_PAD0 | pad, input_data, total_size, flags);
852 	} else {
853 		kern_return_t ret;
854 		mach_vm_address_t uaddr = 0;
855 		ret = kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_ARRAY_PAD0 | pad, total_size, flags, &uaddr);
856 		if (ret != KERN_SUCCESS) {
857 			return ret;
858 		}
859 
860 		kcdata_memcpy(data, uaddr, input_data, total_size);
861 		return KERN_SUCCESS;
862 	}
863 }
864 
865 /* A few words on how window compression works:
866  *
867  * This is how the buffer looks when the window is opened:
868  *
869  * X---------------------------------------------------------------------X
870  * |                                |                                    |
871  * |   Filled with stackshot data   |            Zero bytes              |
872  * |                                |                                    |
873  * X---------------------------------------------------------------------X
874  *                                  ^
875  *									\ - kcd_addr_end
876  *
877  * Opening a window will save the current kcd_addr_end to kcd_cd_mark_begin.
878  *
879  * Any kcdata_* operation will then push data to the buffer like normal. (If
880  * you call any compressing functions they will pass-through, i.e. no
881  * compression will be done) Once the window is closed, the following takes
882  * place:
883  *
884  * X---------------------------------------------------------------------X
885  * |               |                    |                    |           |
886  * | Existing data |     New data       |   Scratch buffer   |           |
887  * |               |                    |                    |           |
888  * X---------------------------------------------------------------------X
889  *				   ^                    ^                    ^
890  *				   |                    |                    |
891  *				   \ -kcd_cd_mark_begin |                    |
892  *							            |                    |
893  *							            \ - kcd_addr_end     |
894  *							                                 |
895  *		 kcd_addr_end + (kcd_addr_end - kcd_cd_mark_begin) - /
896  *
897  *	(1) The data between kcd_cd_mark_begin and kcd_addr_end is fed to the
898  *      compression algorithm to compress to the scratch buffer.
899  *  (2) The scratch buffer's contents are copied into the area denoted "New
900  *      data" above. Effectively overwriting the uncompressed data with the
901  *      compressed one.
902  *  (3) kcd_addr_end is then rewound to kcd_cd_mark_begin + sizeof_compressed_data
903  */
904 
905 /* Record the state, and restart compression from this later */
906 void
kcdata_compression_window_open(kcdata_descriptor_t data)907 kcdata_compression_window_open(kcdata_descriptor_t data)
908 {
909 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
910 	assert((cd->kcd_cd_flags & KCD_CD_FLAG_IN_MARK) == 0);
911 
912 	if (data->kcd_flags & KCFLAG_USE_COMPRESSION) {
913 		cd->kcd_cd_flags |= KCD_CD_FLAG_IN_MARK;
914 		cd->kcd_cd_mark_begin = data->kcd_addr_end;
915 	}
916 }
917 
918 /* Compress the region between the mark and the current end */
919 kern_return_t
kcdata_compression_window_close(kcdata_descriptor_t data)920 kcdata_compression_window_close(kcdata_descriptor_t data)
921 {
922 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
923 	uint64_t total_size, max_size;
924 	void *space_start, *space_ptr;
925 	size_t total_uncompressed_space_remaining, wrote = 0;
926 	kern_return_t kr;
927 
928 	if ((data->kcd_flags & KCFLAG_USE_COMPRESSION) == 0) {
929 		return KERN_SUCCESS;
930 	}
931 
932 	assert(cd->kcd_cd_flags & KCD_CD_FLAG_IN_MARK);
933 
934 	if (data->kcd_addr_end == (mach_vm_address_t) cd->kcd_cd_mark_begin) {
935 		/* clear the window marker and return, this is a no-op */
936 		cd->kcd_cd_flags &= ~KCD_CD_FLAG_IN_MARK;
937 		return KERN_SUCCESS;
938 	}
939 
940 	assert(cd->kcd_cd_mark_begin < data->kcd_addr_end);
941 	total_size = data->kcd_addr_end - (uint64_t) cd->kcd_cd_mark_begin;
942 	max_size = (uint64_t) kcdata_compression_bound(data, total_size);
943 	kcdata_debug_printf("%s: total_size = %lld\n", __func__, total_size);
944 
945 	/*
946 	 * first, get memory space. The uncompressed size must fit in the remained
947 	 * of the kcdata buffer, in case the compression algorithm doesn't actually
948 	 * compress the data at all.
949 	 */
950 	if (max_size > data->kcd_length ||
951 	    data->kcd_length - max_size < data->kcd_addr_end - data->kcd_addr_begin) {
952 		kcdata_debug_printf("%s: insufficient buffer size: kcd_length => %d e-b=> %lld our size: %lld\n",
953 		    __func__, data->kcd_length, data->kcd_addr_end - data->kcd_addr_begin, max_size);
954 		return KERN_INSUFFICIENT_BUFFER_SIZE;
955 	}
956 
957 	/* clear the window marker */
958 	cd->kcd_cd_flags &= ~KCD_CD_FLAG_IN_MARK;
959 
960 	space_start = (void *) data->kcd_addr_end;
961 	space_ptr = space_start;
962 	total_uncompressed_space_remaining = (unsigned int) max_size;
963 	kr = kcdata_do_compress(data, (void *) cd->kcd_cd_mark_begin, total_size, space_ptr,
964 	    total_uncompressed_space_remaining, &wrote, KCDCF_SYNC_FLUSH);
965 	if (kr != KERN_SUCCESS) {
966 		return kr;
967 	}
968 	kcdata_debug_printf("%s: first wrote = %zu\n", __func__, wrote);
969 	if (wrote == 0) {
970 		return KERN_FAILURE;
971 	}
972 	space_ptr = (void *)((uintptr_t)space_ptr + wrote);
973 	total_uncompressed_space_remaining  -= wrote;
974 
975 	assert((size_t)((uintptr_t)space_ptr - (uintptr_t)space_start) <= max_size);
976 
977 	/* copy to the original location */
978 	kcdata_memcpy(data, cd->kcd_cd_mark_begin, space_start, (uint32_t) (max_size - total_uncompressed_space_remaining));
979 
980 	/* rewind the end marker */
981 	data->kcd_addr_end = cd->kcd_cd_mark_begin + (max_size - total_uncompressed_space_remaining);
982 
983 	return KERN_SUCCESS;
984 }
985 
986 static kern_return_t
kcdata_get_compression_stats_zlib(kcdata_descriptor_t data,uint64_t * totalout,uint64_t * totalin)987 kcdata_get_compression_stats_zlib(kcdata_descriptor_t data, uint64_t *totalout, uint64_t *totalin)
988 {
989 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
990 	z_stream *zs = &cd->kcd_cd_zs;
991 
992 	assert((cd->kcd_cd_flags & KCD_CD_FLAG_IN_MARK) == 0);
993 
994 	*totalout = (uint64_t) zs->total_out;
995 	*totalin = (uint64_t) zs->total_in;
996 
997 	return KERN_SUCCESS;
998 }
999 
1000 static kern_return_t
kcdata_get_compression_stats(kcdata_descriptor_t data,uint64_t * totalout,uint64_t * totalin)1001 kcdata_get_compression_stats(kcdata_descriptor_t data, uint64_t *totalout, uint64_t *totalin)
1002 {
1003 	kern_return_t kr;
1004 
1005 	switch (data->kcd_comp_d.kcd_cd_compression_type) {
1006 	case KCDCT_ZLIB:
1007 		kr = kcdata_get_compression_stats_zlib(data, totalout, totalin);
1008 		break;
1009 	case KCDCT_NONE:
1010 		*totalout = *totalin = kcdata_memory_get_used_bytes(data);
1011 		kr = KERN_SUCCESS;
1012 		break;
1013 	default:
1014 		panic("invalid compression flag 0x%llx in kcdata_write_compression_stats", (data->kcd_comp_d.kcd_cd_compression_type));
1015 	}
1016 
1017 	return kr;
1018 }
1019 
1020 kern_return_t
kcdata_write_compression_stats(kcdata_descriptor_t data)1021 kcdata_write_compression_stats(kcdata_descriptor_t data)
1022 {
1023 	kern_return_t kr;
1024 	uint64_t totalout, totalin;
1025 
1026 	kr = kcdata_get_compression_stats(data, &totalout, &totalin);
1027 	if (kr != KERN_SUCCESS) {
1028 		return kr;
1029 	}
1030 
1031 	*(uint64_t *)data->kcd_comp_d.kcd_cd_totalout_addr = totalout;
1032 	*(uint64_t *)data->kcd_comp_d.kcd_cd_totalin_addr = totalin;
1033 
1034 	return kr;
1035 }
1036 
1037 static kern_return_t
kcdata_finish_compression_zlib(kcdata_descriptor_t data)1038 kcdata_finish_compression_zlib(kcdata_descriptor_t data)
1039 {
1040 	struct kcdata_compress_descriptor *cd = &data->kcd_comp_d;
1041 	z_stream *zs = &cd->kcd_cd_zs;
1042 
1043 	/*
1044 	 * macOS on x86 w/ coprocessor ver. 2 and later context: Stackshot compression leaves artifacts
1045 	 * in the panic buffer which interferes with CRC checks. The CRC is calculated here over the full
1046 	 * buffer but only the portion with valid panic data is sent to iBoot via the SMC. When iBoot
1047 	 * calculates the CRC to compare with the value in the header it uses a zero-filled buffer.
1048 	 * The stackshot compression leaves non-zero bytes behind so those must be cleared prior to the CRC calculation.
1049 	 * This doesn't get the compression metadata; that's zeroed by kcdata_release_endallocs().
1050 	 *
1051 	 * All other contexts: The stackshot compression artifacts are present in its panic buffer but the CRC check
1052 	 * is done on the same buffer for the before and after calculation so there's nothing functionally
1053 	 * broken. The same buffer cleanup is done here for completeness' sake.
1054 	 * From rdar://problem/64381661
1055 	 */
1056 
1057 	void* stackshot_end = (char*)data->kcd_addr_begin + kcdata_memory_get_used_bytes(data);
1058 	uint32_t zero_fill_size = data->kcd_length - kcdata_memory_get_used_bytes(data);
1059 	bzero(stackshot_end, zero_fill_size);
1060 
1061 	if (deflateEnd(zs) == Z_OK) {
1062 		return KERN_SUCCESS;
1063 	} else {
1064 		return KERN_FAILURE;
1065 	}
1066 }
1067 
1068 static kern_return_t
kcdata_finish_compression(kcdata_descriptor_t data)1069 kcdata_finish_compression(kcdata_descriptor_t data)
1070 {
1071 	kcdata_write_compression_stats(data);
1072 
1073 	switch (data->kcd_comp_d.kcd_cd_compression_type) {
1074 	case KCDCT_ZLIB:
1075 		return kcdata_finish_compression_zlib(data);
1076 	case KCDCT_NONE:
1077 		return KERN_SUCCESS;
1078 	default:
1079 		panic("invalid compression type 0x%llxin kcdata_finish_compression", data->kcd_comp_d.kcd_cd_compression_type);
1080 	}
1081 }
1082 
1083 kern_return_t
kcdata_finish(kcdata_descriptor_t data)1084 kcdata_finish(kcdata_descriptor_t data)
1085 {
1086 	int ret = KERN_SUCCESS;
1087 	if (data->kcd_flags & KCFLAG_USE_COMPRESSION) {
1088 		ret = kcdata_finish_compression(data);
1089 	}
1090 	kcdata_release_endallocs(data);
1091 	return ret;
1092 }
1093 
1094 void
kcd_finalize_compression(kcdata_descriptor_t data)1095 kcd_finalize_compression(kcdata_descriptor_t data)
1096 {
1097 	if (data->kcd_flags & KCFLAG_USE_COMPRESSION) {
1098 		data->kcd_comp_d.kcd_cd_flags |= KCD_CD_FLAG_FINALIZE;
1099 	}
1100 }
1101 
1102 /*
1103  * Routine: kcdata_get_memory_addr
1104  * Desc: get memory address in the userspace memory for corpse info
1105  *       NOTE: The caller is responsible for zeroing the resulting memory or
1106  *             using other means to mark memory if it has failed populating the
1107  *             data in middle of operation.
1108  * params:  data - pointer describing the crash info allocation
1109  *	        type - type of data to be put. See corpse.h for defined types
1110  *          size - size requested. The header describes this size
1111  * returns: mach_vm_address_t address in user memory for copyout().
1112  */
1113 kern_return_t
kcdata_get_memory_addr(kcdata_descriptor_t data,uint32_t type,uint32_t size,mach_vm_address_t * user_addr)1114 kcdata_get_memory_addr(kcdata_descriptor_t data, uint32_t type, uint32_t size, mach_vm_address_t * user_addr)
1115 {
1116 	/* record number of padding bytes as lower 4 bits of flags */
1117 	uint64_t flags = (KCDATA_FLAGS_STRUCT_PADDING_MASK & kcdata_calc_padding(size)) | KCDATA_FLAGS_STRUCT_HAS_PADDING;
1118 	return kcdata_get_memory_addr_with_flavor(data, type, size, flags, user_addr);
1119 }
1120 
1121 /*
1122  * Routine: kcdata_add_buffer_end
1123  *
1124  * Desc: Write buffer end marker.  This does not advance the end pointer in the
1125  * kcdata_descriptor_t, so it may be used conservatively before additional data
1126  * is added, as long as it is at least called after the last time data is added.
1127  *
1128  * params:  data - pointer describing the crash info allocation
1129  */
1130 
1131 kern_return_t
kcdata_write_buffer_end(kcdata_descriptor_t data)1132 kcdata_write_buffer_end(kcdata_descriptor_t data)
1133 {
1134 	struct kcdata_item info;
1135 	bzero(&info, sizeof(info));
1136 	info.type = KCDATA_TYPE_BUFFER_END;
1137 	info.size = 0;
1138 	return kcdata_memcpy(data, data->kcd_addr_end, &info, sizeof(info));
1139 }
1140 
1141 /*
1142  * Routine: kcdata_get_memory_addr_with_flavor
1143  * Desc: internal function with flags field. See documentation for kcdata_get_memory_addr for details
1144  */
1145 
1146 static kern_return_t
kcdata_get_memory_addr_with_flavor(kcdata_descriptor_t data,uint32_t type,uint32_t size,uint64_t flags,mach_vm_address_t * user_addr)1147 kcdata_get_memory_addr_with_flavor(
1148 	kcdata_descriptor_t data,
1149 	uint32_t type,
1150 	uint32_t size,
1151 	uint64_t flags,
1152 	mach_vm_address_t *user_addr)
1153 {
1154 	kern_return_t kr;
1155 	struct kcdata_item info;
1156 
1157 	uint32_t orig_size = size;
1158 	/* make sure 16 byte aligned */
1159 	uint32_t padding = kcdata_calc_padding(size);
1160 	size += padding;
1161 	uint32_t total_size  = size + sizeof(info);
1162 
1163 	if (user_addr == NULL || data == NULL || total_size + sizeof(info) < orig_size) {
1164 		return KERN_INVALID_ARGUMENT;
1165 	}
1166 
1167 	assert(((data->kcd_flags & KCFLAG_USE_COMPRESSION) && (data->kcd_comp_d.kcd_cd_flags & KCD_CD_FLAG_IN_MARK))
1168 	    || ((data->kcd_flags & KCFLAG_USE_COMPRESSION) == 0));
1169 
1170 	bzero(&info, sizeof(info));
1171 	info.type  = type;
1172 	info.size = size;
1173 	info.flags = flags;
1174 
1175 	/* check available memory, including trailer size for KCDATA_TYPE_BUFFER_END */
1176 	if (total_size + sizeof(info) > data->kcd_length ||
1177 	    data->kcd_length - (total_size + sizeof(info)) < data->kcd_addr_end - data->kcd_addr_begin) {
1178 		return KERN_INSUFFICIENT_BUFFER_SIZE;
1179 	}
1180 
1181 	kr = kcdata_memcpy(data, data->kcd_addr_end, &info, sizeof(info));
1182 	if (kr) {
1183 		return kr;
1184 	}
1185 
1186 	data->kcd_addr_end += sizeof(info);
1187 
1188 	if (padding) {
1189 		kr = kcdata_bzero(data, data->kcd_addr_end + size - padding, padding);
1190 		if (kr) {
1191 			return kr;
1192 		}
1193 	}
1194 
1195 	*user_addr = data->kcd_addr_end;
1196 	data->kcd_addr_end += size;
1197 
1198 	if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
1199 		/* setup the end header as well */
1200 		return kcdata_write_buffer_end(data);
1201 	} else {
1202 		return KERN_SUCCESS;
1203 	}
1204 }
1205 
1206 /* Routine: kcdata_get_memory_size_for_data
1207  * Desc: returns the amount of memory that is required to store the information
1208  *       in kcdata
1209  */
1210 static size_t
kcdata_get_memory_size_for_data(uint32_t size)1211 kcdata_get_memory_size_for_data(uint32_t size)
1212 {
1213 	return size + kcdata_calc_padding(size) + sizeof(struct kcdata_item);
1214 }
1215 
1216 /*
1217  * Routine: kcdata_get_memory_addr_for_array
1218  * Desc: get memory address in the userspace memory for corpse info
1219  *       NOTE: The caller is responsible to zero the resulting memory or
1220  *             user other means to mark memory if it has failed populating the
1221  *             data in middle of operation.
1222  * params:  data - pointer describing the crash info allocation
1223  *          type_of_element - type of data to be put. See kern_cdata.h for defined types
1224  *          size_of_element - size of element. The header describes this size
1225  *          count - num of elements in array.
1226  * returns: mach_vm_address_t address in user memory for copyout().
1227  */
1228 
1229 kern_return_t
kcdata_get_memory_addr_for_array(kcdata_descriptor_t data,uint32_t type_of_element,uint32_t size_of_element,uint32_t count,mach_vm_address_t * user_addr)1230 kcdata_get_memory_addr_for_array(
1231 	kcdata_descriptor_t data,
1232 	uint32_t type_of_element,
1233 	uint32_t size_of_element,
1234 	uint32_t count,
1235 	mach_vm_address_t *user_addr)
1236 {
1237 	/* for arrays we record the number of padding bytes as the low-order 4 bits
1238 	 * of the type field.  KCDATA_TYPE_ARRAY_PAD{x} means x bytes of pad. */
1239 	uint64_t flags      = type_of_element;
1240 	flags               = (flags << 32) | count;
1241 	uint32_t total_size = count * size_of_element;
1242 	uint32_t pad        = kcdata_calc_padding(total_size);
1243 
1244 	return kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_ARRAY_PAD0 | pad, total_size, flags, user_addr);
1245 }
1246 
1247 /*
1248  * Routine: kcdata_add_container_marker
1249  * Desc: Add a container marker in the buffer for type and identifier.
1250  * params:  data - pointer describing the crash info allocation
1251  *          header_type - one of (KCDATA_TYPE_CONTAINER_BEGIN ,KCDATA_TYPE_CONTAINER_END)
1252  *          container_type - type of data to be put. See kern_cdata.h for defined types
1253  *          identifier - unique identifier. This is required to match nested containers.
1254  * returns: return value of kcdata_get_memory_addr()
1255  */
1256 
1257 kern_return_t
kcdata_add_container_marker(kcdata_descriptor_t data,uint32_t header_type,uint32_t container_type,uint64_t identifier)1258 kcdata_add_container_marker(
1259 	kcdata_descriptor_t data,
1260 	uint32_t header_type,
1261 	uint32_t container_type,
1262 	uint64_t identifier)
1263 {
1264 	mach_vm_address_t user_addr;
1265 	kern_return_t kr;
1266 	uint32_t data_size;
1267 
1268 	assert(header_type == KCDATA_TYPE_CONTAINER_END || header_type == KCDATA_TYPE_CONTAINER_BEGIN);
1269 
1270 	data_size = (header_type == KCDATA_TYPE_CONTAINER_BEGIN)? sizeof(uint32_t): 0;
1271 
1272 	if (!(data->kcd_flags & KCFLAG_USE_COMPRESSION)) {
1273 		kr = kcdata_get_memory_addr_with_flavor(data, header_type, data_size, identifier, &user_addr);
1274 		if (kr != KERN_SUCCESS) {
1275 			return kr;
1276 		}
1277 
1278 		if (data_size) {
1279 			kr = kcdata_memcpy(data, user_addr, &container_type, data_size);
1280 		}
1281 	} else {
1282 		kr = kcdata_compress_chunk_with_flags(data, header_type, &container_type, data_size, identifier);
1283 	}
1284 
1285 	return kr;
1286 }
1287 
1288 /*
1289  * Routine: kcdata_undo_addcontainer_begin
1290  * Desc: call this after adding a container begin but before adding anything else to revert.
1291  */
1292 kern_return_t
kcdata_undo_add_container_begin(kcdata_descriptor_t data)1293 kcdata_undo_add_container_begin(kcdata_descriptor_t data)
1294 {
1295 	/*
1296 	 * the payload of a container begin is a single uint64_t.  It is padded out
1297 	 * to 16 bytes.
1298 	 */
1299 	const mach_vm_address_t padded_payload_size = 16;
1300 	data->kcd_addr_end -= sizeof(struct kcdata_item) + padded_payload_size;
1301 
1302 	if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
1303 		/* setup the end header as well */
1304 		return kcdata_write_buffer_end(data);
1305 	} else {
1306 		return KERN_SUCCESS;
1307 	}
1308 }
1309 
1310 /*
1311  * Routine: kcdata_memcpy
1312  * Desc: a common function to copy data out based on either copyout or memcopy flags
1313  * params:  data - pointer describing the kcdata buffer
1314  *          dst_addr - destination address
1315  *          src_addr - source address
1316  *          size - size in bytes to copy.
1317  * returns: KERN_NO_ACCESS if copyout fails.
1318  */
1319 
1320 kern_return_t
kcdata_memcpy(kcdata_descriptor_t data,mach_vm_address_t dst_addr,const void * src_addr,uint32_t size)1321 kcdata_memcpy(kcdata_descriptor_t data, mach_vm_address_t dst_addr, const void *src_addr, uint32_t size)
1322 {
1323 	if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
1324 		if (copyout(src_addr, dst_addr, size)) {
1325 			return KERN_NO_ACCESS;
1326 		}
1327 	} else {
1328 		memcpy((void *)dst_addr, src_addr, size);
1329 	}
1330 	return KERN_SUCCESS;
1331 }
1332 
1333 /*
1334  * Routine: kcdata_bzero
1335  * Desc: zero out a portion of a kcdata buffer.
1336  */
1337 kern_return_t
kcdata_bzero(kcdata_descriptor_t data,mach_vm_address_t dst_addr,uint32_t size)1338 kcdata_bzero(kcdata_descriptor_t data, mach_vm_address_t dst_addr, uint32_t size)
1339 {
1340 	kern_return_t kr = KERN_SUCCESS;
1341 	if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
1342 		uint8_t zeros[16] = {};
1343 		while (size) {
1344 			uint32_t block_size = MIN(size, 16);
1345 			kr = copyout(&zeros, dst_addr, block_size);
1346 			if (kr) {
1347 				return KERN_NO_ACCESS;
1348 			}
1349 			size -= block_size;
1350 		}
1351 		return KERN_SUCCESS;
1352 	} else {
1353 		bzero((void*)dst_addr, size);
1354 		return KERN_SUCCESS;
1355 	}
1356 }
1357 
1358 /*
1359  * Routine: kcdata_add_type_definition
1360  * Desc: add type definition to kcdata buffer.
1361  *       see feature description in documentation above.
1362  * params:  data - pointer describing the kcdata buffer
1363  *          type_id - unique type identifier for this data
1364  *          type_name - a string of max KCDATA_DESC_MAXLEN size for name of type
1365  *          elements_array - address to descriptors for each field in struct
1366  *          elements_count - count of how many fields are there in struct.
1367  * returns: return code from kcdata_get_memory_addr in case of failure.
1368  */
1369 
1370 kern_return_t
kcdata_add_type_definition(kcdata_descriptor_t data,uint32_t type_id,char * type_name,struct kcdata_subtype_descriptor * elements_array_addr,uint32_t elements_count)1371 kcdata_add_type_definition(
1372 	kcdata_descriptor_t data,
1373 	uint32_t type_id,
1374 	char *type_name,
1375 	struct kcdata_subtype_descriptor *elements_array_addr,
1376 	uint32_t elements_count)
1377 {
1378 	kern_return_t kr = KERN_SUCCESS;
1379 	struct kcdata_type_definition kc_type_definition;
1380 	mach_vm_address_t user_addr;
1381 	uint32_t total_size = sizeof(struct kcdata_type_definition);
1382 	bzero(&kc_type_definition, sizeof(kc_type_definition));
1383 
1384 	if (strlen(type_name) >= KCDATA_DESC_MAXLEN) {
1385 		return KERN_INVALID_ARGUMENT;
1386 	}
1387 	strlcpy(&kc_type_definition.kct_name[0], type_name, KCDATA_DESC_MAXLEN);
1388 	kc_type_definition.kct_num_elements = elements_count;
1389 	kc_type_definition.kct_type_identifier = type_id;
1390 
1391 	total_size += elements_count * sizeof(struct kcdata_subtype_descriptor);
1392 	/* record number of padding bytes as lower 4 bits of flags */
1393 	if (KERN_SUCCESS != (kr = kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_TYPEDEFINTION, total_size,
1394 	    kcdata_calc_padding(total_size), &user_addr))) {
1395 		return kr;
1396 	}
1397 	if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)&kc_type_definition, sizeof(struct kcdata_type_definition)))) {
1398 		return kr;
1399 	}
1400 	user_addr += sizeof(struct kcdata_type_definition);
1401 	if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)elements_array_addr, elements_count * sizeof(struct kcdata_subtype_descriptor)))) {
1402 		return kr;
1403 	}
1404 	return kr;
1405 }
1406 
1407 kern_return_t
kcdata_add_uint64_with_description(kcdata_descriptor_t data_desc,uint64_t data,const char * description)1408 kcdata_add_uint64_with_description(kcdata_descriptor_t data_desc, uint64_t data, const char * description)
1409 {
1410 	if (strlen(description) >= KCDATA_DESC_MAXLEN) {
1411 		return KERN_INVALID_ARGUMENT;
1412 	}
1413 
1414 	kern_return_t kr = 0;
1415 	mach_vm_address_t user_addr;
1416 	struct _uint64_with_description_data save_data;
1417 	const uint64_t size_req = sizeof(save_data);
1418 	bzero(&save_data, size_req);
1419 
1420 	strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
1421 	save_data.data = data;
1422 
1423 	if (data_desc->kcd_flags & KCFLAG_USE_COMPRESSION) {
1424 		/* allocate space for the output */
1425 		return kcdata_compress_chunk(data_desc, KCDATA_TYPE_UINT64_DESC, &save_data, size_req);
1426 	}
1427 
1428 	kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT64_DESC, size_req, &user_addr);
1429 	if (kr != KERN_SUCCESS) {
1430 		return kr;
1431 	}
1432 
1433 	if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
1434 		if (copyout(&save_data, user_addr, size_req)) {
1435 			return KERN_NO_ACCESS;
1436 		}
1437 	} else {
1438 		memcpy((void *)user_addr, &save_data, size_req);
1439 	}
1440 	return KERN_SUCCESS;
1441 }
1442 
1443 kern_return_t
kcdata_add_uint32_with_description(kcdata_descriptor_t data_desc,uint32_t data,const char * description)1444 kcdata_add_uint32_with_description(
1445 	kcdata_descriptor_t data_desc,
1446 	uint32_t data,
1447 	const char *description)
1448 {
1449 	assert(strlen(description) < KCDATA_DESC_MAXLEN);
1450 	if (strlen(description) >= KCDATA_DESC_MAXLEN) {
1451 		return KERN_INVALID_ARGUMENT;
1452 	}
1453 	kern_return_t kr = 0;
1454 	mach_vm_address_t user_addr;
1455 	struct _uint32_with_description_data save_data;
1456 	const uint64_t size_req = sizeof(save_data);
1457 
1458 	bzero(&save_data, size_req);
1459 	strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
1460 	save_data.data = data;
1461 
1462 	if (data_desc->kcd_flags & KCFLAG_USE_COMPRESSION) {
1463 		/* allocate space for the output */
1464 		return kcdata_compress_chunk(data_desc, KCDATA_TYPE_UINT32_DESC, &save_data, size_req);
1465 	}
1466 
1467 	kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT32_DESC, size_req, &user_addr);
1468 	if (kr != KERN_SUCCESS) {
1469 		return kr;
1470 	}
1471 
1472 	if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
1473 		if (copyout(&save_data, user_addr, size_req)) {
1474 			return KERN_NO_ACCESS;
1475 		}
1476 	} else {
1477 		memcpy((void *)user_addr, &save_data, size_req);
1478 	}
1479 
1480 	return KERN_SUCCESS;
1481 }
1482 
1483 
1484 /* end buffer management api */
1485