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