ObjFW  Check-in [0e45b7bb1c]

Overview
Comment:OFSecureData: Add a memory allocator

This avoids having at last one page per OFSecureData and allows multiple
small OFSecureData sharing the same page, which is important on systems
where the number of pages that can be locked is very limited.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0e45b7bb1cc12f331efe06b547568af612cd83a8800365c2bec504da86c95830
User & Date: js on 2018-04-08 13:37:31
Other Links: manifest | tags
Context
2018-04-08
16:09
Use -integrated-as on OpenBSD/SPARC64 with Clang check-in: 3a831de131 user: js tags: trunk
13:37
OFSecureData: Add a memory allocator check-in: 0e45b7bb1c user: js tags: trunk
10:46
OFHMAC: Fix a missing rethrow check-in: 2c80eb12b9 user: js tags: trunk
Changes

Modified src/OFList.m from [c4c7020709] to [2c2e8bdce6].

84
85
86
87
88
89
90

91
92
93
94
95
96
97
	listObject->next = NULL;
	listObject->previous = _lastListObject;

	if (_lastListObject != NULL)
		_lastListObject->next = listObject;

	_lastListObject = listObject;

	if (_firstListObject == NULL)
		_firstListObject = listObject;

	_count++;
	_mutations++;

	return listObject;







>







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
	listObject->next = NULL;
	listObject->previous = _lastListObject;

	if (_lastListObject != NULL)
		_lastListObject->next = listObject;

	_lastListObject = listObject;

	if (_firstListObject == NULL)
		_firstListObject = listObject;

	_count++;
	_mutations++;

	return listObject;

Modified src/OFSecureData.h from [531e4cada4] to [ac5cf895db].

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 *	  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.
 */
@interface OFSecureData: OFData
{
	size_t _mappingSize;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic, getter=isSecure) bool secure;
#endif

/*!







|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 *	  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.
 */
@interface OFSecureData: OFData
{
	struct page *_page;
}

#ifdef OF_HAVE_CLASS_PROPERTIES
@property (class, readonly, nonatomic, getter=isSecure) bool secure;
#endif

