Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* |
michael@0 | 2 | * Copyright (c) 2004i-2010 Alex Pankratov. All rights reserved. |
michael@0 | 3 | * |
michael@0 | 4 | * Hierarchical memory allocator, 1.2.1 |
michael@0 | 5 | * http://swapped.cc/halloc |
michael@0 | 6 | */ |
michael@0 | 7 | |
michael@0 | 8 | /* |
michael@0 | 9 | * The program is distributed under terms of BSD license. |
michael@0 | 10 | * You can obtain the copy of the license by visiting: |
michael@0 | 11 | * |
michael@0 | 12 | * http://www.opensource.org/licenses/bsd-license.php |
michael@0 | 13 | */ |
michael@0 | 14 | |
michael@0 | 15 | #include <stdlib.h> /* realloc */ |
michael@0 | 16 | #include <string.h> /* memset & co */ |
michael@0 | 17 | |
michael@0 | 18 | #include "halloc.h" |
michael@0 | 19 | #include "align.h" |
michael@0 | 20 | #include "hlist.h" |
michael@0 | 21 | |
michael@0 | 22 | /* |
michael@0 | 23 | * block control header |
michael@0 | 24 | */ |
michael@0 | 25 | typedef struct hblock |
michael@0 | 26 | { |
michael@0 | 27 | #ifndef NDEBUG |
michael@0 | 28 | #define HH_MAGIC 0x20040518L |
michael@0 | 29 | long magic; |
michael@0 | 30 | #endif |
michael@0 | 31 | hlist_item_t siblings; /* 2 pointers */ |
michael@0 | 32 | hlist_head_t children; /* 1 pointer */ |
michael@0 | 33 | max_align_t data[1]; /* not allocated, see below */ |
michael@0 | 34 | |
michael@0 | 35 | } hblock_t; |
michael@0 | 36 | |
michael@0 | 37 | #define sizeof_hblock offsetof(hblock_t, data) |
michael@0 | 38 | |
michael@0 | 39 | /* |
michael@0 | 40 | * |
michael@0 | 41 | */ |
michael@0 | 42 | realloc_t halloc_allocator = NULL; |
michael@0 | 43 | |
michael@0 | 44 | #define allocator halloc_allocator |
michael@0 | 45 | |
michael@0 | 46 | /* |
michael@0 | 47 | * static methods |
michael@0 | 48 | */ |
michael@0 | 49 | static void _set_allocator(void); |
michael@0 | 50 | static void * _realloc(void * ptr, size_t n); |
michael@0 | 51 | |
michael@0 | 52 | static int _relate(hblock_t * b, hblock_t * p); |
michael@0 | 53 | static void _free_children(hblock_t * p); |
michael@0 | 54 | |
michael@0 | 55 | /* |
michael@0 | 56 | * Core API |
michael@0 | 57 | */ |
michael@0 | 58 | void * halloc(void * ptr, size_t len) |
michael@0 | 59 | { |
michael@0 | 60 | hblock_t * p; |
michael@0 | 61 | |
michael@0 | 62 | /* set up default allocator */ |
michael@0 | 63 | if (! allocator) |
michael@0 | 64 | { |
michael@0 | 65 | _set_allocator(); |
michael@0 | 66 | assert(allocator); |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | /* calloc */ |
michael@0 | 70 | if (! ptr) |
michael@0 | 71 | { |
michael@0 | 72 | if (! len) |
michael@0 | 73 | return NULL; |
michael@0 | 74 | |
michael@0 | 75 | p = allocator(0, len + sizeof_hblock); |
michael@0 | 76 | if (! p) |
michael@0 | 77 | return NULL; |
michael@0 | 78 | #ifndef NDEBUG |
michael@0 | 79 | p->magic = HH_MAGIC; |
michael@0 | 80 | #endif |
michael@0 | 81 | hlist_init(&p->children); |
michael@0 | 82 | hlist_init_item(&p->siblings); |
michael@0 | 83 | |
michael@0 | 84 | return p->data; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | p = structof(ptr, hblock_t, data); |
michael@0 | 88 | assert(p->magic == HH_MAGIC); |
michael@0 | 89 | |
michael@0 | 90 | /* realloc */ |
michael@0 | 91 | if (len) |
michael@0 | 92 | { |
michael@0 | 93 | p = allocator(p, len + sizeof_hblock); |
michael@0 | 94 | if (! p) |
michael@0 | 95 | return NULL; |
michael@0 | 96 | |
michael@0 | 97 | hlist_relink(&p->siblings); |
michael@0 | 98 | hlist_relink_head(&p->children); |
michael@0 | 99 | |
michael@0 | 100 | return p->data; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | /* free */ |
michael@0 | 104 | _free_children(p); |
michael@0 | 105 | hlist_del(&p->siblings); |
michael@0 | 106 | allocator(p, 0); |
michael@0 | 107 | |
michael@0 | 108 | return NULL; |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | void hattach(void * block, void * parent) |
michael@0 | 112 | { |
michael@0 | 113 | hblock_t * b, * p; |
michael@0 | 114 | |
michael@0 | 115 | if (! block) |
michael@0 | 116 | { |
michael@0 | 117 | assert(! parent); |
michael@0 | 118 | return; |
michael@0 | 119 | } |
michael@0 | 120 | |
michael@0 | 121 | /* detach */ |
michael@0 | 122 | b = structof(block, hblock_t, data); |
michael@0 | 123 | assert(b->magic == HH_MAGIC); |
michael@0 | 124 | |
michael@0 | 125 | hlist_del(&b->siblings); |
michael@0 | 126 | |
michael@0 | 127 | if (! parent) |
michael@0 | 128 | return; |
michael@0 | 129 | |
michael@0 | 130 | /* attach */ |
michael@0 | 131 | p = structof(parent, hblock_t, data); |
michael@0 | 132 | assert(p->magic == HH_MAGIC); |
michael@0 | 133 | |
michael@0 | 134 | /* sanity checks */ |
michael@0 | 135 | assert(b != p); /* trivial */ |
michael@0 | 136 | assert(! _relate(p, b)); /* heavy ! */ |
michael@0 | 137 | |
michael@0 | 138 | hlist_add(&p->children, &b->siblings); |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | /* |
michael@0 | 142 | * malloc/free api |
michael@0 | 143 | */ |
michael@0 | 144 | void * h_malloc(size_t len) |
michael@0 | 145 | { |
michael@0 | 146 | return halloc(0, len); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | void * h_calloc(size_t n, size_t len) |
michael@0 | 150 | { |
michael@0 | 151 | void * ptr = halloc(0, len*=n); |
michael@0 | 152 | return ptr ? memset(ptr, 0, len) : NULL; |
michael@0 | 153 | } |
michael@0 | 154 | |
michael@0 | 155 | void * h_realloc(void * ptr, size_t len) |
michael@0 | 156 | { |
michael@0 | 157 | return halloc(ptr, len); |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | void h_free(void * ptr) |
michael@0 | 161 | { |
michael@0 | 162 | halloc(ptr, 0); |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | char * h_strdup(const char * str) |
michael@0 | 166 | { |
michael@0 | 167 | size_t len = strlen(str); |
michael@0 | 168 | char * ptr = halloc(0, len + 1); |
michael@0 | 169 | return ptr ? (ptr[len] = 0, memcpy(ptr, str, len)) : NULL; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | /* |
michael@0 | 173 | * static stuff |
michael@0 | 174 | */ |
michael@0 | 175 | static void _set_allocator(void) |
michael@0 | 176 | { |
michael@0 | 177 | void * p; |
michael@0 | 178 | assert(! allocator); |
michael@0 | 179 | |
michael@0 | 180 | /* |
michael@0 | 181 | * the purpose of the test below is to check the behaviour |
michael@0 | 182 | * of realloc(ptr, 0), which is defined in the standard |
michael@0 | 183 | * as an implementation-specific. if it returns zero, |
michael@0 | 184 | * then it's equivalent to free(). it can however return |
michael@0 | 185 | * non-zero, in which case it cannot be used for freeing |
michael@0 | 186 | * memory blocks and we'll need to supply our own version |
michael@0 | 187 | * |
michael@0 | 188 | * Thanks to Stan Tobias for pointing this tricky part out. |
michael@0 | 189 | */ |
michael@0 | 190 | allocator = realloc; |
michael@0 | 191 | if (! (p = malloc(1))) |
michael@0 | 192 | /* hmm */ |
michael@0 | 193 | return; |
michael@0 | 194 | |
michael@0 | 195 | if ((p = realloc(p, 0))) |
michael@0 | 196 | { |
michael@0 | 197 | /* realloc cannot be used as free() */ |
michael@0 | 198 | allocator = _realloc; |
michael@0 | 199 | free(p); |
michael@0 | 200 | } |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | static void * _realloc(void * ptr, size_t n) |
michael@0 | 204 | { |
michael@0 | 205 | /* |
michael@0 | 206 | * free'ing realloc() |
michael@0 | 207 | */ |
michael@0 | 208 | if (n) |
michael@0 | 209 | return realloc(ptr, n); |
michael@0 | 210 | free(ptr); |
michael@0 | 211 | return NULL; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | static int _relate(hblock_t * b, hblock_t * p) |
michael@0 | 215 | { |
michael@0 | 216 | hlist_item_t * i; |
michael@0 | 217 | |
michael@0 | 218 | if (!b || !p) |
michael@0 | 219 | return 0; |
michael@0 | 220 | |
michael@0 | 221 | /* |
michael@0 | 222 | * since there is no 'parent' pointer, which would've allowed |
michael@0 | 223 | * O(log(n)) upward traversal, the check must use O(n) downward |
michael@0 | 224 | * iteration of the entire hierarchy; and this can be VERY SLOW |
michael@0 | 225 | */ |
michael@0 | 226 | hlist_for_each(i, &p->children) |
michael@0 | 227 | { |
michael@0 | 228 | hblock_t * q = structof(i, hblock_t, siblings); |
michael@0 | 229 | if (q == b || _relate(b, q)) |
michael@0 | 230 | return 1; |
michael@0 | 231 | } |
michael@0 | 232 | return 0; |
michael@0 | 233 | } |
michael@0 | 234 | |
michael@0 | 235 | static void _free_children(hblock_t * p) |
michael@0 | 236 | { |
michael@0 | 237 | hlist_item_t * i, * tmp; |
michael@0 | 238 | |
michael@0 | 239 | #ifndef NDEBUG |
michael@0 | 240 | /* |
michael@0 | 241 | * this catches loops in hierarchy with almost zero |
michael@0 | 242 | * overhead (compared to _relate() running time) |
michael@0 | 243 | */ |
michael@0 | 244 | assert(p && p->magic == HH_MAGIC); |
michael@0 | 245 | p->magic = 0; |
michael@0 | 246 | #endif |
michael@0 | 247 | hlist_for_each_safe(i, tmp, &p->children) |
michael@0 | 248 | { |
michael@0 | 249 | hblock_t * q = structof(i, hblock_t, siblings); |
michael@0 | 250 | _free_children(q); |
michael@0 | 251 | allocator(q, 0); |
michael@0 | 252 | } |
michael@0 | 253 | } |
michael@0 | 254 |