/src/nss-nspr/nss/lib/util/secport.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | /* |
6 | | * secport.c - portability interfaces for security libraries |
7 | | * |
8 | | * This file abstracts out libc functionality that libsec depends on |
9 | | * |
10 | | * NOTE - These are not public interfaces |
11 | | */ |
12 | | |
13 | | #include "seccomon.h" |
14 | | #include "prmem.h" |
15 | | #include "prerror.h" |
16 | | #include "plarena.h" |
17 | | #include "secerr.h" |
18 | | #include "prmon.h" |
19 | | #include "nssilock.h" |
20 | | #include "secport.h" |
21 | | #include "prenv.h" |
22 | | #include "prinit.h" |
23 | | |
24 | | #include <stdint.h> |
25 | | |
26 | | #ifdef DEBUG |
27 | | #define THREADMARK |
28 | | #endif /* DEBUG */ |
29 | | |
30 | | #ifdef THREADMARK |
31 | | #include "prthread.h" |
32 | | #endif /* THREADMARK */ |
33 | | |
34 | | #if defined(XP_UNIX) |
35 | | #include <stdlib.h> |
36 | | #else |
37 | | #include "wtypes.h" |
38 | | #endif |
39 | | |
40 | | #define SET_ERROR_CODE /* place holder for code to set PR error code. */ |
41 | | |
42 | | #ifdef THREADMARK |
43 | | typedef struct threadmark_mark_str { |
44 | | struct threadmark_mark_str *next; |
45 | | void *mark; |
46 | | } threadmark_mark; |
47 | | |
48 | | #endif /* THREADMARK */ |
49 | | |
50 | | /* The value of this magic must change each time PORTArenaPool changes. */ |
51 | 20.5k | #define ARENAPOOL_MAGIC 0xB8AC9BDF |
52 | | |
53 | 0 | #define CHEAP_ARENAPOOL_MAGIC 0x3F16BB09 |
54 | | |
55 | | typedef struct PORTArenaPool_str { |
56 | | PLArenaPool arena; |
57 | | PRUint32 magic; |
58 | | PRLock *lock; |
59 | | #ifdef THREADMARK |
60 | | PRThread *marking_thread; |
61 | | threadmark_mark *first_mark; |
62 | | #endif |
63 | | } PORTArenaPool; |
64 | | |
65 | | /* locations for registering Unicode conversion functions. |
66 | | * XXX is this the appropriate location? or should they be |
67 | | * moved to client/server specific locations? |
68 | | */ |
69 | | PORTCharConversionFunc ucs4Utf8ConvertFunc; |
70 | | PORTCharConversionFunc ucs2Utf8ConvertFunc; |
71 | | PORTCharConversionWSwapFunc ucs2AsciiConvertFunc; |
72 | | |
73 | | /* NSPR memory allocation functions (PR_Malloc, PR_Calloc, and PR_Realloc) |
74 | | * use the PRUint32 type for the size parameter. Before we pass a size_t or |
75 | | * unsigned long size to these functions, we need to ensure it is <= half of |
76 | | * the maximum PRUint32 value to avoid truncation and catch a negative size. |
77 | | */ |
78 | 20.3k | #define MAX_SIZE (PR_UINT32_MAX >> 1) |
79 | | |
80 | | void * |
81 | | PORT_Alloc(size_t bytes) |
82 | 3.47k | { |
83 | 3.47k | void *rv = NULL; |
84 | | |
85 | 3.47k | if (bytes <= MAX_SIZE) { |
86 | | /* Always allocate a non-zero amount of bytes */ |
87 | 3.47k | rv = PR_Malloc(bytes ? bytes : 1); |
88 | 3.47k | } |
89 | 3.47k | if (!rv) { |
90 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
91 | 0 | } |
92 | 3.47k | return rv; |
93 | 3.47k | } |
94 | | |
95 | | void * |
96 | | PORT_Realloc(void *oldptr, size_t bytes) |
97 | 8 | { |
98 | 8 | void *rv = NULL; |
99 | | |
100 | 8 | if (bytes <= MAX_SIZE) { |
101 | 8 | rv = PR_Realloc(oldptr, bytes); |
102 | 8 | } |
103 | 8 | if (!rv) { |
104 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
105 | 0 | } |
106 | 8 | return rv; |
107 | 8 | } |
108 | | |
109 | | void * |
110 | | PORT_ZAlloc(size_t bytes) |
111 | 3.18k | { |
112 | 3.18k | void *rv = NULL; |
113 | | |
114 | 3.18k | if (bytes <= MAX_SIZE) { |
115 | | /* Always allocate a non-zero amount of bytes */ |
116 | 3.18k | rv = PR_Calloc(1, bytes ? bytes : 1); |
117 | 3.18k | } |
118 | 3.18k | if (!rv) { |
119 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
120 | 0 | } |
121 | 3.18k | return rv; |
122 | 3.18k | } |
123 | | |
124 | | /* aligned_alloc is C11. This is an alternative to get aligned memory. */ |
125 | | void * |
126 | | PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem) |
127 | 31 | { |
128 | 31 | size_t x = alignment - 1; |
129 | | |
130 | | /* This only works if alignment is a power of 2. */ |
131 | 31 | if ((alignment == 0) || (alignment & (alignment - 1))) { |
132 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
133 | 0 | return NULL; |
134 | 0 | } |
135 | | |
136 | 31 | if (!mem) { |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | | /* Always allocate a non-zero amount of bytes */ |
141 | 31 | *mem = PORT_ZAlloc((bytes ? bytes : 1) + x); |
142 | 31 | if (!*mem) { |
143 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
144 | 0 | return NULL; |
145 | 0 | } |
146 | | |
147 | 31 | return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x); |
148 | 31 | } |
149 | | |
150 | | void * |
151 | | PORT_ZAllocAlignedOffset(size_t size, size_t alignment, size_t offset) |
152 | 31 | { |
153 | 31 | PORT_Assert(offset < size); |
154 | 31 | if (offset > size) { |
155 | 0 | return NULL; |
156 | 0 | } |
157 | | |
158 | 31 | void *mem = NULL; |
159 | 31 | void *v = PORT_ZAllocAligned(size, alignment, &mem); |
160 | 31 | if (!v) { |
161 | 0 | return NULL; |
162 | 0 | } |
163 | | |
164 | 31 | PORT_Assert(mem); |
165 | 31 | *((void **)((uintptr_t)v + offset)) = mem; |
166 | 31 | return v; |
167 | 31 | } |
168 | | |
169 | | void |
170 | | PORT_Free(void *ptr) |
171 | 3.57k | { |
172 | 3.57k | if (ptr) { |
173 | 3.54k | PR_Free(ptr); |
174 | 3.54k | } |
175 | 3.57k | } |
176 | | |
177 | | void |
178 | | PORT_ZFree(void *ptr, size_t len) |
179 | 2.71k | { |
180 | 2.71k | if (ptr) { |
181 | 2.71k | memset(ptr, 0, len); |
182 | 2.71k | PR_Free(ptr); |
183 | 2.71k | } |
184 | 2.71k | } |
185 | | |
186 | | char * |
187 | | PORT_Strdup(const char *str) |
188 | 6 | { |
189 | 6 | size_t len = PORT_Strlen(str) + 1; |
190 | 6 | char *newstr; |
191 | | |
192 | 6 | newstr = (char *)PORT_Alloc(len); |
193 | 6 | if (newstr) { |
194 | 6 | PORT_Memcpy(newstr, str, len); |
195 | 6 | } |
196 | 6 | return newstr; |
197 | 6 | } |
198 | | |
199 | | void |
200 | | PORT_SetError(int value) |
201 | 1.03k | { |
202 | 1.03k | PR_SetError(value, 0); |
203 | 1.03k | return; |
204 | 1.03k | } |
205 | | |
206 | | int |
207 | | PORT_GetError(void) |
208 | 78 | { |
209 | 78 | return (PR_GetError()); |
210 | 78 | } |
211 | | |
212 | | /********************* Arena code follows ***************************** |
213 | | * ArenaPools are like heaps. The memory in them consists of large blocks, |
214 | | * called arenas, which are allocated from the/a system heap. Inside an |
215 | | * ArenaPool, the arenas are organized as if they were in a stack. Newly |
216 | | * allocated arenas are "pushed" on that stack. When you attempt to |
217 | | * allocate memory from an ArenaPool, the code first looks to see if there |
218 | | * is enough unused space in the top arena on the stack to satisfy your |
219 | | * request, and if so, your request is satisfied from that arena. |
220 | | * Otherwise, a new arena is allocated (or taken from NSPR's list of freed |
221 | | * arenas) and pushed on to the stack. The new arena is always big enough |
222 | | * to satisfy the request, and is also at least a minimum size that is |
223 | | * established at the time that the ArenaPool is created. |
224 | | * |
225 | | * The ArenaMark function returns the address of a marker in the arena at |
226 | | * the top of the arena stack. It is the address of the place in the arena |
227 | | * on the top of the arena stack from which the next block of memory will |
228 | | * be allocated. Each ArenaPool has its own separate stack, and hence |
229 | | * marks are only relevant to the ArenaPool from which they are gotten. |
230 | | * Marks may be nested. That is, a thread can get a mark, and then get |
231 | | * another mark. |
232 | | * |
233 | | * It is intended that all the marks in an ArenaPool may only be owned by a |
234 | | * single thread. In DEBUG builds, this is enforced. In non-DEBUG builds, |
235 | | * it is not. In DEBUG builds, when a thread gets a mark from an |
236 | | * ArenaPool, no other thread may acquire a mark in that ArenaPool while |
237 | | * that mark exists, that is, until that mark is unmarked or released. |
238 | | * Therefore, it is important that every mark be unmarked or released when |
239 | | * the creating thread has no further need for exclusive ownership of the |
240 | | * right to manage the ArenaPool. |
241 | | * |
242 | | * The ArenaUnmark function discards the ArenaMark at the address given, |
243 | | * and all marks nested inside that mark (that is, acquired from that same |
244 | | * ArenaPool while that mark existed). It is an error for a thread other |
245 | | * than the mark's creator to try to unmark it. When a thread has unmarked |
246 | | * all its marks from an ArenaPool, then another thread is able to set |
247 | | * marks in that ArenaPool. ArenaUnmark does not deallocate (or "pop") any |
248 | | * memory allocated from the ArenaPool since the mark was created. |
249 | | * |
250 | | * ArenaRelease "pops" the stack back to the mark, deallocating all the |
251 | | * memory allocated from the arenas in the ArenaPool since that mark was |
252 | | * created, and removing any arenas from the ArenaPool that have no |
253 | | * remaining active allocations when that is done. It implicitly releases |
254 | | * any marks nested inside the mark being explicitly released. It is the |
255 | | * only operation, other than destroying the arenapool, that potentially |
256 | | * reduces the number of arenas on the stack. Otherwise, the stack grows |
257 | | * until the arenapool is destroyed, at which point all the arenas are |
258 | | * freed or returned to a "free arena list", depending on their sizes. |
259 | | */ |
260 | | PLArenaPool * |
261 | | PORT_NewArena(unsigned long chunksize) |
262 | 1.95k | { |
263 | 1.95k | PORTArenaPool *pool; |
264 | | |
265 | 1.95k | if (chunksize > MAX_SIZE) { |
266 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
267 | 0 | return NULL; |
268 | 0 | } |
269 | 1.95k | pool = PORT_ZNew(PORTArenaPool); |
270 | 1.95k | if (!pool) { |
271 | 0 | return NULL; |
272 | 0 | } |
273 | 1.95k | pool->magic = ARENAPOOL_MAGIC; |
274 | 1.95k | pool->lock = PZ_NewLock(nssILockArena); |
275 | 1.95k | if (!pool->lock) { |
276 | 0 | PORT_Free(pool); |
277 | 0 | return NULL; |
278 | 0 | } |
279 | 1.95k | PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double)); |
280 | 1.95k | return (&pool->arena); |
281 | 1.95k | } |
282 | | |
283 | | void |
284 | | PORT_InitCheapArena(PORTCheapArenaPool *pool, unsigned long chunksize) |
285 | 0 | { |
286 | 0 | pool->magic = CHEAP_ARENAPOOL_MAGIC; |
287 | 0 | PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double)); |
288 | 0 | } |
289 | | |
290 | | void * |
291 | | PORT_ArenaAlloc(PLArenaPool *arena, size_t size) |
292 | 11.6k | { |
293 | 11.6k | void *p = NULL; |
294 | | |
295 | 11.6k | PORTArenaPool *pool = (PORTArenaPool *)arena; |
296 | | |
297 | 11.6k | if (size <= 0) { |
298 | 0 | size = 1; |
299 | 0 | } |
300 | | |
301 | 11.6k | if (size > MAX_SIZE) { |
302 | | /* you lose. */ |
303 | 0 | } else |
304 | | /* Is it one of ours? Assume so and check the magic */ |
305 | 11.6k | if (ARENAPOOL_MAGIC == pool->magic) { |
306 | 11.6k | PZ_Lock(pool->lock); |
307 | 11.6k | #ifdef THREADMARK |
308 | | /* Most likely one of ours. Is there a thread id? */ |
309 | 11.6k | if (pool->marking_thread && |
310 | 11.6k | pool->marking_thread != PR_GetCurrentThread()) { |
311 | | /* Another thread holds a mark in this arena */ |
312 | 0 | PZ_Unlock(pool->lock); |
313 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
314 | 0 | PORT_Assert(0); |
315 | 0 | return NULL; |
316 | 0 | } /* tid != null */ |
317 | 11.6k | #endif /* THREADMARK */ |
318 | 11.6k | PL_ARENA_ALLOCATE(p, arena, size); |
319 | 11.6k | PZ_Unlock(pool->lock); |
320 | 11.6k | } else { |
321 | 0 | PL_ARENA_ALLOCATE(p, arena, size); |
322 | 0 | } |
323 | | |
324 | 11.6k | if (!p) { |
325 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
326 | 0 | } |
327 | | |
328 | 11.6k | return (p); |
329 | 11.6k | } |
330 | | |
331 | | void * |
332 | | PORT_ArenaZAlloc(PLArenaPool *arena, size_t size) |
333 | 3.03k | { |
334 | 3.03k | void *p; |
335 | | |
336 | 3.03k | if (size <= 0) |
337 | 0 | size = 1; |
338 | | |
339 | 3.03k | p = PORT_ArenaAlloc(arena, size); |
340 | | |
341 | 3.03k | if (p) { |
342 | 3.03k | PORT_Memset(p, 0, size); |
343 | 3.03k | } |
344 | | |
345 | 3.03k | return (p); |
346 | 3.03k | } |
347 | | |
348 | | static PRCallOnceType setupUseFreeListOnce; |
349 | | static PRBool useFreeList; |
350 | | |
351 | | static PRStatus |
352 | | SetupUseFreeList(void) |
353 | 2 | { |
354 | 2 | useFreeList = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL); |
355 | 2 | return PR_SUCCESS; |
356 | 2 | } |
357 | | |
358 | | /* |
359 | | * If zero is true, zeroize the arena memory before freeing it. |
360 | | */ |
361 | | void |
362 | | PORT_FreeArena(PLArenaPool *arena, PRBool zero) |
363 | 1.95k | { |
364 | 1.95k | PORTArenaPool *pool = (PORTArenaPool *)arena; |
365 | 1.95k | PRLock *lock = (PRLock *)0; |
366 | 1.95k | size_t len = sizeof *arena; |
367 | | |
368 | 1.95k | if (!pool) |
369 | 0 | return; |
370 | 1.95k | if (ARENAPOOL_MAGIC == pool->magic) { |
371 | 1.95k | len = sizeof *pool; |
372 | 1.95k | lock = pool->lock; |
373 | 1.95k | PZ_Lock(lock); |
374 | 1.95k | } |
375 | 1.95k | if (zero) { |
376 | 640 | PL_ClearArenaPool(arena, 0); |
377 | 640 | } |
378 | 1.95k | (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList); |
379 | 1.95k | if (useFreeList) { |
380 | 1.95k | PL_FreeArenaPool(arena); |
381 | 1.95k | } else { |
382 | 0 | PL_FinishArenaPool(arena); |
383 | 0 | } |
384 | 1.95k | PORT_ZFree(arena, len); |
385 | 1.95k | if (lock) { |
386 | 1.95k | PZ_Unlock(lock); |
387 | 1.95k | PZ_DestroyLock(lock); |
388 | 1.95k | } |
389 | 1.95k | } |
390 | | |
391 | | void |
392 | | PORT_DestroyCheapArena(PORTCheapArenaPool *pool) |
393 | 0 | { |
394 | 0 | (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList); |
395 | 0 | if (useFreeList) { |
396 | 0 | PL_FreeArenaPool(&pool->arena); |
397 | 0 | } else { |
398 | 0 | PL_FinishArenaPool(&pool->arena); |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | | void * |
403 | | PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize) |
404 | 0 | { |
405 | 0 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
406 | 0 | PORT_Assert(newsize >= oldsize); |
407 | |
|
408 | 0 | if (newsize > MAX_SIZE) { |
409 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
410 | 0 | return NULL; |
411 | 0 | } |
412 | | |
413 | 0 | if (ARENAPOOL_MAGIC == pool->magic) { |
414 | 0 | PZ_Lock(pool->lock); |
415 | | /* Do we do a THREADMARK check here? */ |
416 | 0 | PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize)); |
417 | 0 | PZ_Unlock(pool->lock); |
418 | 0 | } else { |
419 | 0 | PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize)); |
420 | 0 | } |
421 | |
|
422 | 0 | return (ptr); |
423 | 0 | } |
424 | | |
425 | | void * |
426 | | PORT_ArenaMark(PLArenaPool *arena) |
427 | 2.49k | { |
428 | 2.49k | void *result; |
429 | | |
430 | 2.49k | PORTArenaPool *pool = (PORTArenaPool *)arena; |
431 | 2.49k | if (ARENAPOOL_MAGIC == pool->magic) { |
432 | 2.49k | PZ_Lock(pool->lock); |
433 | 2.49k | #ifdef THREADMARK |
434 | 2.49k | { |
435 | 2.49k | threadmark_mark *tm, **pw; |
436 | 2.49k | PRThread *currentThread = PR_GetCurrentThread(); |
437 | | |
438 | 2.49k | if (!pool->marking_thread) { |
439 | | /* First mark */ |
440 | 1.75k | pool->marking_thread = currentThread; |
441 | 1.75k | } else if (currentThread != pool->marking_thread) { |
442 | 0 | PZ_Unlock(pool->lock); |
443 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
444 | 0 | PORT_Assert(0); |
445 | 0 | return NULL; |
446 | 0 | } |
447 | | |
448 | 2.49k | result = PL_ARENA_MARK(arena); |
449 | 2.49k | PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark)); |
450 | 2.49k | if (!tm) { |
451 | 0 | PZ_Unlock(pool->lock); |
452 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
453 | 0 | return NULL; |
454 | 0 | } |
455 | | |
456 | 2.49k | tm->mark = result; |
457 | 2.49k | tm->next = (threadmark_mark *)NULL; |
458 | | |
459 | 2.49k | pw = &pool->first_mark; |
460 | 3.61k | while (*pw) { |
461 | 1.11k | pw = &(*pw)->next; |
462 | 1.11k | } |
463 | | |
464 | 2.49k | *pw = tm; |
465 | 2.49k | } |
466 | | #else /* THREADMARK */ |
467 | | result = PL_ARENA_MARK(arena); |
468 | | #endif /* THREADMARK */ |
469 | 2.49k | PZ_Unlock(pool->lock); |
470 | 2.49k | } else { |
471 | | /* a "pure" NSPR arena */ |
472 | 0 | result = PL_ARENA_MARK(arena); |
473 | 0 | } |
474 | 2.49k | return result; |
475 | 2.49k | } |
476 | | |
477 | | /* |
478 | | * This function accesses the internals of PLArena, which is why it needs |
479 | | * to use the NSPR internal macro PL_MAKE_MEM_UNDEFINED before the memset |
480 | | * calls. |
481 | | * |
482 | | * We should move this function to NSPR as PL_ClearArenaAfterMark or add |
483 | | * a PL_ARENA_CLEAR_AND_RELEASE macro. |
484 | | * |
485 | | * TODO: remove the #ifdef PL_MAKE_MEM_UNDEFINED tests when NSPR 4.10+ is |
486 | | * widely available. |
487 | | */ |
488 | | static void |
489 | | port_ArenaZeroAfterMark(PLArenaPool *arena, void *mark) |
490 | 992 | { |
491 | 992 | PLArena *a = arena->current; |
492 | 992 | if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { |
493 | | /* fast path: mark falls in the current arena */ |
494 | 992 | #ifdef PL_MAKE_MEM_UNDEFINED |
495 | 992 | PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); |
496 | 992 | #endif |
497 | 992 | memset(mark, 0, a->avail - (PRUword)mark); |
498 | 992 | } else { |
499 | | /* slow path: need to find the arena that mark falls in */ |
500 | 0 | for (a = arena->first.next; a; a = a->next) { |
501 | 0 | PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); |
502 | 0 | if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { |
503 | 0 | #ifdef PL_MAKE_MEM_UNDEFINED |
504 | 0 | PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); |
505 | 0 | #endif |
506 | 0 | memset(mark, 0, a->avail - (PRUword)mark); |
507 | 0 | a = a->next; |
508 | 0 | break; |
509 | 0 | } |
510 | 0 | } |
511 | 0 | for (; a; a = a->next) { |
512 | 0 | PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); |
513 | 0 | #ifdef PL_MAKE_MEM_UNDEFINED |
514 | 0 | PL_MAKE_MEM_UNDEFINED((void *)a->base, a->avail - a->base); |
515 | 0 | #endif |
516 | 0 | memset((void *)a->base, 0, a->avail - a->base); |
517 | 0 | } |
518 | 0 | } |
519 | 992 | } |
520 | | |
521 | | static void |
522 | | port_ArenaRelease(PLArenaPool *arena, void *mark, PRBool zero) |
523 | 992 | { |
524 | 992 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
525 | 992 | if (ARENAPOOL_MAGIC == pool->magic) { |
526 | 992 | PZ_Lock(pool->lock); |
527 | 992 | #ifdef THREADMARK |
528 | 992 | { |
529 | 992 | threadmark_mark **pw; |
530 | | |
531 | 992 | if (PR_GetCurrentThread() != pool->marking_thread) { |
532 | 0 | PZ_Unlock(pool->lock); |
533 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
534 | 0 | PORT_Assert(0); |
535 | 0 | return /* no error indication available */; |
536 | 0 | } |
537 | | |
538 | 992 | pw = &pool->first_mark; |
539 | 2.10k | while (*pw && (mark != (*pw)->mark)) { |
540 | 1.11k | pw = &(*pw)->next; |
541 | 1.11k | } |
542 | | |
543 | 992 | if (!*pw) { |
544 | | /* bad mark */ |
545 | 0 | PZ_Unlock(pool->lock); |
546 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
547 | 0 | PORT_Assert(0); |
548 | 0 | return /* no error indication available */; |
549 | 0 | } |
550 | | |
551 | 992 | *pw = (threadmark_mark *)NULL; |
552 | | |
553 | 992 | if (zero) { |
554 | 992 | port_ArenaZeroAfterMark(arena, mark); |
555 | 992 | } |
556 | 992 | PL_ARENA_RELEASE(arena, mark); |
557 | | |
558 | 992 | if (!pool->first_mark) { |
559 | 248 | pool->marking_thread = (PRThread *)NULL; |
560 | 248 | } |
561 | 992 | } |
562 | | #else /* THREADMARK */ |
563 | | if (zero) { |
564 | | port_ArenaZeroAfterMark(arena, mark); |
565 | | } |
566 | | PL_ARENA_RELEASE(arena, mark); |
567 | | #endif /* THREADMARK */ |
568 | 992 | PZ_Unlock(pool->lock); |
569 | 992 | } else { |
570 | 0 | if (zero) { |
571 | 0 | port_ArenaZeroAfterMark(arena, mark); |
572 | 0 | } |
573 | 0 | PL_ARENA_RELEASE(arena, mark); |
574 | 0 | } |
575 | 992 | } |
576 | | |
577 | | void |
578 | | PORT_ArenaRelease(PLArenaPool *arena, void *mark) |
579 | 0 | { |
580 | 0 | port_ArenaRelease(arena, mark, PR_FALSE); |
581 | 0 | } |
582 | | |
583 | | /* |
584 | | * Zeroize the arena memory before releasing it. |
585 | | */ |
586 | | void |
587 | | PORT_ArenaZRelease(PLArenaPool *arena, void *mark) |
588 | 992 | { |
589 | 992 | port_ArenaRelease(arena, mark, PR_TRUE); |
590 | 992 | } |
591 | | |
592 | | void |
593 | | PORT_ArenaUnmark(PLArenaPool *arena, void *mark) |
594 | 1.50k | { |
595 | 1.50k | #ifdef THREADMARK |
596 | 1.50k | PORTArenaPool *pool = (PORTArenaPool *)arena; |
597 | 1.50k | if (ARENAPOOL_MAGIC == pool->magic) { |
598 | 1.50k | threadmark_mark **pw; |
599 | | |
600 | 1.50k | PZ_Lock(pool->lock); |
601 | | |
602 | 1.50k | if (PR_GetCurrentThread() != pool->marking_thread) { |
603 | 0 | PZ_Unlock(pool->lock); |
604 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
605 | 0 | PORT_Assert(0); |
606 | 0 | return /* no error indication available */; |
607 | 0 | } |
608 | | |
609 | 1.50k | pw = &pool->first_mark; |
610 | 1.50k | while (((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark)) { |
611 | 0 | pw = &(*pw)->next; |
612 | 0 | } |
613 | | |
614 | 1.50k | if ((threadmark_mark *)NULL == *pw) { |
615 | | /* bad mark */ |
616 | 0 | PZ_Unlock(pool->lock); |
617 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
618 | 0 | PORT_Assert(0); |
619 | 0 | return /* no error indication available */; |
620 | 0 | } |
621 | | |
622 | 1.50k | *pw = (threadmark_mark *)NULL; |
623 | | |
624 | 1.50k | if (!pool->first_mark) { |
625 | 1.50k | pool->marking_thread = (PRThread *)NULL; |
626 | 1.50k | } |
627 | | |
628 | 1.50k | PZ_Unlock(pool->lock); |
629 | 1.50k | } |
630 | 1.50k | #endif /* THREADMARK */ |
631 | 1.50k | } |
632 | | |
633 | | char * |
634 | | PORT_ArenaStrdup(PLArenaPool *arena, const char *str) |
635 | 8 | { |
636 | 8 | int len = PORT_Strlen(str) + 1; |
637 | 8 | char *newstr; |
638 | | |
639 | 8 | newstr = (char *)PORT_ArenaAlloc(arena, len); |
640 | 8 | if (newstr) { |
641 | 8 | PORT_Memcpy(newstr, str, len); |
642 | 8 | } |
643 | 8 | return newstr; |
644 | 8 | } |
645 | | |
646 | | /********************** end of arena functions ***********************/ |
647 | | |
648 | | /****************** unicode conversion functions ***********************/ |
649 | | /* |
650 | | * NOTE: These conversion functions all assume that the multibyte |
651 | | * characters are going to be in NETWORK BYTE ORDER, not host byte |
652 | | * order. This is because the only time we deal with UCS-2 and UCS-4 |
653 | | * are when the data was received from or is going to be sent out |
654 | | * over the wire (in, e.g. certificates). |
655 | | */ |
656 | | |
657 | | void |
658 | | PORT_SetUCS4_UTF8ConversionFunction(PORTCharConversionFunc convFunc) |
659 | 0 | { |
660 | 0 | ucs4Utf8ConvertFunc = convFunc; |
661 | 0 | } |
662 | | |
663 | | void |
664 | | PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc) |
665 | 0 | { |
666 | 0 | ucs2AsciiConvertFunc = convFunc; |
667 | 0 | } |
668 | | |
669 | | void |
670 | | PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc) |
671 | 0 | { |
672 | 0 | ucs2Utf8ConvertFunc = convFunc; |
673 | 0 | } |
674 | | |
675 | | PRBool |
676 | | PORT_UCS4_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, |
677 | | unsigned int inBufLen, unsigned char *outBuf, |
678 | | unsigned int maxOutBufLen, unsigned int *outBufLen) |
679 | 0 | { |
680 | 0 | if (!ucs4Utf8ConvertFunc) { |
681 | 0 | return sec_port_ucs4_utf8_conversion_function(toUnicode, |
682 | 0 | inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); |
683 | 0 | } |
684 | | |
685 | 0 | return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
686 | 0 | maxOutBufLen, outBufLen); |
687 | 0 | } |
688 | | |
689 | | PRBool |
690 | | PORT_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, |
691 | | unsigned int inBufLen, unsigned char *outBuf, |
692 | | unsigned int maxOutBufLen, unsigned int *outBufLen) |
693 | 0 | { |
694 | 0 | if (!ucs2Utf8ConvertFunc) { |
695 | 0 | return sec_port_ucs2_utf8_conversion_function(toUnicode, |
696 | 0 | inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); |
697 | 0 | } |
698 | | |
699 | 0 | return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
700 | 0 | maxOutBufLen, outBufLen); |
701 | 0 | } |
702 | | |
703 | | PRBool |
704 | | PORT_ISO88591_UTF8Conversion(const unsigned char *inBuf, |
705 | | unsigned int inBufLen, unsigned char *outBuf, |
706 | | unsigned int maxOutBufLen, unsigned int *outBufLen) |
707 | 0 | { |
708 | 0 | return sec_port_iso88591_utf8_conversion_function(inBuf, inBufLen, |
709 | 0 | outBuf, maxOutBufLen, outBufLen); |
710 | 0 | } |
711 | | |
712 | | PRBool |
713 | | PORT_UCS2_ASCIIConversion(PRBool toUnicode, unsigned char *inBuf, |
714 | | unsigned int inBufLen, unsigned char *outBuf, |
715 | | unsigned int maxOutBufLen, unsigned int *outBufLen, |
716 | | PRBool swapBytes) |
717 | 0 | { |
718 | 0 | if (!ucs2AsciiConvertFunc) { |
719 | 0 | return PR_FALSE; |
720 | 0 | } |
721 | | |
722 | 0 | return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
723 | 0 | maxOutBufLen, outBufLen, swapBytes); |
724 | 0 | } |
725 | | |
726 | | /* Portable putenv. Creates/replaces an environment variable of the form |
727 | | * envVarName=envValue |
728 | | */ |
729 | | int |
730 | | NSS_PutEnv(const char *envVarName, const char *envValue) |
731 | 0 | { |
732 | 0 | SECStatus result = SECSuccess; |
733 | | #ifdef _WIN32 |
734 | | PRBool setOK; |
735 | | |
736 | | setOK = SetEnvironmentVariable(envVarName, envValue); |
737 | | if (!setOK) { |
738 | | SET_ERROR_CODE |
739 | | return SECFailure; |
740 | | } |
741 | | #elif defined(__GNUC__) && __GNUC__ >= 7 |
742 | | int setEnvFailed; |
743 | | setEnvFailed = setenv(envVarName, envValue, 1); |
744 | | if (setEnvFailed) { |
745 | | SET_ERROR_CODE |
746 | | return SECFailure; |
747 | | } |
748 | | #else |
749 | 0 | char *encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue)); |
750 | 0 | if (!encoded) { |
751 | 0 | return SECFailure; |
752 | 0 | } |
753 | 0 | strcpy(encoded, envVarName); |
754 | 0 | strcat(encoded, "="); |
755 | 0 | strcat(encoded, envValue); |
756 | 0 | int putEnvFailed = putenv(encoded); /* adopt. */ |
757 | |
|
758 | 0 | if (putEnvFailed) { |
759 | 0 | SET_ERROR_CODE |
760 | 0 | result = SECFailure; |
761 | 0 | PORT_Free(encoded); |
762 | 0 | } |
763 | 0 | #endif |
764 | 0 | return result; |
765 | 0 | } |
766 | | |
767 | | /* |
768 | | * Perform a constant-time compare of two memory regions. The return value is |
769 | | * 0 if the memory regions are equal and non-zero otherwise. |
770 | | */ |
771 | | int |
772 | | NSS_SecureMemcmp(const void *ia, const void *ib, size_t n) |
773 | 1 | { |
774 | 1 | const unsigned char *a = (const unsigned char *)ia; |
775 | 1 | const unsigned char *b = (const unsigned char *)ib; |
776 | 1 | int r = 0; |
777 | | |
778 | 5 | for (size_t i = 0; i < n; ++i) { |
779 | 4 | r |= a[i] ^ b[i]; |
780 | 4 | } |
781 | | |
782 | | /* 0 <= r < 256, so -r has bit 8 set when r != 0 */ |
783 | 1 | return 1 & (-r >> 8); |
784 | 1 | } |
785 | | |
786 | | /* |
787 | | * Perform a constant-time check if a memory region is all 0. The return value |
788 | | * is 0 if the memory region is all zero. |
789 | | */ |
790 | | unsigned int |
791 | | NSS_SecureMemcmpZero(const void *mem, size_t n) |
792 | 0 | { |
793 | 0 | const unsigned char *a = (const unsigned char *)mem; |
794 | 0 | int r = 0; |
795 | |
|
796 | 0 | for (size_t i = 0; i < n; ++i) { |
797 | 0 | r |= a[i]; |
798 | 0 | } |
799 | | |
800 | | /* 0 <= r < 256, so -r has bit 8 set when r != 0 */ |
801 | 0 | return 1 & (-r >> 8); |
802 | 0 | } |
803 | | |
804 | | /* |
805 | | * A "value barrier" prevents the compiler from making optimizations based on |
806 | | * the value that a variable takes. |
807 | | * |
808 | | * Standard C does not have value barriers, so C implementations of them are |
809 | | * compiler-specific and are not guaranteed to be effective. Thus, the value |
810 | | * barriers here are a best-effort, defense-in-depth, strategy. They are not a |
811 | | * substitute for standard constant-time programming discipline. |
812 | | * |
813 | | * Some implementations have a performance penalty, so value barriers should |
814 | | * be used sparingly. |
815 | | */ |
816 | | static inline int |
817 | | value_barrier_int(int x) |
818 | 0 | { |
819 | 0 | #if defined(__GNUC__) || defined(__clang__) |
820 | | /* This inline assembly trick from Chandler Carruth's CppCon 2015 talk |
821 | | * generates no instructions. |
822 | | * |
823 | | * "+r"(x) means that x will be mapped to a register that is both an input |
824 | | * and an output to the assembly routine (""). The compiler will not |
825 | | * inspect the assembly routine itself, so it cannot assume anything about |
826 | | * the value of x after this line. |
827 | | */ |
828 | 0 | __asm__("" |
829 | 0 | : "+r"(x) |
830 | 0 | : /* no other inputs */); |
831 | 0 | return x; |
832 | | #else |
833 | | /* If the compiler does not support the inline assembly trick above, we can |
834 | | * put x in `volatile` storage and read it out again. This will generate |
835 | | * explict store and load instructions, and possibly more depending on the |
836 | | * target. |
837 | | */ |
838 | | volatile int y = x; |
839 | | return y; |
840 | | #endif |
841 | 0 | } |
842 | | |
843 | | /* |
844 | | * A branch-free implementation of |
845 | | * if (!b) { |
846 | | * memmove(dest, src0, n); |
847 | | * } else { |
848 | | * memmove(dest, src1, n); |
849 | | * } |
850 | | * |
851 | | * The memmove is performed with src0 if `b == 0` and with src1 |
852 | | * otherwise. |
853 | | * |
854 | | * As with memmove, the selected src can overlap dest. |
855 | | * |
856 | | * Each of dest, src0, and src1 must point to an allocated buffer |
857 | | * of at least n bytes. |
858 | | */ |
859 | | void |
860 | | NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b) |
861 | | |
862 | 0 | { |
863 | | // This value barrier makes it safe for the compiler to inline |
864 | | // NSS_SecureSelect into a routine where it could otherwise infer something |
865 | | // about the value of b, e.g. that b is 0/1 valued. |
866 | 0 | int w = value_barrier_int(b); |
867 | | |
868 | | // 0 <= b < 256, and int is at least 16 bits, so -w has bits 8-15 |
869 | | // set when w != 0. |
870 | 0 | unsigned char mask = 0xff & (-w >> 8); |
871 | |
|
872 | 0 | for (size_t i = 0; i < n; ++i) { |
873 | 0 | unsigned char s0i = ((unsigned char *)src0)[i]; |
874 | 0 | unsigned char s1i = ((unsigned char *)src1)[i]; |
875 | | // if mask == 0 this simplifies to s0 ^ 0 |
876 | | // if mask == -1 this simplifies to s0 ^ s0 ^ s1 |
877 | 0 | ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i)); |
878 | 0 | } |
879 | 0 | } |
880 | | |
881 | | /* |
882 | | * consolidate all the calls to get the system FIPS status in one spot. |
883 | | * This function allows an environment variable to override what is returned. |
884 | | */ |
885 | | PRBool |
886 | | NSS_GetSystemFIPSEnabled(void) |
887 | 4 | { |
888 | | /* if FIPS is disabled in NSS, always return FALSE, even if the environment |
889 | | * variable is set, or the system is in FIPS mode */ |
890 | 4 | #ifndef NSS_FIPS_DISABLED |
891 | 4 | const char *env; |
892 | | |
893 | | /* The environment variable is active for all platforms */ |
894 | 4 | env = PR_GetEnvSecure("NSS_FIPS"); |
895 | | /* we generally accept y, Y, 1, FIPS, TRUE, and ON as turning on FIPS |
896 | | * mode. Anything else is considered 'off' */ |
897 | 4 | if (env && (*env == 'y' || *env == '1' || *env == 'Y' || |
898 | 0 | (PORT_Strcasecmp(env, "fips") == 0) || |
899 | 0 | (PORT_Strcasecmp(env, "true") == 0) || |
900 | 0 | (PORT_Strcasecmp(env, "on") == 0))) { |
901 | 0 | return PR_TRUE; |
902 | 0 | } |
903 | | |
904 | | /* currently only Linux has a system FIPS indicator. Add others here |
905 | | * as they become available/known */ |
906 | 4 | #ifdef LINUX |
907 | 4 | { |
908 | 4 | FILE *f; |
909 | 4 | char d; |
910 | 4 | size_t size; |
911 | 4 | f = fopen("/proc/sys/crypto/fips_enabled", "r"); |
912 | 4 | if (!f) |
913 | 0 | return PR_FALSE; |
914 | | |
915 | 4 | size = fread(&d, 1, 1, f); |
916 | 4 | fclose(f); |
917 | 4 | if (size != 1) |
918 | 0 | return PR_FALSE; |
919 | 4 | if (d == '1') |
920 | 0 | return PR_TRUE; |
921 | 4 | } |
922 | 4 | #endif /* LINUX */ |
923 | 4 | #endif /* NSS_FIPS_DISABLED == 0 */ |
924 | 4 | return PR_FALSE; |
925 | 4 | } |