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