CVE-2024-4323 (C, published 2024-05-20T12:15Z)
Description: value: A memory corruption vulnerability in Fluent Bit versions 2.0.7 thru 3.0.3. This issue lies in the embedded http server’s parsing of trace requests and may result in denial of service conditions, information disclosure, or remote code execution.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/fluent/fluent-bit
Files changed:
src/http_server/api/v1/trace.c
Commit message: Merge pull request from GHSA-5rjf-prwh-pp7q

* api/v1/traces: validate inputs when enabling traces.

validate the array of inputs when enabling multiple traces that
they are strings.

this patch also refactors out the allocation of said input name.

Signed-off-by: Phillip Adair Stewart Whelan <phillip.whelan@chronosphere.io>

* api/v1/traces: disable traces api when tracing is disabled.

Signed-off-by: Phillip Adair Stewart Whelan <phillip.whelan@chronosphere.io>

* api/v1/trace: use macros for strings and lengths in responses.

avoid strlen when creating http response, especially in loops,
by predefining them via macros.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

* api/v1/trace: use sizeof for string length macros.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

* api/v1/trace: use signed lenghts for strings.

this avoid potential integer overflows when using them as
specifiers for format strings.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

* api/v1/traces: use macro for inputs string.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

* api/v1/traces: use sizeof when comparing against base path.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

* api/v1/traces: replace strlen with flb_sds_len when using flb_sds_t.

Signed-off-by: Phillip Whelan <phil@calyptia.com>

---------

Signed-off-by: Phillip Adair Stewart Whelan <phillip.whelan@chronosphere.io>
Signed-off-by: Phillip Whelan <phil@calyptia.com>

Vulnerable code:

static struct flb_input_instance *find_input(struct flb_hs *hs, const char *name)
        if (strcmp(name, in->name) == 0) {
static int enable_trace_input(struct flb_hs *hs, const char *name, const char *prefix, const char *output_name, struct mk_list *props)

    in = find_input(hs, name);
    return (in->chunk_trace_ctxt == NULL ? 503 : 0);
static int disable_trace_input(struct flb_hs *hs, const char *name)
    
    in = find_input(hs, name);
    if (request->real_path.len < strlen(base)) {
    return flb_sds_create_len(&request->real_path.data[strlen(base)],
                              request->real_path.len - strlen(base));
static int http_disable_trace(mk_request_t *request, void *data, const char *input_name, msgpack_packer *mp_pck)
    toggled_on = disable_trace_input(hs, input_name);
        msgpack_pack_str_with_body(mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(mp_pck, "ok", strlen("ok"));
static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *result, const char *input_name)
    if (result->data.type == MSGPACK_OBJECT_MAP) {    
            
        toggled_on = enable_trace_input(hs, input_name, prefix, output_name, props);
static int http_enable_trace(mk_request_t *request, void *data, const char *input_name, msgpack_packer *mp_pck)
    
        ret = enable_trace_input(hs, input_name, "trace.", "stdout", NULL);
                msgpack_pack_str_with_body(mp_pck, "status", strlen("status"));
                msgpack_pack_str_with_body(mp_pck, "ok", strlen("ok"));
            flb_error("unable to enable tracing for %s", input_name);
        flb_error("unable to unpack msgpack parameters for %s", input_name);
            
        ret = enable_trace_input(hs, input_name, prefix, output_name, props);
            input_instance = find_input(hs, input_name);
    msgpack_pack_str_with_body(mp_pck, "status", strlen("status"));
    msgpack_pack_str_with_body(mp_pck, "ok", strlen("ok"));
        response = http_enable_trace(request, data, input_name, &mp_pck);
        response = http_disable_trace(request, data, input_name, &mp_pck);
        msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(&mp_pck, "not found", strlen("not found"));
        msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(&mp_pck, "error", strlen("error"));
    flb_sds_t input_name;
    
        if (result.data.via.map.ptr[i].key.via.str.size < strlen("inputs")) {
        if (strncmp(result.data.via.map.ptr[i].key.via.str.ptr, "inputs", strlen("inputs"))) {
    
    msgpack_pack_str_with_body(&mp_pck, "inputs", strlen("inputs"));
        input_name = flb_sds_create_len(inputs->ptr[i].via.str.ptr, inputs->ptr[i].via.str.size);
        msgpack_pack_str_with_body(&mp_pck, input_name, flb_sds_len(input_name));
        if (inputs->ptr[i].type != MSGPACK_OBJECT_STR) {
            msgpack_pack_map(&mp_pck, 1);
            msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
            msgpack_pack_str_with_body(&mp_pck, "error", strlen("error"));
        else {
            if (request->method == MK_METHOD_POST || request->method == MK_METHOD_GET) {
                ret = msgpack_params_enable_trace((struct flb_hs *)data, &result, input_name);
                if (ret != 0) {
                    msgpack_pack_map(&mp_pck, 2);
                    msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
                    msgpack_pack_str_with_body(&mp_pck, "error", strlen("error"));
                    msgpack_pack_str_with_body(&mp_pck, "returncode", strlen("returncode"));
                    msgpack_pack_int64(&mp_pck, ret);
                }
                else {
                    msgpack_pack_map(&mp_pck, 1);
                    msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
                    msgpack_pack_str_with_body(&mp_pck, "ok", strlen("ok"));
                }
            }
            else if (request->method == MK_METHOD_DELETE) {
                disable_trace_input((struct flb_hs *)data, input_name);
                msgpack_pack_map(&mp_pck, 2);
                msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
                msgpack_pack_str_with_body(&mp_pck, "error", strlen("error"));
                msgpack_pack_str_with_body(&mp_pck, "message", strlen("message"));
                msgpack_pack_str_with_body(&mp_pck, "method not allowed", strlen("method not allowed"));
        msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(&mp_pck, "not found", strlen("not found"));
        msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(&mp_pck, "error", strlen("error"));
        msgpack_pack_str_with_body(&mp_pck, "message", strlen("message"));
            msgpack_pack_str_with_body(&mp_pck, "unknown error", strlen("unknown error"));
        msgpack_pack_str_with_body(&mp_pck, "status", strlen("status"));
        msgpack_pack_str_with_body(&mp_pck, "ok", strlen("ok"));
    mk_vhost_handler(hs->ctx, hs->vid, "/api/v1/traces/", cb_traces, hs);
    mk_vhost_handler(hs->ctx, hs->vid, "/api/v1/trace/*", cb_trace, hs);

Fixed code:
#define STR_INPUTS "inputs"
#define STR_INPUTS_LEN (sizeof(STR_INPUTS)-1)

#define HTTP_FIELD_MESSAGE        "message"
#define HTTP_FIELD_MESSAGE_LEN    (sizeof(HTTP_FIELD_MESSAGE)-1)
#define HTTP_FIELD_STATUS         "status"
#define HTTP_FIELD_STATUS_LEN     (sizeof(HTTP_FIELD_STATUS)-1)
#define HTTP_FIELD_RETURNCODE     "returncode"
#define HTTP_FIELD_RETURNCODE_LEN (sizeof(HTTP_FIELD_RETURNCODE)-1)

#define HTTP_RESULT_OK                   "ok"
#define HTTP_RESULT_OK_LEN               (sizeof(HTTP_RESULT_OK)-1)
#define HTTP_RESULT_ERROR                "error"
#define HTTP_RESULT_ERROR_LEN            (sizeof(HTTP_RESULT_ERROR)-1)
#define HTTP_RESULT_NOTFOUND             "not found"
#define HTTP_RESULT_NOTFOUND_LEN         (sizeof(HTTP_RESULT_NOTFOUND)-1)
#define HTTP_RESULT_METHODNOTALLOWED     "method not allowed"
#define HTTP_RESULT_METHODNOTALLOWED_LEN (sizeof(HTTP_RESULT_METHODNOTALLOWED)-1)
#define HTTP_RESULT_UNKNOWNERROR         "unknown error"
#define HTTP_RESULT_UNKNOWNERROR_LEN     (sizeof(HTTP_RESULT_UNKNOWNERROR)-1)

static struct flb_input_instance *find_input(struct flb_hs *hs, const char *name, size_t nlen)
        if (strlen(in->name) != nlen) {
            continue;
        }
        if (strncmp(name, in->name, nlen) == 0) {
static int enable_trace_input(struct flb_hs *hs, const char *name, ssize_t nlen, const char *prefix,
                              const char *output_name, struct mk_list *props)
    in = find_input(hs, name, nlen);
        flb_error("unable to find input: [%d]%.*s", (int)nlen, (int)nlen, name);

    if (in->chunk_trace_ctxt == NULL) {
        flb_error("unable to start tracing");
        return 503;
    }

    return 0;
static int disable_trace_input(struct flb_hs *hs, const char *name, size_t nlen)

    in = find_input(hs, name, nlen);
    if (request->real_path.len < sizeof(base)-1) {
    return flb_sds_create_len(&request->real_path.data[sizeof(base)-1],
                              request->real_path.len - sizeof(base)-1);
static int http_disable_trace(mk_request_t *request, void *data,
                              const char *input_name, size_t input_nlen,
                              msgpack_packer *mp_pck)
    toggled_on = disable_trace_input(hs, input_name, input_nlen);
        msgpack_pack_str_with_body(mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *result,
                                       const char *input_name, ssize_t input_nlen)
    if (result->data.type == MSGPACK_OBJECT_MAP) {

        toggled_on = enable_trace_input(hs, input_name, input_nlen, prefix, output_name, props);
static int http_enable_trace(mk_request_t *request, void *data,
                             const char *input_name, ssize_t input_nlen,
                             msgpack_packer *mp_pck)

        ret = enable_trace_input(hs, input_name, input_nlen, "trace.", "stdout", NULL);
                msgpack_pack_str_with_body(mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
                msgpack_pack_str_with_body(mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
            flb_error("unable to enable tracing for %.*s", (int)input_nlen, input_name);
        flb_error("unable to unpack msgpack parameters for %.*s", (int)input_nlen, input_name);

        ret = enable_trace_input(hs, input_name, input_nlen, prefix, output_name, props);
            input_instance = find_input(hs, input_name, input_nlen);
    msgpack_pack_str_with_body(mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
    msgpack_pack_str_with_body(mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
        response = http_enable_trace(request, data, input_name, flb_sds_len(input_name), &mp_pck);
        response = http_disable_trace(request, data, input_name, flb_sds_len(input_name), &mp_pck);
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_NOTFOUND, HTTP_RESULT_NOTFOUND_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_ERROR, HTTP_RESULT_ERROR_LEN);
    const char *input_name;
    ssize_t input_nlen;
        if (result.data.via.map.ptr[i].key.via.str.size < STR_INPUTS_LEN) {
        if (strncmp(result.data.via.map.ptr[i].key.via.str.ptr, STR_INPUTS, STR_INPUTS_LEN)) {

    msgpack_pack_str_with_body(&mp_pck, STR_INPUTS, STR_INPUTS_LEN);
        if (inputs->ptr[i].type != MSGPACK_OBJECT_STR || inputs->ptr[i].via.str.ptr == NULL) {
            response = 503;
            error_msg = flb_sds_create("invalid input");
            msgpack_sbuffer_clear(&mp_sbuf);
            goto unpack_error;
    }

    for (i = 0; i < inputs->size; i++) {

        input_name = inputs->ptr[i].via.str.ptr;
        input_nlen = inputs->ptr[i].via.str.size;

        msgpack_pack_str_with_body(&mp_pck, input_name, input_nlen);

        if (request->method == MK_METHOD_POST) {

            ret = msgpack_params_enable_trace((struct flb_hs *)data, &result,
                                              input_name, input_nlen);

            if (ret != 0) {
                msgpack_pack_map(&mp_pck, 2);
                msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
                msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_ERROR, HTTP_RESULT_ERROR_LEN);
                msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_RETURNCODE,
                                           HTTP_FIELD_RETURNCODE_LEN);
                msgpack_pack_int64(&mp_pck, ret);
                msgpack_pack_map(&mp_pck, 1);
                msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
                msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
        else if (request->method == MK_METHOD_DELETE) {
            disable_trace_input((struct flb_hs *)data, input_name, input_nlen);
            msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
            msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
        }
        else {
            msgpack_pack_map(&mp_pck, 2);
            msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
            msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_ERROR, HTTP_RESULT_ERROR_LEN);
            msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_MESSAGE, HTTP_FIELD_MESSAGE_LEN);
            msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_METHODNOTALLOWED,
                                       HTTP_RESULT_METHODNOTALLOWED_LEN);
        }
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_NOTFOUND, HTTP_RESULT_NOTFOUND_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_MESSAGE, HTTP_FIELD_MESSAGE_LEN);
            msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_UNKNOWNERROR,
                                       HTTP_RESULT_UNKNOWNERROR_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_OK, HTTP_RESULT_OK_LEN);
    if (hs->config->enable_chunk_trace == FLB_TRUE) {
        mk_vhost_handler(hs->ctx, hs->vid, "/api/v1/traces/", cb_traces, hs);
        mk_vhost_handler(hs->ctx, hs->vid, "/api/v1/trace/*", cb_trace, hs);
    }