Preparatory work for allowing PSCP to work over SFTP as well as old-
[u/mdw/putty] / misc.c
CommitLineData
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
21struct bufchain_granule {
22 struct bufchain_granule *next;
23 int buflen, bufpos;
24 char buf[BUFFER_GRANULE];
25};
26
27void bufchain_init(bufchain *ch)
28{
29 ch->head = ch->tail = NULL;
30 ch->buffersize = 0;
31}
32
33void 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
45int bufchain_size(bufchain *ch)
46{
47 return ch->buffersize;
48}
49
50void 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
81void 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
96void 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
134static int minefield_initialised = 0;
135static void *minefield_region = NULL;
136static long minefield_size = 0;
137static long minefield_npages = 0;
138static long minefield_curpos = 0;
139static unsigned short *minefield_admin = NULL;
140static void *minefield_pages = NULL;
141
32874aea 142static 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 148static 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 192static void minefield_bomb(void)
193{
194 div(1, *(int *) minefield_pages);
b191636d 195}
196
32874aea 197static 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 262static 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 282static 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 300static void *minefield_c_malloc(size_t size)
301{
302 if (!minefield_initialised)
303 minefield_init();
b191636d 304 return minefield_alloc(size);
305}
306
32874aea 307static 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 318static 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
334static FILE *fp = NULL;
335
32874aea 336void 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 347void *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 367void *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 395void 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
415static FILE *debug_fp = NULL;
416static int debug_got_console = 0;
417
32874aea 418static 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 437void 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 449void 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 */