LCOV - code coverage report
Current view: top level - AP_Common - c++.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 34 39 87.2 %
Date: 2026-09-09 11:02:08 Functions: 8 8 100.0 %

          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 &nothrow)
      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 &nothrow)
      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             : }

Generated by: LCOV version 1.14