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