libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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 */