mhd_zzuf_common.h (15888B)
1 /* 2 This file is part of GNU libmicrohttpd 3 Copyright (C) 2026 Christian Grothoff 4 5 GNU libmicrohttpd 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 GNU libmicrohttpd. 17 If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /** 21 * @file testzzuf/mhd_zzuf_common.h 22 * @brief Shared support code for the fuzzing tests in this directory 23 * @author Christian Grothoff 24 * 25 * This header must be included *after* "platform.h" and <microhttpd.h>. 26 * 27 * The helpers here provide: 28 * - the common command line handling ("--dry-run", "--with-socat"), 29 * - the matrix of "hostile" daemon option profiles (small memory pools, 30 * small connection limits, short timeouts, strictness levels), 31 * - a generic driver that runs a client callback against every polling 32 * mode supported by the current MHD build, 33 * - a tiny raw socket HTTP client, needed for all requests that libcurl 34 * is unable to generate (chunk extensions, pipelining, header-less 35 * requests, hand-crafted "Authorization:" headers). 36 */ 37 38 #ifndef MHD_ZZUF_COMMON_H 39 #define MHD_ZZUF_COMMON_H 1 40 41 #include <stddef.h> 42 #include <stdint.h> 43 #include <curl/curl.h> 44 #include <microhttpd.h> 45 46 #ifndef MHD_STATICSTR_LEN_ 47 /** 48 * Determine length of static string / macro strings at compile time. 49 */ 50 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 51 #endif /* ! MHD_STATICSTR_LEN_ */ 52 53 /** 54 * The IP address MHD is listening on. 55 * Must match 'mhd_listen_ip' in zzuf_test_runner.sh, as this is the only 56 * address for which zzuf is instructed to fuzz the network traffic. 57 */ 58 #define ZZUF_MHD_LISTEN_IP "127.0.0.1" 59 60 /** 61 * The IP address used by the test clients as the source address. 62 * Must be different from #ZZUF_MHD_LISTEN_IP so that the traffic 63 * generated by the clients themselves is not fuzzed. 64 */ 65 #define ZZUF_CLIENT_BIND_IP "127.0.0.101" 66 67 /** 68 * The IP address socat is listening on. 69 * Must match 'socat_listen_ip' in zzuf_socat_test_runner.sh. 70 */ 71 #define ZZUF_SOCAT_IP "127.0.0.121" 72 73 /** 74 * The port socat is listening on. 75 * Must match 'socat_listen_port' in zzuf_socat_test_runner.sh. 76 */ 77 #define ZZUF_SOCAT_PORT 10121 78 79 /** 80 * The port MHD is listening on when the port cannot be auto-detected. 81 * Must match 'mhd_listen_port' in zzuf_socat_test_runner.sh. 82 */ 83 #define ZZUF_BASE_PORT 4010 84 85 /** 86 * The default MHD connection timeout (in seconds) used by the tests. 87 */ 88 #define ZZUF_MHD_TIMEOUT 2 89 90 /** 91 * The client-side timeout (in seconds). 92 */ 93 #define ZZUF_CLIENT_TIMEOUT 5 94 95 /** 96 * A larger loop count runs more random tests, but takes longer. 97 * Can be redefined by CPPFLAGS=-DZZUF_LOOP_COUNT=123 98 */ 99 #ifndef ZZUF_LOOP_COUNT 100 #ifndef _MHD_VHEAVY_TESTS 101 #define ZZUF_LOOP_COUNT 10 102 #else /* _MHD_VHEAVY_TESTS */ 103 #define ZZUF_LOOP_COUNT 200 104 #endif /* _MHD_VHEAVY_TESTS */ 105 #endif /* ! ZZUF_LOOP_COUNT */ 106 107 108 /* *** Global test parameters, initialised by zzuf_parse_common_args() *** */ 109 110 /** 111 * Non-zero if the traffic is relayed (and fuzzed) by socat. 112 */ 113 extern int zzuf_run_with_socat; 114 115 /** 116 * Non-zero if this is a "dry run", i.e. no real client requests are made. 117 */ 118 extern int zzuf_dry_run; 119 120 /** 121 * Non-zero if HTTP/1.1 should be used by the clients, zero for HTTP/1.0. 122 */ 123 extern int zzuf_oneone; 124 125 /** 126 * Non-zero if MHD should close the connection after each reply. 127 */ 128 extern int zzuf_use_close; 129 130 /** 131 * Parse the common command line parameters and derive the test flavour 132 * from the program name. Also makes SIGPIPE non-fatal (if applicable). 133 * 134 * @param argc as given to main() 135 * @param argv as given to main() 136 */ 137 void 138 zzuf_parse_common_args (int argc, char *const *argv); 139 140 141 /** 142 * Check whether the name of the running program contains @a marker. 143 * zzuf_parse_common_args() must have been called before. 144 * 145 * @param marker the marker to look for 146 * @return non-zero if the marker is present, zero otherwise 147 */ 148 int 149 zzuf_name_has (const char *marker); 150 151 152 /** 153 * The number of client iterations to perform, zero for "dry runs". 154 */ 155 unsigned int 156 zzuf_loop_count (void); 157 158 159 /* *** The matrix of "hostile" daemon option profiles *** */ 160 161 /** 162 * One set of daemon options used to widen the tested option matrix. 163 */ 164 struct zzuf_opt_profile 165 { 166 /** 167 * Human readable name of the profile, used for logging. 168 */ 169 const char *name; 170 171 /** 172 * The value for #MHD_OPTION_CONNECTION_MEMORY_LIMIT. 173 * Zero means "keep the MHD default". 174 * Values below MHD_BUF_INC_SIZE (1500) enable the read buffer 175 * "shift back" code path in MHD. 176 */ 177 size_t mem_limit; 178 179 /** 180 * The value for #MHD_OPTION_CONNECTION_LIMIT. 181 * Zero means "keep the MHD default". 182 */ 183 unsigned int conn_limit; 184 185 /** 186 * The value for #MHD_OPTION_CONNECTION_TIMEOUT (seconds). 187 * Never zero, as the tests rely on MHD closing idle connections. 188 */ 189 unsigned int timeout; 190 191 /** 192 * The value for #MHD_OPTION_CLIENT_DISCIPLINE_LVL (or, when 193 * @a use_legacy_strict is set, for #MHD_OPTION_STRICT_FOR_CLIENT). 194 */ 195 int discipline_lvl; 196 197 /** 198 * If non-zero, the (older) #MHD_OPTION_STRICT_FOR_CLIENT option is used 199 * instead of #MHD_OPTION_CLIENT_DISCIPLINE_LVL, so that the legacy 200 * option translation code is exercised as well. 201 */ 202 int use_legacy_strict; 203 }; 204 205 206 /** 207 * The number of available option profiles. 208 */ 209 unsigned int 210 zzuf_num_opt_profiles (void); 211 212 213 /** 214 * Get the option profile number @a idx (wrapping around). 215 * 216 * Profile zero is always the "plain" profile, i.e. the set of options 217 * that was used by the fuzzing tests before the option matrix was added. 218 */ 219 const struct zzuf_opt_profile * 220 zzuf_opt_profile (unsigned int idx); 221 222 223 /** 224 * The profile that is in use by the currently running daemon. 225 * Never NULL after the first daemon has been started. 226 */ 227 extern const struct zzuf_opt_profile *zzuf_active_profile; 228 229 230 /* *** Daemon handling *** */ 231 232 /** 233 * Select the port to be used by the daemon. 234 * 235 * @param offset the per-test offset added to #ZZUF_BASE_PORT if the port 236 * cannot be auto-detected 237 * @return the port to be given to MHD_start_daemon(), may be zero 238 */ 239 uint16_t 240 zzuf_pick_port (uint16_t offset); 241 242 243 /** 244 * Start a daemon for the test. 245 * 246 * @param daemon_flags the flags for MHD_start_daemon() 247 * @param[in,out] pport the port to use, updated with the real port 248 * @param prof the option profile to apply, never NULL 249 * @param mem_limit_override if non-zero, this value is used for 250 * #MHD_OPTION_CONNECTION_MEMORY_LIMIT instead of 251 * the value from @a prof 252 * @param ahc the access handler callback 253 * @param ahc_cls the closure for @a ahc 254 * @param rcc the request completed callback, may be NULL 255 * @param rcc_cls the closure for @a rcc 256 * @param extra_opts additional options, terminated by #MHD_OPTION_END; 257 * may be NULL 258 * @return the daemon, or NULL on error 259 */ 260 struct MHD_Daemon * 261 zzuf_start_daemon (unsigned int daemon_flags, 262 uint16_t *pport, 263 const struct zzuf_opt_profile *prof, 264 size_t mem_limit_override, 265 MHD_AccessHandlerCallback ahc, 266 void *ahc_cls, 267 MHD_RequestCompletedCallback rcc, 268 void *rcc_cls, 269 const struct MHD_OptionItem *extra_opts); 270 271 272 /** 273 * The client callback invoked by zzuf_run_polling_modes() once per daemon. 274 * 275 * @param d_extern the daemon that must be driven by MHD_run() while the 276 * client is waiting; NULL if the daemon uses an internal 277 * polling thread 278 * @param port the port the daemon is listening on 279 * @param cls the closure 280 * @return zero on success, 77 to skip the test, 99 for an external 281 * (non-MHD) error, any other non-zero value for a test failure 282 */ 283 typedef unsigned int 284 (*zzuf_client_func)(struct MHD_Daemon *d_extern, 285 uint16_t port, 286 void *cls); 287 288 289 /** 290 * Parameters for zzuf_run_polling_modes(). 291 */ 292 struct zzuf_run_params 293 { 294 /** 295 * The port to use, updated in place. 296 */ 297 uint16_t port; 298 299 /** 300 * The access handler callback. 301 */ 302 MHD_AccessHandlerCallback ahc; 303 304 /** 305 * The closure for @e ahc. 306 */ 307 void *ahc_cls; 308 309 /** 310 * The request completed callback, may be NULL. 311 */ 312 MHD_RequestCompletedCallback rcc; 313 314 /** 315 * The closure for @e rcc. 316 */ 317 void *rcc_cls; 318 319 /** 320 * If non-zero, this value is always used for the connection memory 321 * limit, overriding the value from the option profile. 322 */ 323 size_t mem_limit_override; 324 325 /** 326 * If non-zero, a different option profile is used for every started 327 * daemon, sweeping the whole option matrix during a single test run. 328 */ 329 int sweep_profiles; 330 331 /** 332 * The number of the first option profile to use when @e sweep_profiles 333 * is set. Use 1 to skip the "plain" profile, i.e. to make sure that 334 * every daemon of the test uses a small connection memory pool. 335 */ 336 unsigned int profile_start; 337 338 /** 339 * The client callback. 340 */ 341 zzuf_client_func client; 342 343 /** 344 * The closure for @e client. 345 */ 346 void *client_cls; 347 348 /** 349 * Additional daemon options, terminated by #MHD_OPTION_END. 350 * May be NULL. 351 */ 352 const struct MHD_OptionItem *extra_opts; 353 }; 354 355 356 /** 357 * Run the client callback against a daemon in every polling mode 358 * supported by the current MHD build. 359 * 360 * @param[in,out] p the parameters 361 * @return zero if all checks succeeded, 77 to skip, 99 for an external 362 * error, any other non-zero value on failure 363 */ 364 unsigned int 365 zzuf_run_polling_modes (struct zzuf_run_params *p); 366 367 368 /** 369 * Check whether the test can be run at all with the current build and 370 * the current invocation mode. 371 * 372 * @return zero if the test may run, 77 if the test must be skipped 373 */ 374 unsigned int 375 zzuf_check_runnable (void); 376 377 378 /* *** The raw socket client *** */ 379 380 /** 381 * One piece of a raw request. Every piece is written with a separate 382 * send() call, so the pieces typically end up in separate TCP segments. 383 * This is used deliberately to feed MHD with partial header, chunk-size 384 * and chunk-extension lines. 385 */ 386 struct zzuf_raw_part 387 { 388 /** 389 * The data to send, not zero-terminated. 390 */ 391 const char *data; 392 393 /** 394 * The number of bytes to send. 395 */ 396 size_t size; 397 }; 398 399 /** 400 * The raw exchange has been performed (whatever the result was). 401 */ 402 #define ZZUF_RAW_OK 0 403 404 /** 405 * The raw client could not be set up. This is an external error and 406 * must not be reported as a test failure. 407 */ 408 #define ZZUF_RAW_SETUP_FAILED 1 409 410 411 /** 412 * Connect to the daemon (via socat, if used), send @a parts and read the 413 * reply until the peer closes the connection or the timeout expires. 414 * 415 * The reply is discarded: under fuzzing, any reply (including no reply 416 * at all) is a valid outcome. Only crashes and hangs are failures. 417 * 418 * @param d_extern if not NULL, MHD_run() is called while waiting 419 * @param port the port MHD is listening on (ignored if socat is used) 420 * @param parts the pieces of the request 421 * @param num_parts the number of @a parts 422 * @param timeout_sec the maximum number of seconds to spend 423 * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED 424 */ 425 int 426 zzuf_raw_exchange (struct MHD_Daemon *d_extern, 427 uint16_t port, 428 const struct zzuf_raw_part *parts, 429 size_t num_parts, 430 unsigned int timeout_sec); 431 432 433 /** 434 * The same as zzuf_raw_exchange(), but with two extra abilities. 435 * 436 * With @a bypass_relay set, the client connects to MHD directly instead of 437 * going through socat. As zzuf only fuzzes the socat process, such an 438 * exchange is *not* fuzzed at all. This makes it possible to run a small 439 * deterministic self-check of the parser inside the very same test binary. 440 * Note that this only works in the socat mode: when zzuf runs the test 441 * binary itself, all traffic accepted by MHD is fuzzed and there is no 442 * clean channel to MHD. 443 * 444 * @param d_extern if not NULL, MHD_run() is called while waiting 445 * @param port the port MHD is listening on 446 * @param bypass_relay if non-zero, connect to MHD directly 447 * @param parts the pieces of the request 448 * @param num_parts the number of @a parts 449 * @param timeout_sec the maximum number of seconds to spend 450 * @param[out] resp_buf the buffer for the reply, may be NULL 451 * @param resp_buf_size the size of @a resp_buf 452 * @param[out] resp_len set to the number of bytes stored in @a resp_buf, 453 * may be NULL 454 * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED 455 */ 456 int 457 zzuf_raw_exchange2 (struct MHD_Daemon *d_extern, 458 uint16_t port, 459 int bypass_relay, 460 const struct zzuf_raw_part *parts, 461 size_t num_parts, 462 unsigned int timeout_sec, 463 char *resp_buf, 464 size_t resp_buf_size, 465 size_t *resp_len); 466 467 468 /** 469 * Convenience wrapper around zzuf_raw_exchange() for a request that is 470 * sent as a single zero-terminated string. 471 * 472 * @param d_extern if not NULL, MHD_run() is called while waiting 473 * @param port the port MHD is listening on (ignored if socat is used) 474 * @param request the zero-terminated request 475 * @return #ZZUF_RAW_OK or #ZZUF_RAW_SETUP_FAILED 476 */ 477 int 478 zzuf_raw_request (struct MHD_Daemon *d_extern, 479 uint16_t port, 480 const char *request); 481 482 483 /* *** libcurl helpers *** */ 484 485 /** 486 * The sink for the data downloaded by libcurl. 487 */ 488 struct zzuf_curl_sink 489 { 490 /** 491 * The number of bytes received so far. 492 */ 493 size_t dn_pos; 494 495 /** 496 * The scratch buffer. 497 */ 498 char buf[2048]; 499 }; 500 501 502 /** 503 * Create an "easy" handle with the settings that are common to all 504 * fuzzing tests in this directory. 505 * 506 * @param port the port MHD is listening on (ignored if socat is used) 507 * @param uri_tail the path (and query string) of the request-target, 508 * must start with a slash 509 * @param sink the download sink, must stay valid while the handle is used 510 * @return the handle, or NULL on failure 511 */ 512 CURL * 513 zzuf_curl_setup (uint16_t port, 514 const char *uri_tail, 515 struct zzuf_curl_sink *sink); 516 517 518 /** 519 * The state needed to run "easy" handles against a daemon that may or 520 * may not have an internal polling thread. 521 */ 522 struct zzuf_curl_driver 523 { 524 /** 525 * The daemon to drive with MHD_run(), NULL for daemons with an internal 526 * polling thread. 527 */ 528 struct MHD_Daemon *d_extern; 529 530 /** 531 * The "multi" handle, only used when @e d_extern is not NULL. 532 */ 533 CURLM *multi; 534 }; 535 536 537 /** 538 * Initialise the driver. 539 * 540 * @param[out] drv the driver to initialise 541 * @param d_extern the daemon to drive, or NULL 542 * @return non-zero on success, zero on failure 543 */ 544 int 545 zzuf_curl_driver_init (struct zzuf_curl_driver *drv, 546 struct MHD_Daemon *d_extern); 547 548 549 /** 550 * Release the resources of the driver. 551 * 552 * @param drv the driver to clean up 553 */ 554 void 555 zzuf_curl_driver_deinit (struct zzuf_curl_driver *drv); 556 557 558 /** 559 * Run a single transfer to completion (or to the timeout). 560 * 561 * The result of the transfer is deliberately ignored: with the traffic 562 * being fuzzed, libcurl may fail in arbitrary ways. 563 * 564 * @param drv the driver 565 * @param c the "easy" handle to run 566 */ 567 void 568 zzuf_curl_driver_perform (struct zzuf_curl_driver *drv, 569 CURL *c); 570 571 572 #endif /* MHD_ZZUF_COMMON_H */