/* * Map a range of virtual memory. Note that the size asked for here * is literally what the upper level has asked for. We need to do * any rounding, etc. here. If mapping fails return 0, otherwise * return the address of the base of the mapped memory. */ void * sysMapMem(size_t requestedSize, size_t *mappedSize) { void *mappedAddr; *mappedSize = roundUpToGrain(requestedSize); #ifdef USE_MALLOC mappedAddr = (void *) sysMalloc(*mappedSize); /* Returns 0 on failure */ #ifdef __linux__ if (mappedAddr) { memset(mappedAddr, 0, *mappedSize); mappedAddr = (void *) roundUpToGrain(mappedAddr); } #endif #else mappedAddr = mapChunk(*mappedSize); /* Returns 0 on failure */ #endif /* USE_MALLOC */ if (mappedAddr) { Log3(2, "sysMapMem: 0x%x bytes at 0x%x (request: 0x%x bytes)\n", *mappedSize, mappedAddr, requestedSize); } else { Log1(2, "sysMapMem failed: (request: 0x%x bytes)\n", requestedSize); } return mappedAddr; }
/* * Return the address of the base of the newly committed memory, or 0 * if committing failed. */ void * sysCommitMem(void *requestedAddr, size_t requestedSize, size_t *committedSize) { void *committedAddr; char *ret; *committedSize = roundUpToGrain(requestedSize); committedAddr = (void *) roundDownToGrain((long) requestedAddr); #ifdef USE_MALLOC #ifdef __linux__ ret = committedAddr; #else ret = requestedAddr; #endif #else ret = mapChunkReserve(committedAddr, *committedSize); #endif if (ret) { committedAddr = ret; Log4(2, "sysCommitMem: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *committedSize, committedAddr, requestedSize, requestedAddr); } else { committedAddr = 0; Log2(2, "sysCommitMem failed: (request: 0x%x bytes at 0x%x)\n", requestedSize, requestedAddr); } return committedAddr; }
/* * Unmap a range of virtual memory. Note that the size asked for here * is literally what the upper level has asked for. We need to do any * rounding, etc. here. If unmapping fails return 0, otherwise return * the address of the base of the unmapped memory. */ void * sysUnmapMem(void *requestedAddr, size_t requestedSize, size_t *unmappedSize) { void *unmappedAddr; int ret; *unmappedSize = roundUpToGrain(requestedSize); #ifdef USE_MALLOC sysFree(requestedAddr); ret = 1; #else ret = unmapChunk(requestedAddr, *unmappedSize); #endif /* USE_MALLOC */ if (ret) { unmappedAddr = requestedAddr; Log4(2, "sysUnmapMem: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *unmappedSize, unmappedAddr, requestedSize, requestedAddr); } else { unmappedAddr = 0; Log2(2, "sysUnmapMem failed: (request: 0x%x bytes at 0x%x)\n", requestedSize, requestedAddr); } return unmappedAddr; }
/* Purpose: Relinquishes a range of memory which was previously reserved using CVMmemMap(). The memory range may be a subset of the range returned by CVMmemMap(). The range to relinquish is specified by addr as the starting address of the range, and requestedSize as the size of the range in units of bytes. Returns: The starting address of the unmmapped range is returned. The actual size of the unmapped range is set in *unmappedSize in units of bytes. Else, if failed to unmap, NULL is returned and *unmappedSize is set to 0. Note: memory sizes must be in increments of the page size as returned by CVMmemPageSize() in units of bytes. */ void *CVMmemUnmap(void *requestedAddr, size_t requestedSize, size_t *unmappedSize) { void *unmappedAddr = NULL; CVMBool success = CVM_FALSE; CVMassert(requestedAddr != NULL); CVMassert(requestedSize == roundUpToGrain(requestedSize)); if (requestedSize != 0) { success = VirtualFree(requestedAddr, 0, MEM_RELEASE); } if (success) { unmappedAddr = requestedAddr; *unmappedSize = requestedSize; } else { *unmappedSize = 0; } #ifdef DEBUG_MMAP if (success) { CVMconsolePrintf( "CVMmemUnmap: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *unmappedSize, unmappedAddr, requestedSize, requestedAddr); } else { CVMconsolePrintf("CVMmemUnmap failed: Error %d, (request: 0x%x bytes at 0x%x)\n", GetLastError(), requestedSize, requestedAddr); } #endif return unmappedAddr; }
/* Purpose: Decommits a range of memory which was previously committed using CVMmemCommit(). The memory range may be a subset of the range returned by CVMmemCommit(). The range to decommit is specified by addr as the starting address of the range, and requestedSize as the size of the range in units of bytes. Returns: The starting address of the decommitted range is returned. The actual size of the decommitted range is set in *decommittedSize in units of bytes. Else, if failed to decommit, NULL is returned and *decommittedSize is set to 0. Note: memory sizes must be in increments of the page size as returned by CVMmemPageSize() in units of bytes. Committed memory must be uncommitted using CVMmemDecommit() before it is unmmapped with CVMmemUnmap(). If this order is not adhered to, then the state of the committed memory will be undefined. */ void *CVMmemDecommit(void *requestedAddr, size_t requestedSize, size_t *decommittedSize) { void *decommittedAddr = NULL; CVMBool success = CVM_FALSE; CVMassert(requestedSize == roundDownToGrain(requestedSize)); CVMassert(requestedAddr == (void *)roundUpToGrain((CVMAddr)requestedAddr)); if (requestedSize != 0) { success = VirtualFree(requestedAddr, requestedSize, MEM_DECOMMIT); #ifdef WINCE if (!success && ((DWORD)requestedAddr + requestedSize) >= ROUND_UP_32MB(requestedAddr)) { /* crossing 32MB boundary, need to break up the decommit */ size_t origSize = requestedSize; void *newAddr = requestedAddr; while(((DWORD)newAddr + origSize) >= ROUND_UP_32MB(newAddr)) { size_t newSize = ROUND_UP_32MB(newAddr) - (DWORD)newAddr; success = VirtualFree(newAddr, newSize, MEM_DECOMMIT); if (!success) { break; } newAddr = (void*)((DWORD)newAddr + newSize); origSize -= newSize; } if (success && origSize > 0) { /* Some residual pages to decommit */ success = VirtualFree(newAddr, origSize, MEM_DECOMMIT); } } #endif } if (success) { decommittedAddr = requestedAddr; *decommittedSize = requestedSize; } else { *decommittedSize = 0; } #ifdef DEBUG_MMAP if (success) { CVMconsolePrintf( "CVMmemDecommit: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *decommittedSize, decommittedAddr, requestedSize, requestedAddr); } else { CVMconsolePrintf( "CVMmemDecommit failed: (request: 0x%x bytes at 0x%x)\n", requestedSize, requestedAddr); } #endif return decommittedAddr; }
/* Purpose: Reserves a memory address range of the requested size for later use. Returns: The starting address of the reserved memory range if successful. The actual size of the reserved memory range will be set in *mappedSize in units of bytes. Else if failed to map, NULL is returned and *mappedSize is set to 0. Note: memory sizes must be in increments of the page size as returned by CVMmemPageSize() in units of bytes. */ void *CVMmemMap(size_t requestedSize, size_t *mappedSize) { void *mappedAddr = NULL; CVMassert(requestedSize == roundUpToGrain(requestedSize)); if (requestedSize != 0) { DWORD protectAttr; /* For WinCE 5.0 and earlier, calling VirtualAlloc() with PAGE_READONLY will ensure that the virtual address range is reserved from within the 32M process space. This range is protected from reads and writes by other non-trusted processes. On WinCE systems where all processes are trusted, other processes can still read and write into our process via WinCE system calls. If instead of PAGE_READONLY, we use PAGE_NOACCESS together with MEM_RESERVE and we request a size greater than 2M, than the address range will be reserved from shared memory. Note that shared memory can be read and written to by other processes as long as they know the address. This could open the heap data to security vulnerabilities, and expose the VM to attacks more easily. Note that PAGE_READONLY doesn't actually mean that the page is read only later, nor does PAGE_NOACCESS means that the page is in accessible. Before the pages are used, they will be committed in CVMmemCommit() using VirtualAlloc(... PAGE_READWRITE). Hence, we'll have read write access to the committed heap pages. Relevant MSDN references: http://msdn2.microsoft.com/EN-US/library/aa454885.aspx#effective_memory_storage_power_mgmt_wm5_topic2 http://msdn2.microsoft.com/en-us/library/aa908768.aspx */ protectAttr = PAGE_READONLY; #ifdef WINCE if (CVMglobals.target.useLargeMemoryArea) { protectAttr = PAGE_NOACCESS; } #endif /* Returns NULL on failure */ mappedAddr = VirtualAlloc(0, requestedSize, MEM_RESERVE, protectAttr); } *mappedSize = (mappedAddr != NULL) ? requestedSize : 0; #ifdef DEBUG_MMAP if (mappedAddr != NULL) { CVMconsolePrintf( "CVMmemMap: 0x%x bytes at 0x%x (request: 0x%x bytes)\n", *mappedSize, mappedAddr, requestedSize); } else { CVMconsolePrintf("CVMmemMap failed: Error %d, (request: 0x%x bytes)\n", GetLastError(), requestedSize); } #endif return mappedAddr; }
/* * Return the address of the base of the newly decommitted memory, or 0 * if decommitting failed. */ void * sysDecommitMem(void *requestedAddr, size_t requestedSize, size_t *decommittedSize) { void *decommittedAddr; char *ret; *decommittedSize = roundDownToGrain(requestedSize); decommittedAddr = (void *) roundUpToGrain((long) requestedAddr); #ifdef USE_MALLOC ret = 0; #else ret = mapChunkNoreserve(decommittedAddr, *decommittedSize); #endif Log4(2, "sysDecommitMem: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *decommittedSize, decommittedAddr, requestedSize, requestedAddr); return ret; }
/* Purpose: Commits a range of memory which was previously reserved using CVMmemMap(). The memory range may be a subset of the range returned by CVMmemMap(). The range to commit is specified by addr as the starting address of the range, and requestedSize as the size of the range in units of bytes. Returns: The starting address of the committed range is returned. The actual size of the committed range is set in *committedSize in units of bytes. Else, if failed to commit, NULL is returned and *committedSize is set to 0. Note: memory sizes must be in increments of the page size as returned by CVMmemPageSize() in units of bytes. Committed memory must be uncommitted using CVMmemDecommit() before it is unmmapped with CVMmemUnmap(). If this order is not adhered to, then the state of the committed memory will be undefined. */ void *CVMmemCommit(void *requestedAddr, size_t requestedSize, size_t *committedSize) { void *committedAddr = NULL; MEMORY_BASIC_INFORMATION mb; CVMassert(requestedSize == roundUpToGrain(requestedSize)); CVMassert(requestedAddr == (void *)roundDownToGrain((CVMAddr)requestedAddr)); if (requestedSize != 0) { committedAddr = VirtualAlloc(requestedAddr, requestedSize, MEM_COMMIT, PAGE_READWRITE); #ifdef WINCE if (committedAddr == NULL && ((DWORD)requestedAddr + requestedSize) >= ROUND_UP_32MB(requestedAddr)) { /* hitting/crossing 32MB boundary, need to break up the commit */ size_t origSize = requestedSize; void *newAddr = requestedAddr; size_t pageSize = CVMmemPageSize(); while(((DWORD)newAddr + origSize) >= ROUND_UP_32MB(newAddr)) { size_t newSize = ROUND_UP_32MB(newAddr) - (DWORD)newAddr; if (newSize >= pageSize * 2) { /* Sometimes, for whatever reason, if you * allocate right up to the 32MB boundary it returns * INVALID PARAMETER error. So, back off a page */ newSize -= pageSize; } committedAddr = VirtualAlloc(newAddr, newSize, MEM_COMMIT, PAGE_READWRITE); if (committedAddr == NULL) { break; } newAddr = (void*)((DWORD)newAddr + newSize); origSize -= newSize; } #if _WIN32_WCE < 600 while(committedAddr != NULL && origSize > 0) { /* Some residual pages to commit */ /* WinCE < 6.0 fails on commits that are too big, where too big * is some unknown value I can't seem to pin down. So just do * it a page at a time. */ committedAddr = VirtualAlloc(newAddr, pageSize, MEM_COMMIT, PAGE_READWRITE); origSize -= pageSize; newAddr = (void *)((DWORD)newAddr + pageSize); } #else if (committedAddr != NULL) { committedAddr = VirtualAlloc(newAddr, origSize, MEM_COMMIT, PAGE_READWRITE); } #endif if (committedAddr != NULL) { committedAddr = requestedAddr; } } #endif } *committedSize = (committedAddr != NULL) ? (CVMassert(committedAddr == requestedAddr), requestedSize) : 0; #ifdef WINCE if (committedAddr == NULL) { /* Must have had an error committing, attempt to decommit anything we * committed */ size_t decommittedSize; CVMmemDecommit(requestedAddr, requestedSize, &decommittedSize); } #endif #ifdef DEBUG_MMAP if (committedAddr != NULL) { CVMconsolePrintf( "CVMmemCommit: 0x%x bytes at 0x%x (request: 0x%x bytes at 0x%x)\n", *committedSize, committedAddr, requestedSize, requestedAddr); } else { CVMconsolePrintf( "CVMmemCommit failed: Error %d, (request: 0x%x bytes at 0x%x)\n", GetLastError(), requestedSize, requestedAddr); CVMconsolePrintf("CVMmemCommit: State:\n"); VirtualQuery(requestedAddr, &mb, sizeof(mb)); CVMconsolePrintf(" Base: 0x%x\n AllocBase: 0x%x\n AllocProtect: 0x%x\n Size: 0x%x\n State: 0x%x\n Protect: 0x%x\n Type: 0x%x\n", (int)mb.BaseAddress, mb.AllocationBase, mb.AllocationProtect, mb.RegionSize, mb.State, mb.Protect, mb.Type); } #endif return committedAddr; }