1 /*
2 * Copyright (c) 2000 Apple Computer, 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 /* IOData.m created by rsulack on Thu 25-Sep-1997 */
29
30 #include <string.h>
31
32 #include <vm/vm_kern.h>
33
34 #define IOKIT_ENABLE_SHARED_PTR
35
36 #include <libkern/c++/OSData.h>
37 #include <libkern/c++/OSSerialize.h>
38 #include <libkern/c++/OSLib.h>
39 #include <libkern/c++/OSString.h>
40 #include <IOKit/IOLib.h>
41
42 #define super OSObject
43
44 OSDefineMetaClassAndStructorsWithZone(OSData, OSObject, ZC_ZFREE_CLEARMEM)
45 OSMetaClassDefineReservedUsedX86(OSData, 0); // setDeallocFunction
46 OSMetaClassDefineReservedUnused(OSData, 1);
47 OSMetaClassDefineReservedUnused(OSData, 2);
48 OSMetaClassDefineReservedUnused(OSData, 3);
49 OSMetaClassDefineReservedUnused(OSData, 4);
50 OSMetaClassDefineReservedUnused(OSData, 5);
51 OSMetaClassDefineReservedUnused(OSData, 6);
52 OSMetaClassDefineReservedUnused(OSData, 7);
53
54 #define EXTERNAL ((unsigned int) -1)
55
56 bool
initWithCapacity(unsigned int inCapacity)57 OSData::initWithCapacity(unsigned int inCapacity)
58 {
59 void *_data = NULL;
60
61 if (data) {
62 OSCONTAINER_ACCUMSIZE(-((size_t)capacity));
63 if (!inCapacity || (capacity < inCapacity)) {
64 // clean out old data's storage if it isn't big enough
65 if (capacity < page_size) {
66 kfree_data_container(data, capacity);
67 } else {
68 kmem_free(kernel_map, (vm_offset_t)data, capacity);
69 }
70 data = NULL;
71 capacity = 0;
72 }
73 }
74
75 if (!super::init()) {
76 return false;
77 }
78
79 if (inCapacity && !data) {
80 if (inCapacity < page_size) {
81 data = (void *)kalloc_data_container(inCapacity, Z_WAITOK);
82 } else {
83 kern_return_t kr;
84 if (round_page_overflow(inCapacity, &inCapacity)) {
85 kr = KERN_RESOURCE_SHORTAGE;
86 } else {
87 kr = kernel_memory_allocate(kernel_map,
88 (vm_offset_t *)&_data, inCapacity,
89 0, (kma_flags_t) (KMA_ATOMIC | KMA_DATA),
90 IOMemoryTag(kernel_map));
91 data = _data;
92 }
93 if (KERN_SUCCESS != kr) {
94 data = NULL;
95 }
96 }
97 if (!data) {
98 return false;
99 }
100 capacity = inCapacity;
101 }
102 OSCONTAINER_ACCUMSIZE(capacity);
103
104 length = 0;
105 if (inCapacity < 16) {
106 capacityIncrement = 16;
107 } else {
108 capacityIncrement = inCapacity;
109 }
110
111 return true;
112 }
113
114 bool
initWithBytes(const void * bytes,unsigned int inLength)115 OSData::initWithBytes(const void *bytes, unsigned int inLength)
116 {
117 if ((inLength && !bytes) || !initWithCapacity(inLength)) {
118 return false;
119 }
120
121 if (bytes != data) {
122 bcopy(bytes, data, inLength);
123 }
124 length = inLength;
125
126 return true;
127 }
128
129 bool
initWithBytesNoCopy(void * bytes,unsigned int inLength)130 OSData::initWithBytesNoCopy(void *bytes, unsigned int inLength)
131 {
132 if (!super::init()) {
133 return false;
134 }
135
136 length = inLength;
137 capacity = EXTERNAL;
138 data = bytes;
139
140 return true;
141 }
142
143 bool
initWithData(const OSData * inData)144 OSData::initWithData(const OSData *inData)
145 {
146 return initWithBytes(inData->data, inData->length);
147 }
148
149 bool
initWithData(const OSData * inData,unsigned int start,unsigned int inLength)150 OSData::initWithData(const OSData *inData,
151 unsigned int start, unsigned int inLength)
152 {
153 const void *localData = inData->getBytesNoCopy(start, inLength);
154
155 if (localData) {
156 return initWithBytes(localData, inLength);
157 } else {
158 return false;
159 }
160 }
161
162 OSSharedPtr<OSData>
withCapacity(unsigned int inCapacity)163 OSData::withCapacity(unsigned int inCapacity)
164 {
165 OSSharedPtr<OSData> me = OSMakeShared<OSData>();
166
167 if (me && !me->initWithCapacity(inCapacity)) {
168 return nullptr;
169 }
170
171 return me;
172 }
173
174 OSSharedPtr<OSData>
withBytes(const void * bytes,unsigned int inLength)175 OSData::withBytes(const void *bytes, unsigned int inLength)
176 {
177 OSSharedPtr<OSData> me = OSMakeShared<OSData>();
178
179 if (me && !me->initWithBytes(bytes, inLength)) {
180 return nullptr;
181 }
182 return me;
183 }
184
185 OSSharedPtr<OSData>
withBytesNoCopy(void * bytes,unsigned int inLength)186 OSData::withBytesNoCopy(void *bytes, unsigned int inLength)
187 {
188 OSSharedPtr<OSData> me = OSMakeShared<OSData>();
189
190 if (me && !me->initWithBytesNoCopy(bytes, inLength)) {
191 return nullptr;
192 }
193
194 return me;
195 }
196
197 OSSharedPtr<OSData>
withData(const OSData * inData)198 OSData::withData(const OSData *inData)
199 {
200 OSSharedPtr<OSData> me = OSMakeShared<OSData>();
201
202 if (me && !me->initWithData(inData)) {
203 return nullptr;
204 }
205
206 return me;
207 }
208
209 OSSharedPtr<OSData>
withData(const OSData * inData,unsigned int start,unsigned int inLength)210 OSData::withData(const OSData *inData,
211 unsigned int start, unsigned int inLength)
212 {
213 OSSharedPtr<OSData> me = OSMakeShared<OSData>();
214
215 if (me && !me->initWithData(inData, start, inLength)) {
216 return nullptr;
217 }
218
219 return me;
220 }
221
222 void
free()223 OSData::free()
224 {
225 if ((capacity != EXTERNAL) && data && capacity) {
226 if (capacity < page_size) {
227 kfree_data_container(data, capacity);
228 } else {
229 kmem_free(kernel_map, (vm_offset_t)data, capacity);
230 }
231 OSCONTAINER_ACCUMSIZE( -((size_t)capacity));
232 } else if (capacity == EXTERNAL) {
233 DeallocFunction freemem = reserved ? reserved->deallocFunction : NULL;
234 if (freemem && data && length) {
235 freemem(data, length);
236 }
237 }
238 if (reserved) {
239 kfree_type(ExpansionData, reserved);
240 }
241 super::free();
242 }
243
244 unsigned int
getLength() const245 OSData::getLength() const
246 {
247 return length;
248 }
249 unsigned int
getCapacity() const250 OSData::getCapacity() const
251 {
252 return capacity;
253 }
254
255 unsigned int
getCapacityIncrement() const256 OSData::getCapacityIncrement() const
257 {
258 return capacityIncrement;
259 }
260
261 unsigned int
setCapacityIncrement(unsigned increment)262 OSData::setCapacityIncrement(unsigned increment)
263 {
264 return capacityIncrement = increment;
265 }
266
267 // xx-review: does not check for capacity == EXTERNAL
268
269 unsigned int
ensureCapacity(unsigned int newCapacity)270 OSData::ensureCapacity(unsigned int newCapacity)
271 {
272 unsigned char * newData;
273 unsigned int finalCapacity;
274 void * copydata;
275 kern_return_t kr;
276
277 if (newCapacity <= capacity) {
278 return capacity;
279 }
280
281 finalCapacity = (((newCapacity - 1) / capacityIncrement) + 1)
282 * capacityIncrement;
283
284 // integer overflow check
285 if (finalCapacity < newCapacity) {
286 return capacity;
287 }
288
289 copydata = data;
290
291 if (finalCapacity >= page_size) {
292 // round up
293 finalCapacity = round_page_32(finalCapacity);
294 // integer overflow check
295 if (finalCapacity < newCapacity) {
296 return capacity;
297 }
298 if (capacity >= page_size) {
299 copydata = NULL;
300 kr = kmem_realloc(kernel_map,
301 (vm_offset_t)data,
302 capacity,
303 (vm_offset_t *)&newData,
304 finalCapacity,
305 IOMemoryTag(kernel_map));
306 } else {
307 kr = kernel_memory_allocate(kernel_map, (vm_offset_t *)&newData,
308 finalCapacity, 0, (kma_flags_t) (KMA_ATOMIC | KMA_DATA),
309 IOMemoryTag(kernel_map));
310 }
311 if (KERN_SUCCESS != kr) {
312 newData = NULL;
313 }
314 } else {
315 newData = (unsigned char *)kalloc_data_container(finalCapacity, Z_WAITOK);
316 }
317
318 if (newData) {
319 bzero(newData + capacity, finalCapacity - capacity);
320 if (copydata) {
321 bcopy(copydata, newData, capacity);
322 }
323 if (data) {
324 if (capacity < page_size) {
325 kfree_data_container(data, capacity);
326 } else {
327 kmem_free(kernel_map, (vm_offset_t)data, capacity);
328 }
329 }
330 OSCONTAINER_ACCUMSIZE(((size_t)finalCapacity) - ((size_t)capacity));
331 data = (void *) newData;
332 capacity = finalCapacity;
333 }
334
335 return capacity;
336 }
337
338 bool
clipForCopyout()339 OSData::clipForCopyout()
340 {
341 unsigned int newCapacity = (uint32_t)round_page(length);
342
343 /*
344 * OSData allocations are atomic, which means that if copyoutkdata()
345 * is used on them, and that there are fully unused pages at the end
346 * of the OSData buffer, then vm_map_copyin() will try to clip the VM
347 * entry which will panic.
348 *
349 * In order to avoid this, trim down the unused pages.
350 */
351 if (length >= msg_ool_size_small && newCapacity < capacity) {
352 kmem_realloc_down(kernel_map, (vm_offset_t)data,
353 capacity, newCapacity);
354 OSCONTAINER_ACCUMSIZE(((size_t)newCapacity) - ((size_t)capacity));
355 capacity = newCapacity;
356 }
357 return true;
358 }
359
360 bool
appendBytes(const void * bytes,unsigned int inLength)361 OSData::appendBytes(const void *bytes, unsigned int inLength)
362 {
363 unsigned int newSize;
364
365 if (!inLength) {
366 return true;
367 }
368
369 if (capacity == EXTERNAL) {
370 return false;
371 }
372
373 if (os_add_overflow(length, inLength, &newSize)) {
374 return false;
375 }
376
377 if ((newSize > capacity) && newSize > ensureCapacity(newSize)) {
378 return false;
379 }
380
381 if (bytes) {
382 bcopy(bytes, &((unsigned char *)data)[length], inLength);
383 } else {
384 bzero(&((unsigned char *)data)[length], inLength);
385 }
386
387 length = newSize;
388
389 return true;
390 }
391
392 bool
appendByte(unsigned char byte,unsigned int inLength)393 OSData::appendByte(unsigned char byte, unsigned int inLength)
394 {
395 unsigned int newSize;
396
397 if (!inLength) {
398 return true;
399 }
400
401 if (capacity == EXTERNAL) {
402 return false;
403 }
404
405 if (os_add_overflow(length, inLength, &newSize)) {
406 return false;
407 }
408
409 if ((newSize > capacity) && newSize > ensureCapacity(newSize)) {
410 return false;
411 }
412
413 memset(&((unsigned char *)data)[length], byte, inLength);
414 length = newSize;
415
416 return true;
417 }
418
419 bool
appendBytes(const OSData * other)420 OSData::appendBytes(const OSData *other)
421 {
422 return appendBytes(other->data, other->length);
423 }
424
425 const void *
getBytesNoCopy() const426 OSData::getBytesNoCopy() const
427 {
428 if (!length) {
429 return NULL;
430 } else {
431 return data;
432 }
433 }
434
435 const void *
getBytesNoCopy(unsigned int start,unsigned int inLength) const436 OSData::getBytesNoCopy(unsigned int start,
437 unsigned int inLength) const
438 {
439 const void *outData = NULL;
440
441 if (length
442 && start < length
443 && (start + inLength) >= inLength // overflow check
444 && (start + inLength) <= length) {
445 outData = (const void *) ((char *) data + start);
446 }
447
448 return outData;
449 }
450
451 bool
isEqualTo(const OSData * aData) const452 OSData::isEqualTo(const OSData *aData) const
453 {
454 unsigned int len;
455
456 len = aData->length;
457 if (length != len) {
458 return false;
459 }
460
461 return isEqualTo(aData->data, len);
462 }
463
464 bool
isEqualTo(const void * someData,unsigned int inLength) const465 OSData::isEqualTo(const void *someData, unsigned int inLength) const
466 {
467 return (length >= inLength) && (bcmp(data, someData, inLength) == 0);
468 }
469
470 bool
isEqualTo(const OSMetaClassBase * obj) const471 OSData::isEqualTo(const OSMetaClassBase *obj) const
472 {
473 OSData * otherData;
474 OSString * str;
475
476 if ((otherData = OSDynamicCast(OSData, obj))) {
477 return isEqualTo(otherData);
478 } else if ((str = OSDynamicCast(OSString, obj))) {
479 return isEqualTo(str);
480 } else {
481 return false;
482 }
483 }
484
485 bool
isEqualTo(const OSString * obj) const486 OSData::isEqualTo(const OSString *obj) const
487 {
488 const char * aCString;
489 char * dataPtr;
490 unsigned int checkLen = length;
491 unsigned int stringLen;
492
493 if (!obj) {
494 return false;
495 }
496
497 stringLen = obj->getLength();
498
499 dataPtr = (char *)data;
500
501 if (stringLen != checkLen) {
502 // check for the fact that OSData may be a buffer that
503 // that includes a termination byte and will thus have
504 // a length of the actual string length PLUS 1. In this
505 // case we verify that the additional byte is a terminator
506 // and if so count the two lengths as being the same.
507
508 if ((checkLen - stringLen) == 1) {
509 if (dataPtr[checkLen - 1] != 0) { // non-zero means not a terminator and thus not likely the same
510 return false;
511 }
512 checkLen--;
513 } else {
514 return false;
515 }
516 }
517
518 aCString = obj->getCStringNoCopy();
519
520 for (unsigned int i = 0; i < checkLen; i++) {
521 if (*dataPtr++ != aCString[i]) {
522 return false;
523 }
524 }
525
526 return true;
527 }
528
529 //this was taken from CFPropertyList.c
530 static const char __CFPLDataEncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
531
532 bool
serialize(OSSerialize * s) const533 OSData::serialize(OSSerialize *s) const
534 {
535 unsigned int i;
536 const unsigned char *p;
537 unsigned char c;
538 unsigned int serializeLength;
539
540 if (s->previouslySerialized(this)) {
541 return true;
542 }
543
544 if (!s->addXMLStartTag(this, "data")) {
545 return false;
546 }
547
548 serializeLength = length;
549 if (reserved && reserved->disableSerialization) {
550 serializeLength = 0;
551 }
552
553 for (i = 0, p = (unsigned char *)data; i < serializeLength; i++, p++) {
554 /* 3 bytes are encoded as 4 */
555 switch (i % 3) {
556 case 0:
557 c = __CFPLDataEncodeTable[((p[0] >> 2) & 0x3f)];
558 if (!s->addChar(c)) {
559 return false;
560 }
561 break;
562 case 1:
563 c = __CFPLDataEncodeTable[((((p[-1] << 8) | p[0]) >> 4) & 0x3f)];
564 if (!s->addChar(c)) {
565 return false;
566 }
567 break;
568 case 2:
569 c = __CFPLDataEncodeTable[((((p[-1] << 8) | p[0]) >> 6) & 0x3f)];
570 if (!s->addChar(c)) {
571 return false;
572 }
573 c = __CFPLDataEncodeTable[(p[0] & 0x3f)];
574 if (!s->addChar(c)) {
575 return false;
576 }
577 break;
578 }
579 }
580 switch (i % 3) {
581 case 0:
582 break;
583 case 1:
584 c = __CFPLDataEncodeTable[((p[-1] << 4) & 0x30)];
585 if (!s->addChar(c)) {
586 return false;
587 }
588 if (!s->addChar('=')) {
589 return false;
590 }
591 if (!s->addChar('=')) {
592 return false;
593 }
594 break;
595 case 2:
596 c = __CFPLDataEncodeTable[((p[-1] << 2) & 0x3c)];
597 if (!s->addChar(c)) {
598 return false;
599 }
600 if (!s->addChar('=')) {
601 return false;
602 }
603 break;
604 }
605
606 return s->addXMLEndTag("data");
607 }
608
609 void
setDeallocFunction(DeallocFunction func)610 OSData::setDeallocFunction(DeallocFunction func)
611 {
612 if (!reserved) {
613 reserved = (typeof(reserved))kalloc_type(ExpansionData, (zalloc_flags_t)(Z_WAITOK | Z_ZERO));
614 if (!reserved) {
615 return;
616 }
617 }
618 reserved->deallocFunction = func;
619 }
620
621 void
setSerializable(bool serializable)622 OSData::setSerializable(bool serializable)
623 {
624 if (!reserved) {
625 reserved = (typeof(reserved))kalloc_type(ExpansionData, (zalloc_flags_t)(Z_WAITOK | Z_ZERO));
626 if (!reserved) {
627 return;
628 }
629 }
630 reserved->disableSerialization = (!serializable);
631 }
632
633 bool
isSerializable(void)634 OSData::isSerializable(void)
635 {
636 return !reserved || !reserved->disableSerialization;
637 }
638