Index: src/OFSecureData.h ================================================================== --- src/OFSecureData.h +++ src/OFSecureData.h @@ -20,40 +20,38 @@ OF_ASSUME_NONNULL_BEGIN /*! * @class OFSecureData OFSecureData.h ObjFW/OFSecureData.h * - * @brief A class for storing arbitrary data in secure memory, securely wiping - * it when it gets deallocated. + * @brief A class for storing arbitrary data in secure (non-swappable) memory, + * securely wiping it when it gets deallocated. * - * @note Secure memory might be unavailable on the platform, in which case this - * falls back to insecure (potentially swappable) memory. + * @warning Non-swappable memory might be unavailable, in which case this falls + * back to swappable memory, but still wipes the data when it gets + * deallocated. Check the @ref swappable property to see whether a + * particular OFSecureData was allocated in swappable memory. */ OF_SUBCLASSING_RESTRICTED @interface OFSecureData: OFData { struct page *_page; + bool _swappable; } -#ifdef OF_HAVE_CLASS_PROPERTIES -@property (class, readonly, nonatomic, getter=isSecure) bool secure; -#endif +/*! + * @brief Whether the OFSecureData is in swappable memory. + */ +@property (readonly, nonatomic, getter=isSwappable) bool swappable; /*! * @brief All items of the OFSecureData as a C array. * * Modifying the returned array directly is allowed and will change the contents * of the data. */ @property (readonly, nonatomic) void *mutableItems OF_RETURNS_INNER_POINTER; -/*! - * @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. Index: src/OFSecureData.m ================================================================== --- src/OFSecureData.m +++ src/OFSecureData.m @@ -40,10 +40,11 @@ #define CHUNK_SIZE 16 struct page { struct page *next, *previous; void *map; + bool swappable; unsigned char *page; }; #if defined(OF_HAVE_COMPILER_TLS) static thread_local struct page *firstPage = NULL; @@ -59,11 +60,11 @@ static struct page **preallocatedPages = NULL; static size_t numPreallocatedPages = 0; #endif static void * -mapPages(size_t numPages) +mapPages(size_t numPages, bool *swappable) { size_t pageSize = [OFSystemInfo pageSize]; void *pointer; if (numPages > SIZE_MAX / pageSize) @@ -73,32 +74,33 @@ if ((pointer = mmap(NULL, numPages * pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)) == MAP_FAILED) @throw [OFOutOfMemoryException exceptionWithRequestedSize: pageSize]; - if (mlock(pointer, numPages * pageSize) != 0 && errno != EPERM) - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: pageSize]; + *swappable = (mlock(pointer, numPages * pageSize) != 0); #else if ((pointer = malloc(numPages * pageSize)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: pageSize]; + + *swappable = true; #endif return pointer; } static void -unmapPages(void *pointer, size_t numPages) +unmapPages(void *pointer, size_t numPages, bool swappable) { size_t pageSize = [OFSystemInfo pageSize]; if (numPages > SIZE_MAX / pageSize) @throw [OFOutOfRangeException exception]; #if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) - munlock(pointer, numPages * pageSize); + if (!swappable) + munlock(pointer, numPages * pageSize); munmap(pointer, numPages * pageSize); #else free(pointer); #endif } @@ -152,11 +154,11 @@ if ((page->map = calloc(1, mapSize)) == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: mapSize]; - page->page = mapPages(1); + page->page = mapPages(1, &page->swappable); of_explicit_memset(page->page, 0, pageSize); #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) lastPage = of_tlskey_get(lastPageKey); #endif @@ -191,11 +193,11 @@ for (size_t i = 0; i < mapSize; i++) if (map[i] != 0) return; - unmapPages(page->page, 1); + unmapPages(page->page, 1, page->swappable); free(page->map); if (page->previous != NULL) page->previous->next = page->next; if (page->next != NULL) @@ -261,10 +263,12 @@ for (size_t i = 0; i < chunks; i++) of_bitset_clear(page->map, chunkIndex + i); } @implementation OFSecureData +@synthesize swappable = _swappable; + #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) + (void)initialize { if (self != [OFSecureData class]) return; @@ -275,42 +279,10 @@ @throw [OFInitializationFailedException exceptionWithClass: self]; } #endif -+ (bool)isSecure -{ -#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON) - bool isSecure = true; - size_t pageSize = [OFSystemInfo pageSize]; - void *pointer; - - if ((pointer = mmap(NULL, pageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON, -1, 0)) == MAP_FAILED) - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: pageSize]; - - if (mlock(pointer, pageSize) != 0) { - if (errno != EPERM) { - munmap(pointer, pageSize); - - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: pageSize]; - } - - isSecure = false; - } - - munlock(pointer, pageSize); - munmap(pointer, pageSize); - - 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) @@ -396,11 +368,11 @@ if (count > SIZE_MAX / itemSize) @throw [OFOutOfRangeException exception]; if (count * itemSize >= pageSize) _items = mapPages(OF_ROUND_UP_POW2(pageSize, - count * itemSize) / pageSize); + count * itemSize) / pageSize, &_swappable); else { #if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS) struct page *lastPage = of_tlskey_get(lastPageKey); #endif @@ -422,10 +394,12 @@ if (_items == NULL) @throw [OFOutOfMemoryException exceptionWithRequestedSize: count * itemSize]; } + + _swappable = _page->swappable; } _itemSize = itemSize; _count = count; } @catch (id e) { @@ -496,11 +470,12 @@ { size_t pageSize = [OFSystemInfo pageSize]; if (_count * _itemSize > pageSize) unmapPages(_items, - OF_ROUND_UP_POW2(pageSize, _count * _itemSize) / pageSize); + OF_ROUND_UP_POW2(pageSize, _count * _itemSize) / pageSize, + _swappable); else if (_page != NULL) { if (_items != NULL) freeMemory(_page, _items, _count * _itemSize); removePageIfEmpty(_page);