aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRatakor <ratakor@disroot.org>2023-08-01 17:11:49 +0200
committerRatakor <ratakor@disroot.org>2023-08-01 17:11:49 +0200
commitfc61b2616b77956a9ac91165d026679aaaf1dced (patch)
tree46e7a85ce58c87c0dd095f24b188db10aa1f5d33
parent677a197027a04e8703613971fefc90606d3b12e6 (diff)
Merge update from libre
-rw-r--r--README.md21
-rw-r--r--dalloc.c346
-rw-r--r--dalloc.h26
3 files changed, 189 insertions, 204 deletions
diff --git a/README.md b/README.md
index a10cce3..5fedeba 100644
--- a/README.md
+++ b/README.md
@@ -8,15 +8,12 @@ dalloc.c and dalloc.h should be dropped into an existing project and
compiled with the `-DDALLOC` flag to define the macro that enables dalloc.
dalloc will replace free(), malloc(), calloc(), realloc(), reallocarray(),
strdup(), strndup(), vasprintf() and asprintf() by a more secure version that
-will check for buffer overflow and memory leak. It will also output a recap at
-the end of the program.
+will check for buffer overflow and memory leak.
+It will also output a recap at the end of the program.
-By defining `EXITSEGV` all exit() call will be replaced by a segmentation fault
-which can be very useful to check where an overflow occur with a real debugger.
-
-strdup, strndup, reallocarray, vasprintf and asprintf are not standard so you
-will probably need to define `_DEFAULT_SOURCE` or equivalent to use them
-outside of dalloc.
+reallocarray, strdup, strndup, vasprintf and asprintf are not standard so
+you'll probably need to define `_DEFAULT_SOURCE` or include
+[ubik](https://github.com/ratakor/libre) to use them outside of dalloc.
Functions
---------
@@ -29,10 +26,6 @@ Output all allocation that were not freed to stderr.
#### dalloc_check_all(void)
Run both dalloc_check_free() and dalloc_check_overflow() on program exit.
-#### dalloc_sighandler(int sig)
-Output signal meaning and exit. To be used with signal() from signal.h.
-e.g.: `signal(SIG, dalloc_sighandler);` Require `_DEFAULT_SOURCE` or equivalent.
-
#### dalloc_ignore(void *p)
Ignore the pointer in argument for memory leak check. This can be useful when
developing an application that never stop.
@@ -45,5 +38,5 @@ When `DALLOC` is not defined this function does nothing.
Notes
-----
-An error with "Unknown pointer" can be caused by a double free or when freeing
-a pointer allocated with a function not supported by dalloc.
+An error with "Unknown pointer" is either caused by a double free or when
+freeing a pointer allocated with a function not supported by dalloc.
diff --git a/dalloc.c b/dalloc.c
index bff2c0f..ccc402d 100644
--- a/dalloc.c
+++ b/dalloc.c
@@ -24,83 +24,122 @@
#define DALLOC_INTERNAL
#include "dalloc.h"
-#define EXIT_STATUS 9
-
-#ifdef DALLOC
-#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
-#define OVER_ALLOC 64
-#define MAGIC_NUMBER 0x99
-#define MAX_POINTERS 512
-#define COMMENT_MAX 128
-
-struct Pointer {
+#define EXIT_STATUS 9
+#define OVER_ALLOC 64
+#define MAGIC_NUMBER 0x99
+
+#define x1 ((char)(MAGIC_NUMBER))
+#define x2 x1, x1
+#define x4 x2, x2
+#define x8 x4, x4
+#define x16 x8, x8
+#define x32 x16, x16
+#define x64 x32, x32
+#define xCAT(X) x##X
+#define MAGIC_INIT(X) xCAT(X)
+#define OVERFLOW(p, s) (memcmp(((char *)(p)) + (s), magic_numbers, OVER_ALLOC))
+
+typedef struct dalloc_ptr dalloc_ptr;
+struct dalloc_ptr {
void *p;
size_t siz;
- char comment[COMMENT_MAX];
+ char *comment;
+ char *file;
int ignored;
- char file[FILENAME_MAX];
int line;
+ dalloc_ptr *next;
};
-static int overflow(void *p, size_t siz);
-static size_t find_pointer_index(void *p, char *file, int line);
-extern void dalloc_check_all(void) __attribute__((destructor));
+static void eprintf(const char *fmt, ...);
+static char *xstrdup(const char *s, char *file, int line);
+static dalloc_ptr *find_ptr(void *p, char *file, int line);
+static void check_overflow(dalloc_ptr *dp, char *file, int line);
+static const char magic_numbers[OVER_ALLOC] = { MAGIC_INIT(OVER_ALLOC) };
static pthread_mutex_t dalloc_mutex = PTHREAD_MUTEX_INITIALIZER;
-static char magic_numbers[OVER_ALLOC];
-static struct Pointer pointers[MAX_POINTERS];
-static size_t npointers;
+static dalloc_ptr *head;
-static int
-overflow(void *p, size_t siz)
+static void
+eprintf(const char *fmt, ...)
{
- if (*magic_numbers == 0)
- memset(magic_numbers, MAGIC_NUMBER, OVER_ALLOC);
+ va_list ap;
- return memcmp((char *)p + siz, magic_numbers, OVER_ALLOC);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
}
-static size_t
-find_pointer_index(void *p, char *file, int line)
+static char *
+xstrdup(const char *s, char *file, int line)
{
- size_t i = npointers;
+ char *p;
+ size_t siz;
- while (i-- > 0 && p != pointers[i].p);
+ siz = strlen(s) + 1;
+ if ((p = malloc(siz)) == NULL) {
+ eprintf("%s:%d: dalloc: %s", file, line, strerror(errno));
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
+ }
+
+ return memcpy(p, s, siz);
+}
- if (i == (size_t) -1) {
- fprintf(stderr, "%s:%d: dalloc: Unknown pointer %p\n",
- file, line, p);
+static dalloc_ptr *
+find_ptr(void *p, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ for (dp = head; dp && dp->p != p; dp = dp->next);
+
+ if (dp == NULL) {
+ eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p);
pthread_mutex_unlock(&dalloc_mutex);
exit(EXIT_STATUS);
}
- return i;
+ return dp;
+}
+
+static void
+check_overflow(dalloc_ptr *dp, char *file, int line)
+{
+ if (!OVERFLOW(dp->p, dp->siz))
+ return;
+
+ eprintf("%s:%d: dalloc: Memory overflow on %p, total: %zu bytes\n"
+ "The pointer ", file, line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf("'%s' ", dp->comment);
+ eprintf("was allocated in '%s' on line %d.\n", dp->file, dp->line);
+ pthread_mutex_unlock(&dalloc_mutex);
+ exit(EXIT_STATUS);
}
size_t
dalloc_check_overflow(void)
{
- size_t i, sum = 0;
+ dalloc_ptr *dp;
+ size_t sum = 0;
pthread_mutex_lock(&dalloc_mutex);
- fprintf(stderr, "Memory overflow:");
- for (i = 0; i < npointers; i++) {
- if (!overflow(pointers[i].p, pointers[i].siz))
+ eprintf("Memory overflow:");
+ for (dp = head; dp; dp = dp->next) {
+ if (!OVERFLOW(dp->p, dp->siz))
continue;
sum++;
- fprintf(stderr, "\n%s:%d: %p, total: %zu bytes",
- pointers[i].file, pointers[i].line,
- pointers[i].p, pointers[i].siz);
- if (pointers[i].comment[0])
- fprintf(stderr, " /* %s */", pointers[i].comment);
+ eprintf("\n%s:%d: %p, total: %zu bytes",
+ dp->file, dp->line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf(" /* %s */", dp->comment);
}
pthread_mutex_unlock(&dalloc_mutex);
if (sum == 0)
- fprintf(stderr, " 0 overflow :)\n");
+ eprintf(" 0 overflow :)\n");
else
- fprintf(stderr, "\nTotal overflow: %zu\n", sum);
+ eprintf("\nTotal overflow: %zu\n", sum);
return sum;
}
@@ -108,28 +147,28 @@ dalloc_check_overflow(void)
void
dalloc_check_free(void)
{
- size_t i, n = 0, sum = 0;
+ dalloc_ptr *dp;
+ size_t n = 0, sum = 0;
pthread_mutex_lock(&dalloc_mutex);
- fprintf(stderr, "Memory allocated and not freed:");
- for (i = 0; i < npointers; i++) {
- if (pointers[i].ignored)
+ eprintf("Memory allocated and not freed:");
+ for (dp = head; dp; dp = dp->next) {
+ if (dp->ignored)
continue;
n++;
- sum += pointers[i].siz;
- fprintf(stderr, "\n%s:%d: %p, %zu bytes",
- pointers[i].file, pointers[i].line,
- pointers[i].p, pointers[i].siz);
- if (pointers[i].comment[0])
- fprintf(stderr, " /* %s */", pointers[i].comment);
+ sum += dp->siz;
+ eprintf("\n%s:%d: %p, %zu bytes",
+ dp->file, dp->line, dp->p, dp->siz);
+ if (dp->comment)
+ eprintf(" /* %s */", dp->comment);
}
pthread_mutex_unlock(&dalloc_mutex);
if (sum == 0)
- fprintf(stderr, " 0 byte :)\n");
+ eprintf(" 0 byte :)\n");
else
- fprintf(stderr, "\nTotal: %zu bytes, %zu pointers\n", sum, n);
+ eprintf("\nTotal: %zu bytes, %zu pointers\n", sum, n);
}
void
@@ -142,101 +181,107 @@ dalloc_check_all(void)
void
_dalloc_ignore(void *p, char *file, int line)
{
- size_t i;
+ dalloc_ptr *dp;
pthread_mutex_lock(&dalloc_mutex);
- i = find_pointer_index(p, file, line);
- pointers[i].ignored = 1;
+ dp = find_ptr(p, file, line);
+ dp->ignored = 1;
pthread_mutex_unlock(&dalloc_mutex);
}
void
_dalloc_comment(void *p, const char *comment, char *file, int line)
{
- size_t i, siz;
+ dalloc_ptr *dp;
if (comment == NULL)
return;
- siz = MIN(strlen(comment), COMMENT_MAX - 1);
pthread_mutex_lock(&dalloc_mutex);
- i = find_pointer_index(p, file, line);
- memcpy(pointers[i].comment, comment, siz);
- pointers[i].comment[siz] = '\0';
+ dp = find_ptr(p, file, line);
+ free(dp->comment);
+ dp->comment = xstrdup(comment, file, line);
+ pthread_mutex_unlock(&dalloc_mutex);
+}
+
+void
+_dalloc_query(void *p, char *file, int line)
+{
+ dalloc_ptr *dp;
+
+ pthread_mutex_lock(&dalloc_mutex);
+ dp = find_ptr(p, file, line);
+ fprintf(stderr, "%s:%d: dalloc: %p: %s:%d: %zu bytes",
+ file, line, dp->p, dp->file, dp->line, dp->siz);
+ if (dp->comment)
+ fprintf(stderr, " /* %s */", dp->comment);
+ fputc('\n', stderr);
pthread_mutex_unlock(&dalloc_mutex);
}
void
_dalloc_free(void *p, char *file, int line)
{
- size_t i;
+ dalloc_ptr *dp, *prev;
if (p == NULL)
return;
pthread_mutex_lock(&dalloc_mutex);
- i = find_pointer_index(p, file, line);
- if (overflow(pointers[i].p, pointers[i].siz)) {
- fprintf(stderr, "%s:%d: dalloc: "
- "Memory overflow on %p, total: %zu bytes\n",
- file, line, pointers[i].p, pointers[i].siz);
- fprintf(stderr, "The pointer ");
- if (pointers[i].comment[0])
- fprintf(stderr, "'%s' ", pointers[i].comment);
- fprintf(stderr, "was allocated in '%s' on line %d.\n",
- pointers[i].file, pointers[i].line);
+ for (dp = head; dp && dp->p != p; prev = dp, dp = dp->next);
+ if (dp == NULL) {
+ eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p);
pthread_mutex_unlock(&dalloc_mutex);
exit(EXIT_STATUS);
}
- for (i++; i < npointers; i++)
- pointers[i - 1] = pointers[i];
- npointers--;
- free(p);
+ check_overflow(dp, file, line);
+
+ if (dp == head)
+ head = dp->next;
+ else
+ prev->next = dp->next;
pthread_mutex_unlock(&dalloc_mutex);
+
+ free(dp->p);
+ free(dp->file);
+ free(dp->comment);
+ free(dp);
}
void *
_dalloc_malloc(size_t siz, char *file, int line)
{
- void *p = NULL;
- size_t sizfname;
+ dalloc_ptr *dp;
if (siz == 0) {
- fprintf(stderr, "%s:%d: dalloc: malloc with size == 0\n",
- file, line);
+ eprintf("%s:%d: dalloc: malloc with size == 0\n", file, line);
return NULL;
}
- if (npointers == MAX_POINTERS) {
- fprintf(stderr, "dalloc: Too much pointers (max:%d)\n",
- MAX_POINTERS);
- exit(EXIT_STATUS);
- }
-
- if (siz + OVER_ALLOC < OVER_ALLOC)
+ if (siz + OVER_ALLOC < OVER_ALLOC) {
+ dp = NULL;
errno = ENOMEM;
- else
- p = malloc(siz + OVER_ALLOC);
+ } else if ((dp = calloc(1, sizeof(*dp))) != NULL) {
+ dp->p = malloc(siz + OVER_ALLOC);
+ }
- if (p == NULL) {
- fprintf(stderr, "%s:%d: dalloc: %s\n",
- file, line, strerror(errno));
+ if (dp == NULL || dp->p == NULL) {
+ eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno));
exit(EXIT_STATUS);
}
- sizfname = MIN(strlen(file), FILENAME_MAX - 1);
- memset((char *)p + siz, MAGIC_NUMBER, OVER_ALLOC);
+ memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC);
+ dp->siz = siz;
+ dp->line = line;
+
pthread_mutex_lock(&dalloc_mutex);
- pointers[npointers].p = p;
- pointers[npointers].siz = siz;
- memcpy(pointers[npointers].file, file, sizfname);
- pointers[npointers].file[sizfname] = '\0';
- pointers[npointers].line = line;
- npointers++;
+ dp->file = xstrdup(file, file, line);
+ dp->next = head;
+ head = dp;
pthread_mutex_unlock(&dalloc_mutex);
- return p;
+ return dp->p;
}
void *
@@ -244,14 +289,8 @@ _dalloc_calloc(size_t nmemb, size_t siz, char *file, int line)
{
void *p;
- if (nmemb == 0 || siz == 0) {
- fprintf(stderr, "%s:%d: dalloc: calloc with size == 0\n",
- file, line);
- return NULL;
- }
-
- if (nmemb > -1 / siz) {
- fprintf(stderr, "%s:%d: dalloc: calloc: %s\n",
+ if (siz != 0 && nmemb > (size_t) -1 / siz) {
+ eprintf("%s:%d: dalloc: calloc: %s\n",
file, line, strerror(ENOMEM));
exit(EXIT_STATUS);
}
@@ -266,7 +305,7 @@ _dalloc_calloc(size_t nmemb, size_t siz, char *file, int line)
void *
_dalloc_realloc(void *p, size_t siz, char *file, int line)
{
- size_t i, sizfname;
+ dalloc_ptr *dp;
if (p == NULL)
return _dalloc_malloc(siz, file, line);
@@ -277,58 +316,37 @@ _dalloc_realloc(void *p, size_t siz, char *file, int line)
}
pthread_mutex_lock(&dalloc_mutex);
- i = find_pointer_index(p, file, line);
-
- if (overflow(pointers[i].p, pointers[i].siz)) {
- fprintf(stderr, "%s:%d: dalloc: "
- "Memory overflow on %p, total: %zu bytes\n",
- file, line, pointers[i].p, pointers[i].siz);
- fprintf(stderr, "The pointer ");
- if (pointers[i].comment[0])
- fprintf(stderr, "'%s' ", pointers[i].comment);
- fprintf(stderr, "was allocated in '%s' on line %d.\n",
- pointers[i].file, pointers[i].line);
- pthread_mutex_unlock(&dalloc_mutex);
- exit(EXIT_STATUS);
- }
+ dp = find_ptr(p, file, line);
+ check_overflow(dp, file, line);
if (siz + OVER_ALLOC < OVER_ALLOC) {
- p = NULL;
+ dp->p = NULL;
errno = ENOMEM;
} else {
- p = realloc(p, siz + OVER_ALLOC);
+ dp->p = realloc(dp->p, siz + OVER_ALLOC);
}
- if (p == NULL) {
- fprintf(stderr, "%s:%d: dalloc: %s\n",
- file, line, strerror(errno));
+ if (dp->p == NULL) {
+ eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno));
pthread_mutex_unlock(&dalloc_mutex);
exit(EXIT_STATUS);
}
- sizfname = MIN(strlen(file), FILENAME_MAX - 1);
- memset((char *)p + siz, MAGIC_NUMBER, OVER_ALLOC);
- pointers[i].p = p;
- pointers[i].siz = siz;
- memcpy(pointers[i].file, file, sizfname);
- pointers[i].file[sizfname] = '\0';
- pointers[i].line = line;
+ memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC);
+ dp->siz = siz;
+ dp->line = line;
+ free(dp->file);
+ dp->file = xstrdup(file, file, line);
pthread_mutex_unlock(&dalloc_mutex);
- return p;
+ return dp->p;
}
void *
_dalloc_reallocarray(void *p, size_t n, size_t s, char *file, int line)
{
- if (n == 0 || s == 0) {
- fprintf(stderr, "%s:%d: dalloc: reallocarray with size == 0\n",
- file, line);
- return NULL;
- }
-
- if (n > -1 / s) {
- fprintf(stderr, "%s:%d: dalloc: reallocarray: %s\n",
+ if (s != 0 && n > (size_t) -1 / s) {
+ eprintf("%s:%d: dalloc: reallocarray: %s\n",
file, line, strerror(ENOMEM));
exit(EXIT_STATUS);
}
@@ -357,10 +375,10 @@ _dalloc_strndup(const char *s, size_t n, char *file, int line)
size_t siz;
end = memchr(s, '\0', n);
- siz = (end ? (size_t)(end - s) : n) + 1;
- p = _dalloc_malloc(siz, file, line);
- memcpy(p, s, siz - 1);
- p[siz - 1] = '\0';
+ siz = end ? (size_t)(end - s) : n;
+ p = _dalloc_malloc(siz + 1, file, line);
+ memcpy(p, s, siz);
+ p[siz] = '\0';
return p;
}
@@ -376,15 +394,15 @@ _dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line)
rv = vsnprintf(NULL, 0, fmt, ap2);
va_end(ap2);
if (rv < 0) {
- fprintf(stderr, "%s:%d: dalloc: asprintf: %s\n",
+ eprintf("%s:%d: dalloc: asprintf: %s\n",
file, line, strerror(errno));
exit(EXIT_STATUS);
}
- siz = rv + 1;
+ siz = (size_t)rv + 1;
*p = _dalloc_malloc(siz, file, line);
rv = vsnprintf(*p, siz, fmt, ap);
if (rv < 0) {
- fprintf(stderr, "%s:%d: dalloc: asprintf: %s\n",
+ eprintf("%s:%d: dalloc: asprintf: %s\n",
file, line, strerror(errno));
exit(EXIT_STATUS);
}
@@ -404,25 +422,3 @@ _dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...)
return rv;
}
-#endif /* DALLOC */
-
-void
-dalloc_sighandler(int sig)
-{
-#ifdef _WIN32
- fprintf(stderr, "dalloc: signal %d\n", sig);
-#else
- fprintf(stderr, "dalloc: %s\n", strsignal(sig));
-#endif /* _WIN32 */
-
- exit(EXIT_STATUS);
-}
-
-void
-exitsegv(int dummy)
-{
- int *x = NULL;
-
- dalloc_check_all();
- *x = dummy;
-}
diff --git a/dalloc.h b/dalloc.h
index 69a45b2..0f1d74e 100644
--- a/dalloc.h
+++ b/dalloc.h
@@ -8,6 +8,7 @@
#ifndef DALLOC_H
#define DALLOC_H
+#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
@@ -25,19 +26,21 @@ extern "C" {
#define strdup(s) (_dalloc_strdup(s, __FILE__, __LINE__))
#define strndup(s, n) (_dalloc_strndup(s, n, __FILE__, __LINE__))
#define vasprintf(p, fmt, ap) (_dalloc_vasprintf(p, fmt, ap, __FILE__, __LINE__))
-#define asprintf(p, fmt, ...) (_dalloc_asprintf(p, __FILE__, __LINE__, fmt, __VA_ARGS__))
+#define asprintf(p, ...) (_dalloc_asprintf(p, __FILE__, __LINE__, __VA_ARGS__))
#define dalloc_ignore(p) (_dalloc_ignore(p, __FILE__, __LINE__))
#define dalloc_comment(p, str) (_dalloc_comment(p, str, __FILE__, __LINE__))
+#define dalloc_query(p) (_dalloc_query(p, __FILE__, __LINE__))
#endif /* DALLOC && !DALLOC_INTERNAL */
-#ifdef DALLOC
+#if defined(DALLOC) || defined(DALLOC_INTERNAL)
size_t dalloc_check_overflow(void);
void dalloc_check_free(void);
-void dalloc_check_all(void);
+void dalloc_check_all(void) __attribute__((destructor));
void _dalloc_ignore(void *p, char *file, int line);
void _dalloc_comment(void *p, const char *comment, char *file, int line);
+void _dalloc_query(void *p, char *file, int line);
void _dalloc_free(void *p, char *file, int line);
void *_dalloc_malloc(size_t siz, char *file, int line);
void *_dalloc_calloc(size_t nmemb, size_t siz, char *file, int line);
@@ -48,20 +51,13 @@ char *_dalloc_strndup(const char *s, size_t n, char *file, int line);
int _dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line);
int _dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...);
#else
-#define _NO_DALLOC "dalloc: Define `DALLOC` to enable dalloc"
+#define dalloc_check_overflow() 0
+#define dalloc_check_free()
+#define dalloc_check_all()
#define dalloc_ignore(p)
#define dalloc_comment(p, str)
-#define dalloc_check_overflow() (fprintf(stderr, "%s\n", _NO_DALLOC), 0)
-#define dalloc_check_free() (fprintf(stderr, "%s\n", _NO_DALLOC))
-#define dalloc_check_all() (fprintf(stderr, "%s\n", _NO_DALLOC))
-#endif /* DALLOC */
-
-#ifdef EXITSEGV
-#define exit(dummy) (exitsegv(dummy))
-#endif /* EXITSEGV */
-
-void dalloc_sighandler(int sig);
-void exitsegv(int dummy);
+#define dalloc_query(p)
+#endif /* DALLOC || DALLOC_INTERNAL */
#ifdef __cplusplus
}