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 */