Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/make_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
CFLAGS="-std=c17 -Wall -Wextra -Wpedantic -Werror -g \
-fsanitize=address,undefined -fno-omit-frame-pointer \
-D_POSIX_C_SOURCE=200112L -Iinclude -Iinclude/mnet -Isrc -pthread"
for t in test_mnet test_http test_mnet_parser test_stress test_features test_security test_client test_welcome test_starvation test_reentrancy; do
for t in test_mnet test_http test_mnet_parser test_stress test_features test_security test_client test_welcome test_starvation test_reentrancy test_http_edge; do
gcc $CFLAGS src/mnet.c src/mnet_app.c src/mnet_response.c \
src/mnet_request.c src/mnet_router.c test/$t.c -o ${t}_asan || exit 1
done
Expand Down Expand Up @@ -119,6 +119,9 @@ jobs:
- name: Run reentrancy tests under ASan/UBSan
run: ASAN_OPTIONS=detect_leaks=0 ./test_reentrancy_asan

- name: Run HTTP edge-case tests under ASan/UBSan
run: ASAN_OPTIONS=detect_leaks=0 ./test_http_edge_asan

test-valgrind:
runs-on: ubuntu-latest
steps:
Expand All @@ -129,7 +132,7 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y valgrind

- name: Build tests
run: make test_mnet test_http test_parser test_stress test_features test_security test_client test_welcome test_starvation test_reentrancy
run: make test_mnet test_http test_parser test_stress test_features test_security test_client test_welcome test_starvation test_reentrancy test_http_edge

- name: Valgrind unit tests
run: valgrind --leak-check=full --error-exitcode=1 ./test_mnet
Expand All @@ -155,6 +158,9 @@ jobs:
- name: Valgrind reentrancy tests
run: valgrind --leak-check=full --error-exitcode=1 --trace-children=yes ./test_reentrancy

- name: Valgrind HTTP edge-case tests
run: valgrind --leak-check=full --error-exitcode=1 --trace-children=yes ./test_http_edge

# The worker pool is the one part of the library that is only exercised with
# threads, so run the concurrency tests under ThreadSanitizer too.
test-tsan:
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ target_link_libraries(test_reentrancy PRIVATE mnet_static)
target_include_directories(test_reentrancy PRIVATE include src test)
add_test(NAME mnet_reentrancy_tests COMMAND test_reentrancy)

add_executable(test_http_edge test/test_http_edge.c)
target_link_libraries(test_http_edge PRIVATE mnet_static)
target_include_directories(test_http_edge PRIVATE include src test)
add_test(NAME mnet_http_edge_tests COMMAND test_http_edge)

# Install
install(TARGETS mnet_static mnet_shared
ARCHIVE DESTINATION lib
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ SRCS = src/mnet.c src/mnet_app.c src/mnet_response.c \
TEST_SRCS = test/test_mnet.c test/test_http.c test/test_mnet_parser.c \
test/test_stress.c test/test_features.c test/test_security.c \
test/test_client.c test/test_welcome.c test/test_starvation.c \
test/test_reentrancy.c
test/test_reentrancy.c test/test_http_edge.c

TEST_BINS = test_mnet test_http test_parser test_stress test_features \
test_security test_client test_welcome test_starvation test_reentrancy
test_security test_client test_welcome test_starvation test_reentrancy test_http_edge

# Default target: build everything
all: lib libmnet.a libmnet.so examples
Expand Down Expand Up @@ -160,6 +160,9 @@ test_starvation: $(SRCS) test/test_starvation.c
test_reentrancy: $(SRCS) test/test_reentrancy.c
$(CC) $(CFLAGS) -Isrc $(SRCS) test/test_reentrancy.c -o $@ $(LDFLAGS)

test_http_edge: $(SRCS) test/test_http_edge.c
$(CC) $(CFLAGS) -Isrc $(SRCS) test/test_http_edge.c -o $@ $(LDFLAGS)

# ---- Run the examples (build + run) ----
# Usage: make run EXAMPLE=example_http_server PORT=8080
# EXAMPLE Example binary to build and run (default: example_http_server)
Expand Down
8 changes: 8 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ test_reentrancy = executable('test_reentrancy',

test('mnet_reentrancy_tests', test_reentrancy)

test_http_edge = executable('test_http_edge',
'test/test_http_edge.c',
link_with: mnet_static,
include_directories: [mnet_inc, include_directories('src'), include_directories('test')],
)

test('mnet_http_edge_tests', test_http_edge)

# Install headers (libraries are installed by default)
install_headers('include/mnet/mnet.h', 'include/mnet/mnet_app.h', 'include/mnet/mnet_request.h',
'include/mnet/mnet_response.h', 'include/mnet/mnet_router.h', 'include/mnet/mnet_socket.h',
Expand Down
5 changes: 5 additions & 0 deletions src/mnet_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,11 @@ static mnet_response_t welcome_response(mnet_app_t *app)
app->timeout_seconds, app->keep_alive_timeout,
app->max_body_size, app->route_count,
time_str, commit_hash);
/* snprintf reports the would-be length on truncation: the buffer has
512 bytes of slack over the template, but clamp anyway so a future
template edit cannot turn truncation into an over-read. */
if (written < 0) written = 0;
if ((size_t)written >= total_len) written = (int)total_len - 1;

mnet_response_t r;
r.status = 200;
Expand Down
37 changes: 37 additions & 0 deletions src/mnet_response.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,26 @@ mnet_response_t mnet_jsonfv(const char *format, va_list args)
else if (mod_j) sv = va_arg(args, long long);
else if (mod_t) sv = (long long)va_arg(args, ptrdiff_t);
else sv = va_arg(args, int);
/* Size the buffer first: a width larger than the 64-byte
stack buffer makes snprintf truncate while reporting
the full length, and the later memcpy would then
over-read the stack. */
int need = snprintf(NULL, 0, nspec, sv);
if (need < 0) need = 0;
if ((size_t)need + 1 > val_cap) {
val = malloc((size_t)need + 1);
if (val == NULL) {
free(buf);
mnet_response_t r = {
.status = 500,
.content_type = "application/json",
.body = strdup("null"),
.body_length = 4,
};
return r;
}
val_cap = (size_t)need + 1;
}
vlen = snprintf(val, val_cap, nspec, sv);
(void)uv;
} else {
Expand All @@ -706,6 +726,23 @@ mnet_response_t mnet_jsonfv(const char *format, va_list args)
else if (mod_j) uv = va_arg(args, unsigned long long);
else if (mod_t) uv = (unsigned long long)va_arg(args, ptrdiff_t);
else uv = va_arg(args, unsigned int);
/* Size the buffer first (see the signed branch). */
int need = snprintf(NULL, 0, nspec, uv);
if (need < 0) need = 0;
if ((size_t)need + 1 > val_cap) {
val = malloc((size_t)need + 1);
if (val == NULL) {
free(buf);
mnet_response_t r = {
.status = 500,
.content_type = "application/json",
.body = strdup("null"),
.body_length = 4,
};
return r;
}
val_cap = (size_t)need + 1;
}
vlen = snprintf(val, val_cap, nspec, uv);
}
}
Expand Down
Loading
Loading