static int action_getconfig(struct mansession *s, const struct message *m)
{
	struct ast_config *cfg;
	const char *fn = astman_get_header(m, "Filename");
	const char *category = astman_get_header(m, "Category");
	const char *filter = astman_get_header(m, "Filter");
	const char *category_name;
	int catcount = 0;
	int lineno = 0;
	struct ast_category *cur_category = NULL;
	struct ast_variable *v;
	struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };

	if (ast_strlen_zero(fn)) {
		astman_send_error(s, m, "Filename not specified");
		return 0;
	}

	if (restrictedFile(fn)) {
		astman_send_error(s, m, "File requires escalated priveledges");
		return 0;
	}

	cfg = ast_config_load2(fn, "manager", config_flags);
	if (cfg == CONFIG_STATUS_FILEMISSING) {
		astman_send_error(s, m, "Config file not found");
		return 0;
	} else if (cfg == CONFIG_STATUS_FILEINVALID) {
		astman_send_error(s, m, "Config file has invalid format");
		return 0;
	}

	astman_start_ack(s, m);
	while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
		struct ast_str *templates;

		category_name = ast_category_get_name(cur_category);
		lineno = 0;
		astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);

		if (ast_category_is_template(cur_category)) {
			astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
		}

		if ((templates = ast_category_get_templates(cur_category))
			&& ast_str_strlen(templates) > 0) {
			astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
			ast_free(templates);
		}

		for (v = ast_category_first(cur_category); v; v = v->next) {
			astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
		}

		catcount++;
	}

	if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
		astman_append(s, "No categories found\r\n");
	}

	ast_config_destroy(cfg);
	astman_append(s, "\r\n");

	return 0;
}

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.


static int restrictedFile(const char *filename)
{
	if (!live_dangerously && !strncasecmp(filename, "/", 1) &&
		 strncasecmp(filename, ast_config_AST_CONFIG_DIR, strlen(ast_config_AST_CONFIG_DIR))) {
		return 1;
	}
	return 0;
}


OE_INLINE void _handle_oret(
    oe_sgx_td_t* td,
    uint16_t func,
    uint16_t result,
    uint64_t arg)
{
    oe_callsite_t* callsite = td->callsites;

    if (!callsite)
        return;

    td->oret_func = func;
    td->oret_result = result;
    td->oret_arg = arg;

    /* Restore the FXSTATE and flags */
    asm volatile("pushq %[rflags] \n\t" // Restore flags.
                 "popfq \n\t"
                 "fldcw %[fcw] \n\t"     // Restore x87 control word
                 "ldmxcsr %[mxcsr] \n\t" // Restore MXCSR
                 : [mxcsr] "=m"(callsite->mxcsr),
                   [fcw] "=m"(callsite->fcw),
                   [rflags] "=m"(callsite->rflags)
                 :
                 : "cc");

    oe_longjmp(&callsite->jmpbuf, 1);
}

The product does not initialize or incorrectly initializes a resource, which might leave the resource in an unexpected state when it is accessed or used.


void _reset_fxsave_state()
{
    /* Initialize the FXSAVE state values to Linux x86-64 ABI defined values:
     * FCW = 0x037F, MXCSR = 0x1F80, MXCSR mask = 0xFFFF */
    static OE_ALIGNED(OE_FXSAVE_ALIGNMENT) const uint64_t
        _initial_fxstate[OE_FXSAVE_AREA_SIZE / sizeof(uint64_t)] = {
            0x037F, 0, 0, 0xFFFF00001F80,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
            0,      0, 0, 0,
        };

    asm volatile("fxrstor %[fx_state] \n\t"
                 :
                 : [fx_state] "m"(_initial_fxstate)
                 :);
}


JSON_read(int fd)
{
    uint32_t hsize, nsize;
    char *str;
    cJSON *json = NULL;
    int rc;

    /*
     * Read a four-byte integer, which is the length of the JSON to follow.
     * Then read the JSON into a buffer and parse it.  Return a parsed JSON
     * structure, NULL if there was an error.
     */
    if (Nread(fd, (char*) &nsize, sizeof(nsize), Ptcp) >= 0) {
	hsize = ntohl(nsize);
	/* Allocate a buffer to hold the JSON */
	str = (char *) calloc(sizeof(char), hsize+1);	/* +1 for trailing null */
	if (str != NULL) {
	    rc = Nread(fd, str, hsize, Ptcp);
	    if (rc >= 0) {
		/*
		 * We should be reading in the number of bytes corresponding to the
		 * length in that 4-byte integer.  If we don't the socket might have
		 * prematurely closed.  Only do the JSON parsing if we got the
		 * correct number of bytes.
		 */
		if (rc == hsize) {
		    json = cJSON_Parse(str);
		}
		else {
		    printf("WARNING:  Size of data read does not correspond to offered length\n");
		}
	    }
	}
	free(str);
    }
    return json;
}

The product performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can introduce other weaknesses when the calculation is used for resource management or execution control.


static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalSize)
{
	UINT32 left = originalSize;

	while (left > 4)
	{
		const BYTE value = *in++;
		UINT32 len = 0;

		if (left == 5)
		{
			if (outSize < 1)
				return FALSE;

			outSize--;
			*out++ = value;
			left--;
		}
		else if (value == *in)
		{
			in++;

			if (*in < 0xFF)
			{
				len = (UINT32)*in++;
				len += 2;
			}
			else
			{
				in++;
				len = ((UINT32)(*in++));
				len |= ((UINT32)(*in++)) << 8U;
				len |= ((UINT32)(*in++)) << 16U;
				len |= ((UINT32)(*in++)) << 24U;
			}

			if (outSize < len)
				return FALSE;

			outSize -= len;
			FillMemory(out, len, value);
			out += len;
			left -= len;
		}
		else
		{
			if (outSize < 1)
				return FALSE;

			outSize--;
			*out++ = value;
			left--;
		}
	}

	if ((outSize < 4) || (left < 4))
		return FALSE;

	memcpy(out, in, 4);
	return TRUE;
}

The product reads data past the end, or before the beginning, of the intended buffer.


static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
{
	if (!context)
		return FALSE;

	BYTE* rle = context->Planes;
	WINPR_ASSERT(rle);

	for (size_t i = 0; i < 4; i++)
	{
		const UINT32 originalSize = context->OrgByteCount[i];
		const UINT32 planeSize = context->PlaneByteCount[i];

		if (planeSize == 0)
		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

			FillMemory(context->priv->PlaneBuffers[i], originalSize, 0xFF);
		}
		else if (planeSize < originalSize)
		{
			if (!nsc_rle_decode(rle, context->priv->PlaneBuffers[i],
			                    context->priv->PlaneBuffersLength, originalSize))
				return FALSE;
		}
		else
		{
			if (context->priv->PlaneBuffersLength < originalSize)
				return FALSE;

			CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize);
		}

		rle += planeSize;
	}

	return TRUE;
}


_blackbox_vlogger(int32_t target,
		  struct qb_log_callsite *cs, struct timespec *timestamp, va_list ap)
{
	size_t max_size;
	size_t actual_size;
	uint32_t fn_size;
	char *chunk;
	char *msg_len_pt;
	uint32_t msg_len;
	struct qb_log_target *t = qb_log_target_get(target);

	if (t->instance == NULL) {
		return;
	}

	fn_size = strlen(cs->function) + 1;

	actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(struct timespec);
	max_size = actual_size + t->max_line_length;

	chunk = qb_rb_chunk_alloc(t->instance, max_size);

	if (chunk == NULL) {
		/* something bad has happened. abort blackbox logging */
		qb_util_perror(LOG_ERR, "Blackbox allocation error, aborting blackbox log %s", t->filename);
		qb_rb_close(qb_rb_lastref_and_ret(
			(struct qb_ringbuffer_s **) &t->instance
		));
		return;
	}

	/* line number */
	memcpy(chunk, &cs->lineno, sizeof(uint32_t));
	chunk += sizeof(uint32_t);

	/* tags */
	memcpy(chunk, &cs->tags, sizeof(uint32_t));
	chunk += sizeof(uint32_t);

	/* log level/priority */
	memcpy(chunk, &cs->priority, sizeof(uint8_t));
	chunk += sizeof(uint8_t);

	/* function name */
	memcpy(chunk, &fn_size, sizeof(uint32_t));
	chunk += sizeof(uint32_t);
	memcpy(chunk, cs->function, fn_size);
	chunk += fn_size;

	/* timestamp */
	memcpy(chunk, timestamp, sizeof(struct timespec));
	chunk += sizeof(struct timespec);

	/* log message length */
	msg_len_pt = chunk;
	chunk += sizeof(uint32_t);

	/* log message */
	msg_len = qb_vsnprintf_serialize(chunk, max_size, cs->format, ap);
	if (msg_len >= max_size) {
	    chunk = msg_len_pt + sizeof(uint32_t); /* Reset */

	    /* Leave this at QB_LOG_MAX_LEN so as not to overflow the blackbox */
	    msg_len = qb_vsnprintf_serialize(chunk, QB_LOG_MAX_LEN,
		"Log message too long to be stored in the blackbox.  "\
		"Maximum is QB_LOG_MAX_LEN" , ap);
	}

	actual_size += msg_len;

	/* now that we know the length, write it
	 */
	memcpy(msg_len_pt, &msg_len, sizeof(uint32_t));

	(void)qb_rb_chunk_commit(t->instance, actual_size);
}

Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')

The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.


START_TEST(test_log_long_msg)
{
	int lpc;
	int rc;
	int i, max = 1000;
	char *buffer = calloc(1, max);

	qb_log_init("test", LOG_USER, LOG_DEBUG);
	rc = qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
	ck_assert_int_eq(rc, 0);
	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 1024);
	ck_assert_int_eq(rc, 0);
	rc = qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
	ck_assert_int_eq(rc, 0);
	rc = qb_log_filter_ctl(QB_LOG_BLACKBOX, QB_LOG_FILTER_ADD,
			  QB_LOG_FILTER_FILE, "*", LOG_TRACE);
	ck_assert_int_eq(rc, 0);

	for (lpc = 500; lpc < max; lpc++) {
		lpc++;
		for(i = 0; i < max; i++) {
			buffer[i] = 'a' + (i % 10);
		}
		buffer[lpc%600] = 0;
		qb_log(LOG_INFO, "Message %d %d - %s", lpc, lpc%600, buffer);
	}

        qb_log_blackbox_write_to_file("blackbox.dump");
        qb_log_blackbox_print_from_file("blackbox.dump");
	unlink("blackbox.dump");
	qb_log_fini();
}


	consume_count(type)
		const char **type;
{
	int count = 0;

	if (!isdigit((unsigned char)**type))
		return -1;

	while (isdigit((unsigned char)**type)) {
		count *= 10;

		/* Check for overflow.
		   We assume that count is represented using two's-complement;
		   no power of two is divisible by ten, so if an overflow occurs
		   when multiplying by ten, the result will not be a multiple of
		   ten.  */
		if ((count % 10) != 0) {
			while (isdigit((unsigned char)**type))
				(*type)++;
			return -1;
		}

		count += **type - '0';
		(*type)++;
	}

	return (count);
}


