|
9 | 9 | #include <cstdio> |
10 | 10 | #include <cstdlib> |
11 | 11 |
|
| 12 | +#include <OpenImageIO/oiioversion.h> |
12 | 13 | #include <OpenImageIO/platform.h> |
13 | 14 |
|
14 | 15 |
|
| 16 | + |
| 17 | +// General resources about security and hardening for C++: |
| 18 | +// |
| 19 | +// https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html |
| 20 | +// https://www.gnu.org/software/libc/manual/html_node/Source-Fortification.html |
| 21 | +// https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros.html |
| 22 | +// https://libcxx.llvm.org/Hardening.html |
| 23 | +// https://cheatsheetseries.owasp.org/cheatsheets/C-Based_Toolchain_Hardening_Cheat_Sheet.html |
| 24 | +// https://stackoverflow.com/questions/13544512/what-is-the-most-hardened-set-of-options-for-gcc-compiling-c-c |
| 25 | +// https://medium.com/@simontoth/daily-bit-e-of-c-hardened-mode-of-standard-library-implementations-18be2422c372 |
| 26 | +// https://en.cppreference.com/w/cpp/contract |
| 27 | +// https://en.cppreference.com/w/cpp/language/contracts |
| 28 | + |
| 29 | + |
| 30 | + |
| 31 | +// Define hardening levels for OIIO: which checks should we do? |
| 32 | +// NONE - YOLO mode, no extra checks (not recommended) |
| 33 | +// FAST - Minimal checks that have low performance impact |
| 34 | +// EXTENSIVE - More thorough checks, may impact performance |
| 35 | +// DEBUG - Maximum checks, for debugging purposes |
| 36 | +#define OIIO_HARDENING_NONE 0 |
| 37 | +#define OIIO_HARDENING_FAST 1 |
| 38 | +#define OIIO_HARDENING_EXTENSIVE 2 |
| 39 | +#define OIIO_HARDENING_DEBUG 3 |
| 40 | + |
| 41 | +// OIIO_HARDENING_DEFAULT defines the default hardening level we actually use. |
| 42 | +// By default, we use FAST for release builds and DEBUG for debug builds. But |
| 43 | +// it can be overridden: |
| 44 | +// - For OIIO internals, at OIIO build time with the `OIIO_HARDENING` CMake |
| 45 | +// variable. |
| 46 | +// - For other projects using OIIO's headers, any translation unit may |
| 47 | +// override this by defining OIIO_HARDENING_DEFAULT before including any |
| 48 | +// OIIO headers. But note that this only affects calls to inline functions |
| 49 | +// or templates defined in the headers. Non-inline functions compiled into |
| 50 | +// the OIIO library itself will have been compiled with whatever hardening |
| 51 | +// level was selected when the library was built. |
| 52 | +#ifndef OIIO_HARDENING_DEFAULT |
| 53 | +# ifdef NDEBUG |
| 54 | +# define OIIO_HARDENING_DEFAULT OIIO_HARDENING_FAST |
| 55 | +# else |
| 56 | +# define OIIO_HARDENING_DEFAULT OIIO_HARDENING_DEBUG |
| 57 | +# endif |
| 58 | +#endif |
| 59 | + |
| 60 | + |
| 61 | +// Choices for what to do when a contract assertion fails. |
| 62 | +// This mimics the C++26 standard's std::contract behavior. |
| 63 | +#define OIIO_ASSERTION_RESPONSE_IGNORE 0 |
| 64 | +#define OIIO_ASSERTION_RESPONSE_OBSERVE 1 |
| 65 | +#define OIIO_ASSERTION_RESPONSE_ENFORCE 2 |
| 66 | +#define OIIO_ASSERTION_RESPONSE_QUICK_ENFORCE 3 |
| 67 | + |
| 68 | +// OIIO_ASSERTION_RESPONSE_DEFAULT defines the default response to failed |
| 69 | +// contract assertions. By default, we enforce them, UNLESS we are a release |
| 70 | +// mode build that has set the hardening mode to NONE. But any translation |
| 71 | +// unit (including clients of OIIO) may override this by defining |
| 72 | +// OIIO_ASSERTION_RESPONSE_DEFAULT before including any OIIO headers. But note |
| 73 | +// that this only affects calls to inline functions or templates defined in |
| 74 | +// the headers. Non-inline functions compiled into the OIIO library itself |
| 75 | +// will have been compiled with whatever response was selected when the |
| 76 | +// library was built. |
| 77 | +#ifndef OIIO_ASSERTION_RESPONSE_DEFAULT |
| 78 | +# if OIIO_HARDENING_DEFAULT == OIIO_HARDENING_NONE && defined(NDEBUG) |
| 79 | +# define OIIO_ASSERTION_RESPONSE_DEFAULT OIIO_ASSERTION_RESPONSE_IGNORE |
| 80 | +# else |
| 81 | +# define OIIO_ASSERTION_RESPONSE_DEFAULT OIIO_ASSERTION_RESPONSE_ENFORCE |
| 82 | +# endif |
| 83 | +#endif |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +// `OIIO_CONTRACT_ASSERT(condition)` checks if the condition is met, and if |
| 88 | +// not, calls the contract violation handler with the appropriate response. |
| 89 | +// `OIIO_CONTRACT_ASSERT_MSG(condition, msg)` is the same, but allows a |
| 90 | +// different message to be passed to the handler. |
| 91 | +#if OIIO_ASSERTION_RESPONSE_DEFAULT == OIIO_ASSERTION_RESPONSE_IGNORE |
| 92 | +# define OIIO_CONTRACT_ASSERT_MSG(condition, message) (void)0 |
| 93 | +#elif OIIO_ASSERTION_RESPONSE_DEFAULT == OIIO_ASSERTION_RESPONSE_QUICK_ENFORCE |
| 94 | +# define OIIO_CONTRACT_ASSERT_MSG(condition, message) \ |
| 95 | + (OIIO_LIKELY(condition) ? ((void)0) : (std::abort(), (void)0)) |
| 96 | +#elif OIIO_ASSERTION_RESPONSE_DEFAULT == OIIO_ASSERTION_RESPONSE_OBSERVE |
| 97 | +# define OIIO_CONTRACT_ASSERT_MSG(condition, message) \ |
| 98 | + (OIIO_LIKELY(condition) ? ((void)0) \ |
| 99 | + : (OIIO::contract_violation_handler( \ |
| 100 | + __FILE__ ":" OIIO_STRINGIZE(__LINE__), \ |
| 101 | + OIIO_PRETTY_FUNCTION, message), \ |
| 102 | + (void)0)) |
| 103 | +#elif OIIO_ASSERTION_RESPONSE_DEFAULT == OIIO_ASSERTION_RESPONSE_ENFORCE |
| 104 | +# define OIIO_CONTRACT_ASSERT_MSG(condition, message) \ |
| 105 | + (OIIO_LIKELY(condition) ? ((void)0) \ |
| 106 | + : (OIIO::contract_violation_handler( \ |
| 107 | + __FILE__ ":" OIIO_STRINGIZE(__LINE__), \ |
| 108 | + OIIO_PRETTY_FUNCTION, message), \ |
| 109 | + std::abort(), (void)0)) |
| 110 | +#else |
| 111 | +# error "Unknown OIIO_ASSERTION_RESPONSE_DEFAULT" |
| 112 | +#endif |
| 113 | + |
| 114 | +#define OIIO_CONTRACT_ASSERT(condition) \ |
| 115 | + OIIO_CONTRACT_ASSERT_MSG(condition, #condition) |
| 116 | + |
| 117 | +// Macros to use to select whether or not to do a contract check, based on the |
| 118 | +// hardening level: |
| 119 | +// - OIIO_HARDENING_ASSERT_FAST : only checks contract for >= FAST hardening. |
| 120 | +// - OIIO_HARDENING_ASSERT_EXTENSIVE : only checks contract for >= EXTENSIVE. |
| 121 | +// - OIIO_HARDENING_ASSERT_DEBUG : only checks contract for DEBUG hardening. |
| 122 | +#if OIIO_HARDENING_DEFAULT >= OIIO_HARDENING_FAST |
| 123 | +# define OIIO_HARDENING_ASSERT_FAST_MSG(condition, message) \ |
| 124 | + OIIO_CONTRACT_ASSERT_MSG(condition, message) |
| 125 | +#else |
| 126 | +# define OIIO_HARDENING_ASSERT_FAST_MSG(...) (void)0 |
| 127 | +#endif |
| 128 | + |
| 129 | +#if OIIO_HARDENING_DEFAULT >= OIIO_HARDENING_EXTENSIVE |
| 130 | +# define OIIO_HARDENING_ASSERT_EXTENSIVE_MSG(condition, message) \ |
| 131 | + OIIO_CONTRACT_ASSERT_MSG(condition, message) |
| 132 | +#else |
| 133 | +# define OIIO_HARDENING_ASSERT_EXTENSIVE_MSG(...) (void)0 |
| 134 | +#endif |
| 135 | + |
| 136 | +#if OIIO_HARDENING_DEFAULT >= OIIO_HARDENING_DEBUG |
| 137 | +# define OIIO_HARDENING_ASSERT_DEBUG_MSG(condition, message) \ |
| 138 | + OIIO_CONTRACT_ASSERT_MSG(condition, message) |
| 139 | +#else |
| 140 | +# define OIIO_HARDENING_ASSERT_DEBUG_MSG(...) (void)0 |
| 141 | +#endif |
| 142 | + |
| 143 | +#define OIIO_HARDENING_ASSERT_NONE(condition) \ |
| 144 | + OIIO_HARDENING_ASSERT_NONE_MSG(condition, #condition) |
| 145 | +#define OIIO_HARDENING_ASSERT_FAST(condition) \ |
| 146 | + OIIO_HARDENING_ASSERT_FAST_MSG(condition, #condition) |
| 147 | +#define OIIO_HARDENING_ASSERT_EXTENSIVE(condition) \ |
| 148 | + OIIO_HARDENING_ASSERT_EXTENSIVE_MSG(condition, #condition) |
| 149 | +#define OIIO_HARDENING_ASSERT_DEBUG(condition) \ |
| 150 | + OIIO_HARDENING_ASSERT_DEBUG_MSG(condition, #condition) |
| 151 | + |
| 152 | + |
| 153 | +OIIO_NAMESPACE_3_1_BEGIN |
| 154 | +// Internal contract assertion handler |
| 155 | +OIIO_UTIL_API void |
| 156 | +contract_violation_handler(const char* location, const char* function, |
| 157 | + const char* msg = ""); |
| 158 | +OIIO_NAMESPACE_3_1_END |
| 159 | + |
| 160 | +OIIO_NAMESPACE_BEGIN |
| 161 | +#ifndef OIIO_DOXYGEN |
| 162 | +using v3_1::contract_violation_handler; |
| 163 | +#endif |
| 164 | +OIIO_NAMESPACE_END |
| 165 | + |
| 166 | + |
15 | 167 | /// OIIO_ABORT_IF_DEBUG is a call to abort() for debug builds, but does |
16 | 168 | /// nothing for release builds. |
17 | 169 | #ifndef NDEBUG |
|
0 commit comments