/*!

Modified src/OFSecureData.m from [5b69d2d183] to [43dbd361d6].

23
24
25
26
27
28
29

30
31
32
33

































































































































































































34












35
36
37
38
39
40
41
# include <sys/mman.h>
#endif

#import "OFSecureData.h"
#import "OFString.h"
#import "OFSystemInfo.h"


#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"


































































































































































































@implementation OFSecureData












+ (bool)isSecure
{
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	return true;
#else
	return false;
#endif







>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# include <sys/mman.h>
#endif

#import "OFSecureData.h"
#import "OFString.h"
#import "OFSystemInfo.h"

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfMemoryException.h"
#import "OFOutOfRangeException.h"

#ifdef OF_HAVE_THREADS
# import "threading.h"
#endif

#define CHUNK_SIZE 32

struct page {
	struct page *next, *previous;
	void *map;
	unsigned char *page;
};

#if defined(OF_HAVE_COMPILER_TLS)
static thread_local struct page *firstPage = NULL;
static thread_local struct page *lastPage = NULL;
#elif defined(OF_HAVE_THREADS)
static of_tlskey_t firstPageKey, lastPageKey;
#else
static struct page *firstPage = NULL;
static struct page *lastPage = NULL;
#endif

static void *
mapPages(size_t numPages)
{
	size_t pageSize = [OFSystemInfo pageSize];
	void *pointer;

	if (numPages > SIZE_MAX / pageSize)
		@throw [OFOutOfRangeException exception];

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	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)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: pageSize];
#else
	if ((pointer = malloc(numPages * pageSize)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: pageSize];
#endif

	return pointer;
}

static void
unmapPages(void *pointer, size_t numPages)
{
	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);
	munmap(pointer, numPages * pageSize);
#else
	free(pointer);
#endif
}

static struct page *
addPage(void)
{
	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 ((page = malloc(sizeof(*page))) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: sizeof(*page)];

	if ((page->map = malloc(mapSize)) == NULL)
		@throw [OFOutOfMemoryException
		    exceptionWithRequestedSize: mapSize];

	of_explicit_memset(page->map, 0, mapSize);

	page->page = mapPages(1);
	of_explicit_memset(page->page, 0, pageSize);

#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
	lastPage = of_tlskey_get(lastPageKey);
#endif

	page->previous = lastPage;
	page->next = NULL;

	if (lastPage != NULL)
		lastPage->next = page;

#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	lastPage = page;

	if (firstPage == NULL)
		firstPage = page;
#else
	OF_ENSURE(of_tlskey_set(lastPageKey, page));

	if (of_tlskey_get(firstPageKey) == NULL)
		OF_ENSURE(of_tlskey_set(firstPageKey, page));
#endif

	return page;
}

static void
removePageIfEmpty(struct page *page)
{
	unsigned char *map = page->map;
	size_t pageSize = [OFSystemInfo pageSize];
	size_t mapSize = OF_ROUND_UP_POW2(8, pageSize / CHUNK_SIZE) / 8;

	for (size_t i = 0; i < mapSize; i++)
		if (map[i] != 0)
			return;

	unmapPages(page->page, 1);
	free(page->map);

	if (page->previous != NULL)
		page->previous->next = page->next;
	if (page->next != NULL)
		page->next->previous = page->previous;

#if defined(OF_HAVE_COMPILER_TLS) || !defined(OF_HAVE_THREADS)
	if (firstPage == page)
		firstPage = page->next;
	if (lastPage == page)
		lastPage = page->previous;
#else
	if (of_tlskey_get(firstPageKey) == page)
		OF_ENSURE(of_tlskey_set(firstPageKey, page->next));
	if (of_tlskey_get(lastPageKey) == page)
		OF_ENSURE(of_tlskey_set(lastPageKey, page->previous));
#endif

	free(page);
}

static void *
allocateMemory(struct page *page, size_t bytes)
{
	size_t chunks, chunksLeft, pageSize, i, firstChunk;

	bytes = OF_ROUND_UP_POW2(CHUNK_SIZE, bytes);
	chunks = chunksLeft = bytes / CHUNK_SIZE;
	firstChunk = 0;
	pageSize = [OFSystemInfo pageSize];

	for (i = 0; i < pageSize / CHUNK_SIZE; i++) {
		if (of_bitset_isset(page->map, i)) {
			chunksLeft = chunks;
			firstChunk = i + 1;
			continue;
		}

		if (--chunksLeft == 0)
			break;
	}

	if (chunksLeft == 0) {
		for (size_t j = firstChunk; j < firstChunk + chunks; j++)
			of_bitset_set(page->map, j);

		return page->page + (CHUNK_SIZE * firstChunk);
	}

	return NULL;
}

static void
freeMemory(struct page *page, void *pointer, size_t bytes)
{
	size_t chunks, chunkIndex;

	bytes = OF_ROUND_UP_POW2(CHUNK_SIZE, bytes);
	chunks = bytes / CHUNK_SIZE;
	chunkIndex = ((uintptr_t)pointer - (uintptr_t)page->page) / CHUNK_SIZE;

	of_explicit_memset(pointer, 0, bytes);

	for (size_t i = 0; i < chunks; i++)
		of_bitset_clear(page->map, chunkIndex + i);
}

@implementation OFSecureData
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
+ (void)initialize
{
	if (self != [OFSecureData class])
		return;

	if (!of_tlskey_new(&firstPageKey) || !of_tlskey_new(&lastPageKey))
		@throw [OFInitializationFailedException
		    exceptionWithClass: self];
}
#endif

+ (bool)isSecure
{
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	return true;
#else
	return false;
#endif
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109





110


111
112



113
114
115
116
117
118
119
120


121
122
123
124
125
126
127



128
129
130
131
132
133
134
135
136
137
138
139

- (instancetype)initWithItemSize: (size_t)itemSize
			   count: (size_t)count
{
	self = [super init];

	@try {
		size_t size;
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		size_t pageSize;
#endif

		if OF_UNLIKELY (itemSize == 0)
			@throw [OFInvalidArgumentException exception];

		if OF_UNLIKELY (count > SIZE_MAX / itemSize)
			@throw [OFOutOfRangeException exception];

		size = itemSize * count;
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
		pageSize = [OFSystemInfo pageSize];
		_mappingSize = OF_ROUND_UP_POW2(pageSize, size);








		if OF_UNLIKELY (_mappingSize < size)
			@throw [OFOutOfRangeException exception];




		if OF_UNLIKELY ((_items = mmap(NULL, _mappingSize,
		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)) ==
		    MAP_FAILED)
			@throw [OFOutOfMemoryException
			    exceptionWithRequestedSize: _mappingSize];

		if OF_UNLIKELY (mlock(_items, _mappingSize) != 0)


			@throw [OFOutOfMemoryException
			    exceptionWithRequestedSize: _mappingSize];
#else
		if OF_UNLIKELY ((_items = malloc(size)) == NULL)
			@throw [OFOutOfMemoryException
			    exceptionWithRequestedSize: size];
#endif




		_itemSize = itemSize;
		_count = count;

		[self zero];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}







<
<
|
<

<
<
<
|


|
<
<
|
>
>
>
>
>

>
>
|
|
>
>
>
|
<
<
<
<
<
|
|
>
>
|
|
|
|
|
|
<
>
>
>



<
<







294
295
296
297
298
299
300


301

302



303
304
305
306


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321





322
323
324
325
326
327
328
329
330
331

332
333
334
335
336
337


338
339
340
341
342
343
344

- (instancetype)initWithItemSize: (size_t)itemSize
			   count: (size_t)count
{
	self = [super init];

	@try {


		size_t pageSize = [OFSystemInfo pageSize];





		if (count > SIZE_MAX / itemSize)
			@throw [OFOutOfRangeException exception];

		if (count * itemSize >= pageSize)


			_items = mapPages(OF_ROUND_UP_POW2(pageSize,
			    count * itemSize) / pageSize);
		else {
#if !defined(OF_HAVE_COMPILER_TLS) && defined(OF_HAVE_THREADS)
			struct page *lastPage = of_tlskey_get(lastPageKey);
#endif

			for (struct page *page = lastPage; page != NULL;
			    page = page->previous) {
				_items = allocateMemory(page, count * itemSize);

				if (_items != NULL) {
					_page = page;
					break;
				}





			}

			if (_items == NULL) {
				_page = addPage();
				_items = allocateMemory(_page,
				    count * itemSize);

				if (_items == NULL)
					@throw [OFOutOfMemoryException
					    exceptionWithRequestedSize:

					    count * itemSize];
			}
		}

		_itemSize = itemSize;
		_count = count;


	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}
192
193
194
195
196
197
198
199
200
201
202
203
204
205

206


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	[self zero];

#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	munlock(_items, _mappingSize);
	munmap(_items, _mappingSize);
#else
	free(_items);

#endif



	[super dealloc];
}

- (void)zero
{
#if defined(HAVE_MMAP) && defined(HAVE_MLOCK) && defined(MAP_ANON)
	of_explicit_memset(_items, 0, _mappingSize);
#else
	of_explicit_memset(_items, 0, _count * _itemSize);
#endif
}

- (id)copy
{
	return [[OFSecureData alloc] initWithItems: _items
					  itemSize: _itemSize
					     count: _count];







|

|
|
|
|
|
>
|
>
>






<
<
<

<







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420



421

422
423
424
425
426
427
428
- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	OF_INVALID_INIT_METHOD
}

- (void)dealloc
{
	size_t pageSize = [OFSystemInfo pageSize];

	if (_count * _itemSize > pageSize)
		unmapPages(_items,
		    OF_ROUND_UP_POW2(pageSize, _count * _itemSize) / pageSize);
	else {
		if (_items != NULL)
			freeMemory(_page, _items, _count * _itemSize);

		removePageIfEmpty(_page);
	}

	[super dealloc];
}

- (void)zero
{



	of_explicit_memset(_items, 0, _count * _itemSize);

}

- (id)copy
{
	return [[OFSecureData alloc] initWithItems: _items
					  itemSize: _itemSize
					     count: _count];