Resolve crashes caused by mixing system malloc and jemalloc allocators.
The issue occurred when getline() and hiredis used system malloc, but
rspamd's free() used jemalloc, causing segmentation faults on macOS and
potentially other platforms.
Changes:
- Add rspamd_getline() wrapper using g_malloc/g_realloc/g_free to avoid
system malloc in getline()
- Replace getline() with rspamd_getline() in url.c, dns.c, lua_repl.c
- Fix memory leak in lua_repl.c by freeing input buffer on exit
- Configure hiredis allocators to use glib functions (jemalloc) in
rspamd_init_libs()
This ensures all memory operations use the same allocator (jemalloc)
throughout rspamd, preventing allocator mismatch crashes.
#undef SET_EFFECTIVE
}
+extern "C" {
+#include "../../contrib/hiredis/alloc.h"
+}
+
+/* Wrapper for calloc with correct signature for hiredis */
+static void *
+rspamd_hiredis_calloc(size_t nmemb, size_t size)
+{
+ return g_malloc0_n(nmemb, size);
+}
+
struct rspamd_external_libs_ctx *
rspamd_init_libs(void)
{
rspamd_openssl_maybe_init(ctx);
+ /* Configure hiredis allocators to use glib (jemalloc) */
+ hiredisAllocFuncs hiredis_allocators = {
+ .mallocFn = g_malloc,
+ .callocFn = rspamd_hiredis_calloc,
+ .reallocFn = g_realloc,
+ .strdupFn = g_strdup,
+ .freeFn = g_free,
+ };
+ hiredisSetAllocators(&hiredis_allocators);
+
/* Check if we have rdrand */
if ((ctx->crypto_ctx->cpu_config & CPUID_RDRAND) == 0) {
ottery_config_disable_entropy_sources(ottery_cfg,
return false;
}
- while ((r = getline(&linebuf, &buflen, fp)) > 0) {
+ while ((r = rspamd_getline(&linebuf, &buflen, fp)) > 0) {
if (linebuf[0] == '#' || g_ascii_isspace(linebuf[0])) {
/* Skip comment or empty line */
continue;
}
if (linebuf) {
- free(linebuf);
+ rspamd_getline_free(linebuf);
}
msg_info_config("processed host file %s; %d records added", fname, nadded);
m.start = url_tld_start;
m.prefix = "http://";
- while ((r = getline(&linebuf, &buflen, f)) > 0) {
+ while ((r = rspamd_getline(&linebuf, &buflen, f)) > 0) {
if (linebuf[0] == '/' || g_ascii_isspace(linebuf[0])) {
/* Skip comment or empty line */
continue;
g_array_append_val(url_scanner->matchers_full, m);
}
- free(linebuf);
+ rspamd_getline_free(linebuf);
fclose(f);
return TRUE;
return rspamd_str_has_8bit_u64(beg, len);
}
+
+gssize
+rspamd_getline(char **lineptr, gsize *n, FILE *stream)
+{
+ char *line = *lineptr;
+ gsize len = *n;
+ gssize nread = 0;
+ int c;
+
+ if (line == NULL || len == 0) {
+ len = 120;
+ line = g_malloc(len);
+ if (line == NULL) {
+ return -1;
+ }
+ }
+
+ while ((c = fgetc(stream)) != EOF) {
+ if ((gsize) nread >= len - 1) {
+ gsize new_len = len * 2;
+ char *new_line = g_realloc(line, new_len);
+ if (new_line == NULL) {
+ return -1;
+ }
+ line = new_line;
+ len = new_len;
+ }
+
+ line[nread++] = c;
+
+ if (c == '\n') {
+ break;
+ }
+ }
+
+ if (nread == 0 && c == EOF) {
+ return -1;
+ }
+
+ line[nread] = '\0';
+ *lineptr = line;
+ *n = len;
+
+ return nread;
+}
+
+void rspamd_getline_free(char *lineptr)
+{
+ g_free(lineptr);
+}
#define RSPAMD_LEN_CHECK_STARTS_WITH(s, len, lit) \
((len) >= sizeof(lit) - 1 && g_ascii_strncasecmp((s), (lit), sizeof(lit) - 1) == 0)
+/**
+ * Wrapper around getline(3) that handles allocator mismatches when jemalloc is enabled.
+ * When jemalloc is used, getline() allocates memory with system malloc, but rspamd's
+ * free() uses jemalloc, causing crashes. This function ensures proper cleanup.
+ *
+ * @param lineptr pointer to buffer (will be allocated/reallocated as needed)
+ * @param n pointer to buffer size
+ * @param stream file stream to read from
+ * @return number of characters read, or -1 on error
+ */
+gssize rspamd_getline(char **lineptr, gsize *n, FILE *stream);
+
+/**
+ * Free memory allocated by rspamd_getline using the correct allocator.
+ *
+ * @param lineptr buffer to free
+ */
+void rspamd_getline_free(char *lineptr);
+
#ifdef __cplusplus
}
#endif
#include "lua/lua_thread_pool.h"
#include "message.h"
#include "unix-std.h"
+#include "str_util.h"
#include "replxx.h"
#include "worker_util.h"
for (;;) {
if (is_batch) {
- size_t linecap = 0;
- ssize_t linelen;
+ gsize linecap = 0;
+ gssize linelen;
- linelen = getline(&input, &linecap, stdin);
+ linelen = rspamd_getline(&input, &linecap, stdin);
if (linelen > 0) {
if (input[linelen - 1] == '\n') {
}
}
}
+
+ if (is_batch && input) {
+ rspamd_getline_free(input);
+ }
}
struct rspamadm_lua_repl_context {