X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/2d21ac55c334faf3a56e5634905ed6987fc787d4..2a1bd2d3eef5c7a7bb14f4bb9fdbca9a96ee4752:/pexpert/gen/device_tree.c diff --git a/pexpert/gen/device_tree.c b/pexpert/gen/device_tree.c index 0ea525699..51c9d6ba4 100644 --- a/pexpert/gen/device_tree.c +++ b/pexpert/gen/device_tree.c @@ -2,7 +2,7 @@ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -35,65 +35,115 @@ #include #include +#include #include #include +#include +#include + +#if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) +extern addr64_t kvtophys(vm_offset_t va); +#endif /* defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) */ #include -#ifndef NULL -#define NULL ((void *) 0) -#endif +SECURITY_READ_ONLY_LATE(static int) DTInitialized; +SECURITY_READ_ONLY_LATE(RealDTEntry) DTRootNode; +SECURITY_READ_ONLY_LATE(static vm_size_t) DTSize; +SECURITY_READ_ONLY_LATE(static vm_offset_t) DTEnd; -#define round_long(x) (((x) + 3) & -4) -#define next_prop(x) ((DeviceTreeNodeProperty *) (((int)x) + sizeof(DeviceTreeNodeProperty) + round_long(x->length))) +/* + * + * Support Routines + * + */ -/* Entry*/ -typedef DeviceTreeNode *RealDTEntry; +static inline void +assert_in_dt_region(vm_offset_t const start, vm_offset_t const end, void const *p) +{ + if ((vm_offset_t)p < start || (vm_offset_t)p > end) { + panic("Device tree pointer outside of device tree region: pointer %p, DTEnd %lx\n", p, (unsigned long)DTEnd); + } +} +#define ASSERT_IN_DT(p) assert_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (p)) -typedef struct DTSavedScope { - struct DTSavedScope * nextScope; - RealDTEntry scope; - RealDTEntry entry; - unsigned long index; -} *DTSavedScopePtr; - -/* Entry Iterator*/ -typedef struct OpaqueDTEntryIterator { - RealDTEntry outerScope; - RealDTEntry currentScope; - RealDTEntry currentEntry; - DTSavedScopePtr savedScope; - unsigned long currentIndex; -} *RealDTEntryIterator; - -/* Property Iterator*/ -typedef struct OpaqueDTPropertyIterator { - RealDTEntry entry; - DeviceTreeNodeProperty *currentProperty; - unsigned long currentIndex; -} *RealDTPropertyIterator; +static inline void +assert_prop_in_dt_region(vm_offset_t const start, vm_offset_t const end, DeviceTreeNodeProperty const *prop) +{ + vm_offset_t prop_end; -static int DTInitialized; -static RealDTEntry DTRootNode; + assert_in_dt_region(start, end, prop); + if (os_add3_overflow((vm_offset_t)prop, sizeof(DeviceTreeNodeProperty), prop->length, &prop_end)) { + panic("Device tree property overflow: prop %p, length 0x%x\n", prop, prop->length); + } + assert_in_dt_region(start, end, (void*)prop_end); +} +#define ASSERT_PROP_IN_DT(prop) assert_prop_in_dt_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop)) + +#define ASSERT_HEADER_IN_DT_REGION(start, end, p, size) assert_in_dt_region((start), (end), (uint8_t const *)(p) + (size)) +#define ASSERT_HEADER_IN_DT(p, size) ASSERT_IN_DT((uint8_t const *)(p) + (size)) /* - * Support Routines + * Since there is no way to know the size of a device tree node + * without fully walking it, we employ the following principle to make + * sure that the accessed device tree is fully within its memory + * region: + * + * Internally, we check anything we want to access just before we want + * to access it (not after creating a pointer). + * + * Then, before returning a DTEntry to the caller, we check whether + * the start address (only!) of the entry is still within the device + * tree region. + * + * Before returning a property value the caller, we check whether the + * property is fully within the region. + * + * "DTEntry"s are opaque to the caller, so only checking their + * starting address is enough to satisfy existence within the device + * tree region, while for property values we need to make sure that + * they are fully within the region. */ + +static inline DeviceTreeNodeProperty const * +next_prop_region(vm_offset_t const start, vm_offset_t end, DeviceTreeNodeProperty const *prop) +{ + uintptr_t next_addr; + + ASSERT_HEADER_IN_DT_REGION(start, end, prop, sizeof(DeviceTreeNode)); + + if (os_add3_overflow((uintptr_t)prop, prop->length, sizeof(DeviceTreeNodeProperty) + 3, &next_addr)) { + panic("Device tree property overflow: prop %p, length 0x%x\n", prop, prop->length); + } + + next_addr &= ~(3ULL); + + return (DeviceTreeNodeProperty*)next_addr; +} +#define next_prop(prop) next_prop_region((vm_offset_t)DTRootNode, (vm_offset_t)DTEnd, (prop)) + static RealDTEntry skipProperties(RealDTEntry entry) { - DeviceTreeNodeProperty *prop; + DeviceTreeNodeProperty const *prop; unsigned int k; - if (entry == NULL || entry->nProperties == 0) { + if (entry == NULL) { + return NULL; + } + + ASSERT_HEADER_IN_DT(entry, sizeof(DeviceTreeNode)); + + if (entry->nProperties == 0) { return NULL; } else { - prop = (DeviceTreeNodeProperty *) (entry + 1); + prop = (DeviceTreeNodeProperty const *) (entry + 1); for (k = 0; k < entry->nProperties; k++) { prop = next_prop(prop); } } - return ((RealDTEntry) prop); + ASSERT_IN_DT(prop); + return (RealDTEntry) prop; } static RealDTEntry @@ -102,6 +152,8 @@ skipTree(RealDTEntry root) RealDTEntry entry; unsigned int k; + ASSERT_HEADER_IN_DT(root, sizeof(DeviceTreeNode)); + entry = skipProperties(root); if (entry == NULL) { return NULL; @@ -127,11 +179,18 @@ GetNextChild(RealDTEntry sibling) static const char * GetNextComponent(const char *cp, char *bp) { + size_t length = 0; + char *origbp = bp; + while (*cp != 0) { if (*cp == kDTPathNameSeparator) { cp++; break; } + if (++length > kDTMaxEntryNameLength) { + *origbp = '\0'; + return cp; + } *bp++ = *cp++; } *bp = 0; @@ -141,10 +200,12 @@ GetNextComponent(const char *cp, char *bp) static RealDTEntry FindChild(RealDTEntry cur, char *buf) { - RealDTEntry child; - unsigned long index; - char * str; - unsigned int dummy; + RealDTEntry child; + unsigned long index; + char const * str; + unsigned int dummy; + + ASSERT_HEADER_IN_DT(cur, sizeof(DeviceTreeNode)); if (cur->nChildren == 0) { return NULL; @@ -152,7 +213,7 @@ FindChild(RealDTEntry cur, char *buf) index = 1; child = GetFirstChild(cur); while (1) { - if (DTGetProperty(child, "name", (void **)&str, &dummy) != kSuccess) { + if (SecureDTGetProperty(child, "name", (void const **)&str, &dummy) != kSuccess) { break; } if (strcmp(str, buf) == 0) { @@ -167,75 +228,110 @@ FindChild(RealDTEntry cur, char *buf) return NULL; } - /* * External Routines */ void -DTInit(void *base) +SecureDTInit(void const *base, size_t size) { - DTRootNode = (RealDTEntry) base; + if ((uintptr_t)base + size < (uintptr_t)base) { + panic("DeviceTree overflow: %p, size %#zx", base, size); + } + DTRootNode = base; + DTSize = size; + DTEnd = (vm_offset_t)DTRootNode + DTSize; DTInitialized = (DTRootNode != 0); } +bool +SecureDTIsLockedDown(void) +{ +#if defined(KERNEL_INTEGRITY_KTRR) || defined(KERNEL_INTEGRITY_CTRR) + /* + * We cannot check if the DT is in the CTRR region early on, + * because knowledge of the CTRR region is set up later. But the + * DT is used in all kinds of early bootstrapping before that. + * + * Luckily, we know that the device tree must be in front of the + * kernel if set up in EXTRADATA (which means it's covered by + * CTRR), and after it otherwise. + */ + addr64_t exec_header_phys = kvtophys((vm_offset_t)&_mh_execute_header); + + if (kvtophys((vm_offset_t)DTRootNode) < exec_header_phys) { + assert(kvtophys(DTEnd) < exec_header_phys); + return true; + } + +#endif + return false; +} + int -DTEntryIsEqual(const DTEntry ref1, const DTEntry ref2) +SecureDTEntryIsEqual(const DTEntry ref1, const DTEntry ref2) { /* equality of pointers */ - return (ref1 == ref2); + return ref1 == ref2; } -static char *startingP; // needed for find_entry +static char const *startingP; // needed for find_entry int find_entry(const char *propName, const char *propValue, DTEntry *entryH); -int DTFindEntry(const char *propName, const char *propValue, DTEntry *entryH) +int +SecureDTFindEntry(const char *propName, const char *propValue, DTEntry *entryH) { if (!DTInitialized) { return kError; } - startingP = (char *)DTRootNode; - return(find_entry(propName, propValue, entryH)); + startingP = (char const *)DTRootNode; + return find_entry(propName, propValue, entryH); } -int find_entry(const char *propName, const char *propValue, DTEntry *entryH) +int +find_entry(const char *propName, const char *propValue, DTEntry *entryH) { - DeviceTreeNode *nodeP = (DeviceTreeNode *) startingP; + DeviceTreeNode const *nodeP = (DeviceTreeNode const *) (void const *) startingP; unsigned int k; - if (nodeP->nProperties == 0) return(kError); // End of the list of nodes - startingP = (char *) (nodeP + 1); + ASSERT_HEADER_IN_DT(nodeP, sizeof(DeviceTreeNode)); + + if (nodeP->nProperties == 0) { + return kError; // End of the list of nodes + } + startingP = (char const *) (nodeP + 1); // Search current entry for (k = 0; k < nodeP->nProperties; ++k) { - DeviceTreeNodeProperty *propP = (DeviceTreeNodeProperty *) startingP; + DeviceTreeNodeProperty const *propP = (DeviceTreeNodeProperty const *) (void const *) startingP; + ASSERT_PROP_IN_DT(propP); - startingP += sizeof (*propP) + ((propP->length + 3) & -4); + startingP += sizeof(*propP) + ((propP->length + 3) & -4); - if (strcmp (propP->name, propName) == 0) { - if (propValue == NULL || strcmp( (char *)(propP + 1), propValue) == 0) - { + if (strcmp(propP->name, propName) == 0) { + if (propValue == NULL || strcmp((char const *)(propP + 1), propValue) == 0) { *entryH = (DTEntry)nodeP; - return(kSuccess); + ASSERT_HEADER_IN_DT(*entryH, sizeof(DeviceTreeNode)); + return kSuccess; } } } // Search child nodes - for (k = 0; k < nodeP->nChildren; ++k) - { - if (find_entry(propName, propValue, entryH) == kSuccess) - return(kSuccess); + for (k = 0; k < nodeP->nChildren; ++k) { + if (find_entry(propName, propValue, entryH) == kSuccess) { + return kSuccess; + } } - return(kError); + return kError; } int -DTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEntry) +SecureDTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEntry) { - DTEntryNameBuf buf; - RealDTEntry cur; - const char * cp; + DTEntryNameBuf buf; + RealDTEntry cur; + const char * cp; if (!DTInitialized) { return kError; @@ -245,6 +341,7 @@ DTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEnt } else { cur = searchPoint; } + ASSERT_IN_DT(cur); cp = pathName; if (*cp == kDTPathNameSeparator) { cp++; @@ -266,22 +363,18 @@ DTLookupEntry(const DTEntry searchPoint, const char *pathName, DTEntry *foundEnt } cur = FindChild(cur, buf); - } while (cur != NULL); return kError; } int -DTCreateEntryIterator(const DTEntry startEntry, DTEntryIterator *iterator) +SecureDTInitEntryIterator(const DTEntry startEntry, DTEntryIterator iter) { - RealDTEntryIterator iter; - if (!DTInitialized) { return kError; } - iter = (RealDTEntryIterator) kalloc(sizeof(struct OpaqueDTEntryIterator)); if (startEntry != NULL) { iter->outerScope = (RealDTEntry) startEntry; iter->currentScope = (RealDTEntry) startEntry; @@ -293,28 +386,12 @@ DTCreateEntryIterator(const DTEntry startEntry, DTEntryIterator *iterator) iter->savedScope = NULL; iter->currentIndex = 0; - *iterator = iter; - return kSuccess; -} - -int -DTDisposeEntryIterator(DTEntryIterator iterator) -{ - RealDTEntryIterator iter = iterator; - DTSavedScopePtr scope; - - while ((scope = iter->savedScope) != NULL) { - iter->savedScope = scope->nextScope; - kfree(scope, sizeof(struct DTSavedScope)); - } - kfree(iterator, sizeof(struct OpaqueDTEntryIterator)); return kSuccess; } int -DTEnterEntry(DTEntryIterator iterator, DTEntry childEntry) +SecureDTEnterEntry(DTEntryIterator iter, DTEntry childEntry) { - RealDTEntryIterator iter = iterator; DTSavedScopePtr newScope; if (childEntry == NULL) { @@ -324,7 +401,7 @@ DTEnterEntry(DTEntryIterator iterator, DTEntry childEntry) newScope->nextScope = iter->savedScope; newScope->scope = iter->currentScope; newScope->entry = iter->currentEntry; - newScope->index = iter->currentIndex; + newScope->index = iter->currentIndex; iter->currentScope = childEntry; iter->currentEntry = NULL; @@ -335,9 +412,8 @@ DTEnterEntry(DTEntryIterator iterator, DTEntry childEntry) } int -DTExitEntry(DTEntryIterator iterator, DTEntry *currentPosition) +SecureDTExitEntry(DTEntryIterator iter, DTEntry *currentPosition) { - RealDTEntryIterator iter = iterator; DTSavedScopePtr newScope; newScope = iter->savedScope; @@ -356,10 +432,8 @@ DTExitEntry(DTEntryIterator iterator, DTEntry *currentPosition) } int -DTIterateEntries(DTEntryIterator iterator, DTEntry *nextEntry) +SecureDTIterateEntries(DTEntryIterator iter, DTEntry *nextEntry) { - RealDTEntryIterator iter = iterator; - if (iter->currentIndex >= iter->currentScope->nChildren) { *nextEntry = NULL; return kIterationDone; @@ -370,15 +444,15 @@ DTIterateEntries(DTEntryIterator iterator, DTEntry *nextEntry) } else { iter->currentEntry = GetNextChild(iter->currentEntry); } + ASSERT_IN_DT(iter->currentEntry); *nextEntry = iter->currentEntry; return kSuccess; } } int -DTRestartEntryIteration(DTEntryIterator iterator) +SecureDTRestartEntryIteration(DTEntryIterator iter) { - RealDTEntryIterator iter = iterator; #if 0 // This commented out code allows a second argument (outer) // which (if true) causes restarting at the outer scope @@ -398,77 +472,83 @@ DTRestartEntryIteration(DTEntryIterator iterator) return kSuccess; } -int -DTGetProperty(const DTEntry entry, const char *propertyName, void **propertyValue, unsigned int *propertySize) +static int +SecureDTGetPropertyInternal(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize, vm_offset_t const region_start, vm_size_t region_size) { - DeviceTreeNodeProperty *prop; + DeviceTreeNodeProperty const *prop; unsigned int k; - if (entry == NULL || entry->nProperties == 0) { + if (entry == NULL) { + return kError; + } + + ASSERT_HEADER_IN_DT_REGION(region_start, region_start + region_size, entry, sizeof(DeviceTreeNode)); + + if (entry->nProperties == 0) { return kError; } else { - prop = (DeviceTreeNodeProperty *) (entry + 1); + prop = (DeviceTreeNodeProperty const *) (entry + 1); for (k = 0; k < entry->nProperties; k++) { + assert_prop_in_dt_region(region_start, region_start + region_size, prop); if (strcmp(prop->name, propertyName) == 0) { - *propertyValue = (void *) (((int)prop) - + sizeof(DeviceTreeNodeProperty)); + *propertyValue = (void const *) (((uintptr_t)prop) + + sizeof(DeviceTreeNodeProperty)); *propertySize = prop->length; return kSuccess; } - prop = next_prop(prop); + prop = next_prop_region(region_start, region_start + region_size, prop); } } return kError; } int -DTCreatePropertyIterator(const DTEntry entry, DTPropertyIterator *iterator) +SecureDTGetProperty(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize) { - RealDTPropertyIterator iter; - - iter = (RealDTPropertyIterator) kalloc(sizeof(struct OpaqueDTPropertyIterator)); - iter->entry = entry; - iter->currentProperty = NULL; - iter->currentIndex = 0; + return SecureDTGetPropertyInternal(entry, propertyName, propertyValue, propertySize, + (vm_offset_t)DTRootNode, (vm_size_t)((uintptr_t)DTEnd - (uintptr_t)DTRootNode)); +} - *iterator = iter; - return kSuccess; +int +SecureDTGetPropertyRegion(const DTEntry entry, const char *propertyName, void const **propertyValue, unsigned int *propertySize, vm_offset_t const region_start, vm_size_t region_size) +{ + return SecureDTGetPropertyInternal(entry, propertyName, propertyValue, propertySize, + region_start, region_size); } + int -DTDisposePropertyIterator(DTPropertyIterator iterator) +SecureDTInitPropertyIterator(const DTEntry entry, DTPropertyIterator iter) { - kfree(iterator, sizeof(struct OpaqueDTPropertyIterator)); + iter->entry = entry; + iter->currentProperty = NULL; + iter->currentIndex = 0; return kSuccess; } int -DTIterateProperties(DTPropertyIterator iterator, char **foundProperty) +SecureDTIterateProperties(DTPropertyIterator iter, char const **foundProperty) { - RealDTPropertyIterator iter = iterator; - if (iter->currentIndex >= iter->entry->nProperties) { *foundProperty = NULL; return kIterationDone; } else { iter->currentIndex++; if (iter->currentIndex == 1) { - iter->currentProperty = (DeviceTreeNodeProperty *) (iter->entry + 1); + iter->currentProperty = (DeviceTreeNodeProperty const *) (iter->entry + 1); } else { iter->currentProperty = next_prop(iter->currentProperty); } + ASSERT_PROP_IN_DT(iter->currentProperty); *foundProperty = iter->currentProperty->name; return kSuccess; } } int -DTRestartPropertyIteration(DTPropertyIterator iterator) +SecureDTRestartPropertyIteration(DTPropertyIterator iter) { - RealDTPropertyIterator iter = iterator; - iter->currentProperty = NULL; iter->currentIndex = 0; return kSuccess; } -