374330e2 |
1 | #include <windows.h> |
2 | #include <stdio.h> |
3 | #include <stdlib.h> |
4 | #include "putty.h" |
5 | |
b191636d |
6 | /* |
7 | * My own versions of malloc, realloc and free. Because I want |
8 | * malloc and realloc to bomb out and exit the program if they run |
9 | * out of memory, realloc to reliably call malloc if passed a NULL |
10 | * pointer, and free to reliably do nothing if passed a NULL |
11 | * pointer. We can also put trace printouts in, if we need to; and |
12 | * we can also replace the allocator with an ElectricFence-like |
13 | * one. |
14 | */ |
15 | |
16 | #ifdef MINEFIELD |
17 | /* |
18 | * Minefield - a Windows equivalent for Electric Fence |
19 | */ |
20 | |
21 | #define PAGESIZE 4096 |
22 | |
23 | /* |
24 | * Design: |
25 | * |
26 | * We start by reserving as much virtual address space as Windows |
27 | * will sensibly (or not sensibly) let us have. We flag it all as |
28 | * invalid memory. |
29 | * |
30 | * Any allocation attempt is satisfied by committing one or more |
31 | * pages, with an uncommitted page on either side. The returned |
32 | * memory region is jammed up against the _end_ of the pages. |
33 | * |
34 | * Freeing anything causes instantaneous decommitment of the pages |
35 | * involved, so stale pointers are caught as soon as possible. |
36 | */ |
37 | |
38 | static int minefield_initialised = 0; |
39 | static void *minefield_region = NULL; |
40 | static long minefield_size = 0; |
41 | static long minefield_npages = 0; |
42 | static long minefield_curpos = 0; |
43 | static unsigned short *minefield_admin = NULL; |
44 | static void *minefield_pages = NULL; |
45 | |
32874aea |
46 | static void minefield_admin_hide(int hide) |
47 | { |
b191636d |
48 | int access = hide ? PAGE_NOACCESS : PAGE_READWRITE; |
32874aea |
49 | VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL); |
b191636d |
50 | } |
51 | |
32874aea |
52 | static void minefield_init(void) |
53 | { |
b191636d |
54 | int size; |
55 | int admin_size; |
56 | int i; |
57 | |
32874aea |
58 | for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) { |
59 | minefield_region = VirtualAlloc(NULL, size, |
60 | MEM_RESERVE, PAGE_NOACCESS); |
61 | if (minefield_region) |
62 | break; |
b191636d |
63 | } |
64 | minefield_size = size; |
b191636d |
65 | |
66 | /* |
67 | * Firstly, allocate a section of that to be the admin block. |
68 | * We'll need a two-byte field for each page. |
69 | */ |
70 | minefield_admin = minefield_region; |
71 | minefield_npages = minefield_size / PAGESIZE; |
32874aea |
72 | admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1); |
b191636d |
73 | minefield_npages = (minefield_size - admin_size) / PAGESIZE; |
32874aea |
74 | minefield_pages = (char *) minefield_region + admin_size; |
b191636d |
75 | |
76 | /* |
77 | * Commit the admin region. |
78 | */ |
79 | VirtualAlloc(minefield_admin, minefield_npages * 2, |
32874aea |
80 | MEM_COMMIT, PAGE_READWRITE); |
b191636d |
81 | |
82 | /* |
83 | * Mark all pages as unused (0xFFFF). |
84 | */ |
85 | for (i = 0; i < minefield_npages; i++) |
32874aea |
86 | minefield_admin[i] = 0xFFFF; |
b191636d |
87 | |
88 | /* |
89 | * Hide the admin region. |
90 | */ |
91 | minefield_admin_hide(1); |
92 | |
93 | minefield_initialised = 1; |
94 | } |
95 | |
32874aea |
96 | static void minefield_bomb(void) |
97 | { |
98 | div(1, *(int *) minefield_pages); |
b191636d |
99 | } |
100 | |
32874aea |
101 | static void *minefield_alloc(int size) |
102 | { |
b191636d |
103 | int npages; |
104 | int pos, lim, region_end, region_start; |
105 | int start; |
106 | int i; |
107 | |
32874aea |
108 | npages = (size + PAGESIZE - 1) / PAGESIZE; |
b191636d |
109 | |
110 | minefield_admin_hide(0); |
111 | |
112 | /* |
113 | * Search from current position until we find a contiguous |
114 | * bunch of npages+2 unused pages. |
115 | */ |
116 | pos = minefield_curpos; |
117 | lim = minefield_npages; |
118 | while (1) { |
32874aea |
119 | /* Skip over used pages. */ |
120 | while (pos < lim && minefield_admin[pos] != 0xFFFF) |
121 | pos++; |
122 | /* Count unused pages. */ |
123 | start = pos; |
124 | while (pos < lim && pos - start < npages + 2 && |
125 | minefield_admin[pos] == 0xFFFF) |
126 | pos++; |
127 | if (pos - start == npages + 2) |
128 | break; |
129 | /* If we've reached the limit, reset the limit or stop. */ |
130 | if (pos >= lim) { |
131 | if (lim == minefield_npages) { |
132 | /* go round and start again at zero */ |
133 | lim = minefield_curpos; |
134 | pos = 0; |
135 | } else { |
136 | minefield_admin_hide(1); |
137 | return NULL; |
138 | } |
139 | } |
b191636d |
140 | } |
141 | |
32874aea |
142 | minefield_curpos = pos - 1; |
b191636d |
143 | |
144 | /* |
145 | * We have npages+2 unused pages starting at start. We leave |
146 | * the first and last of these alone and use the rest. |
147 | */ |
32874aea |
148 | region_end = (start + npages + 1) * PAGESIZE; |
b191636d |
149 | region_start = region_end - size; |
150 | /* FIXME: could align here if we wanted */ |
151 | |
152 | /* |
153 | * Update the admin region. |
154 | */ |
32874aea |
155 | for (i = start + 2; i < start + npages - 1; i++) |
156 | minefield_admin[i] = 0xFFFE; /* used but no region starts here */ |
157 | minefield_admin[start + 1] = region_start % PAGESIZE; |
b191636d |
158 | |
159 | minefield_admin_hide(1); |
160 | |
32874aea |
161 | VirtualAlloc((char *) minefield_pages + region_start, size, |
162 | MEM_COMMIT, PAGE_READWRITE); |
163 | return (char *) minefield_pages + region_start; |
b191636d |
164 | } |
165 | |
32874aea |
166 | static void minefield_free(void *ptr) |
167 | { |
b191636d |
168 | int region_start, i, j; |
169 | |
170 | minefield_admin_hide(0); |
171 | |
32874aea |
172 | region_start = (char *) ptr - (char *) minefield_pages; |
b191636d |
173 | i = region_start / PAGESIZE; |
174 | if (i < 0 || i >= minefield_npages || |
32874aea |
175 | minefield_admin[i] != region_start % PAGESIZE) |
176 | minefield_bomb(); |
b191636d |
177 | for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) { |
32874aea |
178 | minefield_admin[j] = 0xFFFF; |
b191636d |
179 | } |
180 | |
32874aea |
181 | VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT); |
b191636d |
182 | |
183 | minefield_admin_hide(1); |
184 | } |
185 | |
32874aea |
186 | static int minefield_get_size(void *ptr) |
187 | { |
b191636d |
188 | int region_start, i, j; |
189 | |
190 | minefield_admin_hide(0); |
191 | |
32874aea |
192 | region_start = (char *) ptr - (char *) minefield_pages; |
b191636d |
193 | i = region_start / PAGESIZE; |
194 | if (i < 0 || i >= minefield_npages || |
32874aea |
195 | minefield_admin[i] != region_start % PAGESIZE) |
196 | minefield_bomb(); |
b191636d |
197 | for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++); |
198 | |
199 | minefield_admin_hide(1); |
200 | |
32874aea |
201 | return j * PAGESIZE - region_start; |
b191636d |
202 | } |
203 | |
32874aea |
204 | static void *minefield_c_malloc(size_t size) |
205 | { |
206 | if (!minefield_initialised) |
207 | minefield_init(); |
b191636d |
208 | return minefield_alloc(size); |
209 | } |
210 | |
32874aea |
211 | static void minefield_c_free(void *p) |
212 | { |
213 | if (!minefield_initialised) |
214 | minefield_init(); |
b191636d |
215 | minefield_free(p); |
216 | } |
217 | |
218 | /* |
219 | * realloc _always_ moves the chunk, for rapid detection of code |
220 | * that assumes it won't. |
221 | */ |
32874aea |
222 | static void *minefield_c_realloc(void *p, size_t size) |
223 | { |
b191636d |
224 | size_t oldsize; |
225 | void *q; |
32874aea |
226 | if (!minefield_initialised) |
227 | minefield_init(); |
b191636d |
228 | q = minefield_alloc(size); |
229 | oldsize = minefield_get_size(p); |
230 | memcpy(q, p, (oldsize < size ? oldsize : size)); |
231 | minefield_free(p); |
232 | return q; |
233 | } |
234 | |
32874aea |
235 | #endif /* MINEFIELD */ |
374330e2 |
236 | |
237 | #ifdef MALLOC_LOG |
238 | static FILE *fp = NULL; |
239 | |
32874aea |
240 | void mlog(char *file, int line) |
241 | { |
c662dbc0 |
242 | if (!fp) { |
374330e2 |
243 | fp = fopen("putty_mem.log", "w"); |
c662dbc0 |
244 | setvbuf(fp, NULL, _IONBF, BUFSIZ); |
245 | } |
374330e2 |
246 | if (fp) |
32874aea |
247 | fprintf(fp, "%s:%d: ", file, line); |
374330e2 |
248 | } |
249 | #endif |
250 | |
32874aea |
251 | void *safemalloc(size_t size) |
252 | { |
b191636d |
253 | void *p; |
254 | #ifdef MINEFIELD |
32874aea |
255 | p = minefield_c_malloc(size); |
b191636d |
256 | #else |
32874aea |
257 | p = malloc(size); |
b191636d |
258 | #endif |
374330e2 |
259 | if (!p) { |
260 | MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error", |
261 | MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); |
262 | exit(1); |
263 | } |
264 | #ifdef MALLOC_LOG |
265 | if (fp) |
266 | fprintf(fp, "malloc(%d) returns %p\n", size, p); |
267 | #endif |
268 | return p; |
269 | } |
270 | |
32874aea |
271 | void *saferealloc(void *ptr, size_t size) |
272 | { |
374330e2 |
273 | void *p; |
b191636d |
274 | if (!ptr) { |
275 | #ifdef MINEFIELD |
32874aea |
276 | p = minefield_c_malloc(size); |
b191636d |
277 | #else |
32874aea |
278 | p = malloc(size); |
b191636d |
279 | #endif |
280 | } else { |
281 | #ifdef MINEFIELD |
32874aea |
282 | p = minefield_c_realloc(ptr, size); |
b191636d |
283 | #else |
32874aea |
284 | p = realloc(ptr, size); |
b191636d |
285 | #endif |
286 | } |
374330e2 |
287 | if (!p) { |
288 | MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error", |
289 | MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); |
290 | exit(1); |
291 | } |
292 | #ifdef MALLOC_LOG |
293 | if (fp) |
294 | fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p); |
295 | #endif |
296 | return p; |
297 | } |
298 | |
32874aea |
299 | void safefree(void *ptr) |
300 | { |
374330e2 |
301 | if (ptr) { |
302 | #ifdef MALLOC_LOG |
303 | if (fp) |
304 | fprintf(fp, "free(%p)\n", ptr); |
305 | #endif |
b191636d |
306 | #ifdef MINEFIELD |
32874aea |
307 | minefield_c_free(ptr); |
b191636d |
308 | #else |
32874aea |
309 | free(ptr); |
b191636d |
310 | #endif |
374330e2 |
311 | } |
312 | #ifdef MALLOC_LOG |
313 | else if (fp) |
314 | fprintf(fp, "freeing null pointer - no action taken\n"); |
315 | #endif |
316 | } |
c82bda52 |
317 | |
318 | #ifdef DEBUG |
319 | static FILE *debug_fp = NULL; |
320 | static int debug_got_console = 0; |
321 | |
32874aea |
322 | static void dputs(char *buf) |
323 | { |
c82bda52 |
324 | DWORD dw; |
c82bda52 |
325 | |
326 | if (!debug_got_console) { |
327 | AllocConsole(); |
328 | debug_got_console = 1; |
329 | } |
330 | if (!debug_fp) { |
331 | debug_fp = fopen("debug.log", "w"); |
332 | } |
333 | |
32874aea |
334 | WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &dw, |
335 | NULL); |
c82bda52 |
336 | fputs(buf, debug_fp); |
337 | fflush(debug_fp); |
db9c0f86 |
338 | } |
339 | |
340 | |
32874aea |
341 | void dprintf(char *fmt, ...) |
342 | { |
db9c0f86 |
343 | char buf[2048]; |
344 | va_list ap; |
345 | |
346 | va_start(ap, fmt); |
347 | vsprintf(buf, fmt, ap); |
32874aea |
348 | dputs(buf); |
c82bda52 |
349 | va_end(ap); |
350 | } |
db9c0f86 |
351 | |
352 | |
32874aea |
353 | void debug_memdump(void *buf, int len, int L) |
354 | { |
db9c0f86 |
355 | int i; |
356 | unsigned char *p = buf; |
765c4200 |
357 | char foo[17]; |
db9c0f86 |
358 | if (L) { |
359 | int delta; |
32874aea |
360 | dprintf("\t%d (0x%x) bytes:\n", len, len); |
db9c0f86 |
361 | delta = 15 & (int) p; |
362 | p -= delta; |
363 | len += delta; |
364 | } |
365 | for (; 0 < len; p += 16, len -= 16) { |
32874aea |
366 | dputs(" "); |
367 | if (L) |
368 | dprintf("%p: ", p); |
369 | strcpy(foo, "................"); /* sixteen dots */ |
db9c0f86 |
370 | for (i = 0; i < 16 && i < len; ++i) { |
371 | if (&p[i] < (unsigned char *) buf) { |
32874aea |
372 | dputs(" "); /* 3 spaces */ |
765c4200 |
373 | foo[i] = ' '; |
db9c0f86 |
374 | } else { |
32874aea |
375 | dprintf("%c%02.2x", |
376 | &p[i] != (unsigned char *) buf |
377 | && i % 4 ? '.' : ' ', p[i] |
378 | ); |
765c4200 |
379 | if (p[i] >= ' ' && p[i] <= '~') |
32874aea |
380 | foo[i] = (char) p[i]; |
db9c0f86 |
381 | } |
382 | } |
765c4200 |
383 | foo[i] = '\0'; |
32874aea |
384 | dprintf("%*s%s\n", (16 - i) * 3 + 2, "", foo); |
db9c0f86 |
385 | } |
386 | } |
387 | |
32874aea |
388 | #endif /* def DEBUG */ |