int h1_parse_cont_len_header(struct h1m *h1m, struct ist *value)
{
	char *e, *n;
	long long cl;
	int not_first = !!(h1m->flags & H1_MF_CLEN);
	struct ist word;

	word.ptr = value->ptr - 1; // -1 for next loop's pre-increment
	e = value->ptr + value->len;

	while (++word.ptr < e) {
		/* skip leading delimiter and blanks */
		if (unlikely(HTTP_IS_LWS(*word.ptr)))
			continue;

		/* digits only now */
		for (cl = 0, n = word.ptr; n < e; n++) {
			unsigned int c = *n - '0';
			if (unlikely(c > 9)) {
				/* non-digit */
				if (unlikely(n == word.ptr)) // spaces only
					goto fail;
				break;
			}
			if (unlikely(cl > ULLONG_MAX / 10ULL))
				goto fail; /* multiply overflow */
			cl = cl * 10ULL;
			if (unlikely(cl + c < cl))
				goto fail; /* addition overflow */
			cl = cl + c;
		}

		/* keep a copy of the exact cleaned value */
		word.len = n - word.ptr;

		/* skip trailing LWS till next comma or EOL */
		for (; n < e; n++) {
			if (!HTTP_IS_LWS(*n)) {
				if (unlikely(*n != ','))
					goto fail;
				break;
			}
		}

		/* if duplicate, must be equal */
		if (h1m->flags & H1_MF_CLEN && cl != h1m->body_len)
			goto fail;

		/* OK, store this result as the one to be indexed */
		h1m->flags |= H1_MF_CLEN;
		h1m->curr_len = h1m->body_len = cl;
		*value = word;
		word.ptr = n;
	}
	/* here we've reached the end with a single value or a series of
	 * identical values, all matching previous series if any. The last
	 * parsed value was sent back into <value>. We just have to decide
	 * if this occurrence has to be indexed (it's the first one) or
	 * silently skipped (it's not the first one)
	 */
	return !not_first;
 fail:
	return -1;
}

Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')

The product acts as an intermediary HTTP agent
         (such as a proxy or firewall) in the data flow between two
         entities such as a client and server, but it does not
         interpret malformed HTTP requests or responses in ways that
         are consistent with how the messages will be processed by
         those entities that are at the ultimate destination.


void client_reset(t_client *client)
{
	char *hash;
	char *msg;
	char *cidinfo;

	debug(LOG_DEBUG, "Resetting client [%s]", client->mac);
	// Reset traffic counters
	client->counters.incoming = 0;
	client->counters.outgoing = 0;
	client->counters.last_updated = time(NULL);

	// Reset session time
	client->session_start = 0;
	client->session_end = 0;

	// Reset token and hid
	hash = safe_calloc(STATUS_BUF);
	client->token = safe_calloc(STATUS_BUF);
	safe_snprintf(client->token, STATUS_BUF, "%04hx%04hx", rand16(), rand16());
	hash_str(hash, STATUS_BUF, client->token);
	client->hid = safe_strdup(hash);
	free(hash);

	// Reset custom and client_type
	client->custom = safe_calloc(MID_BUF);
	client->client_type = safe_calloc(STATUS_BUF);

	//Reset cid and remove cidfile using rmcid
	if (client->cid) {

		if (strlen(client->cid) > 0) {
			msg = safe_calloc(SMALL_BUF);
			cidinfo = safe_calloc(MID_BUF);
			safe_snprintf(cidinfo, MID_BUF, "cid=\"%s\"", client->cid);
			write_client_info(msg, SMALL_BUF, "rmcid", client->cid, cidinfo);
			free(msg);
			free(cidinfo);
		}

		client->cid = safe_calloc(SMALL_BUF);
	}

}

Missing Release of Memory after Effective Lifetime

The product does not sufficiently track and release allocated memory after it has been used, which slowly consumes remaining memory.


static int redirect_to_splashpage(struct MHD_Connection *connection, t_client *client, const char *host, const char *url)
{
	char *originurl_raw;
	char *originurl;
	char *query;
	int ret = 0;
	const char *separator = "&";
	char *querystr;

	query = safe_calloc(QUERYMAXLEN);

	if (!query) {
		ret = send_error(connection, 503);
		free(query);
		return ret;
	}

	querystr = safe_calloc(QUERYMAXLEN);

	if (!querystr) {
		ret = send_error(connection, 503);
		free(querystr);
		return ret;
	}

	originurl_raw = safe_calloc(MID_BUF);

	if (!originurl_raw) {
		ret = send_error(connection, 503);
		free(originurl_raw);
		return ret;
	}

	originurl = safe_calloc(CUSTOM_ENC);

	if (!originurl) {
		ret = send_error(connection, 503);
		free(originurl);
		return ret;
	}

	get_query(connection, &query, separator);

	if (!query) {
		debug(LOG_DEBUG, "Unable to get query string - error 503");
		free(query);
		// probably no mem
		return send_error(connection, 503);
	}

	debug(LOG_DEBUG, "Query string is [ %s ]", query);

	safe_asprintf(&originurl_raw, "http://%s%s%s", host, url, query);
	uh_urlencode(originurl, CUSTOM_ENC, originurl_raw, strlen(originurl_raw));

	if (strcmp(url, "/login") == 0) {
		client->cpi_query = safe_strdup(originurl);
		debug(LOG_DEBUG, "RFC8910 request: %s", client->cpi_query);
	} else {
		client->cpi_query = "none";
	}

	debug(LOG_DEBUG, "originurl_raw: %s", originurl_raw);
	debug(LOG_DEBUG, "originurl: %s", originurl);

	querystr=construct_querystring(connection, client, originurl, querystr);
	ret = encode_and_redirect_to_splashpage(connection, client, originurl, querystr);
	free(originurl_raw);
	free(originurl);
	free(query);
	free(querystr);
	return ret;
}


void crypto_bignum_free(struct bignum *a)
{
	if (a)
		panic();
}

The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.


static TEE_Result do_allocate_keypair(struct dh_keypair *key, size_t size_bits)
{
	DH_TRACE("Allocate Keypair of %zu bits", size_bits);

	/* Initialize the key fields to NULL */
	memset(key, 0, sizeof(*key));

	/* Allocate Generator Scalar */
	key->g = crypto_bignum_allocate(size_bits);
	if (!key->g)
		goto err;

	/* Allocate Prime Number Modulus */
	key->p = crypto_bignum_allocate(size_bits);
	if (!key->p)
		goto err;

	/* Allocate Private key X */
	key->x = crypto_bignum_allocate(size_bits);
	if (!key->x)
		goto err;

	/* Allocate Public Key Y */
	key->y = crypto_bignum_allocate(size_bits);
	if (!key->y)
		goto err;

	/* Allocate Subprime even if not used */
	key->q = crypto_bignum_allocate(size_bits);
	if (!key->q)
		goto err;

	return TEE_SUCCESS;

err:
	DH_TRACE("Allocation error");

	crypto_bignum_free(key->g);
	crypto_bignum_free(key->p);
	crypto_bignum_free(key->x);
	crypto_bignum_free(key->y);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static TEE_Result do_allocate_publickey(struct dsa_public_key *key,
					size_t l_bits, size_t n_bits)
{
	DSA_TRACE("DSA Allocate Public of L=%zu bits and N=%zu bits", l_bits,
		  n_bits);

	/* Initialize the key fields to NULL */
	memset(key, 0, sizeof(*key));

	/* Allocate Generator Scalar */
	key->g = crypto_bignum_allocate(l_bits);
	if (!key->g)
		goto err;

	/* Allocate Prime Number Modulus */
	key->p = crypto_bignum_allocate(l_bits);
	if (!key->p)
		goto err;

	/* Allocate Prime Number Modulus */
	key->q = crypto_bignum_allocate(n_bits);
	if (!key->q)
		goto err;

	/* Allocate Public Key Y */
	key->y = crypto_bignum_allocate(l_bits);
	if (!key->y)
		goto err;

	return TEE_SUCCESS;

err:
	DSA_TRACE("Allocation error");

	crypto_bignum_free(key->g);
	crypto_bignum_free(key->p);
	crypto_bignum_free(key->q);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static TEE_Result do_allocate_keypair(struct dsa_keypair *key, size_t l_bits,
				      size_t n_bits)
{
	DSA_TRACE("DSA allocate Keypair of L=%zu bits and N=%zu bits", l_bits,
		  n_bits);

	/* Initialize the key fields to NULL */
	memset(key, 0, sizeof(*key));

	/* Allocate Generator Scalar */
	key->g = crypto_bignum_allocate(l_bits);
	if (!key->g)
		goto err;

	/* Allocate Prime Number Modulus */
	key->p = crypto_bignum_allocate(l_bits);
	if (!key->p)
		goto err;

	/* Allocate Prime Number Modulus */
	key->q = crypto_bignum_allocate(n_bits);
	if (!key->q)
		goto err;

	/* Allocate Private key X */
	key->x = crypto_bignum_allocate(n_bits);
	if (!key->x)
		goto err;

	/* Allocate Public Key Y */
	key->y = crypto_bignum_allocate(l_bits);
	if (!key->y)
		goto err;

	return TEE_SUCCESS;

err:
	DSA_TRACE("Allocation error");

	crypto_bignum_free(key->g);
	crypto_bignum_free(key->p);
	crypto_bignum_free(key->q);
	crypto_bignum_free(key->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static TEE_Result do_allocate_publickey(struct ecc_public_key *key,
					uint32_t type __unused,
					size_t size_bits)
{
	ECC_TRACE("Allocate Public Key of %zu bits", size_bits);

	/* Initialize the key fields to NULL */
	memset(key, 0, sizeof(*key));

	/* Allocate Public coordinate X */
	key->x = crypto_bignum_allocate(size_bits);
	if (!key->x)
		goto err;

	/* Allocate Public coordinate Y */
	key->y = crypto_bignum_allocate(size_bits);
	if (!key->y)
		goto err;

	return TEE_SUCCESS;

err:
	ECC_TRACE("Allocation error");

	crypto_bignum_free(key->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void do_free_publickey(struct ecc_public_key *key)
{
	crypto_bignum_free(key->x);
	crypto_bignum_free(key->y);
}


static TEE_Result do_allocate_keypair(struct ecc_keypair *key,
				      uint32_t type __unused,
				      size_t size_bits)
{
	ECC_TRACE("Allocate Keypair of %zu bits", size_bits);

	/* Initialize the key fields to NULL */
	memset(key, 0, sizeof(*key));

	/* Allocate Secure Scalar */
	key->d = crypto_bignum_allocate(size_bits);
	if (!key->d)
		goto err;

	/* Allocate Public coordinate X */
	key->x = crypto_bignum_allocate(size_bits);
	if (!key->x)
		goto err;

	/* Allocate Public coordinate Y */
	key->y = crypto_bignum_allocate(size_bits);
	if (!key->y)
		goto err;

	return TEE_SUCCESS;

err:
	ECC_TRACE("Allocation error");

	crypto_bignum_free(key->d);
	crypto_bignum_free(key->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void do_free_keypair(struct rsa_keypair *key)
{
	crypto_bignum_free(key->e);
	crypto_bignum_free(key->d);
	crypto_bignum_free(key->n);
	crypto_bignum_free(key->p);
	crypto_bignum_free(key->q);
	crypto_bignum_free(key->qp);
	crypto_bignum_free(key->dp);
	crypto_bignum_free(key->dq);
}


static void do_free_publickey(struct rsa_public_key *key)
{
	crypto_bignum_free(key->e);
	crypto_bignum_free(key->n);
}


static TEE_Result do_allocate_publickey(struct rsa_public_key *key,
					size_t size_bits)
{
	RSA_TRACE("Allocate Public Key of %zu bits", size_bits);

	/* Initialize all input key fields to 0 */
	memset(key, 0, sizeof(*key));

	/* Allocate the Public Exponent to maximum size */
	key->e = crypto_bignum_allocate(MAX_BITS_EXP_E);
	if (!key->e)
		goto err_alloc_publickey;

	/* Allocate the Modulus (size_bits) [n = p * q] */
	key->n = crypto_bignum_allocate(size_bits);
	if (!key->n)
		goto err_alloc_publickey;

	return TEE_SUCCESS;

err_alloc_publickey:
	RSA_TRACE("Allocation error");

	crypto_bignum_free(key->e);
	crypto_bignum_free(key->n);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void do_free_publickey(struct ecc_public_key *s)
{
	if (!s)
		return;

	crypto_bignum_free(s->x);
	crypto_bignum_free(s->y);
}


static TEE_Result do_alloc_publickey(struct ecc_public_key *s, uint32_t type,
				     size_t size_bits __unused)
{
	/* This driver only supports ECDH/ECDSA */
	if (type != TEE_TYPE_ECDSA_PUBLIC_KEY &&
	    type != TEE_TYPE_ECDH_PUBLIC_KEY)
		return TEE_ERROR_NOT_IMPLEMENTED;

	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->x))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->x);
	crypto_bignum_free(s->y);
	return TEE_ERROR_OUT_OF_MEMORY;
}


static TEE_Result do_alloc_keypair(struct ecc_keypair *s, uint32_t type,
				   size_t size_bits __unused)
{
	/* This driver only supports ECDH/ECDSA */
	if (type != TEE_TYPE_ECDSA_KEYPAIR &&
	    type != TEE_TYPE_ECDH_KEYPAIR)
		return TEE_ERROR_NOT_IMPLEMENTED;

	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->d))
		goto err;
	if (!bn_alloc_max(&s->x))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->d);
	crypto_bignum_free(s->x);
	crypto_bignum_free(s->y);
	return TEE_ERROR_OUT_OF_MEMORY;
}


static TEE_Result do_alloc_keypair(struct rsa_keypair *s,
				   size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->e))
		return TEE_ERROR_OUT_OF_MEMORY;
	if (!bn_alloc_max(&s->d))
		goto err;
	if (!bn_alloc_max(&s->n))
		goto err;
	if (!bn_alloc_max(&s->p))
		goto err;
	if (!bn_alloc_max(&s->q))
		goto err;
	if (!bn_alloc_max(&s->qp))
		goto err;
	if (!bn_alloc_max(&s->dp))
		goto err;
	if (!bn_alloc_max(&s->dq))
		goto err;

	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->e);
	crypto_bignum_free(s->d);
	crypto_bignum_free(s->n);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->q);
	crypto_bignum_free(s->qp);
	crypto_bignum_free(s->dp);
	crypto_bignum_free(s->dq);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void do_free_publickey(struct rsa_public_key *s)
{
	if (s) {
		crypto_bignum_free(s->n);
		crypto_bignum_free(s->e);
	}
}


static void do_free_keypair(struct rsa_keypair *s)
{
	sss_status_t st = kStatus_SSS_Fail;
	sss_se05x_object_t k_object = { };
	uint32_t key_id = 0;

	if (!s)
		return;

	key_id = se050_rsa_keypair_from_nvm(s);
	if (key_id) {
		st = sss_se05x_key_object_get_handle(&k_object, key_id);
		if (st == kStatus_SSS_Success)
			sss_se05x_key_store_erase_key(se050_kstore, &k_object);
	}

	crypto_bignum_free(s->e);
	crypto_bignum_free(s->d);
	crypto_bignum_free(s->n);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->q);
	crypto_bignum_free(s->qp);
	crypto_bignum_free(s->dp);
	crypto_bignum_free(s->dq);
}


static TEE_Result do_alloc_publickey(struct rsa_public_key *s,
				     size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->e))
		return TEE_ERROR_OUT_OF_MEMORY;
	if (!bn_alloc_max(&s->n)) {
		crypto_bignum_free(s->e);
		return TEE_ERROR_OUT_OF_MEMORY;
	}

	return TEE_SUCCESS;
}


TEE_Result crypto_acipher_alloc_dh_keypair(struct dh_keypair *s,
					   size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->g))
		return TEE_ERROR_OUT_OF_MEMORY;
	if (!bn_alloc_max(&s->p))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;
	if (!bn_alloc_max(&s->x))
		goto err;
	if (!bn_alloc_max(&s->q))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->g);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->y);
	crypto_bignum_free(s->x);
	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result crypto_acipher_alloc_dsa_public_key(struct dsa_public_key *s,
					       size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->g))
		return TEE_ERROR_OUT_OF_MEMORY;

	if (!bn_alloc_max(&s->p))
		goto err;
	if (!bn_alloc_max(&s->q))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->g);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->q);
	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result crypto_acipher_alloc_dsa_keypair(struct dsa_keypair *s,
					    size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->g))
		return TEE_ERROR_OUT_OF_MEMORY;

	if (!bn_alloc_max(&s->p))
		goto err;
	if (!bn_alloc_max(&s->q))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;
	if (!bn_alloc_max(&s->x))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->g);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->q);
	crypto_bignum_free(s->y);
	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result crypto_asym_alloc_ecc_public_key(struct ecc_public_key *s,
					    uint32_t key_type,
					    size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));

	switch (key_type) {
	case TEE_TYPE_ECDSA_PUBLIC_KEY:
	case TEE_TYPE_ECDH_PUBLIC_KEY:
		s->ops = &ecc_public_key_ops;
		break;
	case TEE_TYPE_SM2_DSA_PUBLIC_KEY:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_DSA))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_dsa_public_key_ops;
		break;
	case TEE_TYPE_SM2_PKE_PUBLIC_KEY:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_PKE))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_pke_public_key_ops;
		break;
	case TEE_TYPE_SM2_KEP_PUBLIC_KEY:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_KEP))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_kep_public_key_ops;
		break;
	default:
		return TEE_ERROR_NOT_IMPLEMENTED;
	}

	if (!bn_alloc_max(&s->x))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;

	return TEE_SUCCESS;

err:
	s->ops = NULL;

	crypto_bignum_free(s->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result crypto_asym_alloc_ecc_keypair(struct ecc_keypair *s,
					 uint32_t key_type,
					 size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));

	switch (key_type) {
	case TEE_TYPE_ECDSA_KEYPAIR:
	case TEE_TYPE_ECDH_KEYPAIR:
		s->ops = &ecc_keypair_ops;
		break;
	case TEE_TYPE_SM2_DSA_KEYPAIR:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_DSA))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_dsa_keypair_ops;
		break;
	case TEE_TYPE_SM2_PKE_KEYPAIR:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_PKE))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_pke_keypair_ops;
		break;
	case TEE_TYPE_SM2_KEP_KEYPAIR:
		if (!IS_ENABLED2(_CFG_CORE_LTC_SM2_KEP))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_kep_keypair_ops;
		break;
	default:
		return TEE_ERROR_NOT_IMPLEMENTED;
	}

	if (!bn_alloc_max(&s->d))
		goto err;
	if (!bn_alloc_max(&s->x))
		goto err;
	if (!bn_alloc_max(&s->y))
		goto err;

	return TEE_SUCCESS;

err:
	s->ops = NULL;

	crypto_bignum_free(s->d);
	crypto_bignum_free(s->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void _ltc_ecc_free_public_key(struct ecc_public_key *s)
{
	if (!s)
		return;

	crypto_bignum_free(s->x);
	crypto_bignum_free(s->y);
}


void crypto_bignum_free(struct bignum *s)
{
	mbedtls_mpi_free((mbedtls_mpi *)s);
	free(s);
}


TEE_Result sw_crypto_acipher_alloc_rsa_public_key(struct rsa_public_key *s,
						  size_t key_size_bits __unused)
{
	memset(s, 0, sizeof(*s));
	if (!bn_alloc_max(&s->e))
		return TEE_ERROR_OUT_OF_MEMORY;
	if (!bn_alloc_max(&s->n))
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->e);
	return TEE_ERROR_OUT_OF_MEMORY;
}


void sw_crypto_acipher_free_rsa_public_key(struct rsa_public_key *s)
{
	if (!s)
		return;
	crypto_bignum_free(s->n);
	crypto_bignum_free(s->e);
}


void sw_crypto_acipher_free_rsa_keypair(struct rsa_keypair *s)
{
	if (!s)
		return;
	crypto_bignum_free(s->e);
	crypto_bignum_free(s->d);
	crypto_bignum_free(s->n);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->q);
	crypto_bignum_free(s->qp);
	crypto_bignum_free(s->dp);
	crypto_bignum_free(s->dq);
}


static void op_attr_bignum_free(void *attr)
{
	struct bignum **bn = attr;

	crypto_bignum_free(*bn);
	*bn = NULL;
}


TEE_Result crypto_acipher_alloc_dh_keypair(struct dh_keypair *s,
					   size_t key_size_bits)
{
	memset(s, 0, sizeof(*s));
	s->g = crypto_bignum_allocate(key_size_bits);
	if (!s->g)
		goto err;
	s->p = crypto_bignum_allocate(key_size_bits);
	if (!s->p)
		goto err;
	s->y = crypto_bignum_allocate(key_size_bits);
	if (!s->y)
		goto err;
	s->x = crypto_bignum_allocate(key_size_bits);
	if (!s->x)
		goto err;
	s->q = crypto_bignum_allocate(key_size_bits);
	if (!s->q)
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->g);
	crypto_bignum_free(s->p);
	crypto_bignum_free(s->y);
	crypto_bignum_free(s->x);
	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result crypto_asym_alloc_ecc_keypair(struct ecc_keypair *s,
					 uint32_t key_type,
					 size_t key_size_bits)
{
	memset(s, 0, sizeof(*s));

	switch (key_type) {
	case TEE_TYPE_ECDSA_KEYPAIR:
	case TEE_TYPE_ECDH_KEYPAIR:
		s->ops = &ecc_keypair_ops;
		break;
	case TEE_TYPE_SM2_DSA_KEYPAIR:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_DSA))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_dsa_keypair_ops;
		break;
	case TEE_TYPE_SM2_PKE_KEYPAIR:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_PKE))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_pke_keypair_ops;
		break;
	case TEE_TYPE_SM2_KEP_KEYPAIR:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_KEP))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_kep_keypair_ops;
		break;
	default:
		return TEE_ERROR_NOT_IMPLEMENTED;
	}

	s->d = crypto_bignum_allocate(key_size_bits);
	if (!s->d)
		goto err;
	s->x = crypto_bignum_allocate(key_size_bits);
	if (!s->x)
		goto err;
	s->y = crypto_bignum_allocate(key_size_bits);
	if (!s->y)
		goto err;

	return TEE_SUCCESS;

err:
	crypto_bignum_free(s->d);
	crypto_bignum_free(s->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


static void ecc_free_public_key(struct ecc_public_key *s)
{
	if (!s)
		return;

	crypto_bignum_free(s->x);
	crypto_bignum_free(s->y);
}


TEE_Result crypto_asym_alloc_ecc_public_key(struct ecc_public_key *s,
					    uint32_t key_type,
					    size_t key_size_bits)
{
	memset(s, 0, sizeof(*s));

	switch (key_type) {
	case TEE_TYPE_ECDSA_PUBLIC_KEY:
	case TEE_TYPE_ECDH_PUBLIC_KEY:
		s->ops = &ecc_public_key_ops;
		break;
	case TEE_TYPE_SM2_DSA_PUBLIC_KEY:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_DSA))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_dsa_public_key_ops;
		break;
	case TEE_TYPE_SM2_PKE_PUBLIC_KEY:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_PKE))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_pke_public_key_ops;
		break;
	case TEE_TYPE_SM2_KEP_PUBLIC_KEY:
		if (!IS_ENABLED(CFG_CRYPTO_SM2_KEP))
			return TEE_ERROR_NOT_IMPLEMENTED;

		s->curve = TEE_ECC_CURVE_SM2;
		s->ops = &sm2_kep_public_key_ops;
		break;
	default:
		return TEE_ERROR_NOT_IMPLEMENTED;
	}

	s->x = crypto_bignum_allocate(key_size_bits);
	if (!s->x)
		goto err;
	s->y = crypto_bignum_allocate(key_size_bits);
	if (!s->y)
		goto err;

	return TEE_SUCCESS;

err:
	crypto_bignum_free(s->x);

	return TEE_ERROR_OUT_OF_MEMORY;
}


TEE_Result sw_crypto_acipher_alloc_rsa_public_key(struct rsa_public_key *s,
						  size_t key_size_bits)
{
	memset(s, 0, sizeof(*s));
	s->e = crypto_bignum_allocate(key_size_bits);
	if (!s->e)
		return TEE_ERROR_OUT_OF_MEMORY;
	s->n = crypto_bignum_allocate(key_size_bits);
	if (!s->n)
		goto err;
	return TEE_SUCCESS;
err:
	crypto_bignum_free(s->e);
	return TEE_ERROR_OUT_OF_MEMORY;
}


int LiSendMouseButtonEvent(char action, int button) {
    PPACKET_HOLDER holder;
    int err;

    if (!initialized) {
        return -2;
    }

    holder = malloc(sizeof(*holder));
    if (holder == NULL) {
        return -1;
    }

    holder->packetLength = sizeof(NV_MOUSE_BUTTON_PACKET);
    holder->packet.mouseButton.header.packetType = htonl(PACKET_TYPE_MOUSE_BUTTON);
    holder->packet.mouseButton.action = action;
    if (ServerMajorVersion >= 5) {
        holder->packet.mouseButton.action++;
    }
    holder->packet.mouseButton.button = htonl(button);

    err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
    if (err != LBQ_SUCCESS) {
        free(holder);
    }

    return err;
}


int startInputStream(void) {
    int err;

    // After Gen 5, we send input on the control stream
    if (ServerMajorVersion < 5) {
        inputSock = connectTcpSocket(&RemoteAddr, RemoteAddrLen,
            35043, INPUT_STREAM_TIMEOUT_SEC);
        if (inputSock == INVALID_SOCKET) {
            return LastSocketFail();
        }

        enableNoDelay(inputSock);
    }

    err = PltCreateThread(inputSendThreadProc, NULL, &inputSendThread);
    if (err != 0) {
        return err;
    }

    return err;
}


int LiSendScrollEvent(signed char scrollClicks) {
    PPACKET_HOLDER holder;
    int err;

    if (!initialized) {
        return -2;
    }

    holder = malloc(sizeof(*holder));
    if (holder == NULL) {
        return -1;
    }

    holder->packetLength = sizeof(NV_SCROLL_PACKET);
    holder->packet.scroll.header.packetType = htonl(PACKET_TYPE_SCROLL);
    holder->packet.scroll.magicA = MAGIC_A;
    // On Gen 5 servers, the header code is incremented by one
    if (ServerMajorVersion >= 5) {
        holder->packet.scroll.magicA++;
    }
    holder->packet.scroll.zero1 = 0;
    holder->packet.scroll.zero2 = 0;
    holder->packet.scroll.scrollAmt1 = htons(scrollClicks * 120);
    holder->packet.scroll.scrollAmt2 = holder->packet.scroll.scrollAmt1;
    holder->packet.scroll.zero3 = 0;

    err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
    if (err != LBQ_SUCCESS) {
        free(holder);
    }

    return err;
}


int LiSendMouseMoveEvent(short deltaX, short deltaY) {
    PPACKET_HOLDER holder;
    int err;

    if (!initialized) {
        return -2;
    }

    holder = malloc(sizeof(*holder));
    if (holder == NULL) {
        return -1;
    }

    holder->packetLength = sizeof(NV_MOUSE_MOVE_PACKET);
    holder->packet.mouseMove.header.packetType = htonl(PACKET_TYPE_MOUSE_MOVE);
    holder->packet.mouseMove.magic = MOUSE_MOVE_MAGIC;
    // On Gen 5 servers, the header code is incremented by one
    if (ServerMajorVersion >= 5) {
        holder->packet.mouseMove.magic++;
    }
    holder->packet.mouseMove.deltaX = htons(deltaX);
    holder->packet.mouseMove.deltaY = htons(deltaY);

    err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
    if (err != LBQ_SUCCESS) {
        free(holder);
    }

    return err;
}


static int transactRtspMessage(PRTSP_MESSAGE request, PRTSP_MESSAGE response, int expectingPayload, int* error) {
    // Gen 5+ does RTSP over ENet not TCP
    if (ServerMajorVersion >= 5) {
        return transactRtspMessageEnet(request, response, expectingPayload, error);
    }
    else {
        return transactRtspMessageTcp(request, response, expectingPayload, error);
    }
}


static int setupStream(PRTSP_MESSAGE response, char* target, int* error) {
    RTSP_MESSAGE request;
    int ret;
    char* transportValue;

    *error = -1;

    ret = initializeRtspRequest(&request, "SETUP", target);
    if (ret != 0) {
        if (hasSessionId) {
            if (!addOption(&request, "Session", sessionIdString)) {
                ret = 0;
                goto FreeMessage;
            }
        }

        if (ServerMajorVersion >= 6) {
            // It looks like GFE doesn't care what we say our port is but
            // we need to give it some port to successfully complete the
            // handshake process.
            transportValue = "unicast;X-GS-ClientPort=50000-50001";
        }
        else {
            transportValue = " ";
        }

        if (addOption(&request, "Transport", transportValue) &&
            addOption(&request, "If-Modified-Since",
                "Thu, 01 Jan 1970 00:00:00 GMT")) {
            ret = transactRtspMessage(&request, response, 0, error);
        }
        else {
            ret = 0;
        }

    FreeMessage:
        freeMessage(&request);
    }

    return ret;
}


static bool parseUrlAddrFromRtspUrlString(const char* rtspUrlString, char* destination) {
    char* rtspUrlScratchBuffer;
    char* portSeparator;
    char* v6EscapeEndChar;
    char* urlPathSeparator;
    int prefixLen;

    // Create a copy that we can modify
    rtspUrlScratchBuffer = strdup(rtspUrlString);
    if (rtspUrlScratchBuffer == NULL) {
        return false;
    }

    // If we have a v6 address, we want to stop one character after the closing ]
    // If we have a v4 address, we want to stop at the port separator
    portSeparator = strrchr(rtspUrlScratchBuffer, ':');
    v6EscapeEndChar = strchr(rtspUrlScratchBuffer, ']');

    // Count the prefix length to skip past the initial rtsp:// or rtspru:// part
    for (prefixLen = 2; rtspUrlScratchBuffer[prefixLen - 2] != 0 && (rtspUrlScratchBuffer[prefixLen - 2] != '/' || rtspUrlScratchBuffer[prefixLen - 1] != '/'); prefixLen++);

    // If we hit the end of the string prior to parsing the prefix, we cannot proceed
    if (rtspUrlScratchBuffer[prefixLen - 2] == 0) {
        free(rtspUrlScratchBuffer);
        return false;
    }

    // Look for a slash at the end of the host portion of the URL (may not be present)
    urlPathSeparator = strchr(rtspUrlScratchBuffer + prefixLen, '/');

    // Check for a v6 address first since they also have colons
    if (v6EscapeEndChar) {
        // Terminate the string at the next character
        *(v6EscapeEndChar + 1) = 0;
    }
    else if (portSeparator) {
        // Terminate the string prior to the port separator
        *portSeparator = 0;
    }
    else if (urlPathSeparator) {
        // Terminate the string prior to the path separator
        *urlPathSeparator = 0;
    }

    strcpy(destination, rtspUrlScratchBuffer + prefixLen);

    free(rtspUrlScratchBuffer);
    return true;
}


void track_set_index(Track *track, int i, long ind)
{
	if (i > MAXINDEX) {
		fprintf(stderr, "too many indexes\n");
                return;
        }

	track->index[i] = ind;
}

The product writes data past the end, or before the beginning, of the intended buffer.


void InitCodec(const vpx_codec_iface_t &iface, int width, int height,
               vpx_codec_ctx_t *enc, vpx_codec_enc_cfg_t *cfg) {
  ASSERT_EQ(vpx_codec_enc_config_default(&iface, cfg, 0), VPX_CODEC_OK);
  cfg->g_w = width;
  cfg->g_h = height;
  cfg->g_lag_in_frames = 0;
  cfg->g_pass = VPX_RC_ONE_PASS;
  ASSERT_EQ(vpx_codec_enc_init(enc, &iface, cfg, 0), VPX_CODEC_OK);

  ASSERT_EQ(vpx_codec_control_(enc, VP8E_SET_CPUUSED, 2), VPX_CODEC_OK);
}


TEST(EncodeAPI, ConfigResizeChangeThreadCount) {
  constexpr int kInitWidth = 1024;
  constexpr int kInitHeight = 1024;

  for (const auto *iface : kCodecIfaces) {
    SCOPED_TRACE(vpx_codec_iface_name(iface));
    if (!IsVP9(iface)) {
      GTEST_SKIP() << "TODO(https://crbug.com/1486441) remove this condition "
                      "after VP8 is fixed.";
    }
    for (int i = 0; i < (IsVP9(iface) ? 2 : 1); ++i) {
      vpx_codec_enc_cfg_t cfg = {};
      struct Encoder {
        ~Encoder() { EXPECT_EQ(vpx_codec_destroy(&ctx), VPX_CODEC_OK); }
        vpx_codec_ctx_t ctx = {};
      } enc;

      ASSERT_EQ(vpx_codec_enc_config_default(iface, &cfg, 0), VPX_CODEC_OK);
      // Start in threaded mode to ensure resolution and thread related
      // allocations are updated correctly across changes in resolution and
      // thread counts. See https://crbug.com/1486441.
      cfg.g_threads = 4;
      EXPECT_NO_FATAL_FAILURE(
          InitCodec(*iface, kInitWidth, kInitHeight, &enc.ctx, &cfg));
      if (IsVP9(iface)) {
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_TILE_COLUMNS, 6),
                  VPX_CODEC_OK);
        EXPECT_EQ(vpx_codec_control_(&enc.ctx, VP9E_SET_ROW_MT, i),
                  VPX_CODEC_OK);
      }

      cfg.g_w = 1000;
      cfg.g_h = 608;
      EXPECT_EQ(vpx_codec_enc_config_set(&enc.ctx, &cfg), VPX_CODEC_OK)
          << vpx_codec_error_detail(&enc.ctx);

      cfg.g_w = 16;
      cfg.g_h = 720;

      for (const auto threads : { 1, 4, 8, 6, 2, 1 }) {
        cfg.g_threads = threads;
        EXPECT_NO_FATAL_FAILURE(EncodeWithConfig(cfg, &enc.ctx))
            << "iteration: " << i << " threads: " << threads;
      }
    }
  }
}


int vp9_alloc_context_buffers(VP9_COMMON *cm, int width, int height) {
  int new_mi_size;

  vp9_set_mb_mi(cm, width, height);
  new_mi_size = cm->mi_stride * calc_mi_size(cm->mi_rows);
  if (cm->mi_alloc_size < new_mi_size) {
    cm->free_mi(cm);
    if (cm->alloc_mi(cm, new_mi_size)) goto fail;
  }

  if (cm->seg_map_alloc_size < cm->mi_rows * cm->mi_cols) {
    // Create the segmentation map structure and set to 0.
    free_seg_map(cm);
    if (alloc_seg_map(cm, cm->mi_rows * cm->mi_cols)) goto fail;
  }

  if (cm->above_context_alloc_cols < cm->mi_cols) {
    vpx_free(cm->above_context);
    cm->above_context = (ENTROPY_CONTEXT *)vpx_calloc(
        2 * mi_cols_aligned_to_sb(cm->mi_cols) * MAX_MB_PLANE,
        sizeof(*cm->above_context));
    if (!cm->above_context) goto fail;

    vpx_free(cm->above_seg_context);
    cm->above_seg_context = (PARTITION_CONTEXT *)vpx_calloc(
        mi_cols_aligned_to_sb(cm->mi_cols), sizeof(*cm->above_seg_context));
    if (!cm->above_seg_context) goto fail;
    cm->above_context_alloc_cols = cm->mi_cols;
  }

  if (vp9_alloc_loop_filter(cm)) goto fail;

  return 0;

fail:
  // clear the mi_* values to force a realloc on resync
  vp9_set_mb_mi(cm, 0, 0);
  vp9_free_context_buffers(cm);
  return 1;
}

Improper Handling of Exceptional Conditions

The product does not handle or incorrectly handles an exceptional condition.


static int handle_dot_dotdot_filename(struct exfat_de_iter *iter,
				      struct exfat_dentry *dentry,
				      int strm_name_len)
{
	char *filename;
	char error_msg[150];
	int num;

	if (!memcmp(dentry->name_unicode, MSDOS_DOT, strm_name_len * 2))
		filename = ".";
	else if (!memcmp(dentry->name_unicode, MSDOS_DOTDOT,
			 strm_name_len * 2))
		filename = "..";
	else
		return 0;

	sprintf(error_msg, "ERROR: '%s' filename is not allowed.\n"
			" [1] Insert the name you want to rename.\n"
			" [2] Automatically renames filename.\n"
			" [3] Bypass this check(No repair)\n", filename);
ask_again:
	num = exfat_repair_ask(&exfat_fsck, ER_DE_DOT_NAME,
			       error_msg);
	if (num) {
		__le16 utf16_name[ENTRY_NAME_MAX];
		char *rename = NULL;
		__u16 hash;
		struct exfat_dentry *stream_de;
		int name_len, ret;

		switch (num) {
		case 1:
			rename = get_rename_from_user(iter);
			break;
		case 2:
			rename = generate_rename(iter);
			break;
		case 3:
			break;
		default:
			exfat_info("select 1 or 2 number instead of %d\n", num);
			goto ask_again;
		}

		if (!rename)
			return -EINVAL;

		exfat_info("%s filename is renamed to %s\n", filename, rename);

		exfat_de_iter_get_dirty(iter, 2, &dentry);

		memset(utf16_name, 0, sizeof(utf16_name));
		ret = exfat_utf16_enc(rename, utf16_name, sizeof(utf16_name));
		free(rename);
		if (ret < 0)
			return ret;

		memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
		name_len = exfat_utf16_len(utf16_name, ENTRY_NAME_MAX * 2);
		hash = exfat_calc_name_hash(iter->exfat, utf16_name, (int)name_len);
		exfat_de_iter_get_dirty(iter, 1, &stream_de);
		stream_de->stream_name_len = (__u8)name_len;
		stream_de->stream_name_hash = cpu_to_le16(hash);
	}

	return 0;
}


static int read_file_dentry_set(struct exfat_de_iter *iter,
				struct exfat_inode **new_node, int *skip_dentries)
{
	struct exfat_dentry *file_de, *stream_de, *dentry;
	struct exfat_inode *node = NULL;
	int i, ret;

	ret = exfat_de_iter_get(iter, 0, &file_de);
	if (ret || file_de->type != EXFAT_FILE) {
		exfat_debug("failed to get file dentry\n");
		return -EINVAL;
	}

	ret = exfat_de_iter_get(iter, 1, &stream_de);
	if (ret || stream_de->type != EXFAT_STREAM) {
		exfat_debug("failed to get stream dentry\n");
		*skip_dentries = 2;
		goto skip_dset;
	}

	*new_node = NULL;
	node = exfat_alloc_inode(le16_to_cpu(file_de->file_attr));
	if (!node)
		return -ENOMEM;

	for (i = 2; i <= file_de->file_num_ext; i++) {
		ret = exfat_de_iter_get(iter, i, &dentry);
		if (ret || dentry->type != EXFAT_NAME)
			break;
		memcpy(node->name +
		       (i - 2) * ENTRY_NAME_MAX, dentry->name_unicode,
		       sizeof(dentry->name_unicode));
	}

	node->first_clus = le32_to_cpu(stream_de->stream_start_clu);
	node->is_contiguous =
		((stream_de->stream_flags & EXFAT_SF_CONTIGUOUS) != 0);
	node->size = le64_to_cpu(stream_de->stream_size);

	*skip_dentries = i;
	*new_node = node;
	return 0;
skip_dset:
	*new_node = NULL;
	exfat_free_inode(node);
	return -EINVAL;
}


static int ksu_sha256(const unsigned char *data, unsigned int datalen,
		unsigned char *digest)
{
	struct crypto_shash *alg;
	char *hash_alg_name = "sha256";
	int ret;

	alg = crypto_alloc_shash(hash_alg_name, 0, 0);
	if (IS_ERR(alg)) {
		pr_info("can't alloc alg %s\n", hash_alg_name);
		return PTR_ERR(alg);
	}
	ret = calc_hash(alg, data, datalen, digest);
	crypto_free_shash(alg);
	return ret;
}

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.


static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
			unsigned expected_size, const char* expected_sha256)
{
	ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
	ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
	ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length

	*offset += 0x4 * 3;

	ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length

	*pos += *size4;
	*offset += 0x4 + *size4;

	ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
	ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
	*offset += 0x4 * 2;

	if (*size4 == expected_size) {
		*offset += *size4;

		#define CERT_MAX_LENGTH 1024
		char cert[CERT_MAX_LENGTH];
		if (*size4 > CERT_MAX_LENGTH) {
			pr_info("cert length overlimit\n");
			return false;
		}
		ksu_kernel_read_compat(fp, cert, *size4, pos);
		unsigned char digest[SHA256_DIGEST_SIZE];
		if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
			pr_info("sha256 error\n");
			return false;
		}

		char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
		hash_str[SHA256_DIGEST_SIZE * 2] = '\0';

		bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
		pr_info("sha256: %s, expected: %s\n", hash_str, expected_sha256);
		if (strcmp(expected_sha256, hash_str) == 0) {
			return true;
		}
	}
	return false;
}


static int ref_pic_list_struct(GetBitContext *gb, RefPicListStruct *rpl)
{
    uint32_t delta_poc_st, strp_entry_sign_flag = 0;
    rpl->ref_pic_num = get_ue_golomb_long(gb);
    if (rpl->ref_pic_num > 0) {
        delta_poc_st = get_ue_golomb_long(gb);

        rpl->ref_pics[0] = delta_poc_st;
        if (rpl->ref_pics[0] != 0) {
            strp_entry_sign_flag = get_bits(gb, 1);

            rpl->ref_pics[0] *= 1 - (strp_entry_sign_flag << 1);
        }
    }

    for (int i = 1; i < rpl->ref_pic_num; ++i) {
        delta_poc_st = get_ue_golomb_long(gb);
        if (delta_poc_st != 0)
            strp_entry_sign_flag = get_bits(gb, 1);
        rpl->ref_pics[i] = rpl->ref_pics[i - 1] + delta_poc_st * (1 - (strp_entry_sign_flag << 1));
    }

    return 0;
}


static int _process_request_metaflags(mcp_parser_t *pr, int token) {
    if (pr->ntokens <= token) {
        pr->t.meta.flags = 0; // no flags found.
        return 0;
    }
    const char *cur = pr->request + pr->tokens[token];
    const char *end = pr->request + pr->reqlen - 2;

    // We blindly convert flags into bits, since the range of possible
    // flags is deliberately < 64.
    int state = 0;
    while (cur != end) {
        switch (state) {
            case 0:
                if (*cur == ' ') {
                    cur++;
                } else {
                    if (*cur < 65 || *cur > 122) {
                        return -1;
                    }
                    P_DEBUG("%s: setting meta flag: %d\n", __func__, *cur - 65);
                    pr->t.meta.flags |= (uint64_t)1 << (*cur - 65);
                    state = 1;
                }
                break;
            case 1:
                if (*cur != ' ') {
                    cur++;
                } else {
                    state = 0;
                }
                break;
        }
    }

    // not too great hack for noreply detection: this can be flattened out
    // once a few other contexts are fixed and we detect the noreply from the
    // coroutine start instead.
    if (pr->t.meta.flags & ((uint64_t)1 << 48)) {
        pr->noreply = true;
    }

    return 0;
}

A product calculates or uses an incorrect maximum or minimum value that is 1 more, or 1 less, than the correct value.


static int _process_tokenize(mcp_parser_t *pr, const size_t max) {
    const char *s = pr->request;
    int len = pr->reqlen - 2;

    // since multigets can be huge, we can't purely judge reqlen against this
    // limit, but we also can't index past it since the tokens are shorts.
    if (len > PARSER_MAXLEN) {
        len = PARSER_MAXLEN;
    }
    const char *end = s + len;
    int curtoken = 0;

    int state = 0;
    while (s != end) {
        switch (state) {
            case 0:
                // scanning for first non-space to find a token.
                if (*s != ' ') {
                    pr->tokens[curtoken] = s - pr->request;
                    if (++curtoken == max) {
                        s++;
                        state = 2;
                        break;
                    }
                    state = 1;
                }
                s++;
                break;
            case 1:
                // advance over a token
                if (*s != ' ') {
                    s++;
                } else {
                    state = 0;
                }
                break;
            case 2:
                // hit max tokens before end of the line.
                // keep advancing so we can place endcap token.
                if (*s == ' ') {
                    goto endloop;
                }
                s++;
                break;
        }
    }
endloop:

    // endcap token so we can quickly find the length of any token by looking
    // at the next one.
    pr->tokens[curtoken] = s - pr->request;
    pr->ntokens = curtoken;
    P_DEBUG("%s: cur_tokens: %d\n", __func__, curtoken);

    return 0;
}


size_t _process_request_next_key(mcp_parser_t *pr) {
    const char *cur = pr->request + pr->parsed;
    int remain = pr->reqlen - pr->parsed - 2;

    // chew off any leading whitespace.
    while (remain) {
        if (*cur == ' ') {
            remain--;
            cur++;
            pr->parsed++;
        } else {
            break;
        }
    }

    const char *s = memchr(cur, ' ', remain);
    if (s != NULL) {
        pr->klen = s - cur;
        pr->parsed += s - cur;
    } else {
        pr->klen = remain;
        pr->parsed += remain;
    }

    return cur - pr->request;
}


static void ClearMetadata(VP8LMetadata* const hdr) {
  assert(hdr != NULL);

  WebPSafeFree(hdr->huffman_image_);
  WebPSafeFree(hdr->huffman_tables_);
  VP8LHtreeGroupsFree(hdr->htree_groups_);
  VP8LColorCacheClear(&hdr->color_cache_);
  VP8LColorCacheClear(&hdr->saved_color_cache_);
  InitMetadata(hdr);
}


int VP8LDecodeImage(VP8LDecoder* const dec) {
  VP8Io* io = NULL;
  WebPDecParams* params = NULL;

  if (dec == NULL) return 0;

  assert(dec->hdr_.huffman_tables_ != NULL);
  assert(dec->hdr_.htree_groups_ != NULL);
  assert(dec->hdr_.num_htree_groups_ > 0);

  io = dec->io_;
  assert(io != NULL);
  params = (WebPDecParams*)io->opaque;
  assert(params != NULL);

  // Initialization.
  if (dec->state_ != READ_DATA) {
    dec->output_ = params->output;
    assert(dec->output_ != NULL);

    if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) {
      VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
      goto Err;
    }

    if (!AllocateInternalBuffers32b(dec, io->width)) goto Err;

#if !defined(WEBP_REDUCE_SIZE)
    if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err;
#else
    if (io->use_scaling) {
      VP8LSetError(dec, VP8_STATUS_INVALID_PARAM);
      goto Err;
    }
#endif
    if (io->use_scaling || WebPIsPremultipliedMode(dec->output_->colorspace)) {
      // need the alpha-multiply functions for premultiplied output or rescaling
      WebPInitAlphaProcessing();
    }

    if (!WebPIsRGBMode(dec->output_->colorspace)) {
      WebPInitConvertARGBToYUV();
      if (dec->output_->u.YUVA.a != NULL) WebPInitAlphaProcessing();
    }
    if (dec->incremental_) {
      if (dec->hdr_.color_cache_size_ > 0 &&
          dec->hdr_.saved_color_cache_.colors_ == NULL) {
        if (!VP8LColorCacheInit(&dec->hdr_.saved_color_cache_,
                                dec->hdr_.color_cache_.hash_bits_)) {
          VP8LSetError(dec, VP8_STATUS_OUT_OF_MEMORY);
          goto Err;
        }
      }
    }
    dec->state_ = READ_DATA;
  }

  // Decode.
  if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_,
                       io->crop_bottom, ProcessRows)) {
    goto Err;
  }

  params->last_y = dec->last_out_row_;
  return 1;

 Err:
  VP8LClear(dec);
  assert(dec->status_ != VP8_STATUS_OK);
  return 0;
}


static int ReadHuffmanCodeLengths(
    VP8LDecoder* const dec, const int* const code_length_code_lengths,
    int num_symbols, int* const code_lengths) {
  int ok = 0;
  VP8LBitReader* const br = &dec->br_;
  int symbol;
  int max_symbol;
  int prev_code_len = DEFAULT_CODE_LENGTH;
  HuffmanCode table[1 << LENGTHS_TABLE_BITS];

  if (!VP8LBuildHuffmanTable(table, LENGTHS_TABLE_BITS,
                             code_length_code_lengths,
                             NUM_CODE_LENGTH_CODES)) {
    goto End;
  }

  if (VP8LReadBits(br, 1)) {    // use length
    const int length_nbits = 2 + 2 * VP8LReadBits(br, 3);
    max_symbol = 2 + VP8LReadBits(br, length_nbits);
    if (max_symbol > num_symbols) {
      goto End;
    }
  } else {
    max_symbol = num_symbols;
  }

  symbol = 0;
  while (symbol < num_symbols) {
    const HuffmanCode* p;
    int code_len;
    if (max_symbol-- == 0) break;
    VP8LFillBitWindow(br);
    p = &table[VP8LPrefetchBits(br) & LENGTHS_TABLE_MASK];
    VP8LSetBitPos(br, br->bit_pos_ + p->bits);
    code_len = p->value;
    if (code_len < kCodeLengthLiterals) {
      code_lengths[symbol++] = code_len;
      if (code_len != 0) prev_code_len = code_len;
    } else {
      const int use_prev = (code_len == kCodeLengthRepeatCode);
      const int slot = code_len - kCodeLengthLiterals;
      const int extra_bits = kCodeLengthExtraBits[slot];
      const int repeat_offset = kCodeLengthRepeatOffsets[slot];
      int repeat = VP8LReadBits(br, extra_bits) + repeat_offset;
      if (symbol + repeat > num_symbols) {
        goto End;
      } else {
        const int length = use_prev ? prev_code_len : 0;
        while (repeat-- > 0) code_lengths[symbol++] = length;
      }
    }
  }
  ok = 1;

 End:
  if (!ok) return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
  return ok;
}


static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec,
                           int* const code_lengths, HuffmanCode* const table) {
  int ok = 0;
  int size = 0;
  VP8LBitReader* const br = &dec->br_;
  const int simple_code = VP8LReadBits(br, 1);

  memset(code_lengths, 0, alphabet_size * sizeof(*code_lengths));

  if (simple_code) {  // Read symbols, codes & code lengths directly.
    const int num_symbols = VP8LReadBits(br, 1) + 1;
    const int first_symbol_len_code = VP8LReadBits(br, 1);
    // The first code is either 1 bit or 8 bit code.
    int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
    code_lengths[symbol] = 1;
    // The second code (if present), is always 8 bits long.
    if (num_symbols == 2) {
      symbol = VP8LReadBits(br, 8);
      code_lengths[symbol] = 1;
    }
    ok = 1;
  } else {  // Decode Huffman-coded code lengths.
    int i;
    int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 };
    const int num_codes = VP8LReadBits(br, 4) + 4;
    assert(num_codes <= NUM_CODE_LENGTH_CODES);

    for (i = 0; i < num_codes; ++i) {
      code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3);
    }
    ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size,
                                code_lengths);
  }

  ok = ok && !br->eos_;
  if (ok) {
    size = VP8LBuildHuffmanTable(table, HUFFMAN_TABLE_BITS,
                                 code_lengths, alphabet_size);
  }
  if (!ok || size == 0) {
    return VP8LSetError(dec, VP8_STATUS_BITSTREAM_ERROR);
  }
  return size;
}


int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
                          const int code_lengths[], int code_lengths_size) {
  int total_size;
  assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
  if (root_table == NULL) {
    total_size = BuildHuffmanTable(NULL, root_bits,
                                   code_lengths, code_lengths_size, NULL);
  } else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
    // use local stack-allocated array.
    uint16_t sorted[SORTED_SIZE_CUTOFF];
    total_size = BuildHuffmanTable(root_table, root_bits,
                                   code_lengths, code_lengths_size, sorted);
  } else {   // rare case. Use heap allocation.
    uint16_t* const sorted =
        (uint16_t*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
    if (sorted == NULL) return 0;
    total_size = BuildHuffmanTable(root_table, root_bits,
                                   code_lengths, code_lengths_size, sorted);
    WebPSafeFree(sorted);
  }
  return total_size;
}


int jvp_number_cmp(jv a, jv b) {
  assert(JVP_HAS_KIND(a, JV_KIND_NUMBER));
  assert(JVP_HAS_KIND(b, JV_KIND_NUMBER));

#ifdef USE_DECNUM
  if (JVP_HAS_FLAGS(a, JVP_FLAGS_NUMBER_LITERAL) && JVP_HAS_FLAGS(b, JVP_FLAGS_NUMBER_LITERAL)) {
    decNumber res;
    decNumberCompare(&res,
                     jvp_dec_number_ptr(a),
                     jvp_dec_number_ptr(b),
                     DEC_CONTEXT()
                     );
    if (decNumberIsZero(&res)) {
      return 0;
    } else if (decNumberIsNegative(&res)) {
      return -1;
    } else {
      return 1;
    }
  }
#endif
  double da = jv_number_value(a), db = jv_number_value(b);
  if (da < db) {
    return -1;
  } else if (da == db) {
    return 0;
  } else {
    return 1;
  }
}


static const char* jvp_literal_number_literal(jv n) {
  assert(JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL));
  decNumber *pdec = jvp_dec_number_ptr(n);
  jvp_literal_number* plit = jvp_literal_number_ptr(n);

  if (decNumberIsNaN(pdec)) {
    return "null";
  }

  if (decNumberIsInfinite(pdec)) {
    // We cannot preserve the literal data of numbers outside the limited
    // range of exponent. Since `decNumberToString` returns "Infinity"
    // (or "-Infinity"), and to reduce stack allocations as possible, we
    // normalize infinities in the callers instead of printing the maximum
    // (or minimum) double here.
    return NULL;
  }

  if (plit->literal_data == NULL) {
    int len = jvp_dec_number_ptr(n)->digits + 14;
    plit->literal_data = jv_mem_alloc(len);

    // Preserve the actual precision as we have parsed it
    // don't do decNumberTrim(pdec);

    decNumberToString(pdec, plit->literal_data);
  }

  return plit->literal_data;
}


comics_document_save (EvDocument *document,
		      const char *uri,
		      GError    **error)
{
	ComicsDocument *comics_document = COMICS_DOCUMENT (document);

	return ev_xfer_uri_simple (comics_document->archive, uri, error);
}

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.


render_pixbuf_size_prepared_cb (GdkPixbufLoader *loader,
				gint             width,
				gint             height,
				gpointer         data)
{
	double *scale = data;
	int w = (width  * (*scale) + 0.5);
	int h = (height * (*scale) + 0.5);

	gdk_pixbuf_loader_set_size (loader, w, h);
}


comics_generate_command_lines (ComicsDocument *comics_document,
			       GError         **error)
{
	gchar *quoted_file, *quoted_file_aux;
	gchar *quoted_command;
	ComicBookDecompressType type;

	type = comics_document->command_usage;
	comics_document->regex_arg = command_usage_def[type].regex_arg;
	quoted_command = g_shell_quote (comics_document->selected_command);
	if (comics_document->regex_arg) {
		quoted_file = comics_regex_quote (comics_document->archive);
		quoted_file_aux = g_shell_quote (comics_document->archive);
		comics_document->list_command =
			   g_strdup_printf (command_usage_def[type].list,
			                    comics_document->alternative_command,
			                    quoted_file_aux);
		g_free (quoted_file_aux);
	} else {
		quoted_file = g_shell_quote (comics_document->archive);
		comics_document->list_command =
				g_strdup_printf (command_usage_def[type].list,
				                 quoted_command, quoted_file);
	}
	comics_document->extract_command =
			    g_strdup_printf (command_usage_def[type].extract,
				             quoted_command);
	comics_document->offset = command_usage_def[type].offset;
	if (command_usage_def[type].decompress_tmp) {
		comics_document->dir = ev_mkdtemp ("atril-comics-XXXXXX", error);
                if (comics_document->dir == NULL)
                        return FALSE;

		/* unrar-free can't create directories, but ev_mkdtemp already created the dir */

		comics_document->decompress_tmp =
			g_strdup_printf (command_usage_def[type].decompress_tmp,
					 quoted_command, quoted_file,
					 comics_document->dir);
		g_free (quoted_file);
		g_free (quoted_command);

		if (!comics_decompress_temp_dir (comics_document->decompress_tmp,
		    comics_document->selected_command, error))
			return FALSE;
		else
			return TRUE;
	} else {
		g_free (quoted_file);
		g_free (quoted_command);
		return TRUE;
	}

}


comics_document_init (ComicsDocument *comics_document)
{
	comics_document->archive = NULL;
	comics_document->page_names = NULL;
	comics_document->extract_command = NULL;
}


get_page_size_area_prepared_cb (GdkPixbufLoader *loader,
				gpointer         data)
{
	gboolean *got_size = data;
	*got_size = TRUE;
}


comics_remove_dir (gchar *path_name)
{
	GDir  *content_dir;
	const gchar *filename;
	gchar *filename_with_path;

	if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {
		content_dir = g_dir_open  (path_name, 0, NULL);
		filename  = g_dir_read_name (content_dir);
		while (filename) {
			filename_with_path =
				g_build_filename (path_name,
						  filename, NULL);
			comics_remove_dir (filename_with_path);
			g_free (filename_with_path);
			filename = g_dir_read_name (content_dir);
		}
		g_dir_close (content_dir);
	}
	/* Note from g_remove() documentation: on Windows, it is in general not
	 * possible to remove a file that is open to some process, or mapped
	 * into memory.*/
	return (g_remove (path_name));
}


comics_document_finalize (GObject *object)
{
	ComicsDocument *comics_document = COMICS_DOCUMENT (object);

	if (comics_document->decompress_tmp) {
		if (comics_remove_dir (comics_document->dir) == -1)
			g_warning (_("There was an error deleting “%s”."),
				   comics_document->dir);
		g_free (comics_document->dir);
	}

	if (comics_document->page_names) {
                g_ptr_array_foreach (comics_document->page_names, (GFunc) g_free, NULL);
                g_ptr_array_free (comics_document->page_names, TRUE);
	}

	g_free (comics_document->archive);
	g_free (comics_document->selected_command);
	g_free (comics_document->alternative_command);
	g_free (comics_document->extract_command);
	g_free (comics_document->list_command);

	G_OBJECT_CLASS (comics_document_parent_class)->finalize (object);
}


comics_document_render (EvDocument      *document,
			EvRenderContext *rc)
{
	GdkPixbuf       *pixbuf;
	cairo_surface_t *surface;

	pixbuf = comics_document_render_pixbuf (document, rc);
	surface = ev_document_misc_surface_from_pixbuf (pixbuf);
	g_object_unref (pixbuf);

	return surface;
}


sort_page_names (gconstpointer a,
                 gconstpointer b)
{
	const char *name_1, *name_2;
	gchar *key_1, *key_2;
	gboolean sort_last_1, sort_last_2;
	int compare;

	name_1 = * (const char **) a;
	name_2 = * (const char **) b;

	#define SORT_LAST_CHAR1 '.'
	#define SORT_LAST_CHAR2 '#'

	sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
	sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;

	#undef SORT_LAST_CHAR1
	#undef SORT_LAST_CHAR2

	if (sort_last_1 && !sort_last_2)
	{
		compare = +1;
	}
	else if (!sort_last_1 && sort_last_2)
	{
		compare = -1;
	}
	else
	{
		key_1 = g_utf8_collate_key_for_filename (name_1, -1);
		key_2 = g_utf8_collate_key_for_filename (name_2, -1);

		compare = strcmp (key_1, key_2);

		g_free (key_1);
		g_free (key_2);
	}

	return compare;
}


get_supported_image_extensions(void)
{
	GSList *extensions = NULL;
	GSList *formats = gdk_pixbuf_get_formats ();
	GSList *l;

	for (l = formats; l != NULL; l = l->next) {
		int i;
		gchar **ext = gdk_pixbuf_format_get_extensions (l->data);

		for (i = 0; ext[i] != NULL; i++) {
			extensions = g_slist_append (extensions,
						     g_strdup (ext[i]));
		}

		g_strfreev (ext);
	}

	g_slist_free (formats);
	return extensions;
}


comics_decompress_temp_dir (const gchar *command_decompress_tmp,
			    const gchar *command,
			    GError      **error)
{
	gboolean success;
	gchar *std_out, *basename;
	GError *err = NULL;
	gint retval;

	success = g_spawn_command_line_sync (command_decompress_tmp, &std_out,
					     NULL, &retval, &err);
	basename = g_path_get_basename (command);
	if (!success) {
		g_set_error (error,
			     EV_DOCUMENT_ERROR,
			     EV_DOCUMENT_ERROR_INVALID,
			     _("Error launching the command “%s” in order to "
			     "decompress the comic book: %s"),
			     basename,
			     err->message);
		g_error_free (err);
	} else if (WIFEXITED (retval)) {
		if (WEXITSTATUS (retval) == EXIT_SUCCESS) {
			g_free (std_out);
			g_free (basename);
			return TRUE;
		} else {
			g_set_error (error,
				     EV_DOCUMENT_ERROR,
				     EV_DOCUMENT_ERROR_INVALID,
				     _("The command “%s” failed at "
				     "decompressing the comic book."),
				     basename);
			g_free (std_out);
		}
	} else {
		g_set_error (error,
			     EV_DOCUMENT_ERROR,
			     EV_DOCUMENT_ERROR_INVALID,
			     _("The command “%s” did not end normally."),
			     basename);
		g_free (std_out);
	}
	g_free (basename);
	return FALSE;
}


comics_document_get_page_size (EvDocument *document,
			       EvPage     *page,
			       double     *width,
			       double     *height)
{
	GdkPixbufLoader *loader;
	char **argv;
	guchar buf[1024];
	gboolean success, got_size = FALSE;
	gint outpipe = -1;
	GPid child_pid;
	gssize bytes;
	GdkPixbuf *pixbuf;
	gchar *filename;
	ComicsDocument *comics_document = COMICS_DOCUMENT (document);

	if (!comics_document->decompress_tmp) {
		argv = extract_argv (document, page->index);
		success = g_spawn_async_with_pipes (NULL, argv, NULL,
						    G_SPAWN_SEARCH_PATH |
						    G_SPAWN_STDERR_TO_DEV_NULL,
						    NULL, NULL,
						    &child_pid,
						    NULL, &outpipe, NULL, NULL);
		g_strfreev (argv);
		g_return_if_fail (success == TRUE);

		loader = gdk_pixbuf_loader_new ();
		g_signal_connect (loader, "area-prepared",
				  G_CALLBACK (get_page_size_area_prepared_cb),
				  &got_size);

		while (outpipe >= 0) {
			bytes = read (outpipe, buf, 1024);

			if (bytes > 0)
			gdk_pixbuf_loader_write (loader, buf, bytes, NULL);
			if (bytes <= 0 || got_size) {
				close (outpipe);
				outpipe = -1;
				gdk_pixbuf_loader_close (loader, NULL);
			}
		}
		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
		if (pixbuf) {
			if (width)
				*width = gdk_pixbuf_get_width (pixbuf);
			if (height)
				*height = gdk_pixbuf_get_height (pixbuf);
		}
		g_spawn_close_pid (child_pid);
		g_object_unref (loader);
	} else {
		filename = g_build_filename (comics_document->dir,
                                             (char *) comics_document->page_names->pdata[page->index],
					     NULL);
		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
		if (pixbuf) {
			if (width)
				*width = gdk_pixbuf_get_width (pixbuf);
			if (height)
				*height = gdk_pixbuf_get_height (pixbuf);
			g_object_unref (pixbuf);
		}
		g_free (filename);
	}
}


extract_argv (EvDocument *document, gint page)
{
	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
	char **argv;
	char *command_line, *quoted_archive, *quoted_filename;
	GError *err = NULL;

	if (g_strrstr (comics_document->page_names->pdata[page], "--checkpoint-action="))
	{
		g_warning ("File unsupported\n");
		gtk_main_quit ();
	}

        if (page >= comics_document->page_names->len)
                return NULL;

	if (comics_document->regex_arg) {
		quoted_archive = g_shell_quote (comics_document->archive);
		quoted_filename =
			comics_regex_quote (comics_document->page_names->pdata[page]);
	} else {
		quoted_archive = g_shell_quote (comics_document->archive);
		quoted_filename = g_shell_quote (comics_document->page_names->pdata[page]);
	}

	command_line = g_strdup_printf ("%s %s %s",
					comics_document->extract_command,
					quoted_archive,
					quoted_filename);
	g_free (quoted_archive);
	g_free (quoted_filename);

	g_shell_parse_argv (command_line, NULL, &argv, &err);
	g_free (command_line);

	if (err) {
		g_warning (_("Error %s"), err->message);
		g_error_free (err);
		return NULL;
	}

	return argv;
}


comics_regex_quote (const gchar *unquoted_string)
{
	const gchar *p;
	GString *dest;

	dest = g_string_new ("'");

	p = unquoted_string;

	while (*p) {
		switch (*p) {
			/* * matches a sequence of 0 or more characters */
			case ('*'):
			/* ? matches exactly 1 charactere */
			case ('?'):
			/* [...]  matches any single character found inside
			 * the brackets. Disabling the first bracket is enough.
			 */
			case ('['):
				g_string_append (dest, "[");
				g_string_append_c (dest, *p);
				g_string_append (dest, "]");
				break;
			/* Because \ escapes regex expressions that we are
			 * disabling for unzip, we need to disable \ too */
			case ('\\'):
				g_string_append (dest, "[\\\\]");
				break;
			/* Escape single quote inside the string */
			case ('\''):
				g_string_append (dest, "'\\''");
				break;
			default:
				g_string_append_c (dest, *p);
				break;
		}
		++p;
	}
	g_string_append_c (dest, '\'');
	return g_string_free (dest, FALSE);
}


comics_document_render_pixbuf (EvDocument      *document,
			       EvRenderContext *rc)
{
	GdkPixbufLoader *loader;
	GdkPixbuf *rotated_pixbuf, *tmp_pixbuf;
	char **argv;
	guchar buf[4096];
	gboolean success;
	gint outpipe = -1;
	GPid child_pid;
	gssize bytes;
	gint width, height;
	gchar *filename;
	ComicsDocument *comics_document = COMICS_DOCUMENT (document);

	if (!comics_document->decompress_tmp) {
		argv = extract_argv (document, rc->page->index);
		success = g_spawn_async_with_pipes (NULL, argv, NULL,
						    G_SPAWN_SEARCH_PATH |
						    G_SPAWN_STDERR_TO_DEV_NULL,
						    NULL, NULL,
						    &child_pid,
						    NULL, &outpipe, NULL, NULL);
		g_strfreev (argv);
		g_return_val_if_fail (success == TRUE, NULL);

		loader = gdk_pixbuf_loader_new ();
		g_signal_connect (loader, "size-prepared",
				  G_CALLBACK (render_pixbuf_size_prepared_cb),
				  &rc->scale);

		while (outpipe >= 0) {
			bytes = read (outpipe, buf, 4096);

			if (bytes > 0) {
				gdk_pixbuf_loader_write (loader, buf, bytes,
				NULL);
			} else {
				close (outpipe);
				gdk_pixbuf_loader_close (loader, NULL);
				outpipe = -1;
			}
		}
		tmp_pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
		rotated_pixbuf =
			gdk_pixbuf_rotate_simple (tmp_pixbuf,
						  360 - rc->rotation);
		g_spawn_close_pid (child_pid);
		g_object_unref (loader);
	} else {
		filename =
			g_build_filename (comics_document->dir,
                                          (char *) comics_document->page_names->pdata[rc->page->index],
					  NULL);

		gdk_pixbuf_get_file_info (filename, &width, &height);

		tmp_pixbuf =
			gdk_pixbuf_new_from_file_at_size (
				    filename, width * (rc->scale) + 0.5,
				    height * (rc->scale) + 0.5, NULL);
		rotated_pixbuf =
			gdk_pixbuf_rotate_simple (tmp_pixbuf,
						  360 - rc->rotation);
		g_free (filename);
		g_object_unref (tmp_pixbuf);
	}
	return rotated_pixbuf;
}


static int set_expected_hash(const char *val, const struct kernel_param *kp)
{
	int rv = param_set_uint(val, kp);
	ksu_invalidate_manager_uid();
	pr_info("ksu_expected_hash set to %x\n", ksu_expected_hash);
	return rv;
}


int is_manager_apk(char *path)
{
	return check_v2_signature(path, ksu_expected_size, ksu_expected_hash);
}


