mirror of
https://github.com/eclipse-mosquitto/mosquitto.git
synced 2026-09-26 03:43:47 +08:00
Check for mismatched mosquitto_malloc / free calls.
This commit is contained in:
@@ -40,8 +40,8 @@ jobs:
|
||||
submodules: 'true'
|
||||
-
|
||||
name: make
|
||||
run: make
|
||||
run: make ALLOC_MISMATCH_ABORT=yes
|
||||
-
|
||||
name: make test
|
||||
run: |
|
||||
make ptest
|
||||
make ALLOC_MISMATCH_ABORT=yes ptest
|
||||
|
||||
@@ -42,6 +42,7 @@ option(WITH_TLS "Include SSL/TLS support?" ON)
|
||||
option(WITH_TLS_PSK "Include TLS-PSK support (requires WITH_TLS)?" ON)
|
||||
option(WITH_EC "Include Elliptic Curve support (requires WITH_TLS)?" ON)
|
||||
option(WITH_TESTS "Enable tests" ON)
|
||||
option(INC_MEMTRACK "Include memory tracking support?" ON)
|
||||
if (WITH_TLS)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
add_definitions("-DWITH_TLS")
|
||||
|
||||
@@ -48,6 +48,17 @@ WITH_PERSISTENCE:=yes
|
||||
# size', but will use slightly less memory and CPU time.
|
||||
WITH_MEMORY_TRACKING:=yes
|
||||
|
||||
# Uncomment to activate a consistency check on the usage of the memory tracking
|
||||
# alloc/free function use. Any memory allocated without a tracking function,
|
||||
# but freed with the tracking function will trigger an invalid memory read in
|
||||
# memory trackers like valgrind memcheck or ASAN.
|
||||
#ALLOC_MISMATCH_INVALID_READ:=yes
|
||||
|
||||
# Uncomment to activate consistency check on the usage of the memory tracking
|
||||
# alloc/free function use. Any memory allocated without a tracking function,
|
||||
# but freed with the tracking function will trigger an abort.
|
||||
#ALLOC_MISMATCH_ABORT:=yes
|
||||
|
||||
# Compile with database upgrading support? If disabled, mosquitto won't
|
||||
# automatically upgrade old database versions.
|
||||
# Not currently supported.
|
||||
|
||||
@@ -69,6 +69,8 @@ libmosqcommon_EXPORT char *mosquitto_strdup(const char *s);
|
||||
libmosqcommon_EXPORT char *mosquitto_strndup(const char *s, size_t n);
|
||||
|
||||
libmosqcommon_EXPORT void mosquitto_memory_set_limit(size_t lim);
|
||||
libmosqcommon_EXPORT unsigned long mosquitto_memory_used(void);
|
||||
libmosqcommon_EXPORT unsigned long mosquitto_max_memory_used(void);
|
||||
|
||||
#define mosquitto_FREE(A) do { mosquitto_free(A); (A) = NULL;} while(0)
|
||||
|
||||
|
||||
@@ -50,6 +50,19 @@ if (WITH_TLS)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(INC_MEMTRACK)
|
||||
target_compile_definitions(libmosquitto_common PUBLIC "WITH_MEMORY_TRACKING")
|
||||
endif()
|
||||
|
||||
option(ALLOC_MISMATCH_INVALID_READ "Memory function mismatch detection." OFF)
|
||||
if(ALLOC_MISMATCH_INVALID_READ)
|
||||
target_compile_definitions(libmosquitto_common PRIVATE "ALLOC_MISMATCH_INVALID_READ")
|
||||
endif()
|
||||
|
||||
option(ALLOC_MISMATCH_ABORT "Memory function mismatch abort." OFF)
|
||||
if(ALLOC_MISMATCH_ABORT)
|
||||
target_compile_definitions(libmosquitto_common PRIVATE "ALLOC_MISMATCH_ABORT")
|
||||
endif()
|
||||
|
||||
set_target_properties(libmosquitto_common PROPERTIES
|
||||
OUTPUT_NAME mosquitto_common
|
||||
|
||||
@@ -6,6 +6,18 @@ LOCAL_CPPFLAGS+=
|
||||
LOCAL_LDFLAGS+=-fPIC
|
||||
LOCAL_LIBADD+=
|
||||
|
||||
ifeq ($(WITH_MEMORY_TRACKING),yes)
|
||||
LOCAL_CPPFLAGS+=-DWITH_MEMORY_TRACKING
|
||||
endif
|
||||
|
||||
ifeq ($(ALLOC_MISMATCH_INVALID_READ),yes)
|
||||
LOCAL_CPPFLAGS+=-DALLOC_MISMATCH_INVALID_READ
|
||||
endif
|
||||
|
||||
ifeq ($(ALLOC_MISMATCH_ABORT),yes)
|
||||
LOCAL_CPPFLAGS+=-DALLOC_MISMATCH_ABORT
|
||||
endif
|
||||
|
||||
# ------------------------------------------
|
||||
# Targets
|
||||
# ------------------------------------------
|
||||
|
||||
+119
-7
@@ -20,10 +20,11 @@ Contributors:
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "mosquitto.h"
|
||||
|
||||
#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER)
|
||||
#if defined(WITH_MEMORY_TRACKING)
|
||||
# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
|
||||
# define REAL_WITH_MEMORY_TRACKING
|
||||
# endif
|
||||
@@ -59,8 +60,82 @@ unsigned long mosquitto_max_memory_used(void)
|
||||
return max_memcount;
|
||||
}
|
||||
|
||||
#ifdef REAL_WITH_MEMORY_TRACKING
|
||||
|
||||
#ifdef WITH_REAL_MEMORY_TRACKING
|
||||
/* ==================================================
|
||||
* Alloc mismatch tracking
|
||||
* ================================================== */
|
||||
#if defined(ALLOC_MISMATCH_INVALID_READ) || defined(ALLOC_MISMATCH_ABORT)
|
||||
#define ALLOC_MARKER_SIZE 8
|
||||
static const char *alloc_marker = "MOSQ_MEM";
|
||||
|
||||
static unsigned long dummycounter = 0;
|
||||
unsigned long mosq__get_dummy_counter(void)
|
||||
{
|
||||
return dummycounter;
|
||||
}
|
||||
|
||||
static void set_alloc_marker(char *mem, size_t size)
|
||||
{
|
||||
memcpy(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE);
|
||||
}
|
||||
|
||||
static bool check_alloc_marker(char *mem, size_t size)
|
||||
{
|
||||
return strncmp(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE) == 0;
|
||||
}
|
||||
|
||||
static void trigger_alloc_mismatch(char *mem, size_t size)
|
||||
{
|
||||
(void)mem;
|
||||
(void)size;
|
||||
#ifdef ALLOC_MISMATCH_INVALID_READ
|
||||
/* Trigger an invalid read on the freed memory and increment dummy counter */
|
||||
if(strncmp(mem + size - ALLOC_MARKER_SIZE, alloc_marker, ALLOC_MARKER_SIZE) == 0){
|
||||
++dummycounter;
|
||||
}
|
||||
#endif
|
||||
#ifdef ALLOC_MISMATCH_ABORT
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#if !defined(__libc_free)
|
||||
void __libc_free(void *ptr);
|
||||
#endif
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
if(!ptr){
|
||||
return;
|
||||
}
|
||||
size_t free_size = malloc_usable_size(ptr);
|
||||
|
||||
/* If we find the marker the memory was allocated using mosquitto_* allocation function */
|
||||
bool alloc_mismatch = check_alloc_marker(ptr, free_size);
|
||||
|
||||
__libc_free(ptr);
|
||||
|
||||
if(alloc_mismatch){
|
||||
trigger_alloc_mismatch(ptr, free_size);
|
||||
}
|
||||
}
|
||||
#endif /* defined(__linux__) */
|
||||
|
||||
#else /* defined(ALLOC_MISMATCH_INVALID_READ) || defined(ALLOC_MISMATCH_ABORT) */
|
||||
|
||||
#define ALLOC_MARKER_SIZE 0
|
||||
|
||||
static void set_alloc_marker(char* mem, size_t size) { UNUSED(mem); UNUSED(size); }
|
||||
|
||||
#endif /* defined(ALLOC_MISMATCH_INVALID_READ) */
|
||||
|
||||
|
||||
/* ==================================================
|
||||
* Alloc functions with tracking
|
||||
* ================================================== */
|
||||
|
||||
BROKER_EXPORT void *mosquitto_malloc(size_t size)
|
||||
{
|
||||
@@ -69,12 +144,14 @@ BROKER_EXPORT void *mosquitto_malloc(size_t size)
|
||||
if(mem_limit && memcount + size > mem_limit){
|
||||
return NULL;
|
||||
}
|
||||
mem = malloc(size);
|
||||
mem = malloc(size + ALLOC_MARKER_SIZE);
|
||||
if(mem){
|
||||
memcount += malloc_usable_size(mem);
|
||||
size = malloc_usable_size(mem);
|
||||
memcount += size;
|
||||
if(memcount > max_memcount){
|
||||
max_memcount = memcount;
|
||||
}
|
||||
set_alloc_marker(mem, size);
|
||||
}
|
||||
|
||||
return mem;
|
||||
@@ -83,7 +160,11 @@ BROKER_EXPORT void *mosquitto_malloc(size_t size)
|
||||
BROKER_EXPORT void *mosquitto_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *mem;
|
||||
size_t free_size = ptr != NULL ? malloc_usable_size(ptr) : 0;
|
||||
size_t free_size = ptr != NULL ? malloc_usable_size(ptr) : 0UL;
|
||||
|
||||
#if ALLOC_MARKER_SIZE
|
||||
bool alloc_mismatch = free_size > 0 && !check_alloc_marker(ptr, free_size);
|
||||
#endif
|
||||
|
||||
/* Avoid counter underflow due to mismatched memory allocation function usage */
|
||||
if(free_size > memcount){
|
||||
@@ -92,13 +173,22 @@ BROKER_EXPORT void *mosquitto_realloc(void *ptr, size_t size)
|
||||
if(mem_limit && memcount - free_size + size > mem_limit){
|
||||
return NULL;
|
||||
}
|
||||
mem = realloc(ptr, size);
|
||||
mem = realloc(ptr, size + ALLOC_MARKER_SIZE);
|
||||
#if ALLOC_MARKER_SIZE
|
||||
if(alloc_mismatch){
|
||||
/* This will not trigger if realloc was able to extend the memory in place. */
|
||||
trigger_alloc_mismatch(ptr, free_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(mem){
|
||||
size = malloc_usable_size(mem);
|
||||
memcount -= free_size;
|
||||
memcount += malloc_usable_size(mem);
|
||||
memcount += size;
|
||||
if(memcount > max_memcount){
|
||||
max_memcount = memcount;
|
||||
}
|
||||
set_alloc_marker(mem, size);
|
||||
}else if(size == 0){
|
||||
memcount -= free_size;
|
||||
}
|
||||
@@ -112,7 +202,20 @@ BROKER_EXPORT void mosquitto_free(void *mem)
|
||||
return;
|
||||
}
|
||||
size_t free_size = malloc_usable_size(mem);
|
||||
#if ALLOC_MARKER_SIZE
|
||||
bool alloc_mismatch = !check_alloc_marker(mem, free_size);
|
||||
#ifdef __linux__
|
||||
__libc_free(mem);
|
||||
#else
|
||||
free(mem);
|
||||
#endif
|
||||
|
||||
if(alloc_mismatch){
|
||||
trigger_alloc_mismatch(mem, free_size);
|
||||
}
|
||||
#else /* ALLOC_MARKER_SIZE */
|
||||
free(mem);
|
||||
#endif /* ALLOC_MARKER_SIZE */
|
||||
|
||||
/* Avoid counter underflow due to mismatched memory function allocation usage */
|
||||
if(free_size > memcount){
|
||||
@@ -123,6 +226,10 @@ BROKER_EXPORT void mosquitto_free(void *mem)
|
||||
|
||||
#else /* #ifdef WITH_REAL_MEMORY_TRACKING */
|
||||
|
||||
/* ==================================================
|
||||
* Alloc functions without tracking
|
||||
* ================================================== */
|
||||
|
||||
BROKER_EXPORT void *mosquitto_malloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
@@ -140,6 +247,11 @@ BROKER_EXPORT void mosquitto_free(void *mem)
|
||||
|
||||
#endif /* #ifdef WITH_REAL_MEMORY_TRACKING */
|
||||
|
||||
|
||||
/* ==================================================
|
||||
* Alloc functions that use the tracked/untracked versions
|
||||
* ================================================== */
|
||||
|
||||
BROKER_EXPORT void *mosquitto_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void *mem;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
ifdef MOSQ_USE_VALGRIND
|
||||
ifeq ($(MOSQ_USE_VALGRIND),callgrind)
|
||||
SANITIZER_COMMAND=valgrind -q --tool=callgrind --log-file=$${t}.vglog
|
||||
endif
|
||||
ifeq ($(MOSQ_USE_VALGRIND),massif)
|
||||
SANITIZER_COMMAND=valgrind -q --tool=massif --log-file=$${t}.vglog
|
||||
endif
|
||||
ifeq ($(MOSQ_USE_VALGRIND),failgrind)
|
||||
SANITIZER_COMMAND=fg-helper
|
||||
endif
|
||||
ifndef SANITIZER_COMMAND
|
||||
SANITIZER_COMMAND=valgrind -q --trace-children=yes --leak-check=full --show-leak-kinds=all --log-file=$${t}.vglog
|
||||
endif
|
||||
else
|
||||
SANITIZER_COMMAND=
|
||||
endif
|
||||
+1
-2
@@ -104,9 +104,8 @@ endif()
|
||||
|
||||
option(INC_DB_UPGRADE "Include database upgrade support? (recommended)" ON)
|
||||
|
||||
option(INC_MEMTRACK "Include memory tracking support?" ON)
|
||||
if(INC_MEMTRACK)
|
||||
target_compile_definitions(mosquitto PRIVATE "WITH_MEMORY_TRACKING")
|
||||
target_compile_definitions(mosquitto PUBLIC "WITH_MEMORY_TRACKING")
|
||||
endif()
|
||||
|
||||
option(WITH_PERSISTENCE "Include persistence support?" ON)
|
||||
|
||||
+4
-4
@@ -62,7 +62,7 @@ struct metric metrics[mosq_metric_max] = {
|
||||
{ 1, 0, "$SYS/broker/subscriptions/count", NULL, false }, /* mosq_gauge_subscription_count */
|
||||
{ 1, 0, "$SYS/broker/shared_subscriptions/count", NULL, false }, /* mosq_gauge_shared_subscription_count */
|
||||
{ 1, 0, "$SYS/broker/retained messages/count", NULL, false }, /* mosq_gauge_retained_message_count */
|
||||
#ifdef REAL_WITH_MEMORY_TRACKING
|
||||
#ifdef WITH_MEMORY_TRACKING
|
||||
{ 1, 0, "$SYS/broker/heap/current", NULL, false }, /* mosq_gauge_heap_current */
|
||||
{ 1, 0, "$SYS/broker/heap/maximum", NULL, true }, /* mosq_gauge_heap_maximum */
|
||||
#else
|
||||
@@ -228,9 +228,9 @@ void sys_tree__update(bool force)
|
||||
metrics[mosq_gauge_subscriptions].next = db.subscription_count;
|
||||
metrics[mosq_gauge_shared_subscriptions].next = db.shared_subscription_count;
|
||||
metrics[mosq_gauge_retained_messages].next = db.retained_count;
|
||||
#ifdef REAL_WITH_MEMORY_TRACKING
|
||||
metrics[mosq_gauge_heap_current].next = (int64_t)mosquitto__memory_used();
|
||||
metrics[mosq_counter_heap_maximum].next = (int64_t)mosquitto__max_memory_used();
|
||||
#ifdef WITH_MEMORY_TRACKING
|
||||
metrics[mosq_gauge_heap_current].next = (int64_t)mosquitto_memory_used();
|
||||
metrics[mosq_counter_heap_maximum].next = (int64_t)mosquitto_max_memory_used();
|
||||
#endif
|
||||
metrics[mosq_gauge_clients_total].next = HASH_CNT(hh_id, db.contexts_by_id);
|
||||
metrics[mosq_counter_clients_maximum].next = HASH_CNT(hh_id, db.contexts_by_id);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
R=../../..
|
||||
include ${R}/config.mk
|
||||
include ${R}/make/broker.mk
|
||||
include ${R}/make/unit-test.mk
|
||||
|
||||
.PHONY: all check test test-compile clean coverage
|
||||
|
||||
@@ -151,20 +152,18 @@ ${R}/src/topic_tok.o : ${R}/src/topic_tok.c
|
||||
${R}/src/util_mosq.o : ${R}/lib/util_mosq.c
|
||||
$(MAKE) -C ${R}/src/ util_mosq.o
|
||||
|
||||
build : bridge_topic_test keepalive_test persist_read_test persist_write_test subs_test
|
||||
ALL_TESTS:=bridge_topic_test keepalive_test persist_read_test persist_write_test subs_test
|
||||
|
||||
build : ${ALL_TESTS}
|
||||
|
||||
test : build
|
||||
./bridge_topic_test
|
||||
./keepalive_test
|
||||
./persist_read_test
|
||||
./persist_write_test
|
||||
./subs_test
|
||||
set -e; for t in ${ALL_TESTS}; do ${SANITIZER_COMMAND} ./$${t}; done
|
||||
|
||||
test-compile: build
|
||||
|
||||
clean :
|
||||
-rm -rf mosq_test bridge_topic_test keepalive_test persist_read_test persist_write_test subs_test
|
||||
-rm -rf *.o *.gcda *.gcno coverage.info out/
|
||||
-rm -rf *.o *.gcda *.gcno coverage.info *.vglog out/
|
||||
|
||||
coverage :
|
||||
lcov --capture --directory . --output-file coverage.info
|
||||
|
||||
Reference in New Issue
Block a user