mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-04 19:31:56 +00:00
Merge commit from fork
* [libesl] Validate `Content-Length` in `esl_recv_event`. `atol()` accepted negative values, allowing a remote ESL peer to cause a one-byte heap underwrite (`Content-Length: -1`) or NULL-pointer dereference (`Content-Length: -2`, since `esl_assert` compiles out under `NDEBUG`). Reject negative and oversized values, and check `malloc` failure instead of relying on `assert`. Cap at `ESL_MAX_CONTENT_LENGTH` (16 MiB). * [libesl] Add test_recv_event.
This commit is contained in:
committed by
GitHub
parent
02ac36bb11
commit
22de26cc7c
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
+14
-3
@@ -1351,9 +1351,19 @@ ESL_DECLARE(esl_status_t) esl_recv_event(esl_handle_t *handle, int check_q, esl_
|
||||
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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user