CVE-2022-3299: Improper Resource Shutdown or Release (CWE-404)
value: A vulnerability was found in Open5GS up to 2.4.10. It has been declared as problematic. Affected by this vulnerability is an unknown functionality in the library lib/sbi/client.c of the component AMF. The manipulation leads to denial of service. The attack can be launched remotely. The name of the patch is 724fa568435dae45ef0c3a48b2aabde052afae88. It is recommended to apply a patch to fix this issue. The identifier VDB-209545 was assigned to this vulnerability.
CWE description: The product does not release or incorrectly releases a resource before it is made available for re-use.
CVSS v3 base score: 6.5
Published: 2022-09-26T13:15Z
Repository: https://github.com/open5gs/open5gs
Language: C
Files changed: lib/sbi/client.c, lib/sbi/nghttp2-server.c
Commit date: 2022-09-25 15:06:37 +0900
Commit message: Fixed HTTP2 crashes for random JSON data (#1769)

Diff: diff --git a/lib/sbi/client.c b/lib/sbi/client.c
index 8f30ac999..e4a5290ca 100644
--- a/lib/sbi/client.c
+++ b/lib/sbi/client.c
@@ -42,10 +42,11 @@ typedef struct connection_s {
 
     char *content;
 
     char *memory;
     size_t size;
+    bool memory_overflow;
 
     char *location;
 
     ogs_timer_t *timer;
     CURL *easy;
@@ -531,10 +532,12 @@ static void check_multi_info(ogs_sbi_client_t *client)
             curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &res_status);
             curl_easy_getinfo(easy, CURLINFO_CONTENT_TYPE, &content_type);
 
             res = resource->data.result;
             if (res == CURLE_OK) {
+                ogs_log_level_e level = OGS_LOG_DEBUG;
+
                 response = ogs_sbi_response_new();
                 ogs_assert(response);
 
                 response->status = res_status;
 
@@ -544,31 +547,42 @@ static void check_multi_info(ogs_sbi_client_t *client)
 
                 /* remove https://localhost:8000 */
                 response->h.uri = ogs_strdup(url);
                 ogs_assert(response->h.uri);
 
-                ogs_debug("[%d:%s] %s",
+                if (content_type)
+                    ogs_sbi_header_set(response->http.headers,
+                            OGS_SBI_CONTENT_TYPE, content_type);
+                if (conn->location)
+                    ogs_sbi_header_set(response->http.headers,
+                            OGS_SBI_LOCATION, conn->location);
+
+                if (conn->memory_overflow == true)
+                    level = OGS_LOG_ERROR;
+
+                ogs_log_message(level, 0, "[%d:%s] %s",
                         response->status, response->h.method, response->h.uri);
 
                 if (conn->memory) {
                     response->http.content =
                         ogs_memdup(conn->memory, conn->size + 1);
                     ogs_assert(response->http.content);
                     response->http.content_length = conn->size;
                     ogs_assert(response->http.content_length);
                 }
 
-                ogs_debug("RECEIVED[%d]", (int)response->http.content_length);
+                ogs_log_message(level, 0, "RECEIVED[%d]",
+                        (int)response->http.content_length);
                 if (response->http.content_length && response->http.content)
-                    ogs_debug("%s", response->http.content);
+                    ogs_log_message(level, 0, "%s", response->http.content);
+
+                if (conn->memory_overflow == true) {
+                    ogs_sbi_response_free(response);
+                    connection_remove(conn);
+                    break;
+                }
 
-                if (content_type)
-                    ogs_sbi_header_set(response->http.headers,
-                            OGS_SBI_CONTENT_TYPE, content_type);
-                if (conn->location)
-                    ogs_sbi_header_set(response->http.headers,
-                            OGS_SBI_LOCATION, conn->location);
             } else
                 ogs_warn("[%d] %s", res, conn->error);
 
             ogs_assert(conn->client_cb);
             conn->client_cb(res == CURLE_OK ? OGS_OK : OGS_ERROR,
@@ -725,12 +739,16 @@ static size_t write_cb(void *contents, size_t size, size_t nmemb, void *data)
     ogs_assert(conn);
 
     realsize = size * nmemb;
     ptr = ogs_realloc(conn->memory, conn->size + realsize + 1);
     if(!ptr) {
-        ogs_fatal("not enough memory (realloc returned NULL)");
-        ogs_assert_if_reached();
+        conn->memory_overflow = true;
+
+        ogs_error("Overflow : conn->size[%d], realsize[%d]",
+                    (int)conn->size, (int)realsize);
+        ogs_log_hexdump(OGS_LOG_ERROR, contents, realsize);
+
         return 0;
     }
 
     conn->memory = ptr;
     memcpy(&(conn->memory[conn->size]), contents, realsize);
diff --git a/lib/sbi/nghttp2-server.c b/lib/sbi/nghttp2-server.c
index 7272ac44e..9c6fc4248 100644
--- a/lib/sbi/nghttp2-server.c
+++ b/lib/sbi/nghttp2-server.c
@@ -80,10 +80,11 @@ typedef struct ogs_sbi_session_s {
 typedef struct ogs_sbi_stream_s {
     ogs_lnode_t             lnode;
 
     int32_t                 stream_id;
     ogs_sbi_request_t       *request;
+    bool                    memory_overflow;
 
     ogs_sbi_session_t       *session;
 } ogs_sbi_stream_t;
 
 static void session_remove(ogs_sbi_session_t *sbi_sess);
@@ -789,16 +790,27 @@ static int on_frame_recv(nghttp2_session *session,
         OGS_GNUC_FALLTHROUGH;
 
     case NGHTTP2_DATA:
         /* HEADERS or DATA frame with +END_STREAM flag */
         if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+            ogs_log_level_e level = OGS_LOG_DEBUG;
 
-            ogs_debug("[%s] %s", request->h.method, request->h.uri);
+            if (stream->memory_overflow == true)
+                level = OGS_LOG_ERROR;
+
+            ogs_log_message(level, 0,
+                    "[%s] %s", request->h.method, request->h.uri);
 
             if (request->http.content_length && request->http.content) {
-                ogs_debug("RECEIVED: %d", (int)request->http.content_length);
-                ogs_debug("%s", request->http.content);
+                ogs_log_message(level, 0,
+                        "RECEIVED: %d", (int)request->http.content_length);
+                ogs_log_message(level, 0, "%s", request->http.content);
+            }
+
+            if (stream->memory_overflow == true) {
+                ogs_error("[DROP] Overflow");
+                break;
             }
 
             if (server->cb(request, stream) != OGS_OK) {
                 ogs_warn("server callback error");
                 ogs_assert(true ==
@@ -965,27 +977,34 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
 
     ogs_assert(data);
     ogs_assert(len);
 
     if (request->http.content == NULL) {
-        request->http.content_length = len;
-        request->http.content =
-            (char*)ogs_malloc(request->http.content_length + 1);
-        ogs_assert(request->http.content);
+        ogs_assert(request->http.content_length == 0);
+        ogs_assert(offset == 0);
+
+        request->http.content = (char*)ogs_malloc(len + 1);
     } else {
-        offset = request->http.content_length;
-        if ((request->http.content_length + len) > OGS_HUGE_LEN) {
-            ogs_error("Overflow : Content-Length[%d], len[%d]",
-                        (int)request->http.content_length, (int)len);
-            ogs_assert_if_reached();
-        }
-        request->http.content_length += len;
-        request->http.content = (char *)ogs_realloc(
-                request->http.content, request->http.content_length + 1);
-        ogs_assert(request->http.content);
+        ogs_assert(request->http.content_length != 0);
+
+        request->http.content = (char*)ogs_realloc(
+                request->http.content, request->http.content_length + len + 1);
     }
 
+    if (!request->http.content) {
+        stream->memory_overflow = true;
+
+        ogs_error("Overflow : Content-Length[%d], len[%d]",
+                    (int)request->http.content_length, (int)len);
+        ogs_log_hexdump(OGS_LOG_ERROR, data, len);
+
+        return 0;
+    }
+
+    offset = request->http.content_length;
+    request->http.content_length += len;
+
     memcpy(request->http.content + offset, data, len);
     request->http.content[request->http.content_length] = '\0';
 
     return 0;
 }

Vulnerable code:                 ogs_debug("[%d:%s] %s",
                ogs_debug("RECEIVED[%d]", (int)response->http.content_length);
                    ogs_debug("%s", response->http.content);
                if (content_type)
                    ogs_sbi_header_set(response->http.headers,
                            OGS_SBI_CONTENT_TYPE, content_type);
                if (conn->location)
                    ogs_sbi_header_set(response->http.headers,
                            OGS_SBI_LOCATION, conn->location);
        ogs_fatal("not enough memory (realloc returned NULL)");
        ogs_assert_if_reached();
            ogs_debug("[%s] %s", request->h.method, request->h.uri);
                ogs_debug("RECEIVED: %d", (int)request->http.content_length);
                ogs_debug("%s", request->http.content);
        request->http.content_length = len;
        request->http.content =
            (char*)ogs_malloc(request->http.content_length + 1);
        ogs_assert(request->http.content);
        offset = request->http.content_length;
        if ((request->http.content_length + len) > OGS_HUGE_LEN) {
            ogs_error("Overflow : Content-Length[%d], len[%d]",
                        (int)request->http.content_length, (int)len);
            ogs_assert_if_reached();
        }
        request->http.content_length += len;
        request->http.content = (char *)ogs_realloc(
                request->http.content, request->http.content_length + 1);
        ogs_assert(request->http.content);
Fixed code:     bool memory_overflow;
                ogs_log_level_e level = OGS_LOG_DEBUG;

                if (content_type)
                    ogs_sbi_header_set(response->http.headers,
                            OGS_SBI_CONTENT_TYPE, content_type);
                if (conn->location)
                    ogs_sbi_header_set(response->http.headers,
                            OGS_SBI_LOCATION, conn->location);

                if (conn->memory_overflow == true)
                    level = OGS_LOG_ERROR;

                ogs_log_message(level, 0, "[%d:%s] %s",
                ogs_log_message(level, 0, "RECEIVED[%d]",
                        (int)response->http.content_length);
                    ogs_log_message(level, 0, "%s", response->http.content);

                if (conn->memory_overflow == true) {
                    ogs_sbi_response_free(response);
                    connection_remove(conn);
                    break;
                }
        conn->memory_overflow = true;

        ogs_error("Overflow : conn->size[%d], realsize[%d]",
                    (int)conn->size, (int)realsize);
        ogs_log_hexdump(OGS_LOG_ERROR, contents, realsize);

    bool                    memory_overflow;
            ogs_log_level_e level = OGS_LOG_DEBUG;
            if (stream->memory_overflow == true)
                level = OGS_LOG_ERROR;

            ogs_log_message(level, 0,
                    "[%s] %s", request->h.method, request->h.uri);
                ogs_log_message(level, 0,
                        "RECEIVED: %d", (int)request->http.content_length);
                ogs_log_message(level, 0, "%s", request->http.content);
            }

            if (stream->memory_overflow == true) {
                ogs_error("[DROP] Overflow");
                break;
        ogs_assert(request->http.content_length == 0);
        ogs_assert(offset == 0);

        request->http.content = (char*)ogs_malloc(len + 1);
        ogs_assert(request->http.content_length != 0);

        request->http.content = (char*)ogs_realloc(
                request->http.content, request->http.content_length + len + 1);
    if (!request->http.content) {
        stream->memory_overflow = true;

        ogs_error("Overflow : Content-Length[%d], len[%d]",
                    (int)request->http.content_length, (int)len);
        ogs_log_hexdump(OGS_LOG_ERROR, data, len);

        return 0;
    }

    offset = request->http.content_length;
    request->http.content_length += len;