Skip to content

get_results() passes 0 as max_size to JSON_read(), so a misread control-channel byte can trigger a multi-GB allocation instead of a clean error #2072

Description

@pau-hedgehog

get_results() passes 0 as max_size to JSON_read(), so a misread control-channel byte can trigger a multi-GB allocation instead of a clean error

In src/iperf_api.c, get_results() reads the peer's final results message off the control connection with no size cap.

    j = JSON_read(test->ctrl_sck, 0);
    if (j == NULL) {
	i_errno = IERECVRESULTS;
        r = -1;

JSON_read() treats max_size == 0 as no limit, so whatever 32-bit value arrives as the length prefix goes straight to calloc without a bounds check.

	hsize = ntohl(nsize);
	if (hsize > 0 && (max_size == 0 || hsize <= max_size)) {
	    /* Allocate a buffer to hold the JSON */
	    strsize = hsize + 1;              /* +1 for trailing NULL */
	    if (strsize) {
	        str = (char *) calloc(sizeof(char), strsize);

We hit this in practice when a server aborted mid results-exchange. SERVER_ERROR is written to the control socket as a single signed byte, 0xFE, followed by two four-byte error codes. A client already blocked inside get_results() at that moment reads that 0xFE byte plus the top three bytes of the following network-order integer, which are zero for the small values that enum holds, as a big-endian length: exactly 0xFE000000, 3.97 GiB. On a small VM that allocation is refused, calloc fails, JSON_read() returns NULL, and the client reports Cannot allocate memory instead of ever reaching the SERVER_ERROR handler in iperf_client_api.c that would have printed the real reason. So this isn't just a missing bound, it actively destroys the server's own error message on abort.

unable to receive results: Cannot allocate memory

The params-read call a few hundred lines earlier in the same file already passes a real bound, MAX_PARAMS_JSON_STRING.

    j = JSON_read(test->ctrl_sck, MAX_PARAMS_JSON_STRING);

get_results() should get the same treatment. Opening a PR with a fix.

Longer writeup of how we found this, including the byte-level decoding: githedgehog/toolbox#43

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions