Fix a few more memory bugs. Still have some to tackle.

This commit is contained in:
Jordan Bancino 2022-10-31 19:52:37 -04:00
parent 629d953518
commit 63dbf9ae35
7 changed files with 321 additions and 106 deletions

View file

@ -52,7 +52,7 @@ Phase 2: Building a foundation
[~] Data abstraction layer [~] Data abstraction layer
[ ] Database upgrades/migration path [ ] Database upgrades/migration path
[ ] Caching and cache control [ ] Caching and cache control
[ ] Make memory info access O(1) [x] Make memory info access O(1)
[x] Error generation [x] Error generation
[x] Properly implement the command line options as stated in telodendria(8) [x] Properly implement the command line options as stated in telodendria(8)
[x] Remove "chroot" option, just chroot into the data dir, and make the log [x] Remove "chroot" option, just chroot into the data dir, and make the log
@ -61,6 +61,7 @@ Phase 2: Building a foundation
[x] Fix memory leaks [x] Fix memory leaks
[ ] Fix bug where the socket stays open after quit. [ ] Fix bug where the socket stays open after quit.
[ ] Figure out how to write unit tests for array/hashmap/etc [ ] Figure out how to write unit tests for array/hashmap/etc
[ ] Add recipe to td script to upload patches to the Matrix room
Phase 3: Welcome to Matrix Phase 3: Welcome to Matrix
@ -135,3 +136,4 @@ Documentation
[x] Add onboarding documentation [x] Add onboarding documentation
[x] Building from source [x] Building from source
[!] Writing config file (see Config.5) [!] Writing config file (see Config.5)

View file

@ -122,7 +122,10 @@ HttpServerContextFree(HttpServerContext * c)
while (HashMapIterate(c->requestHeaders, &key, &val)) while (HashMapIterate(c->requestHeaders, &key, &val))
{ {
/* Since we create these, we know they're always on the heap */ /*
* These are always parsed from the request, so they should
* always be on the heap.
*/
Free(key); Free(key);
Free(val); Free(val);
} }
@ -130,8 +133,25 @@ HttpServerContextFree(HttpServerContext * c)
while (HashMapIterate(c->responseHeaders, &key, &val)) while (HashMapIterate(c->responseHeaders, &key, &val))
{ {
Free(key); /*
Free(val); * These are generated by code. As such, they may be either
* on the heap, or on the stack, depending on how they were
* added.
*
* Basically, if the memory API knows about a pointer, then
* it can be freed. If it doesn't know about a pointer, skip
* freeing it because it's probably a stack pointer.
*/
if (MemoryInfoGet(key))
{
Free(key);
}
if (MemoryInfoGet(val))
{
Free(val);
}
} }
HashMapFree(c->responseHeaders); HashMapFree(c->responseHeaders);
@ -432,7 +452,7 @@ HttpServerWorkerThread(void *args)
} }
/* Get the first line of the request */ /* Get the first line of the request */
lineLen = getline(&line, &lineSize, fp); lineLen = UtilGetLine(&line, &lineSize, fp);
if (lineLen == -1) if (lineLen == -1)
{ {
goto bad_request; goto bad_request;
@ -501,7 +521,7 @@ HttpServerWorkerThread(void *args)
goto internal_error; goto internal_error;
} }
while ((lineLen = getline(&line, &lineSize, fp)) != -1) while ((lineLen = UtilGetLine(&line, &lineSize, fp)) != -1)
{ {
char *headerKey; char *headerKey;
char *headerValue; char *headerValue;

View file

@ -33,16 +33,105 @@ struct MemoryInfo
const char *file; const char *file;
int line; int line;
void *pointer; void *pointer;
MemoryInfo *next;
MemoryInfo *prev;
}; };
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static MemoryInfo *lastAllocation = NULL;
static void (*hook) (MemoryAction, MemoryInfo *, void *) = NULL; static void (*hook) (MemoryAction, MemoryInfo *, void *) = NULL;
static void *hookArgs = NULL; static void *hookArgs = NULL;
static MemoryInfo **allocations = NULL;
static size_t allocationsSize = 0;
static size_t allocationsLen = 0;
static size_t
MemoryHash(void *p)
{
return (((size_t) p) >> 2 * 7) % allocationsSize;
}
static int
MemoryInsert(MemoryInfo * a)
{
size_t hash;
if (!allocations)
{
allocationsSize = 64;
allocations = calloc(allocationsSize, sizeof(void *));
if (!allocations)
{
return 0;
}
}
if ((allocationsLen + 1) >= (0.75 * allocationsSize))
{
size_t i;
size_t tmpAllocationsSize = allocationsSize;
MemoryInfo **tmpAllocations;
allocationsSize *= 2;
tmpAllocations = calloc(allocationsSize, sizeof(void *));
if (!tmpAllocations)
{
return 0;
}
for (i = 0; i < tmpAllocationsSize; i++)
{
if (allocations[i])
{
hash = MemoryHash(allocations[i]->pointer);
while (tmpAllocations[hash])
{
hash = (hash + 1) % allocationsSize;
}
tmpAllocations[hash] = allocations[i];
}
}
free(allocations);
allocations = tmpAllocations;
}
hash = MemoryHash(a->pointer);
while (allocations[hash])
{
hash = (hash + 1) % allocationsSize;
}
allocations[hash] = a;
allocationsLen++;
return 1;
}
static void
MemoryDelete(MemoryInfo * a)
{
size_t hash = MemoryHash(a->pointer);
size_t count = 0;
while (count <= allocationsSize)
{
if (allocations[hash] && allocations[hash] == a)
{
allocations[hash] = NULL;
allocationsLen--;
return;
}
else
{
hash = (hash + 1) % allocationsSize;
count++;
}
}
}
void * void *
MemoryAllocate(size_t size, const char *file, int line) MemoryAllocate(size_t size, const char *file, int line)
{ {
@ -70,16 +159,15 @@ MemoryAllocate(size_t size, const char *file, int line)
a->file = file; a->file = file;
a->line = line; a->line = line;
a->pointer = p; a->pointer = p;
a->next = NULL;
a->prev = lastAllocation;
if (lastAllocation) if (!MemoryInsert(a))
{ {
lastAllocation->next = a; free(a);
free(p);
pthread_mutex_unlock(&lock);
return NULL;
} }
lastAllocation = a;
if (hook) if (hook)
{ {
hook(MEMORY_ALLOCATE, a, hookArgs); hook(MEMORY_ALLOCATE, a, hookArgs);
@ -100,99 +188,102 @@ MemoryReallocate(void *p, size_t size, const char *file, int line)
return MemoryAllocate(size, file, line); return MemoryAllocate(size, file, line);
} }
pthread_mutex_lock(&lock); a = MemoryInfoGet(p);
if (a)
a = lastAllocation;
while (a)
{ {
if (a->pointer == p) pthread_mutex_lock(&lock);
new = realloc(a->pointer, size);
if (new)
{ {
new = realloc(p, size); MemoryDelete(a);
if (new) a->size = size;
{ a->file = file;
a->pointer = new; a->line = line;
a->size = size;
a->file = file;
a->line = line;
if (hook) a->pointer = new;
{ MemoryInsert(a);
hook(MEMORY_REALLOCATE, a, hookArgs);
} if (hook)
{
hook(MEMORY_REALLOCATE, a, hookArgs);
} }
break;
} }
pthread_mutex_unlock(&lock);
a = a->prev; }
else if (hook)
{
a = malloc(sizeof(MemoryInfo));
if (a)
{
a->size = 0;
a->file = file;
a->line = line;
a->pointer = p;
hook(MEMORY_BAD_POINTER, a, hookArgs);
free(a);
}
} }
pthread_mutex_unlock(&lock);
return new; return new;
} }
void void
MemoryFree(void *p) MemoryFree(void *p, const char *file, int line)
{ {
MemoryInfo *a; MemoryInfo *a;
pthread_mutex_lock(&lock); if (!p)
a = lastAllocation;
while (a)
{ {
if (a->pointer == p) return;
{
if (a->prev)
{
a->prev->next = a->next;
}
else
{
lastAllocation = a->next;
}
if (a->next)
{
a->next->prev = a->prev;
}
else
{
lastAllocation = a->prev;
}
if (hook)
{
hook(MEMORY_FREE, a, hookArgs);
}
free(a);
free(p);
break;
}
a = a->prev;
} }
pthread_mutex_unlock(&lock); a = MemoryInfoGet(p);
if (a)
{
pthread_mutex_lock(&lock);
if (hook)
{
a->file = file;
a->line = line;
hook(MEMORY_FREE, a, hookArgs);
}
MemoryDelete(a);
free(a->pointer);
free(a);
pthread_mutex_unlock(&lock);
}
else if (hook)
{
a = malloc(sizeof(MemoryInfo));
if (a)
{
a->file = file;
a->line = line;
a->size = 0;
a->pointer = p;
hook(MEMORY_BAD_POINTER, a, hookArgs);
free(a);
}
}
} }
size_t size_t
MemoryAllocated(void) MemoryAllocated(void)
{ {
MemoryInfo *a; size_t i;
size_t total = 0; size_t total = 0;
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
a = lastAllocation; for (i = 0; i < allocationsSize; i++)
while (a)
{ {
total += a->size; if (allocations[i])
a = a->prev; {
total += allocations[i]->size;
}
} }
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
@ -203,22 +294,23 @@ MemoryAllocated(void)
void void
MemoryFreeAll(void) MemoryFreeAll(void)
{ {
MemoryInfo *a; size_t i;
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
a = lastAllocation; for (i = 0; i < allocationsSize; i++)
while (a)
{ {
MemoryInfo *prev = a->prev; if (allocations[i])
{
free(a->pointer); free(allocations[i]->pointer);
free(a); free(allocations[i]);
}
a = prev;
} }
lastAllocation = NULL; free(allocations);
allocations = NULL;
allocationsSize = 0;
allocationsLen = 0;
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);
} }
@ -226,22 +318,28 @@ MemoryFreeAll(void)
MemoryInfo * MemoryInfo *
MemoryInfoGet(void *p) MemoryInfoGet(void *p)
{ {
MemoryInfo *a; size_t hash, count;
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
a = lastAllocation; hash = MemoryHash(p);
while (a)
count = 0;
while (count <= allocationsSize)
{ {
if (a->pointer == p) if (!allocations[hash] || allocations[hash]->pointer != p)
{
hash = (hash + 1) % allocationsSize;
count++;
}
else
{ {
break; break;
} }
a = a->prev;
} }
return a; pthread_mutex_unlock(&lock);
return allocations[hash];
} }
size_t size_t
@ -291,15 +389,16 @@ MemoryInfoGetPointer(MemoryInfo * a)
void void
MemoryIterate(void (*iterFunc) (MemoryInfo *, void *), void *args) MemoryIterate(void (*iterFunc) (MemoryInfo *, void *), void *args)
{ {
MemoryInfo *a; size_t i;
pthread_mutex_lock(&lock); pthread_mutex_lock(&lock);
a = lastAllocation; for (i = 0; i < allocationsSize; i++)
while (a)
{ {
iterFunc(a, args); if (allocations[i])
a = a->prev; {
iterFunc(allocations[i], args);
}
} }
pthread_mutex_unlock(&lock); pthread_mutex_unlock(&lock);

View file

@ -58,8 +58,8 @@ TelodendriaMemoryHook(MemoryAction a, MemoryInfo * i, void *args)
action = "Freed"; action = "Freed";
break; break;
case MEMORY_BAD_POINTER: case MEMORY_BAD_POINTER:
Log(lc, LOG_WARNING, "Bad pointer passed into a memory function: %p", (void *) i); action = "Bad pointer to";
return; break;
default: default:
action = "Unknown operation on"; action = "Unknown operation on";
break; break;
@ -154,6 +154,7 @@ main(int argc, char **argv)
memset(&matrixArgs, 0, sizeof(matrixArgs)); memset(&matrixArgs, 0, sizeof(matrixArgs));
lc = LogConfigCreate(); lc = LogConfigCreate();
LogConfigLevelSet(lc, LOG_DEBUG);
if (!lc) if (!lc)
{ {

View file

@ -25,12 +25,14 @@
#include <Memory.h> #include <Memory.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <math.h> #include <math.h>
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <errno.h>
unsigned long unsigned long
UtilServerTs(void) UtilServerTs(void)
@ -174,3 +176,86 @@ UtilStringToBytes(char *str)
return bytes; return bytes;
} }
ssize_t
UtilGetDelim(char **linePtr, size_t * n, int delim, FILE * stream)
{
char *curPos, *newLinePtr;
size_t newLinePtrLen;
int c;
if (!linePtr || !n || !stream)
{
errno = EINVAL;
return -1;
}
if (*linePtr == NULL)
{
*n = 128;
if (!(*linePtr = Malloc(*n)))
{
errno = ENOMEM;
return -1;
}
}
curPos = *linePtr;
while (1)
{
c = getc(stream);
if (ferror(stream) || (c == EOF && curPos == *linePtr))
{
return -1;
}
if (c == EOF)
{
break;
}
if ((*linePtr + *n - curPos) < 2)
{
if (SSIZE_MAX / 2 < *n)
{
#ifdef EOVERFLOW
errno = EOVERFLOW;
#else
errno = ERANGE;
#endif
return -1;
}
newLinePtrLen = *n * 2;
if (!(newLinePtr = Realloc(*linePtr, newLinePtrLen)))
{
errno = ENOMEM;
return -1;
}
curPos = newLinePtr + (curPos - *linePtr);
*linePtr = newLinePtr;
*n = newLinePtrLen;
}
*curPos++ = (char) c;
if (c == delim)
{
break;
}
}
*curPos = '\0';
return (ssize_t) (curPos - *linePtr);
}
ssize_t
UtilGetLine(char **linePtr, size_t * n, FILE * stream)
{
return UtilGetDelim(linePtr, n, '\n', stream);
}

View file

@ -36,13 +36,13 @@ typedef enum MemoryAction
#define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__) #define Malloc(x) MemoryAllocate(x, __FILE__, __LINE__)
#define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__) #define Realloc(x, s) MemoryReallocate(x, s, __FILE__, __LINE__)
#define Free(x) MemoryFree(x) #define Free(x) MemoryFree(x, __FILE__, __LINE__)
typedef struct MemoryInfo MemoryInfo; typedef struct MemoryInfo MemoryInfo;
extern void *MemoryAllocate(size_t, const char *, int); extern void *MemoryAllocate(size_t, const char *, int);
extern void *MemoryReallocate(void *, size_t, const char *, int); extern void *MemoryReallocate(void *, size_t, const char *, int);
extern void MemoryFree(void *); extern void MemoryFree(void *, const char *, int);
extern size_t MemoryAllocated(void); extern size_t MemoryAllocated(void);
extern void MemoryFreeAll(void); extern void MemoryFreeAll(void);

View file

@ -31,7 +31,9 @@
#ifndef TELODENDRIA_UTIL_H #ifndef TELODENDRIA_UTIL_H
#define TELODENDRIA_UTIL_H #define TELODENDRIA_UTIL_H
#include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <sys/types.h>
/* /*
* Get the current type in milliseconds since the Unix epoch. This uses * Get the current type in milliseconds since the Unix epoch. This uses
@ -101,4 +103,10 @@ extern int
extern size_t extern size_t
UtilStringToBytes(char *); UtilStringToBytes(char *);
extern ssize_t
UtilGetDelim(char **, size_t *, int, FILE *);
extern ssize_t
UtilGetLine(char **, size_t *, FILE *);
#endif /* TELODENDRIA_UTIL_H */ #endif /* TELODENDRIA_UTIL_H */