VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/memsafer-generic.cpp@ 52033

Last change on this file since 52033 was 52033, checked in by vboxsync, 11 years ago

IPRT/memsafer: Relax the guarding pages protection, some hosts like Linux and Windows don't support SUPR3PageProtect

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.0 KB
Line 
1/* $Id: memsafer-generic.cpp 52033 2014-07-15 09:23:37Z vboxsync $ */
2/** @file
3 * IPRT - Memory Allocate for Sensitive Data, generic heap-based implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/memsafer.h>
33
34#include <iprt/assert.h>
35#include <iprt/string.h>
36#if defined(IN_SUP_R3) && defined(VBOX) && !defined(RT_NO_GIP)
37# include <iprt/param.h>
38# include <VBox/sup.h>
39#endif /* IN_SUP_R3 */
40
41
42/*******************************************************************************
43* Defined Constants And Macros *
44*******************************************************************************/
45/** Allocation size alignment. */
46#define RTMEMSAFER_ALIGN 16
47/** Padding after the block to avoid small overruns. */
48#define RTMEMSAFER_PAD_BEFORE 96
49/** Padding after the block to avoid small underruns. */
50#define RTMEMSAFER_PAD_AFTER 32
51
52/*******************************************************************************
53* Structures and Typedefs *
54*******************************************************************************/
55
56/**
57 * Supported allocation methods.
58 */
59typedef enum RTMEMSAFERALLOCMETHOD
60{
61 /** Invalid method. */
62 RTMEMSAFERALLOCMETHOD_INVALID = 0,
63 /** RTMem{Alloc|Free} methods, least secure!. */
64 RTMEMSAFERALLOCMETHOD_RTMEM,
65 /** Support library. */
66 RTMEMSAFERALLOCMETHOD_SUPR3,
67 /** 32bit hack. */
68 RTMEMSAFERALLOCMETHOD_32BIT_HACK = 0x7fffffff
69} RTMEMSAFERALLOCMETHOD;
70/** Pointer to a allocation method enum. */
71typedef RTMEMSAFERALLOCMETHOD *PRTMEMSAFERALLOCMETHOD;
72
73/**
74 * Memory header for safer memory allocations.
75 *
76 * @note: There is no magic value used deliberately to make identifying this structure
77 * as hard as possible.
78 */
79typedef struct RTMEMSAFERHDR
80{
81 /** Flags passed to this allocation - used for freeing and reallocation. */
82 uint32_t fFlags;
83 /** Allocation method used. */
84 RTMEMSAFERALLOCMETHOD enmAllocMethod;
85 /** Amount of bytes allocated. */
86 size_t cb;
87} RTMEMSAFERHDR;
88/** Pointer to a safer memory header. */
89typedef RTMEMSAFERHDR *PRTMEMSAFERHDR;
90/** Make sure we are staying in the padding area. */
91AssertCompile(sizeof(RTMEMSAFERHDR) < RTMEMSAFER_PAD_BEFORE);
92
93/*******************************************************************************
94* Global Variables *
95*******************************************************************************/
96/** XOR scrambler value.
97 * @todo determine this at runtime */
98#if ARCH_BITS == 32
99static uintptr_t g_uScramblerXor = UINT32_C(0x867af88d);
100#elif ARCH_BITS == 64
101static uintptr_t g_uScramblerXor = UINT64_C(0xed95ecc99416d312);
102#else
103# error "Bad ARCH_BITS value"
104#endif
105
106
107
108/**
109 * Support (SUPR3) based allocator.
110 *
111 * @returns VBox status code.
112 * @retval VERR_NOT_SUPPORTED if this allocation method is not supported in this
113 * version of the library.
114 * @param ppvNew Where to store the pointer to the new buffer on success.
115 * @param cb Amount of bytes to allocate.
116 *
117 * @note: The allocation will have an extra page allocated before and after the
118 * user area with all access rights removed if the host supports that to
119 * prevent heartbleed like attacks.
120 */
121static int rtMemSaferSupR3Alloc(void **ppvNew, size_t cb)
122{
123#if defined(IN_SUP_R3) && defined(VBOX) && !defined(RT_NO_GIP)
124 /*
125 * Allocate locked memory from the support library.
126 *
127 */
128 size_t cbUser = RT_ALIGN_Z(cb, PAGE_SIZE);
129 size_t cPages = cbUser / PAGE_SIZE + 2; /* For the extra guarding pages. */
130 void *pvNew = NULL;
131 int rc = SUPR3PageAllocEx(cPages, 0 /* fFlags */, &pvNew, NULL /* pR0Ptr */, NULL /* paPages */);
132 if (RT_SUCCESS(rc))
133 {
134 /*
135 * Change the memory protection of the pages guarding the allocation.
136 * Some hosts don't support changing the page protection, ignore these
137 * errors.
138 */
139 rc = SUPR3PageProtect(pvNew, NIL_RTR0PTR, 0, PAGE_SIZE, RTMEM_PROT_NONE);
140 if (RT_SUCCESS(rc) || rc == VERR_NOT_SUPPORTED)
141 {
142 Assert(PAGE_SIZE + cbUser == (size_t)((uint32_t)(PAGE_SIZE + cbUser)));
143 if (rc == VERR_NOT_SUPPORTED)
144 rc = VINF_SUCCESS;
145 else
146 rc = SUPR3PageProtect(pvNew, NIL_RTR0PTR, PAGE_SIZE + (uint32_t)cbUser, PAGE_SIZE, RTMEM_PROT_NONE);
147
148 if (RT_SUCCESS(rc))
149 {
150 *ppvNew = (uint8_t *)pvNew + PAGE_SIZE;
151 return VINF_SUCCESS;
152 }
153 }
154
155 rc = SUPR3PageFreeEx(pvNew, cPages);
156 AssertRC(rc);
157 }
158
159 return rc;
160#else
161 return VERR_NOT_SUPPORTED;
162#endif
163}
164
165
166/**
167 * Free method for memory allocated using the Support (SUPR3) based allocator.
168 *
169 * @returns nothing.
170 * @param pv Pointer to the memory to free.
171 * @param cb Amount of bytes allocated.
172 */
173static void rtMemSafeSupR3Free(void *pv, size_t cb)
174{
175#if defined(IN_SUP_R3) && defined(VBOX) && !defined(RT_NO_GIP)
176 size_t cbUser = RT_ALIGN_Z(cb, PAGE_SIZE);
177 size_t cPages = cbUser / PAGE_SIZE + 2; /* For the extra pages. */
178 void *pvStart = (uint8_t *)pv - PAGE_SIZE;
179
180 int rc = SUPR3PageFreeEx(pvStart, cPages);
181 AssertRC(rc);
182#else
183 AssertMsgFailed(("SUPR3 allocated memory but freeing is not supported, messed up\n"));
184#endif
185}
186
187
188RTDECL(int) RTMemSaferScramble(void *pv, size_t cb)
189{
190 PRTMEMSAFERHDR pHdr = (PRTMEMSAFERHDR)((char *)pv - RTMEMSAFER_PAD_BEFORE);
191 AssertMsg(pHdr->cb == cb, ("pHdr->cb=%#zx cb=%#zx\n", pHdr->cb, cb));
192
193 /* Note! This isn't supposed to be safe, just less obvious. */
194 uintptr_t *pu = (uintptr_t *)pv;
195 cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
196 while (cb > 0)
197 {
198 *pu ^= g_uScramblerXor;
199 pu++;
200 cb -= sizeof(*pu);
201 }
202
203 return VINF_SUCCESS;
204}
205RT_EXPORT_SYMBOL(RTMemSaferScramble);
206
207
208RTDECL(int) RTMemSaferUnscramble(void *pv, size_t cb)
209{
210 PRTMEMSAFERHDR pHdr = (PRTMEMSAFERHDR)((char *)pv - RTMEMSAFER_PAD_BEFORE);
211 AssertMsg(pHdr->cb == cb, ("pHdr->cb=%#zx cb=%#zx\n", pHdr->cb, cb));
212
213 /* Note! This isn't supposed to be safe, just less obvious. */
214 uintptr_t *pu = (uintptr_t *)pv;
215 cb = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
216 while (cb > 0)
217 {
218 *pu ^= g_uScramblerXor;
219 pu++;
220 cb -= sizeof(*pu);
221 }
222
223 return VINF_SUCCESS;
224}
225RT_EXPORT_SYMBOL(RTMemSaferUnscramble);
226
227
228RTDECL(int) RTMemSaferAllocZExTag(void **ppvNew, size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW
229{
230 AssertReturn(cb, VERR_INVALID_PARAMETER);
231 AssertPtrReturn(ppvNew, VERR_INVALID_PARAMETER);
232 *ppvNew = NULL;
233
234 /*
235 * Don't request zeroed memory. We want random heap garbage in the
236 * padding zones, nothing that makes our allocations easier to find.
237 */
238 RTMEMSAFERALLOCMETHOD enmAllocMethod = RTMEMSAFERALLOCMETHOD_SUPR3;
239 size_t cbUser = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
240 void *pvNew = NULL;
241 int rc = rtMemSaferSupR3Alloc(&pvNew, cbUser + RTMEMSAFER_PAD_BEFORE + RTMEMSAFER_PAD_AFTER);
242 if ( RT_FAILURE(rc)
243 && fFlags & RTMEMSAFER_ALLOC_EX_ALLOW_PAGEABLE_BACKING)
244 {
245 /* Pageable memory allowed. */
246 enmAllocMethod = RTMEMSAFERALLOCMETHOD_RTMEM;
247 pvNew = RTMemAlloc(cbUser + RTMEMSAFER_PAD_BEFORE + RTMEMSAFER_PAD_AFTER);
248 }
249
250 if (pvNew)
251 {
252 PRTMEMSAFERHDR pHdr = (PRTMEMSAFERHDR)pvNew;
253 pHdr->fFlags = fFlags;
254 pHdr->cb = cb;
255 pHdr->enmAllocMethod = enmAllocMethod;
256#ifdef RT_STRICT /* For checking input in strict builds. */
257 memset((char *)pvNew + sizeof(RTMEMSAFERHDR), 0xad, RTMEMSAFER_PAD_BEFORE - sizeof(RTMEMSAFERHDR));
258 memset((char *)pvNew + RTMEMSAFER_PAD_BEFORE + cb, 0xda, RTMEMSAFER_PAD_AFTER + (cbUser - cb));
259#endif
260
261 void *pvUser = (char *)pvNew + RTMEMSAFER_PAD_BEFORE;
262 *ppvNew = pvUser;
263
264 /* You don't use this API for performance, so we always clean memory. */
265 RT_BZERO(pvUser, cb);
266
267 return VINF_SUCCESS;
268 }
269 return rc;
270}
271RT_EXPORT_SYMBOL(RTMemSaferAllocZExTag);
272
273
274RTDECL(void) RTMemSaferFree(void *pv, size_t cb) RT_NO_THROW
275{
276 if (pv)
277 {
278 Assert(cb);
279
280 size_t cbUser = RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN);
281 void *pvStart = (char *)pv - RTMEMSAFER_PAD_BEFORE;
282 PRTMEMSAFERHDR pHdr = (PRTMEMSAFERHDR)pvStart;
283 AssertMsg(pHdr->cb == cb, ("pHdr->cb=%#zx cb=%#zx\n", pHdr->cb, cb));
284
285 RTMemWipeThoroughly(pv, RT_ALIGN_Z(cb, RTMEMSAFER_ALIGN), 3);
286
287 switch (pHdr->enmAllocMethod)
288 {
289 case RTMEMSAFERALLOCMETHOD_SUPR3:
290 rtMemSafeSupR3Free(pvStart, cbUser + RTMEMSAFER_PAD_BEFORE + RTMEMSAFER_PAD_AFTER);
291 break;
292 case RTMEMSAFERALLOCMETHOD_RTMEM:
293 RTMemFree(pvStart);
294 break;
295 default:
296 AssertMsgFailed(("Invalid allocation method, corrupted header\n"));
297 }
298 }
299 else
300 Assert(cb == 0);
301}
302RT_EXPORT_SYMBOL(RTMemSaferFree);
303
304
305RTDECL(int) RTMemSaferReallocZExTag(size_t cbOld, void *pvOld, size_t cbNew, void **ppvNew, uint32_t fFlags, const char *pszTag) RT_NO_THROW
306{
307 /*
308 * We cannot let the heap move us around because we will be failing in our
309 * duty to clean things up. So, allocate a new block, copy over the old
310 * content, and free the old one.
311 */
312 int rc;
313 /* Real realloc. */
314 if (cbNew && cbOld)
315 {
316 PRTMEMSAFERHDR pHdr = (PRTMEMSAFERHDR)((char *)pvOld - RTMEMSAFER_PAD_BEFORE);
317 AssertPtr(pvOld);
318 AssertMsg(*(size_t *)((char *)pvOld - RTMEMSAFER_PAD_BEFORE) == cbOld,
319 ("*pvStart=%#zx cbOld=%#zx\n", *(size_t *)((char *)pvOld - RTMEMSAFER_PAD_BEFORE), cbOld));
320
321 void *pvNew;
322 rc = RTMemSaferAllocZExTag(&pvNew, cbNew, pHdr->fFlags, pszTag);
323 if (RT_SUCCESS(rc))
324 {
325 memcpy(pvNew, pvOld, RT_MIN(cbNew, cbOld));
326 RTMemSaferFree(pvOld, cbOld);
327 *ppvNew = pvNew;
328 }
329 }
330 /* First allocation. */
331 else if (!cbOld)
332 {
333 Assert(pvOld == NULL);
334 rc = RTMemSaferAllocZExTag(ppvNew, cbNew, fFlags, pszTag);
335 }
336 /* Free operation*/
337 else
338 {
339 RTMemSaferFree(pvOld, cbOld);
340 rc = VINF_SUCCESS;
341 }
342 return rc;
343}
344RT_EXPORT_SYMBOL(RTMemSaferReallocZExTag);
345
346
347RTDECL(void *) RTMemSaferAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW
348{
349 void *pvNew = NULL;
350 int rc = RTMemSaferAllocZExTag(&pvNew, cb, RTMEMSAFER_ALLOC_EX_FLAGS_DEFAULT, pszTag);
351 if (RT_SUCCESS(rc))
352 return pvNew;
353 return NULL;
354}
355RT_EXPORT_SYMBOL(RTMemSaferAllocZTag);
356
357
358RTDECL(void *) RTMemSaferReallocZTag(size_t cbOld, void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW
359{
360 void *pvNew = NULL;
361 int rc = RTMemSaferReallocZExTag(cbOld, pvOld, cbNew, &pvNew, RTMEMSAFER_ALLOC_EX_FLAGS_DEFAULT, pszTag);
362 if (RT_SUCCESS(rc))
363 return pvNew;
364 return NULL;
365}
366RT_EXPORT_SYMBOL(RTMemSaferReallocZTag);
367
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette