Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
michael@0 | 6 | * secport.c - portability interfaces for security libraries |
michael@0 | 7 | * |
michael@0 | 8 | * This file abstracts out libc functionality that libsec depends on |
michael@0 | 9 | * |
michael@0 | 10 | * NOTE - These are not public interfaces |
michael@0 | 11 | */ |
michael@0 | 12 | |
michael@0 | 13 | #include "seccomon.h" |
michael@0 | 14 | #include "prmem.h" |
michael@0 | 15 | #include "prerror.h" |
michael@0 | 16 | #include "plarena.h" |
michael@0 | 17 | #include "secerr.h" |
michael@0 | 18 | #include "prmon.h" |
michael@0 | 19 | #include "nssilock.h" |
michael@0 | 20 | #include "secport.h" |
michael@0 | 21 | #include "prenv.h" |
michael@0 | 22 | |
michael@0 | 23 | #ifdef DEBUG |
michael@0 | 24 | #define THREADMARK |
michael@0 | 25 | #endif /* DEBUG */ |
michael@0 | 26 | |
michael@0 | 27 | #ifdef THREADMARK |
michael@0 | 28 | #include "prthread.h" |
michael@0 | 29 | #endif /* THREADMARK */ |
michael@0 | 30 | |
michael@0 | 31 | #if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) |
michael@0 | 32 | #include <stdlib.h> |
michael@0 | 33 | #else |
michael@0 | 34 | #include "wtypes.h" |
michael@0 | 35 | #endif |
michael@0 | 36 | |
michael@0 | 37 | #define SET_ERROR_CODE /* place holder for code to set PR error code. */ |
michael@0 | 38 | |
michael@0 | 39 | #ifdef THREADMARK |
michael@0 | 40 | typedef struct threadmark_mark_str { |
michael@0 | 41 | struct threadmark_mark_str *next; |
michael@0 | 42 | void *mark; |
michael@0 | 43 | } threadmark_mark; |
michael@0 | 44 | |
michael@0 | 45 | #endif /* THREADMARK */ |
michael@0 | 46 | |
michael@0 | 47 | /* The value of this magic must change each time PORTArenaPool changes. */ |
michael@0 | 48 | #define ARENAPOOL_MAGIC 0xB8AC9BDF |
michael@0 | 49 | |
michael@0 | 50 | typedef struct PORTArenaPool_str { |
michael@0 | 51 | PLArenaPool arena; |
michael@0 | 52 | PRUint32 magic; |
michael@0 | 53 | PRLock * lock; |
michael@0 | 54 | #ifdef THREADMARK |
michael@0 | 55 | PRThread *marking_thread; |
michael@0 | 56 | threadmark_mark *first_mark; |
michael@0 | 57 | #endif |
michael@0 | 58 | } PORTArenaPool; |
michael@0 | 59 | |
michael@0 | 60 | |
michael@0 | 61 | /* count of allocation failures. */ |
michael@0 | 62 | unsigned long port_allocFailures; |
michael@0 | 63 | |
michael@0 | 64 | /* locations for registering Unicode conversion functions. |
michael@0 | 65 | * XXX is this the appropriate location? or should they be |
michael@0 | 66 | * moved to client/server specific locations? |
michael@0 | 67 | */ |
michael@0 | 68 | PORTCharConversionFunc ucs4Utf8ConvertFunc; |
michael@0 | 69 | PORTCharConversionFunc ucs2Utf8ConvertFunc; |
michael@0 | 70 | PORTCharConversionWSwapFunc ucs2AsciiConvertFunc; |
michael@0 | 71 | |
michael@0 | 72 | /* NSPR memory allocation functions (PR_Malloc, PR_Calloc, and PR_Realloc) |
michael@0 | 73 | * use the PRUint32 type for the size parameter. Before we pass a size_t or |
michael@0 | 74 | * unsigned long size to these functions, we need to ensure it is <= half of |
michael@0 | 75 | * the maximum PRUint32 value to avoid truncation and catch a negative size. |
michael@0 | 76 | */ |
michael@0 | 77 | #define MAX_SIZE (PR_UINT32_MAX >> 1) |
michael@0 | 78 | |
michael@0 | 79 | void * |
michael@0 | 80 | PORT_Alloc(size_t bytes) |
michael@0 | 81 | { |
michael@0 | 82 | void *rv = NULL; |
michael@0 | 83 | |
michael@0 | 84 | if (bytes <= MAX_SIZE) { |
michael@0 | 85 | /* Always allocate a non-zero amount of bytes */ |
michael@0 | 86 | rv = PR_Malloc(bytes ? bytes : 1); |
michael@0 | 87 | } |
michael@0 | 88 | if (!rv) { |
michael@0 | 89 | ++port_allocFailures; |
michael@0 | 90 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 91 | } |
michael@0 | 92 | return rv; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | void * |
michael@0 | 96 | PORT_Realloc(void *oldptr, size_t bytes) |
michael@0 | 97 | { |
michael@0 | 98 | void *rv = NULL; |
michael@0 | 99 | |
michael@0 | 100 | if (bytes <= MAX_SIZE) { |
michael@0 | 101 | rv = PR_Realloc(oldptr, bytes); |
michael@0 | 102 | } |
michael@0 | 103 | if (!rv) { |
michael@0 | 104 | ++port_allocFailures; |
michael@0 | 105 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 106 | } |
michael@0 | 107 | return rv; |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | void * |
michael@0 | 111 | PORT_ZAlloc(size_t bytes) |
michael@0 | 112 | { |
michael@0 | 113 | void *rv = NULL; |
michael@0 | 114 | |
michael@0 | 115 | if (bytes <= MAX_SIZE) { |
michael@0 | 116 | /* Always allocate a non-zero amount of bytes */ |
michael@0 | 117 | rv = PR_Calloc(1, bytes ? bytes : 1); |
michael@0 | 118 | } |
michael@0 | 119 | if (!rv) { |
michael@0 | 120 | ++port_allocFailures; |
michael@0 | 121 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 122 | } |
michael@0 | 123 | return rv; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | void |
michael@0 | 127 | PORT_Free(void *ptr) |
michael@0 | 128 | { |
michael@0 | 129 | if (ptr) { |
michael@0 | 130 | PR_Free(ptr); |
michael@0 | 131 | } |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | void |
michael@0 | 135 | PORT_ZFree(void *ptr, size_t len) |
michael@0 | 136 | { |
michael@0 | 137 | if (ptr) { |
michael@0 | 138 | memset(ptr, 0, len); |
michael@0 | 139 | PR_Free(ptr); |
michael@0 | 140 | } |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | char * |
michael@0 | 144 | PORT_Strdup(const char *str) |
michael@0 | 145 | { |
michael@0 | 146 | size_t len = PORT_Strlen(str)+1; |
michael@0 | 147 | char *newstr; |
michael@0 | 148 | |
michael@0 | 149 | newstr = (char *)PORT_Alloc(len); |
michael@0 | 150 | if (newstr) { |
michael@0 | 151 | PORT_Memcpy(newstr, str, len); |
michael@0 | 152 | } |
michael@0 | 153 | return newstr; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | void |
michael@0 | 157 | PORT_SetError(int value) |
michael@0 | 158 | { |
michael@0 | 159 | #ifdef DEBUG_jp96085 |
michael@0 | 160 | PORT_Assert(value != SEC_ERROR_REUSED_ISSUER_AND_SERIAL); |
michael@0 | 161 | #endif |
michael@0 | 162 | PR_SetError(value, 0); |
michael@0 | 163 | return; |
michael@0 | 164 | } |
michael@0 | 165 | |
michael@0 | 166 | int |
michael@0 | 167 | PORT_GetError(void) |
michael@0 | 168 | { |
michael@0 | 169 | return(PR_GetError()); |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | /********************* Arena code follows ***************************** |
michael@0 | 173 | * ArenaPools are like heaps. The memory in them consists of large blocks, |
michael@0 | 174 | * called arenas, which are allocated from the/a system heap. Inside an |
michael@0 | 175 | * ArenaPool, the arenas are organized as if they were in a stack. Newly |
michael@0 | 176 | * allocated arenas are "pushed" on that stack. When you attempt to |
michael@0 | 177 | * allocate memory from an ArenaPool, the code first looks to see if there |
michael@0 | 178 | * is enough unused space in the top arena on the stack to satisfy your |
michael@0 | 179 | * request, and if so, your request is satisfied from that arena. |
michael@0 | 180 | * Otherwise, a new arena is allocated (or taken from NSPR's list of freed |
michael@0 | 181 | * arenas) and pushed on to the stack. The new arena is always big enough |
michael@0 | 182 | * to satisfy the request, and is also at least a minimum size that is |
michael@0 | 183 | * established at the time that the ArenaPool is created. |
michael@0 | 184 | * |
michael@0 | 185 | * The ArenaMark function returns the address of a marker in the arena at |
michael@0 | 186 | * the top of the arena stack. It is the address of the place in the arena |
michael@0 | 187 | * on the top of the arena stack from which the next block of memory will |
michael@0 | 188 | * be allocated. Each ArenaPool has its own separate stack, and hence |
michael@0 | 189 | * marks are only relevant to the ArenaPool from which they are gotten. |
michael@0 | 190 | * Marks may be nested. That is, a thread can get a mark, and then get |
michael@0 | 191 | * another mark. |
michael@0 | 192 | * |
michael@0 | 193 | * It is intended that all the marks in an ArenaPool may only be owned by a |
michael@0 | 194 | * single thread. In DEBUG builds, this is enforced. In non-DEBUG builds, |
michael@0 | 195 | * it is not. In DEBUG builds, when a thread gets a mark from an |
michael@0 | 196 | * ArenaPool, no other thread may acquire a mark in that ArenaPool while |
michael@0 | 197 | * that mark exists, that is, until that mark is unmarked or released. |
michael@0 | 198 | * Therefore, it is important that every mark be unmarked or released when |
michael@0 | 199 | * the creating thread has no further need for exclusive ownership of the |
michael@0 | 200 | * right to manage the ArenaPool. |
michael@0 | 201 | * |
michael@0 | 202 | * The ArenaUnmark function discards the ArenaMark at the address given, |
michael@0 | 203 | * and all marks nested inside that mark (that is, acquired from that same |
michael@0 | 204 | * ArenaPool while that mark existed). It is an error for a thread other |
michael@0 | 205 | * than the mark's creator to try to unmark it. When a thread has unmarked |
michael@0 | 206 | * all its marks from an ArenaPool, then another thread is able to set |
michael@0 | 207 | * marks in that ArenaPool. ArenaUnmark does not deallocate (or "pop") any |
michael@0 | 208 | * memory allocated from the ArenaPool since the mark was created. |
michael@0 | 209 | * |
michael@0 | 210 | * ArenaRelease "pops" the stack back to the mark, deallocating all the |
michael@0 | 211 | * memory allocated from the arenas in the ArenaPool since that mark was |
michael@0 | 212 | * created, and removing any arenas from the ArenaPool that have no |
michael@0 | 213 | * remaining active allocations when that is done. It implicitly releases |
michael@0 | 214 | * any marks nested inside the mark being explicitly released. It is the |
michael@0 | 215 | * only operation, other than destroying the arenapool, that potentially |
michael@0 | 216 | * reduces the number of arenas on the stack. Otherwise, the stack grows |
michael@0 | 217 | * until the arenapool is destroyed, at which point all the arenas are |
michael@0 | 218 | * freed or returned to a "free arena list", depending on their sizes. |
michael@0 | 219 | */ |
michael@0 | 220 | PLArenaPool * |
michael@0 | 221 | PORT_NewArena(unsigned long chunksize) |
michael@0 | 222 | { |
michael@0 | 223 | PORTArenaPool *pool; |
michael@0 | 224 | |
michael@0 | 225 | if (chunksize > MAX_SIZE) { |
michael@0 | 226 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 227 | return NULL; |
michael@0 | 228 | } |
michael@0 | 229 | pool = PORT_ZNew(PORTArenaPool); |
michael@0 | 230 | if (!pool) { |
michael@0 | 231 | return NULL; |
michael@0 | 232 | } |
michael@0 | 233 | pool->magic = ARENAPOOL_MAGIC; |
michael@0 | 234 | pool->lock = PZ_NewLock(nssILockArena); |
michael@0 | 235 | if (!pool->lock) { |
michael@0 | 236 | ++port_allocFailures; |
michael@0 | 237 | PORT_Free(pool); |
michael@0 | 238 | return NULL; |
michael@0 | 239 | } |
michael@0 | 240 | PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double)); |
michael@0 | 241 | return(&pool->arena); |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | void * |
michael@0 | 245 | PORT_ArenaAlloc(PLArenaPool *arena, size_t size) |
michael@0 | 246 | { |
michael@0 | 247 | void *p = NULL; |
michael@0 | 248 | |
michael@0 | 249 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 250 | |
michael@0 | 251 | if (size <= 0) { |
michael@0 | 252 | size = 1; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | if (size > MAX_SIZE) { |
michael@0 | 256 | /* you lose. */ |
michael@0 | 257 | } else |
michael@0 | 258 | /* Is it one of ours? Assume so and check the magic */ |
michael@0 | 259 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 260 | PZ_Lock(pool->lock); |
michael@0 | 261 | #ifdef THREADMARK |
michael@0 | 262 | /* Most likely one of ours. Is there a thread id? */ |
michael@0 | 263 | if (pool->marking_thread && |
michael@0 | 264 | pool->marking_thread != PR_GetCurrentThread() ) { |
michael@0 | 265 | /* Another thread holds a mark in this arena */ |
michael@0 | 266 | PZ_Unlock(pool->lock); |
michael@0 | 267 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 268 | PORT_Assert(0); |
michael@0 | 269 | return NULL; |
michael@0 | 270 | } /* tid != null */ |
michael@0 | 271 | #endif /* THREADMARK */ |
michael@0 | 272 | PL_ARENA_ALLOCATE(p, arena, size); |
michael@0 | 273 | PZ_Unlock(pool->lock); |
michael@0 | 274 | } else { |
michael@0 | 275 | PL_ARENA_ALLOCATE(p, arena, size); |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | if (!p) { |
michael@0 | 279 | ++port_allocFailures; |
michael@0 | 280 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | return(p); |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | void * |
michael@0 | 287 | PORT_ArenaZAlloc(PLArenaPool *arena, size_t size) |
michael@0 | 288 | { |
michael@0 | 289 | void *p; |
michael@0 | 290 | |
michael@0 | 291 | if (size <= 0) |
michael@0 | 292 | size = 1; |
michael@0 | 293 | |
michael@0 | 294 | p = PORT_ArenaAlloc(arena, size); |
michael@0 | 295 | |
michael@0 | 296 | if (p) { |
michael@0 | 297 | PORT_Memset(p, 0, size); |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | return(p); |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | /* |
michael@0 | 304 | * If zero is true, zeroize the arena memory before freeing it. |
michael@0 | 305 | */ |
michael@0 | 306 | void |
michael@0 | 307 | PORT_FreeArena(PLArenaPool *arena, PRBool zero) |
michael@0 | 308 | { |
michael@0 | 309 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 310 | PRLock * lock = (PRLock *)0; |
michael@0 | 311 | size_t len = sizeof *arena; |
michael@0 | 312 | static PRBool checkedEnv = PR_FALSE; |
michael@0 | 313 | static PRBool doFreeArenaPool = PR_FALSE; |
michael@0 | 314 | |
michael@0 | 315 | if (!pool) |
michael@0 | 316 | return; |
michael@0 | 317 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 318 | len = sizeof *pool; |
michael@0 | 319 | lock = pool->lock; |
michael@0 | 320 | PZ_Lock(lock); |
michael@0 | 321 | } |
michael@0 | 322 | if (!checkedEnv) { |
michael@0 | 323 | /* no need for thread protection here */ |
michael@0 | 324 | doFreeArenaPool = (PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST") == NULL); |
michael@0 | 325 | checkedEnv = PR_TRUE; |
michael@0 | 326 | } |
michael@0 | 327 | if (zero) { |
michael@0 | 328 | PL_ClearArenaPool(arena, 0); |
michael@0 | 329 | } |
michael@0 | 330 | if (doFreeArenaPool) { |
michael@0 | 331 | PL_FreeArenaPool(arena); |
michael@0 | 332 | } else { |
michael@0 | 333 | PL_FinishArenaPool(arena); |
michael@0 | 334 | } |
michael@0 | 335 | PORT_ZFree(arena, len); |
michael@0 | 336 | if (lock) { |
michael@0 | 337 | PZ_Unlock(lock); |
michael@0 | 338 | PZ_DestroyLock(lock); |
michael@0 | 339 | } |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | void * |
michael@0 | 343 | PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize) |
michael@0 | 344 | { |
michael@0 | 345 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 346 | PORT_Assert(newsize >= oldsize); |
michael@0 | 347 | |
michael@0 | 348 | if (newsize > MAX_SIZE) { |
michael@0 | 349 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 350 | return NULL; |
michael@0 | 351 | } |
michael@0 | 352 | |
michael@0 | 353 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 354 | PZ_Lock(pool->lock); |
michael@0 | 355 | /* Do we do a THREADMARK check here? */ |
michael@0 | 356 | PL_ARENA_GROW(ptr, arena, oldsize, ( newsize - oldsize ) ); |
michael@0 | 357 | PZ_Unlock(pool->lock); |
michael@0 | 358 | } else { |
michael@0 | 359 | PL_ARENA_GROW(ptr, arena, oldsize, ( newsize - oldsize ) ); |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | return(ptr); |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | void * |
michael@0 | 366 | PORT_ArenaMark(PLArenaPool *arena) |
michael@0 | 367 | { |
michael@0 | 368 | void * result; |
michael@0 | 369 | |
michael@0 | 370 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 371 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 372 | PZ_Lock(pool->lock); |
michael@0 | 373 | #ifdef THREADMARK |
michael@0 | 374 | { |
michael@0 | 375 | threadmark_mark *tm, **pw; |
michael@0 | 376 | PRThread * currentThread = PR_GetCurrentThread(); |
michael@0 | 377 | |
michael@0 | 378 | if (! pool->marking_thread ) { |
michael@0 | 379 | /* First mark */ |
michael@0 | 380 | pool->marking_thread = currentThread; |
michael@0 | 381 | } else if (currentThread != pool->marking_thread ) { |
michael@0 | 382 | PZ_Unlock(pool->lock); |
michael@0 | 383 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 384 | PORT_Assert(0); |
michael@0 | 385 | return NULL; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | result = PL_ARENA_MARK(arena); |
michael@0 | 389 | PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark)); |
michael@0 | 390 | if (!tm) { |
michael@0 | 391 | PZ_Unlock(pool->lock); |
michael@0 | 392 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 393 | return NULL; |
michael@0 | 394 | } |
michael@0 | 395 | |
michael@0 | 396 | tm->mark = result; |
michael@0 | 397 | tm->next = (threadmark_mark *)NULL; |
michael@0 | 398 | |
michael@0 | 399 | pw = &pool->first_mark; |
michael@0 | 400 | while( *pw ) { |
michael@0 | 401 | pw = &(*pw)->next; |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | *pw = tm; |
michael@0 | 405 | } |
michael@0 | 406 | #else /* THREADMARK */ |
michael@0 | 407 | result = PL_ARENA_MARK(arena); |
michael@0 | 408 | #endif /* THREADMARK */ |
michael@0 | 409 | PZ_Unlock(pool->lock); |
michael@0 | 410 | } else { |
michael@0 | 411 | /* a "pure" NSPR arena */ |
michael@0 | 412 | result = PL_ARENA_MARK(arena); |
michael@0 | 413 | } |
michael@0 | 414 | return result; |
michael@0 | 415 | } |
michael@0 | 416 | |
michael@0 | 417 | /* |
michael@0 | 418 | * This function accesses the internals of PLArena, which is why it needs |
michael@0 | 419 | * to use the NSPR internal macro PL_MAKE_MEM_UNDEFINED before the memset |
michael@0 | 420 | * calls. |
michael@0 | 421 | * |
michael@0 | 422 | * We should move this function to NSPR as PL_ClearArenaAfterMark or add |
michael@0 | 423 | * a PL_ARENA_CLEAR_AND_RELEASE macro. |
michael@0 | 424 | * |
michael@0 | 425 | * TODO: remove the #ifdef PL_MAKE_MEM_UNDEFINED tests when NSPR 4.10+ is |
michael@0 | 426 | * widely available. |
michael@0 | 427 | */ |
michael@0 | 428 | static void |
michael@0 | 429 | port_ArenaZeroAfterMark(PLArenaPool *arena, void *mark) |
michael@0 | 430 | { |
michael@0 | 431 | PLArena *a = arena->current; |
michael@0 | 432 | if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { |
michael@0 | 433 | /* fast path: mark falls in the current arena */ |
michael@0 | 434 | #ifdef PL_MAKE_MEM_UNDEFINED |
michael@0 | 435 | PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); |
michael@0 | 436 | #endif |
michael@0 | 437 | memset(mark, 0, a->avail - (PRUword)mark); |
michael@0 | 438 | } else { |
michael@0 | 439 | /* slow path: need to find the arena that mark falls in */ |
michael@0 | 440 | for (a = arena->first.next; a; a = a->next) { |
michael@0 | 441 | PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); |
michael@0 | 442 | if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) { |
michael@0 | 443 | #ifdef PL_MAKE_MEM_UNDEFINED |
michael@0 | 444 | PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark); |
michael@0 | 445 | #endif |
michael@0 | 446 | memset(mark, 0, a->avail - (PRUword)mark); |
michael@0 | 447 | a = a->next; |
michael@0 | 448 | break; |
michael@0 | 449 | } |
michael@0 | 450 | } |
michael@0 | 451 | for (; a; a = a->next) { |
michael@0 | 452 | PR_ASSERT(a->base <= a->avail && a->avail <= a->limit); |
michael@0 | 453 | #ifdef PL_MAKE_MEM_UNDEFINED |
michael@0 | 454 | PL_MAKE_MEM_UNDEFINED((void *)a->base, a->avail - a->base); |
michael@0 | 455 | #endif |
michael@0 | 456 | memset((void *)a->base, 0, a->avail - a->base); |
michael@0 | 457 | } |
michael@0 | 458 | } |
michael@0 | 459 | } |
michael@0 | 460 | |
michael@0 | 461 | static void |
michael@0 | 462 | port_ArenaRelease(PLArenaPool *arena, void *mark, PRBool zero) |
michael@0 | 463 | { |
michael@0 | 464 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 465 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 466 | PZ_Lock(pool->lock); |
michael@0 | 467 | #ifdef THREADMARK |
michael@0 | 468 | { |
michael@0 | 469 | threadmark_mark **pw, *tm; |
michael@0 | 470 | |
michael@0 | 471 | if (PR_GetCurrentThread() != pool->marking_thread ) { |
michael@0 | 472 | PZ_Unlock(pool->lock); |
michael@0 | 473 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 474 | PORT_Assert(0); |
michael@0 | 475 | return /* no error indication available */ ; |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | pw = &pool->first_mark; |
michael@0 | 479 | while( *pw && (mark != (*pw)->mark) ) { |
michael@0 | 480 | pw = &(*pw)->next; |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | if (! *pw ) { |
michael@0 | 484 | /* bad mark */ |
michael@0 | 485 | PZ_Unlock(pool->lock); |
michael@0 | 486 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 487 | PORT_Assert(0); |
michael@0 | 488 | return /* no error indication available */ ; |
michael@0 | 489 | } |
michael@0 | 490 | |
michael@0 | 491 | tm = *pw; |
michael@0 | 492 | *pw = (threadmark_mark *)NULL; |
michael@0 | 493 | |
michael@0 | 494 | if (zero) { |
michael@0 | 495 | port_ArenaZeroAfterMark(arena, mark); |
michael@0 | 496 | } |
michael@0 | 497 | PL_ARENA_RELEASE(arena, mark); |
michael@0 | 498 | |
michael@0 | 499 | if (! pool->first_mark ) { |
michael@0 | 500 | pool->marking_thread = (PRThread *)NULL; |
michael@0 | 501 | } |
michael@0 | 502 | } |
michael@0 | 503 | #else /* THREADMARK */ |
michael@0 | 504 | if (zero) { |
michael@0 | 505 | port_ArenaZeroAfterMark(arena, mark); |
michael@0 | 506 | } |
michael@0 | 507 | PL_ARENA_RELEASE(arena, mark); |
michael@0 | 508 | #endif /* THREADMARK */ |
michael@0 | 509 | PZ_Unlock(pool->lock); |
michael@0 | 510 | } else { |
michael@0 | 511 | if (zero) { |
michael@0 | 512 | port_ArenaZeroAfterMark(arena, mark); |
michael@0 | 513 | } |
michael@0 | 514 | PL_ARENA_RELEASE(arena, mark); |
michael@0 | 515 | } |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | void |
michael@0 | 519 | PORT_ArenaRelease(PLArenaPool *arena, void *mark) |
michael@0 | 520 | { |
michael@0 | 521 | port_ArenaRelease(arena, mark, PR_FALSE); |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | /* |
michael@0 | 525 | * Zeroize the arena memory before releasing it. |
michael@0 | 526 | */ |
michael@0 | 527 | void |
michael@0 | 528 | PORT_ArenaZRelease(PLArenaPool *arena, void *mark) |
michael@0 | 529 | { |
michael@0 | 530 | port_ArenaRelease(arena, mark, PR_TRUE); |
michael@0 | 531 | } |
michael@0 | 532 | |
michael@0 | 533 | void |
michael@0 | 534 | PORT_ArenaUnmark(PLArenaPool *arena, void *mark) |
michael@0 | 535 | { |
michael@0 | 536 | #ifdef THREADMARK |
michael@0 | 537 | PORTArenaPool *pool = (PORTArenaPool *)arena; |
michael@0 | 538 | if (ARENAPOOL_MAGIC == pool->magic ) { |
michael@0 | 539 | threadmark_mark **pw, *tm; |
michael@0 | 540 | |
michael@0 | 541 | PZ_Lock(pool->lock); |
michael@0 | 542 | |
michael@0 | 543 | if (PR_GetCurrentThread() != pool->marking_thread ) { |
michael@0 | 544 | PZ_Unlock(pool->lock); |
michael@0 | 545 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 546 | PORT_Assert(0); |
michael@0 | 547 | return /* no error indication available */ ; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | pw = &pool->first_mark; |
michael@0 | 551 | while( ((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark) ) { |
michael@0 | 552 | pw = &(*pw)->next; |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | if ((threadmark_mark *)NULL == *pw ) { |
michael@0 | 556 | /* bad mark */ |
michael@0 | 557 | PZ_Unlock(pool->lock); |
michael@0 | 558 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 559 | PORT_Assert(0); |
michael@0 | 560 | return /* no error indication available */ ; |
michael@0 | 561 | } |
michael@0 | 562 | |
michael@0 | 563 | tm = *pw; |
michael@0 | 564 | *pw = (threadmark_mark *)NULL; |
michael@0 | 565 | |
michael@0 | 566 | if (! pool->first_mark ) { |
michael@0 | 567 | pool->marking_thread = (PRThread *)NULL; |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | PZ_Unlock(pool->lock); |
michael@0 | 571 | } |
michael@0 | 572 | #endif /* THREADMARK */ |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | char * |
michael@0 | 576 | PORT_ArenaStrdup(PLArenaPool *arena, const char *str) { |
michael@0 | 577 | int len = PORT_Strlen(str)+1; |
michael@0 | 578 | char *newstr; |
michael@0 | 579 | |
michael@0 | 580 | newstr = (char*)PORT_ArenaAlloc(arena,len); |
michael@0 | 581 | if (newstr) { |
michael@0 | 582 | PORT_Memcpy(newstr,str,len); |
michael@0 | 583 | } |
michael@0 | 584 | return newstr; |
michael@0 | 585 | } |
michael@0 | 586 | |
michael@0 | 587 | /********************** end of arena functions ***********************/ |
michael@0 | 588 | |
michael@0 | 589 | /****************** unicode conversion functions ***********************/ |
michael@0 | 590 | /* |
michael@0 | 591 | * NOTE: These conversion functions all assume that the multibyte |
michael@0 | 592 | * characters are going to be in NETWORK BYTE ORDER, not host byte |
michael@0 | 593 | * order. This is because the only time we deal with UCS-2 and UCS-4 |
michael@0 | 594 | * are when the data was received from or is going to be sent out |
michael@0 | 595 | * over the wire (in, e.g. certificates). |
michael@0 | 596 | */ |
michael@0 | 597 | |
michael@0 | 598 | void |
michael@0 | 599 | PORT_SetUCS4_UTF8ConversionFunction(PORTCharConversionFunc convFunc) |
michael@0 | 600 | { |
michael@0 | 601 | ucs4Utf8ConvertFunc = convFunc; |
michael@0 | 602 | } |
michael@0 | 603 | |
michael@0 | 604 | void |
michael@0 | 605 | PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc) |
michael@0 | 606 | { |
michael@0 | 607 | ucs2AsciiConvertFunc = convFunc; |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | void |
michael@0 | 611 | PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc) |
michael@0 | 612 | { |
michael@0 | 613 | ucs2Utf8ConvertFunc = convFunc; |
michael@0 | 614 | } |
michael@0 | 615 | |
michael@0 | 616 | PRBool |
michael@0 | 617 | PORT_UCS4_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, |
michael@0 | 618 | unsigned int inBufLen, unsigned char *outBuf, |
michael@0 | 619 | unsigned int maxOutBufLen, unsigned int *outBufLen) |
michael@0 | 620 | { |
michael@0 | 621 | if(!ucs4Utf8ConvertFunc) { |
michael@0 | 622 | return sec_port_ucs4_utf8_conversion_function(toUnicode, |
michael@0 | 623 | inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
michael@0 | 627 | maxOutBufLen, outBufLen); |
michael@0 | 628 | } |
michael@0 | 629 | |
michael@0 | 630 | PRBool |
michael@0 | 631 | PORT_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf, |
michael@0 | 632 | unsigned int inBufLen, unsigned char *outBuf, |
michael@0 | 633 | unsigned int maxOutBufLen, unsigned int *outBufLen) |
michael@0 | 634 | { |
michael@0 | 635 | if(!ucs2Utf8ConvertFunc) { |
michael@0 | 636 | return sec_port_ucs2_utf8_conversion_function(toUnicode, |
michael@0 | 637 | inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen); |
michael@0 | 638 | } |
michael@0 | 639 | |
michael@0 | 640 | return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
michael@0 | 641 | maxOutBufLen, outBufLen); |
michael@0 | 642 | } |
michael@0 | 643 | |
michael@0 | 644 | PRBool |
michael@0 | 645 | PORT_ISO88591_UTF8Conversion(const unsigned char *inBuf, |
michael@0 | 646 | unsigned int inBufLen, unsigned char *outBuf, |
michael@0 | 647 | unsigned int maxOutBufLen, unsigned int *outBufLen) |
michael@0 | 648 | { |
michael@0 | 649 | return sec_port_iso88591_utf8_conversion_function(inBuf, inBufLen, |
michael@0 | 650 | outBuf, maxOutBufLen, outBufLen); |
michael@0 | 651 | } |
michael@0 | 652 | |
michael@0 | 653 | PRBool |
michael@0 | 654 | PORT_UCS2_ASCIIConversion(PRBool toUnicode, unsigned char *inBuf, |
michael@0 | 655 | unsigned int inBufLen, unsigned char *outBuf, |
michael@0 | 656 | unsigned int maxOutBufLen, unsigned int *outBufLen, |
michael@0 | 657 | PRBool swapBytes) |
michael@0 | 658 | { |
michael@0 | 659 | if(!ucs2AsciiConvertFunc) { |
michael@0 | 660 | return PR_FALSE; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf, |
michael@0 | 664 | maxOutBufLen, outBufLen, swapBytes); |
michael@0 | 665 | } |
michael@0 | 666 | |
michael@0 | 667 | |
michael@0 | 668 | /* Portable putenv. Creates/replaces an environment variable of the form |
michael@0 | 669 | * envVarName=envValue |
michael@0 | 670 | */ |
michael@0 | 671 | int |
michael@0 | 672 | NSS_PutEnv(const char * envVarName, const char * envValue) |
michael@0 | 673 | { |
michael@0 | 674 | SECStatus result = SECSuccess; |
michael@0 | 675 | char * encoded; |
michael@0 | 676 | int putEnvFailed; |
michael@0 | 677 | #ifdef _WIN32 |
michael@0 | 678 | PRBool setOK; |
michael@0 | 679 | |
michael@0 | 680 | setOK = SetEnvironmentVariable(envVarName, envValue); |
michael@0 | 681 | if (!setOK) { |
michael@0 | 682 | SET_ERROR_CODE |
michael@0 | 683 | return SECFailure; |
michael@0 | 684 | } |
michael@0 | 685 | #endif |
michael@0 | 686 | |
michael@0 | 687 | encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue)); |
michael@0 | 688 | strcpy(encoded, envVarName); |
michael@0 | 689 | strcat(encoded, "="); |
michael@0 | 690 | strcat(encoded, envValue); |
michael@0 | 691 | |
michael@0 | 692 | putEnvFailed = putenv(encoded); /* adopt. */ |
michael@0 | 693 | if (putEnvFailed) { |
michael@0 | 694 | SET_ERROR_CODE |
michael@0 | 695 | result = SECFailure; |
michael@0 | 696 | PORT_Free(encoded); |
michael@0 | 697 | } |
michael@0 | 698 | return result; |
michael@0 | 699 | } |
michael@0 | 700 | |
michael@0 | 701 | /* |
michael@0 | 702 | * Perform a constant-time compare of two memory regions. The return value is |
michael@0 | 703 | * 0 if the memory regions are equal and non-zero otherwise. |
michael@0 | 704 | */ |
michael@0 | 705 | int |
michael@0 | 706 | NSS_SecureMemcmp(const void *ia, const void *ib, size_t n) |
michael@0 | 707 | { |
michael@0 | 708 | const unsigned char *a = (const unsigned char*) ia; |
michael@0 | 709 | const unsigned char *b = (const unsigned char*) ib; |
michael@0 | 710 | size_t i; |
michael@0 | 711 | unsigned char r = 0; |
michael@0 | 712 | |
michael@0 | 713 | for (i = 0; i < n; ++i) { |
michael@0 | 714 | r |= *a++ ^ *b++; |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | return r; |
michael@0 | 718 | } |