VYPR
Critical severity9.1NVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-49840

CVE-2026-49840

Description

FreeSWITCH versions prior to 1.11.1 are vulnerable to a heap corruption or crash via a negative Content-Length in ESL frames before authentication.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

FreeSWITCH versions prior to 1.11.1 are vulnerable to a heap corruption or crash via a negative Content-Length in ESL frames before authentication.

Vulnerability

FreeSWITCH versions prior to 1.11.1 are vulnerable to a heap corruption or crash due to improper parsing of the Content-Length header in ESL frames by the esl_recv_event() function. The function uses atol() to parse the length and passes the result directly to malloc(len + 1) without checking for negative values or excessive magnitude. This affects fs_cli, mod_esl, mod_hash, and any third-party application linked against libesl. The outbound socket dialplan application in mod_event_socket is not affected as it uses its own ESL implementation [1].

Exploitation

An attacker or man-in-the-middle attacker can send a specially crafted ESL frame with a negative Content-Length value to a connecting libesl client. This occurs before the client has authenticated to the ESL peer. The malicious frame can be the first event the client reads, requiring no credentials or prior interaction. If Content-Length is -1, a heap buffer overflow occurs. If Content-Length is -2 or lower, a NULL pointer dereference leads to a crash [1].

Impact

Successful exploitation can lead to heap corruption or a crash of the affected process. A heap buffer overflow with attacker-controlled length and contents is possible, though remote code execution has not been demonstrated. In cases of a negative Content-Length less than -1, the process will crash due to dereferencing a NULL pointer returned by malloc [1].

Mitigation

This issue has been fixed in FreeSWITCH version 1.11.1, released on 2026-06-09 [2]. Operators who cannot upgrade immediately should restrict which hosts libesl clients connect to and ensure the control-plane network is unreachable from less-trusted hosts. No reliable workaround is available [1].

AI Insight generated on Jun 9, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2

Patches

2
22de26cc7ca5

Merge commit from fork

https://github.com/signalwire/freeswitchDmitry VerenitsinMay 26, 2026Fixed in 1.11.1via llm-release-walk
7 files changed · +134 6
  • configure.ac+1 0 modified
    @@ -2145,6 +2145,7 @@ AC_CONFIG_FILES([Makefile
     		build/standalone_module/freeswitch.pc
     		build/modmake.rules
                     libs/esl/Makefile
    +                libs/esl/tests/Makefile
                     libs/esl/perl/Makefile
                     libs/esl/php/Makefile
     		libs/xmlrpc-c/include/xmlrpc-c/config.h
    
  • .github/workflows/unit-test.yml+7 0 modified
    @@ -107,6 +107,13 @@ jobs:
             run: |
               ./run-tests.sh ${{ inputs.total-groups }} ${{ inputs.current-group }} --output-dir logs || exit 1
     
    +      - name: Run libesl tests
    +        if: ${{ inputs.current-group == 1 }}
    +        shell: bash
    +        working-directory: ${{ inputs.working-directory }}/../../libs/esl
    +        run: |
    +          make check
    +
           - name: Collect unit test logs
             if: always()
             shell: bash
    
  • libs/esl/Makefile.am+1 1 modified
    @@ -1,5 +1,5 @@
     AUTOMAKE_OPTIONS = foreign subdir-objects
    -SUBDIRS = . perl
    +SUBDIRS = . perl tests
     MYLIB=./.libs/libesl.a
     LIBS=-lncurses -lpthread -lm
     LDFLAGS=-L. $(SYSTEM_LDFLAGS)
    
  • libs/esl/src/esl.c+16 5 modified
    @@ -1349,12 +1349,22 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
     	if ((cl = esl_event_get_header(revent, "content-length"))) {
     		char *body;
     		esl_ssize_t sofar = 0;
    -		
    +
     		len = atol(cl);
    -		body = malloc(len+1);
    -		esl_assert(body);
    -		*(body + len) = '\0';
    -		
    +
    +		if (len < 0 || len > ESL_MAX_CONTENT_LENGTH) {
    +			esl_event_destroy(&revent);
    +			goto fail;
    +		}
    +
    +		body = malloc(len + 1);
    +		if (!body) {
    +			esl_event_destroy(&revent);
    +			goto fail;
    +		}
    +
    +		body[len] = '\0';
    +
     		do {
     			esl_ssize_t r,s = esl_buffer_inuse(handle->packet_buf);
     
    @@ -1367,6 +1377,7 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
     					if (!(strerror_r(handle->errnum, handle->err, sizeof(handle->err))))
     						*(handle->err)=0;
     					free(body);
    +					esl_event_destroy(&revent);
     					goto fail;
     				} else if (r == 0) {
     					continue;
    
  • libs/esl/src/include/esl.h+2 0 modified
    @@ -217,6 +217,8 @@ typedef enum {
     #define esl_strlen_zero_buf(s) (*(s) == '\0')
     #define end_of(_s) *(*_s == '\0' ? _s : _s + strlen(_s) - 1)
     
    +#define ESL_MAX_CONTENT_LENGTH (16 * 1024 * 1024)
    +
     #ifdef WIN32
     #include <winsock2.h>
     #include <windows.h>
    
  • libs/esl/tests/Makefile.am+11 0 added
    @@ -0,0 +1,11 @@
    +AUTOMAKE_OPTIONS = foreign
    +
    +if BUILD_TESTS
    +noinst_PROGRAMS = test_recv_event
    +TESTS = $(noinst_PROGRAMS)
    +
    +test_recv_event_SOURCES = test_recv_event.c
    +test_recv_event_CFLAGS  = $(AM_CFLAGS) -I$(switch_srcdir)/libs/esl/src/include
    +test_recv_event_LDADD   = $(top_builddir)/libs/esl/libesl.la
    +test_recv_event_LDFLAGS = $(AM_LDFLAGS) -lpthread -lm
    +endif
    
  • libs/esl/tests/test_recv_event.c+96 0 added
    @@ -0,0 +1,96 @@
    +/*
    + * test_recv_event.c
    + *
    + * Verifies that esl_recv_event() rejects out-of-range Content-Length
    + * values: negative numbers and values above ESL_MAX_CONTENT_LENGTH must
    + * cause the function to return ESL_FAIL and mark the handle as
    + * disconnected, leaving no allocated state behind.
    + *
    + * POSIX-only: uses socketpair(2). Returns 77 on Windows so automake
    + * marks the test as skipped.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <string.h>
    +
    +#ifdef _WIN32
    +
    +int main(void)
    +{
    +	return 77;
    +}
    +
    +#else
    +
    +#include <sys/types.h>
    +#include <sys/socket.h>
    +#include <unistd.h>
    +
    +#include <esl.h>
    +
    +#define TEST_ASSERT(cond) do {						\
    +	if (!(cond)) {							\
    +		fprintf(stderr, "FAIL %s:%d: %s\n",			\
    +			__FILE__, __LINE__, #cond);			\
    +		exit(1);						\
    +	}								\
    +} while (0)
    +
    +static void prepare_handle(esl_handle_t *h, esl_socket_t s)
    +{
    +	memset(h, 0, sizeof(*h));
    +	h->sock = s;
    +	h->connected = 1;
    +	TEST_ASSERT(esl_mutex_create(&h->mutex) == ESL_SUCCESS);
    +	TEST_ASSERT(esl_buffer_create(&h->packet_buf,
    +		BUF_CHUNK, BUF_START, 0) == ESL_SUCCESS);
    +}
    +
    +static void expect_rejected(const char *frame, const char *desc)
    +{
    +	int sv[2];
    +	esl_handle_t h;
    +	size_t n = strlen(frame);
    +	ssize_t w;
    +
    +	fprintf(stderr, "  case: %s\n", desc);
    +
    +	TEST_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
    +
    +	prepare_handle(&h, sv[0]);
    +
    +	w = write(sv[1], frame, n);
    +	TEST_ASSERT(w == (ssize_t) n);
    +	close(sv[1]);
    +
    +	TEST_ASSERT(esl_recv_event(&h, 0, NULL) == ESL_FAIL);
    +	TEST_ASSERT(h.connected == 0);
    +
    +	esl_disconnect(&h);
    +}
    +
    +int main(void)
    +{
    +	fprintf(stderr, "test_recv_event: invalid Content-Length is rejected\n");
    +
    +	expect_rejected(
    +		"Content-Type: text/event-plain\n"
    +		"Content-Length: -1\n\n",
    +		"negative Content-Length: -1");
    +
    +	expect_rejected(
    +		"Content-Type: text/event-plain\n"
    +		"Content-Length: -2\n\n",
    +		"negative Content-Length: -2");
    +
    +	expect_rejected(
    +		"Content-Type: text/event-plain\n"
    +		"Content-Length: 99999999999\n\n",
    +		"Content-Length above ESL_MAX_CONTENT_LENGTH");
    +
    +	fprintf(stderr, "OK\n");
    +	return 0;
    +}
    +
    +#endif
    
67b62fb969a6

Merge commit from fork

https://github.com/signalwire/freeswitchDmitry VerenitsinMay 26, 2026Fixed in 1.11.1via llm-release-walk
1 file changed · +14 3
  • src/mod/endpoints/mod_verto/mod_verto.c+14 3 modified
    @@ -43,6 +43,7 @@ SWITCH_MODULE_DEFINITION(mod_verto, mod_verto_load, mod_verto_shutdown, mod_vert
     #define HTTP_CHUNK_SIZE 1024 * 32
     #define HTTP_POST_MAX_BODY (10 * 1024 * 1024)   /* max accepted Content-Length for form-urlencoded POST */
     #define EP_NAME "verto.rtc"
    +#define VERTO_SPEED_TEST_MAX_SIZE (10 * 1024 * 1024)
     //#define WSS_STANDALONE 1
     #include "libks/ks.h"
     
    @@ -2112,24 +2113,34 @@ static void client_run(jsock_t *jsock)
     					char repl[2048] = "";
     					switch_time_t a, b;
     
    +					if (!switch_test_flag(jsock, JPFLAG_AUTHED)) {
    +						die("%s Speed-test request before authentication\n", jsock->name);
    +					}
    +
    +					if (bytes < 4) {
    +						continue;
    +					}
    +
     					if (s[1] == 'S' && s[2] == 'P') {
     
     						if (s[3] == 'U') {
    -							int i, size = 0;
    +							int i;
    +							long size;
     							char *p = s+4;
     							int loops = 0;
     							int rem = 0;
     							int dur = 0, j = 0;
     
    -							if ((size = atoi(p)) <= 0) {
    +							size = strtol(p, NULL, 10);
    +							if (size <= 0 || size > VERTO_SPEED_TEST_MAX_SIZE) {
     								continue;
     							}
     
     							a = switch_time_now();
     							do {
     								bytes = kws_read_frame(jsock->ws, &oc, &data);
     								s = (char *) data;
    -							} while (bytes && data && s[0] == '#' && s[3] == 'B');
    +							} while (bytes >= 4 && data && s[0] == '#' && s[3] == 'B');
     							b = switch_time_now();
     
     							if (!bytes || !data) continue;
    

Vulnerability mechanics

Root cause

"The esl_recv_event function mishandles the Content-Length header, allowing negative or excessively large values."

Attack vector

A malicious or man-in-the-middle ESL peer can send a specially crafted frame with a negative or oversized Content-Length header to a process linked against libesl. This occurs before the client has authenticated to the peer. The function `esl_recv_event` parses this value using `atol()` and then attempts to allocate memory based on it, leading to heap corruption or a crash [patch_id=5390373].

Affected code

The vulnerability exists in the `esl_recv_event` function within `libs/esl/src/esl.c`. Specifically, the parsing of the `content-length` header and subsequent memory allocation using `malloc(len+1)` are affected.

What the fix does

The patch modifies `esl_recv_event` to validate the `Content-Length` header. It now rejects negative values and values exceeding `ESL_MAX_CONTENT_LENGTH` (16 MiB). Additionally, it checks for `malloc` failures instead of relying on asserts and caps the maximum accepted length [patch_id=5390373]. This prevents heap corruption and crashes by ensuring memory allocation is safe.

Preconditions

  • inputThe attacker must be able to send a malformed ESL frame with an invalid Content-Length header.
  • networkThe attacker must be able to act as an ESL peer or a man-in-the-middle.

Generated on Jun 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.