| /* |
| * Example torture memory allocator with memory wiping and check for |
| * out-of-bounds writes. |
| * |
| * Allocation structure: |
| * |
| * [ alloc_hdr | red zone before | user area | red zone after ] |
| * |
| * ^ ^ |
| * | `--- pointer returned to Duktape |
| * `--- underlying malloc ptr |
| */ |
| |
| #include "duktape.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| |
| #define RED_ZONE_SIZE 16 |
| #define RED_ZONE_BYTE 0x5a |
| #define INIT_BYTE 0xa5 |
| #define WIPE_BYTE 0x27 |
| |
| typedef struct { |
| /* The double value in the union is there to ensure alignment is |
| * good for IEEE doubles too. In many 32-bit environments 4 bytes |
| * would be sufficiently aligned and the double value is unnecessary. |
| */ |
| union { |
| size_t sz; |
| double d; |
| } u; |
| } alloc_hdr; |
| |
| static void check_red_zone(alloc_hdr *hdr) { |
| size_t size; |
| int i; |
| int err; |
| unsigned char *p; |
| unsigned char *userptr; |
| |
| size = hdr->u.sz; |
| userptr = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE; |
| |
| err = 0; |
| p = (unsigned char *) hdr + sizeof(alloc_hdr); |
| for (i = 0; i < RED_ZONE_SIZE; i++) { |
| if (p[i] != RED_ZONE_BYTE) { |
| err = 1; |
| } |
| } |
| if (err) { |
| fprintf(stderr, "RED ZONE CORRUPTED BEFORE ALLOC: hdr=%p ptr=%p size=%ld\n", |
| (void *) hdr, (void *) userptr, (long) size); |
| fflush(stderr); |
| } |
| |
| err = 0; |
| p = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE + size; |
| for (i = 0; i < RED_ZONE_SIZE; i++) { |
| if (p[i] != RED_ZONE_BYTE) { |
| err = 1; |
| } |
| } |
| if (err) { |
| fprintf(stderr, "RED ZONE CORRUPTED AFTER ALLOC: hdr=%p ptr=%p size=%ld\n", |
| (void *) hdr, (void *) userptr, (long) size); |
| fflush(stderr); |
| } |
| } |
| |
| void *duk_alloc_torture(void *udata, duk_size_t size) { |
| unsigned char *p; |
| |
| (void) udata; /* Suppress warning. */ |
| |
| if (size == 0) { |
| return NULL; |
| } |
| |
| p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| if (!p) { |
| return NULL; |
| } |
| |
| ((alloc_hdr *) (void *) p)->u.sz = size; |
| p += sizeof(alloc_hdr); |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p += RED_ZONE_SIZE; |
| memset((void *) p, INIT_BYTE, size); |
| p += size; |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p -= size; |
| return (void *) p; |
| } |
| |
| void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size) { |
| unsigned char *p, *old_p; |
| size_t old_size; |
| |
| (void) udata; /* Suppress warning. */ |
| |
| /* Handle the ptr-NULL vs. size-zero cases explicitly to minimize |
| * platform assumptions. You can get away with much less in specific |
| * well-behaving environments. |
| */ |
| |
| if (ptr) { |
| old_p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE; |
| old_size = ((alloc_hdr *) (void *) old_p)->u.sz; |
| check_red_zone((alloc_hdr *) (void *) old_p); |
| |
| if (size == 0) { |
| memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| free((void *) old_p); |
| return NULL; |
| } else { |
| /* Force address change on every realloc. */ |
| p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| if (!p) { |
| return NULL; |
| } |
| |
| ((alloc_hdr *) (void *) p)->u.sz = size; |
| p += sizeof(alloc_hdr); |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p += RED_ZONE_SIZE; |
| if (size > old_size) { |
| memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), old_size); |
| memset((void *) (p + old_size), INIT_BYTE, size - old_size); |
| } else { |
| memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), size); |
| } |
| p += size; |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p -= size; |
| |
| memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| free((void *) old_p); |
| |
| return (void *) p; |
| } |
| } else { |
| if (size == 0) { |
| return NULL; |
| } else { |
| p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| if (!p) { |
| return NULL; |
| } |
| |
| ((alloc_hdr *) (void *) p)->u.sz = size; |
| p += sizeof(alloc_hdr); |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p += RED_ZONE_SIZE; |
| memset((void *) p, INIT_BYTE, size); |
| p += size; |
| memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
| p -= size; |
| return (void *) p; |
| } |
| } |
| } |
| |
| void duk_free_torture(void *udata, void *ptr) { |
| unsigned char *p; |
| size_t old_size; |
| |
| (void) udata; /* Suppress warning. */ |
| |
| if (!ptr) { |
| return; |
| } |
| |
| p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE; |
| old_size = ((alloc_hdr *) (void *) p)->u.sz; |
| |
| check_red_zone((alloc_hdr *) (void *) p); |
| memset((void *) p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
| free((void *) p); |
| } |