8 * Generic routines to deal with send buffers: a linked list of
9 * smallish blocks, with the operations
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
16 * - return the current size of the buffer chain in bytes
19 #define BUFFER_GRANULE 512
21 struct bufchain_granule {
22 struct bufchain_granule *next;
24 char buf[BUFFER_GRANULE];
27 void bufchain_init(bufchain *ch)
29 ch->head = ch->tail = NULL;
33 void bufchain_clear(bufchain *ch)
35 struct bufchain_granule *b;
38 ch->head = ch->head->next;
45 int bufchain_size(bufchain *ch)
47 return ch->buffersize;
50 void bufchain_add(bufchain *ch, void *data, int len)
52 char *buf = (char *)data;
54 ch->buffersize += len;
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);
61 ch->tail->buflen += copylen;
64 int grainlen = min(len, BUFFER_GRANULE);
65 struct bufchain_granule *newbuf;
66 newbuf = smalloc(sizeof(struct bufchain_granule));
68 newbuf->buflen = grainlen;
69 memcpy(newbuf->buf, buf, grainlen);
73 ch->tail->next = newbuf;
75 ch->head = ch->tail = newbuf;
81 void bufchain_consume(bufchain *ch, int len)
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;
96 void bufchain_prefix(bufchain *ch, void **data, int *len)
98 *len = ch->head->buflen - ch->head->bufpos;
99 *data = ch->head->buf + ch->head->bufpos;
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
114 * Minefield - a Windows equivalent for Electric Fence
117 #define PAGESIZE 4096
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
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.
130 * Freeing anything causes instantaneous decommitment of the pages
131 * involved, so stale pointers are caught as soon as possible.
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;
142 static void minefield_admin_hide(int hide)
144 int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
145 VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
148 static void minefield_init(void)
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)
160 minefield_size = size;
163 * Firstly, allocate a section of that to be the admin block.
164 * We'll need a two-byte field for each page.
166 minefield_admin = minefield_region;
167 minefield_npages = minefield_size / PAGESIZE;
168 admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
169 minefield_npages = (minefield_size - admin_size) / PAGESIZE;
170 minefield_pages = (char *) minefield_region + admin_size;
173 * Commit the admin region.
175 VirtualAlloc(minefield_admin, minefield_npages * 2,
176 MEM_COMMIT, PAGE_READWRITE);
179 * Mark all pages as unused (0xFFFF).
181 for (i = 0; i < minefield_npages; i++)
182 minefield_admin[i] = 0xFFFF;
185 * Hide the admin region.
187 minefield_admin_hide(1);
189 minefield_initialised = 1;
192 static void minefield_bomb(void)
194 div(1, *(int *) minefield_pages);
197 static void *minefield_alloc(int size)
200 int pos, lim, region_end, region_start;
204 npages = (size + PAGESIZE - 1) / PAGESIZE;
206 minefield_admin_hide(0);
209 * Search from current position until we find a contiguous
210 * bunch of npages+2 unused pages.
212 pos = minefield_curpos;
213 lim = minefield_npages;
215 /* Skip over used pages. */
216 while (pos < lim && minefield_admin[pos] != 0xFFFF)
218 /* Count unused pages. */
220 while (pos < lim && pos - start < npages + 2 &&
221 minefield_admin[pos] == 0xFFFF)
223 if (pos - start == npages + 2)
225 /* If we've reached the limit, reset the limit or stop. */
227 if (lim == minefield_npages) {
228 /* go round and start again at zero */
229 lim = minefield_curpos;
232 minefield_admin_hide(1);
238 minefield_curpos = pos - 1;
241 * We have npages+2 unused pages starting at start. We leave
242 * the first and last of these alone and use the rest.
244 region_end = (start + npages + 1) * PAGESIZE;
245 region_start = region_end - size;
246 /* FIXME: could align here if we wanted */
249 * Update the admin region.
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;
255 minefield_admin_hide(1);
257 VirtualAlloc((char *) minefield_pages + region_start, size,
258 MEM_COMMIT, PAGE_READWRITE);
259 return (char *) minefield_pages + region_start;
262 static void minefield_free(void *ptr)
264 int region_start, i, j;
266 minefield_admin_hide(0);
268 region_start = (char *) ptr - (char *) minefield_pages;
269 i = region_start / PAGESIZE;
270 if (i < 0 || i >= minefield_npages ||
271 minefield_admin[i] != region_start % PAGESIZE)
273 for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
274 minefield_admin[j] = 0xFFFF;
277 VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
279 minefield_admin_hide(1);
282 static int minefield_get_size(void *ptr)
284 int region_start, i, j;
286 minefield_admin_hide(0);
288 region_start = (char *) ptr - (char *) minefield_pages;
289 i = region_start / PAGESIZE;
290 if (i < 0 || i >= minefield_npages ||
291 minefield_admin[i] != region_start % PAGESIZE)
293 for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
295 minefield_admin_hide(1);
297 return j * PAGESIZE - region_start;
300 static void *minefield_c_malloc(size_t size)
302 if (!minefield_initialised)
304 return minefield_alloc(size);
307 static void minefield_c_free(void *p)
309 if (!minefield_initialised)
315 * realloc _always_ moves the chunk, for rapid detection of code
316 * that assumes it won't.
318 static void *minefield_c_realloc(void *p, size_t size)
322 if (!minefield_initialised)
324 q = minefield_alloc(size);
325 oldsize = minefield_get_size(p);
326 memcpy(q, p, (oldsize < size ? oldsize : size));
331 #endif /* MINEFIELD */
334 static FILE *fp = NULL;
336 void mlog(char *file, int line)
339 fp = fopen("putty_mem.log", "w");
340 setvbuf(fp, NULL, _IONBF, BUFSIZ);
343 fprintf(fp, "%s:%d: ", file, line);
347 void *safemalloc(size_t size)
351 p = minefield_c_malloc(size);
356 MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error",
357 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
362 fprintf(fp, "malloc(%d) returns %p\n", size, p);
367 void *saferealloc(void *ptr, size_t size)
372 p = minefield_c_malloc(size);
378 p = minefield_c_realloc(ptr, size);
380 p = realloc(ptr, size);
384 MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error",
385 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
390 fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);
395 void safefree(void *ptr)
400 fprintf(fp, "free(%p)\n", ptr);
403 minefield_c_free(ptr);
410 fprintf(fp, "freeing null pointer - no action taken\n");
415 static FILE *debug_fp = NULL;
416 static int debug_got_console = 0;
418 static void dputs(char *buf)
422 if (!debug_got_console) {
424 debug_got_console = 1;
427 debug_fp = fopen("debug.log", "w");
430 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &dw,
432 fputs(buf, debug_fp);
437 void dprintf(char *fmt, ...)
443 vsprintf(buf, fmt, ap);
449 void debug_memdump(void *buf, int len, int L)
452 unsigned char *p = buf;
456 dprintf("\t%d (0x%x) bytes:\n", len, len);
457 delta = 15 & (int) p;
461 for (; 0 < len; p += 16, len -= 16) {
465 strcpy(foo, "................"); /* sixteen dots */
466 for (i = 0; i < 16 && i < len; ++i) {
467 if (&p[i] < (unsigned char *) buf) {
468 dputs(" "); /* 3 spaces */
472 &p[i] != (unsigned char *) buf
473 && i % 4 ? '.' : ' ', p[i]
475 if (p[i] >= ' ' && p[i] <= '~')
476 foo[i] = (char) p[i];
480 dprintf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
484 #endif /* def DEBUG */