mhd_check.h (8140B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2026 Christian Grothoff 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library. 17 If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * @file microhttpd/mhd_check.h 22 * @brief macros for MHD_CHECK_(), always-compiled memory-safety invariants 23 * @author Christian Grothoff 24 * 25 * mhd_assert() is compiled out in release builds (NDEBUG), which is the right 26 * thing for the several hundred internal state assertions of this library but 27 * the wrong thing for the handful of them that stand between attacker-supplied 28 * data and a memmove()/memcpy()/array index. For those, an assertion that is 29 * absent in the shipped build is not a safety net at all. 30 * 31 * MHD_CHECK_() is the always-compiled companion for exactly that subset. It 32 * does NOT depend on NDEBUG, _DEBUG or --enable-asserts: the test is one 33 * predictable, well-predicted branch that is present in every build. 34 * 35 * RULES FOR USING IT 36 * ------------------ 37 * 1. Only invariants whose violation would mean memory-unsafe behaviour - 38 * pointer/offset arithmetic, buffer bounds, length subtractions that could 39 * underflow, indices into fixed-size arrays. Ordinary state assertions 40 * stay mhd_assert(). 41 * 2. Check the *precondition*, not the symptom. An assertion that compares 42 * two pointers after the bad one has already been derived is decoration: 43 * it happily passes on a NULL-derived pointer. Assert what has to be true 44 * for the following arithmetic to be defined. See commit 29eaa56b. 45 * 3. A failing check must FAIL THE CONNECTION - close it, or reply 4xx/5xx - 46 * and must not continue and must not panic. MHD_PANIC() aborts the whole 47 * daemon, which turns a bounded per-connection problem into a global 48 * denial of service; that is the opposite of what this is for. 49 * 4. Deliberately no mhd_assert() inside: a check must behave identically in 50 * debug and in release builds, otherwise the debug build stops exercising 51 * the recovery path that the release build relies on. 52 * 53 * WHERE THE MACROS COME FROM 54 * -------------------------- 55 * The connection-level macros below expand to the failure exits that 56 * connection.c already uses - connection_close_error() / 57 * CONNECTION_CLOSE_ERROR() and transmit_error_response_static() - rather than 58 * introducing a parallel mechanism. They are therefore usable only from 59 * connection.c and only after those helpers have been defined; this header 60 * intentionally does not include connection.c's internals, the macros are 61 * expanded at the point of use. 62 * 63 * Translation units that have a `struct MHD_Daemon *` at hand (connection.c, 64 * digestauth.c, postprocessor.c, ...) get a log line through the usual 65 * MHD_DLOG()/HAVE_MESSAGES machinery; they must include "internal.h" before 66 * this header. Translation units without a daemon (memorypool.c, mhd_str.c) 67 * use the MHD_CHECK_RET_() form, which is silent. 68 */ 69 70 #ifndef MHD_CHECK_H 71 #define MHD_CHECK_H 1 72 73 #include "mhd_options.h" 74 75 /** 76 * Branch hint: an invariant violation is by construction the unlikely case. 77 */ 78 #if defined(__GNUC__) || defined(__clang__) 79 # define MHD_CHECK_FAILED_(expr) (__builtin_expect (! (expr), 0)) 80 #else /* ! __GNUC__ */ 81 # define MHD_CHECK_FAILED_(expr) (! (expr)) 82 #endif /* ! __GNUC__ */ 83 84 /** 85 * Report a violated invariant through the daemon's log. 86 * 87 * @param daemon the daemon to log to 88 * @param expr_str the stringified invariant that was violated 89 */ 90 #ifdef HAVE_MESSAGES 91 # define MHD_CHECK_LOG_(daemon,expr_str) \ 92 MHD_DLOG (daemon, \ 93 _ ("Internal invariant violated at %s:%u: (%s). " \ 94 "Failing the connection.\n"), \ 95 __FILE__, \ 96 (unsigned int) __LINE__, \ 97 expr_str) 98 #else /* ! HAVE_MESSAGES */ 99 # define MHD_CHECK_LOG_(daemon,expr_str) ((void) 0) 100 #endif /* ! HAVE_MESSAGES */ 101 102 103 /** 104 * The generic always-compiled invariant check. 105 * 106 * @param daemon the daemon to log to 107 * @param expr the invariant, must evaluate to true 108 * @param fail_stmt a single statement to execute when @a expr is false; it 109 * must transfer control out of the current function 110 * (return, goto or break), because execution must not 111 * continue past a violated memory-safety invariant. 112 * When the failure action needs several statements, spell 113 * the check out instead - "if (MHD_CHECK_FAILED_ (expr)) { 114 * MHD_CHECK_LOG_ (daemon, "expr"); ... }" - because a 115 * braced block passed as a macro argument is re-indented 116 * by contrib/uncrustify.cfg 117 */ 118 #define MHD_CHECK_(daemon,expr,fail_stmt) \ 119 do { \ 120 if (MHD_CHECK_FAILED_ (expr)) \ 121 { \ 122 MHD_CHECK_LOG_ (daemon, #expr); \ 123 fail_stmt; \ 124 } \ 125 } while (0) 126 127 128 /** 129 * Always-compiled invariant check for translation units that have no daemon 130 * pointer available for logging (memorypool.c, mhd_str.c). Silent; the 131 * caller is expected to turn the returned failure value into a log message 132 * and a failed connection. 133 * 134 * @param expr the invariant, must evaluate to true 135 * @param retval the value to return when @a expr is false 136 */ 137 #define MHD_CHECK_RET_(expr,retval) \ 138 do { \ 139 if (MHD_CHECK_FAILED_ (expr)) \ 140 return retval; \ 141 } while (0) 142 143 144 /** 145 * Always-compiled invariant check that closes the connection with an error 146 * and returns @a retval. For use in connection.c only, and only after 147 * connection_close_error() has been defined. 148 * 149 * @param c the connection to fail 150 * @param expr the invariant, must evaluate to true 151 * @param retval the value to return when @a expr is false 152 */ 153 #define MHD_CHECK_CONN_CLOSE_RET_(c,expr,retval) \ 154 MHD_CHECK_ ((c)->daemon, \ 155 expr, \ 156 { \ 157 connection_close_error ((c), \ 158 NULL); \ 159 return retval; \ 160 }) 161 162 163 /** 164 * Always-compiled invariant check that closes the connection with an error 165 * and returns from a void function. For use in connection.c only, and only 166 * after connection_close_error() has been defined. 167 * 168 * @param c the connection to fail 169 * @param expr the invariant, must evaluate to true 170 */ 171 #define MHD_CHECK_CONN_CLOSE_RET_VOID_(c,expr) \ 172 MHD_CHECK_ ((c)->daemon, \ 173 expr, \ 174 { \ 175 connection_close_error ((c), \ 176 NULL); \ 177 return; \ 178 }) 179 180 181 /** 182 * Always-compiled invariant check that queues a static error reply for the 183 * connection and returns @a retval. For use in connection.c only, and only 184 * after transmit_error_response_static() has been defined. 185 * 186 * @param c the connection to fail 187 * @param expr the invariant, must evaluate to true 188 * @param code the HTTP status code to reply with 189 * @param msg the static message to reply with 190 * @param retval the value to return when @a expr is false 191 */ 192 #define MHD_CHECK_CONN_REPLY_RET_(c,expr,code,msg,retval) \ 193 MHD_CHECK_ ((c)->daemon, \ 194 expr, \ 195 { \ 196 transmit_error_response_static ((c), \ 197 (code), \ 198 msg); \ 199 return retval; \ 200 }) 201 202 #endif /* ! MHD_CHECK_H */