Line data Source code
1 : /* 2 : wrapper around new for C++ to ensure we always get zero filled memory 3 : */ 4 : 5 : #include <AP_HAL/AP_HAL.h> 6 : #include <stdlib.h> 7 : #include <new> 8 : #include <AP_InternalError/AP_InternalError.h> 9 : 10 : /* 11 : globally override new and delete to ensure that we always start with 12 : zero memory. This ensures consistent behaviour. 13 : 14 : Note that new comes in multiple different variants. When new is used 15 : without std::nothrow the compiler is free to assume it will not fail 16 : as it assumes exceptions are enabled. This makes code like this 17 : unsafe when using -fno-exceptions: 18 : 19 : a = new b; 20 : if (a == nullptr) { 21 : handle_error() 22 : } 23 : 24 : the compiler may remove the error handling. With g++ you can use 25 : -fcheck-new to avoid this, but on clang++ the compiler accepts 26 : -fcheck-new as a valid flag, but doesn't implement it, and may elide 27 : the error checking. That makes using clang++ unsafe with 28 : -fno-exceptions if you ever call new without std::nothrow. 29 : 30 : To avoid this we define NEW_NOTHROW as new(std::nothrow) and use it 31 : everywhere in ArduPilot, then we catch any missing cases with both 32 : an internal error and with a check of the elf for the symbols we 33 : want to avoid 34 : */ 35 : 36 : /* 37 : variant for new(std::nothrow), which is all that should be used in 38 : ArduPilot 39 : */ 40 1940305 : void * operator new(size_t size, std::nothrow_t const ¬hrow) 41 : { 42 1940305 : if (size < 1) { 43 0 : size = 1; 44 : } 45 1940305 : return(calloc(size, 1)); 46 : } 47 : 48 63112 : void * operator new[](size_t size, std::nothrow_t const ¬hrow) 49 : { 50 63112 : if (size < 1) { 51 169 : size = 1; 52 : } 53 63112 : return(calloc(size, 1)); 54 : } 55 : 56 : /* 57 : These variants are for new without std::nothrow. We don't want to ever 58 : use this from ArduPilot code 59 : */ 60 2674332 : void * operator new(size_t size) 61 : { 62 2674332 : if (size < 1) { 63 0 : size = 1; 64 : } 65 2674332 : return(calloc(size, 1)); 66 : } 67 : 68 : 69 238 : void * operator new[](size_t size) 70 : { 71 238 : if (size < 1) { 72 0 : size = 1; 73 : } 74 238 : return(calloc(size, 1)); 75 : } 76 : 77 4208288 : void operator delete(void *p) 78 : { 79 4208288 : if (p) free(p); 80 4208288 : } 81 : 82 52828 : void operator delete[](void * ptr) 83 : { 84 52828 : if (ptr) free(ptr); 85 52828 : } 86 : 87 : #if defined(CYGWIN_BUILD) && CONFIG_HAL_BOARD == HAL_BOARD_SITL 88 : 89 : // intercept malloc to ensure memory so allocated is zeroed. this is rather 90 : // nasty hack that hopefully does not live long! if it breaks again, that's it! 91 : // this is completely unsupported! 92 : 93 : // __imp_malloc is a pointer to a function that will be called to actually 94 : // do the malloc work. we can't replace it (or malloc itself) at build time as 95 : // that will cause cygwin to disable its own malloc. we therefore use a 96 : // constructor function that runs before main but after cygwin's disable check 97 : // to set the pointer to our own malloc which calls back into cygwin's allocator 98 : // through calloc to actually do the work and the zeroing. 99 : 100 : #include <stdio.h> // perror 101 : #include <sys/mman.h> // mprotect and constants 102 : 103 : extern "C" void *__imp_malloc; // the pointer of our affection 104 : 105 : static void *do_the_malloc(size_t size) { // replacement for malloc 106 : // allocate zeroed using calloc (malloc would of course recurse infinitely) 107 : return calloc(1, size); 108 : } 109 : 110 : // called before main to set __imp_malloc. priority of 101 guarantees execution 111 : // before C++ constructors. 112 : __attribute__((constructor(101))) static void hack_in_malloc() { 113 : // __imp_malloc is in .text which is read-only so make it read-write first 114 : 115 : // compute address of its memory page as mprotect mandates page alignment 116 : size_t page_size = (size_t)sysconf(_SC_PAGESIZE); 117 : void *page_base = (void *)((size_t)&__imp_malloc & (~(page_size-1))); 118 : 119 : // make it writable and executable as we (unlikely) may be executing near it 120 : int res = mprotect(page_base, page_size, PROT_READ|PROT_WRITE|PROT_EXEC); 121 : if (res) { 122 : perror("hack_in_malloc() mprotect writable"); 123 : } 124 : 125 : *(void **)&__imp_malloc = (void *)&do_the_malloc; // set the pointer now 126 : 127 : // put the page back to read-only (and let execution keep happening) 128 : res = mprotect(page_base, page_size, PROT_READ|PROT_EXEC); 129 : if (res) { 130 : perror("hack_in_malloc() mprotect readable"); 131 : } 132 : } 133 : 134 : #elif CONFIG_HAL_BOARD != HAL_BOARD_CHIBIOS && CONFIG_HAL_BOARD != HAL_BOARD_QURT 135 : /* 136 : wrapper around malloc to ensure all memory is initialised as zero 137 : ChibiOS and QURT have their own wrappers 138 : */ 139 : extern "C" { 140 : void *__wrap_malloc(size_t size); 141 : void *__real_malloc(size_t size); 142 : } 143 4105837 : void *__wrap_malloc(size_t size) 144 : { 145 4105837 : void *ret = __real_malloc(size); 146 4105837 : if (ret != nullptr) { 147 4105835 : memset(ret, 0, size); 148 : } 149 4105837 : return ret; 150 : } 151 : #endif 152 : 153 : /* 154 : custom realloc which is guaranteed to zero newly-allocated memory 155 : (assuming malloc does). marked as WEAK so tests can override it. 156 : 157 : * always frees ptr (and returns nullptr) if new_size is 0 158 : * allocates if ptr is nullptr and new_size is not 0 159 : * else allocates new_size and copies over the smaller of old_size and new_size 160 : and frees ptr (old_size can be <= actual original requested size) 161 : * returns nullptr if allocation fails and leaves ptr and its data unchanged 162 : */ 163 2597 : void * WEAK mem_realloc(void *ptr, size_t old_size, size_t new_size) 164 : { 165 2597 : if (new_size == 0) { 166 0 : free(ptr); 167 0 : return nullptr; 168 : } 169 : 170 2597 : if (ptr == nullptr) { 171 1307 : return malloc(new_size); 172 : } 173 : 174 1290 : void *new_ptr = malloc(new_size); 175 1290 : if (new_ptr != nullptr) { 176 1290 : size_t copy_size = new_size > old_size ? old_size : new_size; 177 1290 : memcpy(new_ptr, ptr, copy_size); 178 1290 : free(ptr); 179 : } 180 : 181 1290 : return new_ptr; 182 : }