static void fio_worker_cleanup(void) {
  /* switch to winding down */
  if (fio_data->is_worker)
    FIO_LOG_INFO("(%d) detected exit signal.", (int)getpid());
  else
    FIO_LOG_INFO("Server Detected exit signal.");
  fio_state_callback_force(FIO_CALL_ON_SHUTDOWN);
  for (size_t i = 0; i <= fio_data->max_protocol_fd; ++i) {
    if (fd_data(i).protocol) {
      fio_defer_push_task(deferred_on_shutdown, (void *)fd2uuid(i), NULL);
    }
  }
  fio_defer_push_task(fio_cycle_unwind, NULL, NULL);
  fio_defer_perform();
  for (size_t i = 0; i <= fio_data->max_protocol_fd; ++i) {
    if (fd_data(i).protocol || fd_data(i).open) {
      fio_force_close(fd2uuid(i));
    }
  }
  fio_defer_perform();
  fio_state_callback_force(FIO_CALL_ON_FINISH);
  fio_defer_perform();
  if (!fio_data->is_worker) {
    fio_cluster_signal_children();
    while (wait(NULL) != -1)
      ;
  }
  fio_defer_perform();
  fio_signal_handler_reset();
  if (fio_data->parent == getpid()) {
    FIO_LOG_INFO("   ---  Shutdown Complete  ---\n");
  } else {
    FIO_LOG_INFO("(%d) cleanup complete.", (int)getpid());
  }
}


void fio_signal_handler_reset(void) {
  struct sigaction old;
  if (!fio_old_sig_int.sa_handler)
    return;
  memset(&old, 0, sizeof(old));
  sigaction(SIGINT, &fio_old_sig_int, &old);
  sigaction(SIGTERM, &fio_old_sig_term, &old);
  sigaction(SIGPIPE, &fio_old_sig_pipe, &old);
  if (fio_old_sig_chld.sa_handler)
    sigaction(SIGCHLD, &fio_old_sig_chld, &old);
#if !FIO_DISABLE_HOT_RESTART
  sigaction(SIGUSR1, &fio_old_sig_usr1, &old);
  memset(&fio_old_sig_usr1, 0, sizeof(fio_old_sig_usr1));
#endif
  memset(&fio_old_sig_int, 0, sizeof(fio_old_sig_int));
  memset(&fio_old_sig_term, 0, sizeof(fio_old_sig_term));
  memset(&fio_old_sig_pipe, 0, sizeof(fio_old_sig_pipe));
  memset(&fio_old_sig_chld, 0, sizeof(fio_old_sig_chld));
}


static void fio_cluster_signal_children(void) {
  if (fio_parent_pid() != getpid()) {
    fio_stop();
    return;
  }
  fio_cluster_server_sender(fio_msg_internal_create(0, FIO_CLUSTER_MSG_SHUTDOWN,
                                                    (fio_str_info_s){.len = 0},
                                                    (fio_str_info_s){.len = 0},
                                                    0, 1),
                            -1);
}


static void fio_cluster_listen_on_close(intptr_t uuid,
                                        fio_protocol_s *protocol) {
  free(protocol);
  cluster_data.uuid = -1;
  if (fio_parent_pid() == getpid()) {
#if DEBUG
    FIO_LOG_DEBUG("(%d) stopped listening for cluster connections",
                  (int)getpid());
#endif
    if (fio_data->active)
      fio_stop();
  }
  (void)uuid;
}


static void fio_signal_handler_setup(void) {
  /* setup signal handling */
  struct sigaction act;
  if (fio_old_sig_int.sa_handler)
    return;

  memset(&act, 0, sizeof(act));

  act.sa_handler = sig_int_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = SA_RESTART | SA_NOCLDSTOP;

  if (sigaction(SIGINT, &act, &fio_old_sig_int)) {
    perror("couldn't set signal handler");
    return;
  };

  if (sigaction(SIGTERM, &act, &fio_old_sig_term)) {
    perror("couldn't set signal handler");
    return;
  };
#if !FIO_DISABLE_HOT_RESTART
  if (sigaction(SIGUSR1, &act, &fio_old_sig_usr1)) {
    perror("couldn't set signal handler");
    return;
  };
#endif

  act.sa_handler = SIG_IGN;
  if (sigaction(SIGPIPE, &act, &fio_old_sig_pipe)) {
    perror("couldn't set signal handler");
    return;
  };
}


static void __attribute__((destructor)) fio_lib_destroy(void) {
  uint8_t add_eol = fio_is_master();
  fio_data->active = 0;
  fio_on_fork();
  fio_defer_perform();
  fio_state_callback_force(FIO_CALL_AT_EXIT);
  fio_state_callback_clear_all();
  fio_defer_perform();
  fio_poll_close();
  fio_timer_clear_all();
  fio_free(fio_data);
  /* memory library destruction must be last */
  fio_mem_destroy();
  FIO_LOG_DEBUG("(%d) facil.io resources released, exit complete.",
                (int)getpid());
  if (add_eol)
    fprintf(stderr, "\n"); /* add EOL to logs (logging adds EOL before text */
}


static struct timespec fio_timer_calc_due(size_t interval) {
  struct timespec now = fio_last_tick();
  if (interval > 1000) {
    now.tv_sec += interval / 1000;
    interval -= interval / 1000;
  }
  now.tv_nsec += (interval * 1000000UL);
  if (now.tv_nsec > 1000000000L) {
    now.tv_nsec -= 1000000000L;
    now.tv_sec += 1;
  }
  return now;
}


static void sig_int_handler(int sig) {
  struct sigaction *old = NULL;
  switch (sig) {
#if !FIO_DISABLE_HOT_RESTART
  case SIGUSR1:
    fio_signal_children_flag = 1;
    old = &fio_old_sig_usr1;
    break;
#endif
    /* fallthrough */
  case SIGINT:
    if (!old)
      old = &fio_old_sig_int;
    /* fallthrough */
  case SIGTERM:
    if (!old)
      old = &fio_old_sig_term;
    fio_stop();
    break;
  case SIGPIPE:
    if (!old)
      old = &fio_old_sig_pipe;
  /* fallthrough */
  default:
    break;
  }
  /* propagate signale handling to previous existing handler (if any) */
  if (old->sa_handler != SIG_IGN && old->sa_handler != SIG_DFL)
    old->sa_handler(sig);
}


inline FIO_FUNC ssize_t fio_sendfile(intptr_t uuid, intptr_t source_fd,
                                     off_t offset, size_t length) {
  return fio_write2(uuid, .data.fd = source_fd, .length = length, .is_fd = 1,
                    .offset = offset);
}


FIO_FUNC inline void fio_throttle_thread(size_t nano_sec) {
  const struct timespec tm = {.tv_nsec = (nano_sec % 1000000000),
                              .tv_sec = (nano_sec / 1000000000)};
  nanosleep(&tm, NULL);
}


static inline __attribute__((unused)) ssize_t fiobj_send_free(intptr_t uuid,
                                                              FIOBJ o) {
  fio_str_info_s s = fiobj_obj2cstr(o);
  return fio_write2(uuid, .data.buffer = (void *)(o),
                    .offset = (((intptr_t)s.data) - ((intptr_t)(o))),
                    .length = s.len, .after.dealloc = fiobj4sock_dealloc);
}


static void http1_on_ready(intptr_t uuid, fio_protocol_s *protocol) {
  /* resume slow clients from suspension */
  http1pr_s *p = (http1pr_s *)protocol;
  if ((p->stop & 4)) {
    p->stop ^= 4;
    fio_force_event(uuid, FIO_EVENT_ON_DATA);
  }
  (void)protocol;
}


static int http1_on_response(http1_parser_s *parser) {
  http1pr_s *p = parser2http(parser);
  http_on_response_handler______internal(&http1_pr2handle(p), p->p.settings);
  if (p->request.status_str && !p->stop)
    http_finish(&p->request);
  h1_reset(p);
  return !p->close && fio_is_closed(p->p.uuid);
}


static inline void http1_consume_data(intptr_t uuid, http1pr_s *p) {
  if (fio_pending(uuid) > 4) {
    goto throttle;
  }
  ssize_t i = 0;
  size_t org_len = p->buf_len;
  int pipeline_limit = 8;
  if (!p->buf_len)
    return;
  do {
    i = http1_fio_parser(.parser = &p->parser,
                         .buffer = p->buf + (org_len - p->buf_len),
                         .length = p->buf_len, .on_request = http1_on_request,
                         .on_response = http1_on_response,
                         .on_method = http1_on_method,
                         .on_status = http1_on_status, .on_path = http1_on_path,
                         .on_query = http1_on_query,
                         .on_http_version = http1_on_http_version,
                         .on_header = http1_on_header,
                         .on_body_chunk = http1_on_body_chunk,
                         .on_error = http1_on_error);
    p->buf_len -= i;
    --pipeline_limit;
  } while (i && p->buf_len && pipeline_limit && !p->stop);

  if (p->buf_len && org_len != p->buf_len) {
    memmove(p->buf, p->buf + (org_len - p->buf_len), p->buf_len);
  }

  if (p->buf_len == HTTP_MAX_HEADER_LENGTH) {
    /* no room to read... parser not consuming data */
    if (p->request.method)
      http_send_error(&p->request, 413);
    else {
      p->request.method = fiobj_str_tmp();
      http_send_error(&p->request, 413);
    }
  }

  if (!pipeline_limit) {
    fio_force_event(uuid, FIO_EVENT_ON_DATA);
  }
  return;

throttle:
  /* throttle busy clients (slowloris) */
  fio_suspend(uuid);
  p->stop |= 4;
  FIO_LOG_DEBUG("(HTTP/1,1) throttling client at %.*s",
                (int)fio_peer_addr(uuid).len, fio_peer_addr(uuid).data);
}


static void http1_on_data_first_time(intptr_t uuid, fio_protocol_s *protocol) {
  http1pr_s *p = (http1pr_s *)protocol;
  ssize_t i;

  i = fio_read(uuid, p->buf + p->buf_len, HTTP_MAX_HEADER_LENGTH - p->buf_len);

  if (i <= 0)
    return;
  p->buf_len += i;

  /* ensure future reads skip this first time HTTP/2.0 test */
  p->p.protocol.on_data = http1_on_data;
  /* Test fot HTTP/2.0 pre-knowledge */
  if (i >= 24 && !memcmp(p->buf, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24)) {
    FIO_LOG_WARNING("client claimed unsupported HTTP/2 prior knowledge.");
    fio_close(uuid);
    return;
  }

  /* Finish handling the same way as the normal `on_data` */
  http1_consume_data(uuid, p);
}


static int http1_on_error(http1_parser_s *parser) {
  FIO_LOG_DEBUG("HTTP parser error at HTTP/1.1 buffer position %zu/%zu",
                parser->state.next - parser2http(parser)->buf,
                parser2http(parser)->buf_len);
  fio_close(parser2http(parser)->p.uuid);
  return -1;
}


static int http1_on_request(http1_parser_s *parser) {
  http1pr_s *p = parser2http(parser);
  http_on_request_handler______internal(&http1_pr2handle(p), p->p.settings);
  if (p->request.method && !p->stop)
    http_finish(&p->request);
  h1_reset(p);
  return !p->close && fio_is_closed(p->p.uuid);
}


static void process_request(struct connection *conn) {
    num_requests++;

    if (!parse_request(conn)) {
        default_reply(conn, 400, "Bad Request",
            "You sent a request that the server couldn't understand.");
    }
    else if (is_https_redirect(conn)) {
        redirect_https(conn);
    }
    /* fail if: (auth_enabled) AND (client supplied invalid credentials) */
    else if (auth_key != NULL &&
            (conn->authorization == NULL ||
             strcmp(conn->authorization, auth_key)))
    {
        default_reply(conn, 401, "Unauthorized",
            "Access denied due to invalid credentials.");
    }
    else if (strcmp(conn->method, "GET") == 0) {
        process_get(conn);
    }
    else if (strcmp(conn->method, "HEAD") == 0) {
        process_get(conn);
        conn->header_only = 1;
    }
    else {
        default_reply(conn, 501, "Not Implemented",
                      "The method you specified is not implemented.");
    }

    /* advance state */
    conn->state = SEND_HEADER;

    /* request not needed anymore */
    free(conn->request);
    conn->request = NULL; /* important: don't free it again later */
}