Index: src/OFSecureData.h ================================================================== --- src/OFSecureData.h +++ src/OFSecureData.h @@ -41,10 +41,24 @@ * @brief Whether OFSecureData is secure, meaning preventing the data from * being swapped out is supported. */ + (bool)isSecure; +/*! + * @brief Preallocates the specified number of bytes. + * + * This is useful to allocate secure memory before enabling a sandbox that does + * not allow it anymore. + * + * @note This may only be called once per thread! + * @note Preallocated memory is only available for OFSecureData that is smaller + * than a single page! + * + * @param size The number of bytes to preallocate + */ ++ (void)preallocateMemoryWithSize: (size_t)size; + /*! * @brief Creates a new, autoreleased OFSecureData with count items of item * size 1, all set to zero. * * @param count The number of zero items the OFSecureData should contain Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -46,15 +46,20 @@ }; #if defined(OF_HAVE_COMPILER_TLS) static thread_local struct page *firstPage = NULL; static thread_local struct page *lastPage = NULL; +static thread_local struct page **preallocatedPages = NULL; +static thread_local size_t numPreallocatedPages = 0; #elif defined(OF_HAVE_THREADS) static of_tlskey_t firstPageKey, lastPageKey; +static of_tlskey_t preallocatedPagesKey, numPreallocatedPagesKey; #else static struct page *firstPage = NULL; static struct page *lastPage = NULL; +static struct page **preallocatedPages = NULL; +static size_t numPreallocatedPages = 0; #endif static void * mapPages(size_t numPages) { @@ -97,18 +102,51 @@ free(pointer); #endif } static struct page * -addPage(void) +addPage(bool allowPreallocated) { size_t pageSize = [OFSystemInfo pageSize]; size_t mapSize = OF_ROUND_UP_POW2(8, pageSize / CHUNK_SIZE) / 8; struct page *page; #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) struct page *lastPage; #endif + + if (allowPreallocated) { +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + uintptr_t numPreallocatedPages = + (uintptr_t)of_tlskey_get(numPreallocatedPagesKey); +#endif + + if (numPreallocatedPages > 0) { +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + struct page **preallocatedPages = + of_tlskey_get(preallocatedPagesKey); +#endif + + numPreallocatedPages--; +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + OF_ENSURE(of_tlskey_set(numPreallocatedPagesKey, + (void *)numPreallocatedPages)); +#endif + + page = preallocatedPages[numPreallocatedPages]; + + if (numPreallocatedPages == 0) { + free(preallocatedPages); + preallocatedPages = NULL; +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + OF_ENSURE(of_tlskey_set(preallocatedPagesKey, + preallocatedPages)); +#endif + } + + return page; + } + } if ((page = malloc(sizeof(*page))) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: sizeof(*page)]; @@ -229,11 +267,13 @@ + (void)initialize { if (self != [OFSecureData class]) return; - if (!of_tlskey_new(&firstPageKey) || !of_tlskey_new(&lastPageKey)) + if (!of_tlskey_new(&firstPageKey) || !of_tlskey_new(&lastPageKey) || + !of_tlskey_new(&preallocatedPagesKey) || + !of_tlskey_new(&numPreallocatedPagesKey)) @throw [OFInitializationFailedException exceptionWithClass: self]; } #endif @@ -266,10 +306,41 @@ return isSecure; #else return false; #endif } + ++ (void)preallocateMemoryWithSize: (size_t)size +{ + size_t pageSize = [OFSystemInfo pageSize]; + size_t numPages = OF_ROUND_UP_POW2(pageSize, size) / pageSize; +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + struct page **preallocatedPages = of_tlskey_get(preallocatedPagesKey); + size_t numPreallocatedPages; +#endif + + if (preallocatedPages != NULL) + @throw [OFInvalidArgumentException exception]; + + preallocatedPages = calloc(numPages, sizeof(struct page)); + if (preallocatedPages == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: numPages * sizeof(struct page)]; + +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + of_tlskey_set(preallocatedPagesKey, preallocatedPages); +#endif + + for (size_t i = 0; i < numPages; i++) + preallocatedPages[i] = addPage(false); + + numPreallocatedPages = numPages; +#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + of_tlskey_set(numPreallocatedPagesKey, + (void *)(uintptr_t)numPreallocatedPages); +#endif +} + (instancetype)dataWithCount: (size_t)count { return [[[self alloc] initWithCount: count] autorelease]; } @@ -342,11 +413,11 @@ break; } } if (_items == NULL) { - _page = addPage(); + _page = addPage(true); _items = allocateMemory(_page, count * itemSize); if (_items == NULL) @throw [OFOutOfMemoryException