1 /*
2  *  Example torture memory allocator with memory wiping and check for
3  *  out-of-bounds writes.
4  *
5  *  Allocation structure:
6  *
7  *    [ alloc_hdr | red zone before | user area | red zone after ]
8  *
9  *     ^                             ^
10  *     |                             `--- pointer returned to Duktape
11  *     `--- underlying malloc ptr
12  */
13 
14 #include "duktape.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <stdint.h>
19 
20 #define  RED_ZONE_SIZE  16
21 #define  RED_ZONE_BYTE  0x5a
22 #define  INIT_BYTE      0xa5
23 #define  WIPE_BYTE      0x27
24 
25 typedef struct {
26 	/* The double value in the union is there to ensure alignment is
27 	 * good for IEEE doubles too.  In many 32-bit environments 4 bytes
28 	 * would be sufficiently aligned and the double value is unnecessary.
29 	 */
30 	union {
31 		size_t sz;
32 		double d;
33 	} u;
34 } alloc_hdr;
35 
check_red_zone(alloc_hdr * hdr)36 static void check_red_zone(alloc_hdr *hdr) {
37 	size_t size;
38 	int i;
39 	int err;
40 	unsigned char *p;
41 	unsigned char *userptr;
42 
43 	size = hdr->u.sz;
44 	userptr = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE;
45 
46 	err = 0;
47 	p = (unsigned char *) hdr + sizeof(alloc_hdr);
48 	for (i = 0; i < RED_ZONE_SIZE; i++) {
49 		if (p[i] != RED_ZONE_BYTE) {
50 			err = 1;
51 		}
52 	}
53 	if (err) {
54 		fprintf(stderr, "RED ZONE CORRUPTED BEFORE ALLOC: hdr=%p ptr=%p size=%ld\n",
55 		        (void *) hdr, (void *) userptr, (long) size);
56 		fflush(stderr);
57 	}
58 
59 	err = 0;
60 	p = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE + size;
61 	for (i = 0; i < RED_ZONE_SIZE; i++) {
62 		if (p[i] != RED_ZONE_BYTE) {
63 			err = 1;
64 		}
65 	}
66 	if (err) {
67 		fprintf(stderr, "RED ZONE CORRUPTED AFTER ALLOC: hdr=%p ptr=%p size=%ld\n",
68 		        (void *) hdr, (void *) userptr, (long) size);
69 		fflush(stderr);
70 	}
71 }
72 
duk_alloc_torture(void * udata,duk_size_t size)73 void *duk_alloc_torture(void *udata, duk_size_t size) {
74 	unsigned char *p;
75 
76 	(void) udata;  /* Suppress warning. */
77 
78 	if (size == 0) {
79 		return NULL;
80 	}
81 
82 	p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
83 	if (!p) {
84 		return NULL;
85 	}
86 
87 	((alloc_hdr *) (void *) p)->u.sz = size;
88 	p += sizeof(alloc_hdr);
89 	memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
90 	p += RED_ZONE_SIZE;
91 	memset((void *) p, INIT_BYTE, size);
92 	p += size;
93 	memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
94 	p -= size;
95 	return (void *) p;
96 }
97 
duk_realloc_torture(void * udata,void * ptr,duk_size_t size)98 void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size) {
99 	unsigned char *p, *old_p;
100 	size_t old_size;
101 
102 	(void) udata;  /* Suppress warning. */
103 
104 	/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
105 	 * platform assumptions.  You can get away with much less in specific
106 	 * well-behaving environments.
107 	 */
108 
109 	if (ptr) {
110 		old_p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE;
111 		old_size = ((alloc_hdr *) (void *) old_p)->u.sz;
112 		check_red_zone((alloc_hdr *) (void *) old_p);
113 
114 		if (size == 0) {
115 			memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
116 			free((void *) old_p);
117 			return NULL;
118 		} else {
119 			/* Force address change on every realloc. */
120 			p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
121 			if (!p) {
122 				return NULL;
123 			}
124 
125 			((alloc_hdr *) (void *) p)->u.sz = size;
126 			p += sizeof(alloc_hdr);
127 			memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
128 			p += RED_ZONE_SIZE;
129 			if (size > old_size) {
130 				memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), old_size);
131 				memset((void *) (p + old_size), INIT_BYTE, size - old_size);
132 			} else {
133 				memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), size);
134 			}
135 			p += size;
136 			memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
137 			p -= size;
138 
139 			memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
140 			free((void *) old_p);
141 
142 			return (void *) p;
143 		}
144 	} else {
145 		if (size == 0) {
146 			return NULL;
147 		} else {
148 			p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
149 			if (!p) {
150 				return NULL;
151 			}
152 
153 			((alloc_hdr *) (void *) p)->u.sz = size;
154 			p += sizeof(alloc_hdr);
155 			memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
156 			p += RED_ZONE_SIZE;
157 			memset((void *) p, INIT_BYTE, size);
158 			p += size;
159 			memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE);
160 			p -= size;
161 			return (void *) p;
162 		}
163 	}
164 }
165 
duk_free_torture(void * udata,void * ptr)166 void duk_free_torture(void *udata, void *ptr) {
167 	unsigned char *p;
168 	size_t old_size;
169 
170 	(void) udata;  /* Suppress warning. */
171 
172 	if (!ptr) {
173 		return;
174 	}
175 
176 	p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE;
177 	old_size = ((alloc_hdr *) (void *) p)->u.sz;
178 
179 	check_red_zone((alloc_hdr *) (void *) p);
180 	memset((void *) p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE);
181 	free((void *) p);
182 }
183