CVE-2024-4323 (published 2024-05-20T12:15Z)

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.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/fluent/fluent-bit
commit_details: repo_url: https://github.com/fluent/fluent-bit
author: Phillip Whelan
author_date: 2024-05-15 10:51:02-04:00
msg: 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>
repository: repo_url: https://github.com/fluent/fluent-bit
repo_name: fluent/fluent-bit
description: Fast and Lightweight Logs and Metrics processor for Linux, BSD, OSX and Windows
repo_language: C
owner: fluent
file_changes: filename: trace.c
change_type: ModificationType.MODIFY
diff: @@ -30,8 +30,28 @@
 #include <fluent-bit/flb_utils.h>
 #include <msgpack.h>
 
-
-static struct flb_input_instance *find_input(struct flb_hs *hs, const char *name)
+#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)
 {
     struct mk_list *head;
     struct flb_input_instance *in;
@@ -39,7 +59,10 @@ static struct flb_input_instance *find_input(struct flb_hs *hs, const char *name
 
     mk_list_foreach(head, &hs->config->inputs) {
         in = mk_list_entry(head, struct flb_input_instance, _head);
-        if (strcmp(name, in->name) == 0) {
+        if (strlen(in->name) != nlen) {
+            continue;
+        }
+        if (strncmp(name, in->name, nlen) == 0) {
             return in;
         }
         if (in->alias) {
@@ -51,26 +74,33 @@ static struct flb_input_instance *find_input(struct flb_hs *hs, const char *name
     return NULL;
 }
 
-static int enable_trace_input(struct flb_hs *hs, const char *name, const char *prefix, const char *output_name, struct mk_list *props)
+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)
 {
     struct flb_input_instance *in;
 
-
-    in = find_input(hs, name);
+    in = find_input(hs, name, nlen);
     if (in == NULL) {
+        flb_error("unable to find input: [%d]%.*s", (int)nlen, (int)nlen, name);
         return 404;
     }
 
     flb_chunk_trace_context_new(in, output_name, prefix, NULL, props);
-    return (in->chunk_trace_ctxt == NULL ? 503 : 0);
+
+    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)
+static int disable_trace_input(struct flb_hs *hs, const char *name, size_t nlen)
 {
     struct flb_input_instance *in;
-    
 
-    in = find_input(hs, name);
+
+    in = find_input(hs, name, nlen);
     if (in == NULL) {
         return 404;
     }
@@ -89,32 +119,35 @@ static flb_sds_t get_input_name(mk_request_t *request)
     if (request->real_path.data == NULL) {
         return NULL;
     }
-    if (request->real_path.len < strlen(base)) {
+    if (request->real_path.len < sizeof(base)-1) {
         return NULL;
     }
 
-    return flb_sds_create_len(&request->real_path.data[strlen(base)],
-                              request->real_path.len - strlen(base));
+    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, msgpack_packer *mp_pck)
+static int http_disable_trace(mk_request_t *request, void *data,
+                              const char *input_name, size_t input_nlen,
+                              msgpack_packer *mp_pck)
 {
     struct flb_hs *hs = data;
     int toggled_on = 503;
 
 
-    toggled_on = disable_trace_input(hs, input_name);
+    toggled_on = disable_trace_input(hs, input_name, input_nlen);
     if (toggled_on < 300) {
         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"));
+        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);
         return 201;
     }
 
     return toggled_on;
 }
 
-static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *result, const char *input_name)
+static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *result,
+                                       const char *input_name, ssize_t input_nlen)
 {
     int ret = -1;
     int i;
@@ -130,11 +163,11 @@ static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *resu
     msgpack_object_str *param_val;
 
 
-    if (result->data.type == MSGPACK_OBJECT_MAP) {    
+    if (result->data.type == MSGPACK_OBJECT_MAP) {
         for (i = 0; i < result->data.via.map.size; i++) {
             key = &result->data.via.map.ptr[i].key;
             val = &result->data.via.map.ptr[i].val;
-            
+
             if (key->type != MSGPACK_OBJECT_STR) {
                 ret = -1;
                 goto parse_error;
@@ -193,7 +226,7 @@ static int msgpack_params_enable_trace(struct flb_hs *hs, msgpack_unpacked *resu
             output_name = flb_sds_create("stdout");
         }
 
-        toggled_on = enable_trace_input(hs, input_name, prefix, output_name, props);
+        toggled_on = enable_trace_input(hs, input_name, input_nlen, prefix, output_name, props);
         if (!toggled_on) {
             ret = -1;
             goto parse_error;
@@ -210,7 +243,9 @@ parse_error:
     return ret;
 }
 
-static int http_enable_trace(mk_request_t *request, void *data, const char *input_name, msgpack_packer *mp_pck)
+static int http_enable_trace(mk_request_t *request, void *data,
+                             const char *input_name, ssize_t input_nlen,
+                             msgpack_packer *mp_pck)
 {
     char *buf = NULL;
     size_t buf_size;
@@ -229,18 +264,18 @@ static int http_enable_trace(mk_request_t *request, void *data, const char *inpu
     struct mk_list *props = NULL;
     struct flb_chunk_trace_limit limit = { 0 };
     struct flb_input_instance *input_instance;
-    
+
 
     if (request->method == MK_METHOD_GET) {
-        ret = enable_trace_input(hs, input_name, "trace.", "stdout", NULL);
+        ret = enable_trace_input(hs, input_name, input_nlen, "trace.", "stdout", NULL);
         if (ret == 0) {
                 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"));
+                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);
                 return 200;
         }
         else {
-            flb_error("unable to enable tracing for %s", input_name);
+            flb_error("unable to enable tracing for %.*s", (int)input_nlen, input_name);
             goto input_error;
         }
     }
@@ -257,7 +292,7 @@ static int http_enable_trace(mk_request_t *request, void *data, const char *inpu
     rc = msgpack_unpack_next(&result, buf, buf_size, &off);
     if (rc != MSGPACK_UNPACK_SUCCESS) {
         ret = 503;
-        flb_error("unable to unpack msgpack parameters for %s", input_name);
+        flb_error("unable to unpack msgpack parameters for %.*s", (int)input_nlen, input_name);
         goto unpack_error;
     }
 
@@ -265,7 +300,7 @@ static int http_enable_trace(mk_request_t *request, void *data, const char *inpu
         for (i = 0; i < result.data.via.map.size; i++) {
             key = &result.data.via.map.ptr[i].key;
             val = &result.data.via.map.ptr[i].val;
-            
+
             if (key->type != MSGPACK_OBJECT_STR) {
                 ret = 503;
                 flb_error("non string key in parameters");
@@ -359,14 +394,14 @@ static int http_enable_trace(mk_request_t *request, void *data, const char *inpu
             output_name = flb_sds_create("stdout");
         }
 
-        ret = enable_trace_input(hs, input_name, prefix, output_name, props);
+        ret = enable_trace_input(hs, input_name, input_nlen, prefix, output_name, props);
         if (ret != 0) {
             flb_error("error when enabling tracing");
             goto parse_error;
         }
 
         if (limit.type != 0) {
-            input_instance = find_input(hs, input_name);
+            input_instance = find_input(hs, input_name, input_nlen);
             if (limit.type == FLB_CHUNK_TRACE_LIMIT_TIME) {
                 flb_chunk_trace_context_set_limit(input_instance->chunk_trace_ctxt, limit.type, limit.seconds);
             }
@@ -377,8 +412,8 @@ static int http_enable_trace(mk_request_t *request, void *data, const char *inpu
     }
 
     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"));
+    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);
 
     ret = 200;
 parse_error:
@@ -417,21 +452,21 @@ static void cb_trace(mk_request_t *request, void *data)
     }
 
     if (request->method == MK_METHOD_POST || request->method == MK_METHOD_GET) {
-        response = http_enable_trace(request, data, input_name, &mp_pck);
+        response = http_enable_trace(request, data, input_name, flb_sds_len(input_name), &mp_pck);
     }
     else if (request->method == MK_METHOD_DELETE) {
-        response = http_disable_trace(request, data, input_name, &mp_pck);
+        response = http_disable_trace(request, data, input_name, flb_sds_len(input_name), &mp_pck);
     }
 error:
     if (response == 404) {
         msgpack_pack_map(&mp_pck, 1);
-        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, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
+        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_NOTFOUND, HTTP_RESULT_NOTFOUND_LEN);
     }
     else if (response == 503) {
         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"));
+        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);
     }
 
     if (input_name != NULL) {
@@ -466,11 +501,11 @@ static void cb_traces(mk_request_t *request, void *data)
     msgpack_unpacked result;
     flb_sds_t error_msg = NULL;
     int response = 200;
-    flb_sds_t input_name;
+    const char *input_name;
+    ssize_t input_nlen;
     msgpack_object_array *inputs = NULL;
     size_t off = 0;
     int i;
-    
 
     /* initialize buffers */
     msgpack_sbuffer_init(&mp_sbuf);
@@ -503,10 +538,10 @@ static void cb_traces(mk_request_t *request, void *data)
         if (result.data.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR) {
             continue;
         }
-        if (result.data.via.map.ptr[i].key.via.str.size < strlen("inputs")) {
+        if (result.data.via.map.ptr[i].key.via.str.size < STR_INPUTS_LEN) {
             continue;
         }
-        if (strncmp(result.data.via.map.ptr[i].key.via.str.ptr, "inputs", strlen("inputs"))) {
+        if (strncmp(result.data.via.map.ptr[i].key.via.str.ptr, STR_INPUTS, STR_INPUTS_LEN)) {
             continue;
         }
         inputs = &result.data.via.map.ptr[i].val.via.array;
@@ -517,48 +552,61 @@ static void cb_traces(mk_request_t *request, void *data)
         error_msg = flb_sds_create("inputs not found");
         goto unpack_error;
     }
-    
+
     msgpack_pack_map(&mp_pck, 2);
 
-    msgpack_pack_str_with_body(&mp_pck, "inputs", strlen("inputs"));
+    msgpack_pack_str_with_body(&mp_pck, STR_INPUTS, STR_INPUTS_LEN);
     msgpack_pack_map(&mp_pck, inputs->size);
 
     for (i = 0; i < inputs->size; i++) {
-        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"));
+        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;
         }
-        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);
+    }
+
+    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);
             }
             else {
-                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_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, "result", strlen("result"));
@@ -569,26 +617,27 @@ unpack_error:
     msgpack_unpacked_destroy(&result);
     if (response == 404) {
         msgpack_pack_map(&mp_pck, 1);
-        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, HTTP_FIELD_STATUS, HTTP_FIELD_STATUS_LEN);
+        msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_NOTFOUND, HTTP_RESULT_NOTFOUND_LEN);
     }
     else if (response == 503) {
         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, 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);
         if (error_msg) {
             msgpack_pack_str_with_body(&mp_pck, error_msg, flb_sds_len(error_msg));
             flb_sds_destroy(error_msg);
         }
         else {
-            msgpack_pack_str_with_body(&mp_pck, "unknown error", strlen("unknown error"));
+            msgpack_pack_str_with_body(&mp_pck, HTTP_RESULT_UNKNOWNERROR,
+                                       HTTP_RESULT_UNKNOWNERROR_LEN);
         }
     }
     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"));
+        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);
     }
 
     /* Export to JSON */
@@ -609,7 +658,9 @@ unpack_error:
 /* Perform registration */
 int api_v1_trace(struct flb_hs *hs)
 {
-    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);
+    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);
+    }
     return 0;
 }

programming_language: C