diff --git a/FTL.h b/FTL.h index e7aaaab8..220549d7 100644 --- a/FTL.h +++ b/FTL.h @@ -254,3 +254,14 @@ bool rereadgravity; long int lastDBimportedtimestamp; bool ipv4telnet, ipv6telnet; bool istelnet[MAXCONNS]; + +// Use out own memory handling functions that will detect possible errors +// and report accordingly in the log. This will make debugging FTL crashs +// caused by insufficient memory or by code bugs (not properly dealing +// with NULL pointers) much easier. +#define free(param) FTLfree(param, __FILE__, __FUNCTION__, __LINE__) +#define lib_strdup() strdup() +#undef strdup +#define strdup(param) FTLstrdup(param, __FILE__, __FUNCTION__, __LINE__) +#define calloc(p1,p2) FTLcalloc(p1,p2, __FILE__, __FUNCTION__, __LINE__) +#define realloc(p1,p2) FTLrealloc(p1,p2, __FILE__, __FUNCTION__, __LINE__) diff --git a/Makefile b/Makefile index e662a53c..8250d043 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ # Please see LICENSE file for your rights under this license. DEPS = FTL.h routines.h api.h version.h -OBJ = main.o structs.o log.o daemon.o parser.o signals.o socket.o request.o grep.o setupVars.o args.o flush.o threads.o gc.o config.o database.o api.o msgpack.o +OBJ = main.o memory.o log.o daemon.o parser.o signals.o socket.o request.o grep.o setupVars.o args.o flush.o threads.o gc.o config.o database.o api.o msgpack.o # Get git commit version and date GIT_BRANCH := $(shell git branch | sed -n 's/^\* //p') diff --git a/memory.c b/memory.c new file mode 100644 index 00000000..857d39d6 --- /dev/null +++ b/memory.c @@ -0,0 +1,290 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* FTL Engine +* Global variable definitions and memory reallocation handling +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ + +#include "FTL.h" + +FTLFileNamesStruct FTLfiles = { + "/etc/pihole/pihole-FTL.conf", + "/var/log/pihole-FTL.log", + "/var/run/pihole-FTL.pid", + "/var/run/pihole-FTL.port", + NULL, + "/var/run/pihole/FTL.sock" +}; + +logFileNamesStruct files = { + "/var/log/pihole.log", + "/etc/pihole/list.preEventHorizon", + "/etc/pihole/whitelist.txt", + "/etc/pihole/blacklist.txt", + "/etc/pihole/setupVars.conf", + "/etc/dnsmasq.d/03-pihole-wildcard.conf", + "/etc/pihole/auditlog.list", + "/etc/dnsmasq.d/01-pihole.conf" +}; + +countersStruct counters = { 0 }; + +void memory_check(int which) +{ + switch(which) + { + case QUERIES: + if(counters.queries >= counters.queries_MAX) + { + // Have to reallocate memory + counters.queries_MAX += QUERIESALLOCSTEP; + logg_struct_resize("queries",counters.queries_MAX,QUERIESALLOCSTEP); + queries = realloc(queries, counters.queries_MAX*sizeof(queriesDataStruct)); + if(queries == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + } + break; + case FORWARDED: + if(counters.forwarded >= counters.forwarded_MAX) + { + // Have to reallocate memory + counters.forwarded_MAX += FORWARDEDALLOCSTEP; + logg_struct_resize("forwarded",counters.forwarded_MAX,FORWARDEDALLOCSTEP); + forwarded = realloc(forwarded, counters.forwarded_MAX*sizeof(forwardedDataStruct)); + if(forwarded == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + } + break; + case CLIENTS: + if(counters.clients >= counters.clients_MAX) + { + // Have to reallocate memory + counters.clients_MAX += CLIENTSALLOCSTEP; + logg_struct_resize("clients",counters.clients_MAX,CLIENTSALLOCSTEP); + clients = realloc(clients, counters.clients_MAX*sizeof(clientsDataStruct)); + if(clients == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + } + break; + case DOMAINS: + if(counters.domains >= counters.domains_MAX) + { + // Have to reallocate memory + counters.domains_MAX += DOMAINSALLOCSTEP; + logg_struct_resize("domains",counters.domains_MAX,DOMAINSALLOCSTEP); + domains = realloc(domains, counters.domains_MAX*sizeof(domainsDataStruct)); + if(domains == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + } + break; + case OVERTIME: + if(counters.overTime >= counters.overTime_MAX) + { + // Have to reallocate memory + counters.overTime_MAX += OVERTIMEALLOCSTEP; + logg_struct_resize("overTime",counters.overTime_MAX,OVERTIMEALLOCSTEP); + overTime = realloc(overTime, counters.overTime_MAX*sizeof(overTimeDataStruct)); + if(overTime == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + } + break; + case WILDCARD: + // Definitely enlarge wildcard entry + // Enlarge wildcarddomains pointer array + logg_struct_resize("wildcards", (counters.wildcarddomains+1), 1); + wildcarddomains = realloc(wildcarddomains, (counters.wildcarddomains+1)*sizeof(*wildcarddomains)); + if(wildcarddomains == NULL) + { + logg("FATAL: Memory allocation failed! Exiting"); + exit(EXIT_FAILURE); + } + break; + default: + /* That cannot happen */ + logg("Fatal error in memory_check(%i)", which); + exit(EXIT_FAILURE); + break; + } +} + +void validate_access(const char * name, int pos, bool testmagic, int line, const char * function, const char * file) +{ + int limit = 0; + if(name[0] == 'c') limit = counters.clients_MAX; + else if(name[0] == 'd') limit = counters.domains_MAX; + else if(name[0] == 'q') limit = counters.queries_MAX; + else if(name[0] == 'o') limit = counters.overTime_MAX; + else if(name[0] == 'f') limit = counters.forwarded_MAX; + else if(name[0] == 'w') limit = counters.wildcarddomains; + else { logg("Validator error (range)"); killed = 1; } + + if(pos >= limit || pos < 0) + { + logg("FATAL ERROR: Trying to access %s[%i], but maximum is %i", name, pos, limit); + logg(" found in %s() (%s:%i)", function, file, line); + } + // Don't test magic byte if detected potential out-of-bounds error + else if(testmagic) + { + unsigned char magic = 0x00; + if(name[0] == 'c') magic = clients[pos].magic; + else if(name[0] == 'd') magic = domains[pos].magic; + else if(name[0] == 'q') magic = queries[pos].magic; + else if(name[0] == 'o') magic = overTime[pos].magic; + else if(name[0] == 'f') magic = forwarded[pos].magic; + else { logg("Validator error (magic byte)"); killed = 1; } + if(magic != MAGICBYTE) + { + logg("FATAL ERROR: Trying to access %s[%i], but magic byte is %x", name, pos, magic); + logg(" found in %s() (%s:%i)", function, file, line); + } + } +} + +void validate_access_oTfd(int timeidx, int forwardID, int line, const char * function, const char * file) +{ + // Determine if there is enough space for saving the current + // forwardID in the overTime data structure, allocate space otherwise + validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); + if(overTime[timeidx].forwardnum <= forwardID) + { + // Reallocate more space for forwarddata + overTime[timeidx].forwarddata = realloc(overTime[timeidx].forwarddata, (forwardID+1)*sizeof(*overTime[timeidx].forwarddata)); + // Initialize new data fields with zeroes + int j; + for(j = overTime[timeidx].forwardnum; j <= forwardID; j++) + { + overTime[timeidx].forwarddata[j] = 0; + memory.forwarddata++; + } + // Update counter + overTime[timeidx].forwardnum = forwardID + 1; + } + + int limit = overTime[timeidx].forwardnum; + if(forwardID >= limit || forwardID < 0) + { + logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + logg("FATAL ERROR: Trying to access overTime.forwarddata[%i], but maximum is %i", forwardID, limit); + logg(" found in %s() (%s:%i)", function, file, line); + } +} + +void validate_access_oTcl(int timeidx, int clientID, int line, const char * function, const char * file) +{ + // Determine if there is enough space for saving the current + // clientID in the overTime data structure, allocate space otherwise + if(overTime[timeidx].clientnum <= clientID) + { + // Reallocate more space for clientdata + overTime[timeidx].clientdata = realloc(overTime[timeidx].clientdata, (clientID+1)*sizeof(*overTime[timeidx].clientdata)); + // Initialize new data fields with zeroes + int i; + for(i = overTime[timeidx].clientnum; i <= clientID; i++) + { + overTime[timeidx].clientdata[i] = 0; + memory.clientdata++; + } + // Update counter + overTime[timeidx].clientnum = clientID + 1; + } + int limit = overTime[timeidx].clientnum; + if(clientID >= limit || clientID < 0) + { + logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + logg("FATAL ERROR: Trying to access overTime.clientdata[%i], but maximum is %i", clientID, limit); + logg(" found in %s() (%s:%i)", function, file, line); + } +} + +// The special memory handling routines have to be the last ones in this source file +// as we restore the original definition of the strdup, free, calloc, and realloc +// functions in here, i.e. if anything extra would come below these lines, it would +// not be protected by our (error logging) functions! + +#undef strdup +char *FTLstrdup(const char *src, const char * file, const char * function, int line) +{ + // The FTLstrdup() function returns a pointer to a new string which is a + // duplicate of the string s. Memory for the new string is obtained with + // malloc(3), and can be freed with free(3). + if(src == NULL) + { + logg("WARN: Trying to copy a NULL string in %s() (%s:%i)", function, file, line); + return NULL; + } + char *dest = __strdup(src); + if(dest == NULL) + logg("FATAL: Memory allocation failed in %s() (%s:%i)", function, file, line); + + return dest; +} + +#undef calloc +void *FTLcalloc(size_t nmemb, size_t size, const char * file, const char * function, int line) +{ + // The FTLcalloc() function allocates memory for an array of nmemb elements + // of size bytes each and returns a pointer to the allocated memory. The + // memory is set to zero. If nmemb or size is 0, then calloc() returns + // either NULL, or a unique pointer value that can later be successfully + // passed to free(). + void *ptr = calloc(nmemb, size); + if(ptr == NULL) + logg("FATAL: Memory allocation (%u x %u) failed in %s() (%s:%i)", + nmemb, size, function, file, line); + + return ptr; +} + +#undef realloc +void *FTLrealloc(void *ptr_in, size_t size, const char * file, const char * function, int line) +{ + // The FTLrealloc() function changes the size of the memory block pointed to + // by ptr to size bytes. The contents will be unchanged in the range from + // the start of the region up to the minimum of the old and new sizes. If + // the new size is larger than the old size, the added memory will not be + // initialized. If ptr is NULL, then the call is equivalent to malloc(size), + // for all values of size; if size is equal to zero, and ptr is + // not NULL, then the call is equivalent to free(ptr). Unless ptr is + // NULL, it must have been returned by an earlier call to malloc(), cal‐ + // loc() or realloc(). If the area pointed to was moved, a free(ptr) is + // done. + void *ptr_out = realloc(ptr_in, size); + if(ptr_out == NULL) + logg("FATAL: Memory reallocation (%p -> %u) failed in %s() (%s:%i)", + ptr_in, size, function, file, line); + + return ptr_out; +} + +#undef free +void FTLfree(void *ptr, const char * file, const char * function, int line) +{ + // The free() function frees the memory space pointed to by ptr, which + // must have been returned by a previous call to malloc(), calloc(), or + // realloc(). Otherwise, or if free(ptr) has already been called before, + // undefined behavior occurs. If ptr is NULL, no operation is performed. + if(ptr == NULL) + logg("FATAL: Trying to free NULL pointer in %s() (%s:%i)", function, file, line); + + // We intentionally run free() nevertheless to see the crash in the debugger + free(ptr); +} diff --git a/parser.c b/parser.c index abad8dc7..d66483a1 100644 --- a/parser.c +++ b/parser.c @@ -1282,96 +1282,6 @@ int findClientID(const char *client) return clientID; } -void validate_access(const char * name, int pos, bool testmagic, int line, const char * function, const char * file) -{ - int limit = 0; - if(name[0] == 'c') limit = counters.clients_MAX; - else if(name[0] == 'd') limit = counters.domains_MAX; - else if(name[0] == 'q') limit = counters.queries_MAX; - else if(name[0] == 'o') limit = counters.overTime_MAX; - else if(name[0] == 'f') limit = counters.forwarded_MAX; - else if(name[0] == 'w') limit = counters.wildcarddomains; - else { logg("Validator error (range)"); killed = 1; } - - if(pos >= limit || pos < 0) - { - logg("FATAL ERROR: Trying to access %s[%i], but maximum is %i", name, pos, limit); - logg(" found in %s() (line %i) in %s", function, line, file); - } - // Don't test magic byte if detected potential out-of-bounds error - else if(testmagic) - { - unsigned char magic = 0x00; - if(name[0] == 'c') magic = clients[pos].magic; - else if(name[0] == 'd') magic = domains[pos].magic; - else if(name[0] == 'q') magic = queries[pos].magic; - else if(name[0] == 'o') magic = overTime[pos].magic; - else if(name[0] == 'f') magic = forwarded[pos].magic; - else { logg("Validator error (magic byte)"); killed = 1; } - if(magic != MAGICBYTE) - { - logg("FATAL ERROR: Trying to access %s[%i], but magic byte is %x", name, pos, magic); - logg(" found in %s() (line %i) in %s", function, line, file); - } - } -} - -void validate_access_oTfd(int timeidx, int forwardID, int line, const char * function, const char * file) -{ - // Determine if there is enough space for saving the current - // forwardID in the overTime data structure, allocate space otherwise - validate_access("overTime", timeidx, true, __LINE__, __FUNCTION__, __FILE__); - if(overTime[timeidx].forwardnum <= forwardID) - { - // Reallocate more space for forwarddata - overTime[timeidx].forwarddata = realloc(overTime[timeidx].forwarddata, (forwardID+1)*sizeof(*overTime[timeidx].forwarddata)); - // Initialize new data fields with zeroes - int j; - for(j = overTime[timeidx].forwardnum; j <= forwardID; j++) - { - overTime[timeidx].forwarddata[j] = 0; - memory.forwarddata++; - } - // Update counter - overTime[timeidx].forwardnum = forwardID + 1; - } - - int limit = overTime[timeidx].forwardnum; - if(forwardID >= limit || forwardID < 0) - { - logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - logg("FATAL ERROR: Trying to access overTime.forwarddata[%i], but maximum is %i", forwardID, limit); - logg(" found in %s() (line %i) in %s", function, line, file); - } -} - -void validate_access_oTcl(int timeidx, int clientID, int line, const char * function, const char * file) -{ - // Determine if there is enough space for saving the current - // clientID in the overTime data structure, allocate space otherwise - if(overTime[timeidx].clientnum <= clientID) - { - // Reallocate more space for clientdata - overTime[timeidx].clientdata = realloc(overTime[timeidx].clientdata, (clientID+1)*sizeof(*overTime[timeidx].clientdata)); - // Initialize new data fields with zeroes - int i; - for(i = overTime[timeidx].clientnum; i <= clientID; i++) - { - overTime[timeidx].clientdata[i] = 0; - memory.clientdata++; - } - // Update counter - overTime[timeidx].clientnum = clientID + 1; - } - int limit = overTime[timeidx].clientnum; - if(clientID >= limit || clientID < 0) - { - logg("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - logg("FATAL ERROR: Trying to access overTime.clientdata[%i], but maximum is %i", clientID, limit); - logg(" found in %s() (line %i) in %s", function, line, file); - } -} - void reresolveHostnames(void) { int clientID; diff --git a/routines.h b/routines.h index ae59f9dc..b31fb686 100644 --- a/routines.h +++ b/routines.h @@ -29,9 +29,6 @@ void open_pihole_log(void); void handle_signals(void); void process_pihole_log(void); void *pihole_log_thread(void *val); -void validate_access(const char * name, int pos, bool testmagic, int line, const char * function, const char * file); -void validate_access_oTfd(int timeidx, int forwardID, int line, const char * function, const char * file); -void validate_access_oTcl(int timeidx, int clientID, int line, const char * function, const char * file); void reresolveHostnames(void); int findClientID(const char *client); int findDomainID(const char *domain); @@ -88,3 +85,12 @@ void *DB_thread(void *val); int get_number_of_queries_in_DB(void); void save_to_DB(void); void read_data_from_DB(void); + +// memory.c +char *FTLstrdup(const char *src, const char *file, const char *function, int line); +void *FTLcalloc(size_t nmemb, size_t size, const char *file, const char *function, int line); +void *FTLrealloc(void *ptr_in, size_t size, const char *file, const char *function, int line); +void FTLfree(void *ptr, const char* file, const char *function, int line); +void validate_access(const char * name, int pos, bool testmagic, int line, const char * function, const char * file); +void validate_access_oTfd(int timeidx, int forwardID, int line, const char * function, const char * file); +void validate_access_oTcl(int timeidx, int clientID, int line, const char * function, const char * file); diff --git a/structs.c b/structs.c deleted file mode 100644 index b49228fb..00000000 --- a/structs.c +++ /dev/null @@ -1,126 +0,0 @@ -/* Pi-hole: A black hole for Internet advertisements -* (c) 2017 Pi-hole, LLC (https://pi-hole.net) -* Network-wide ad blocking via your own hardware. -* -* FTL Engine -* Global variable definitions and memory reallocation handling -* -* This file is copyright under the latest version of the EUPL. -* Please see LICENSE file for your rights under this license. */ - -#include "FTL.h" - -FTLFileNamesStruct FTLfiles = { - "/etc/pihole/pihole-FTL.conf", - "/var/log/pihole-FTL.log", - "/var/run/pihole-FTL.pid", - "/var/run/pihole-FTL.port", - NULL, - "/var/run/pihole/FTL.sock" -}; - -logFileNamesStruct files = { - "/var/log/pihole.log", - "/etc/pihole/list.preEventHorizon", - "/etc/pihole/whitelist.txt", - "/etc/pihole/blacklist.txt", - "/etc/pihole/setupVars.conf", - "/etc/dnsmasq.d/03-pihole-wildcard.conf", - "/etc/pihole/auditlog.list", - "/etc/dnsmasq.d/01-pihole.conf" -}; - -countersStruct counters = { 0 }; - -void memory_check(int which) -{ - switch(which) - { - case QUERIES: - if(counters.queries >= counters.queries_MAX) - { - // Have to reallocate memory - counters.queries_MAX += QUERIESALLOCSTEP; - logg_struct_resize("queries",counters.queries_MAX,QUERIESALLOCSTEP); - queries = realloc(queries, counters.queries_MAX*sizeof(queriesDataStruct)); - if(queries == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - } - break; - case FORWARDED: - if(counters.forwarded >= counters.forwarded_MAX) - { - // Have to reallocate memory - counters.forwarded_MAX += FORWARDEDALLOCSTEP; - logg_struct_resize("forwarded",counters.forwarded_MAX,FORWARDEDALLOCSTEP); - forwarded = realloc(forwarded, counters.forwarded_MAX*sizeof(forwardedDataStruct)); - if(forwarded == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - } - break; - case CLIENTS: - if(counters.clients >= counters.clients_MAX) - { - // Have to reallocate memory - counters.clients_MAX += CLIENTSALLOCSTEP; - logg_struct_resize("clients",counters.clients_MAX,CLIENTSALLOCSTEP); - clients = realloc(clients, counters.clients_MAX*sizeof(clientsDataStruct)); - if(clients == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - } - break; - case DOMAINS: - if(counters.domains >= counters.domains_MAX) - { - // Have to reallocate memory - counters.domains_MAX += DOMAINSALLOCSTEP; - logg_struct_resize("domains",counters.domains_MAX,DOMAINSALLOCSTEP); - domains = realloc(domains, counters.domains_MAX*sizeof(domainsDataStruct)); - if(domains == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - } - break; - case OVERTIME: - if(counters.overTime >= counters.overTime_MAX) - { - // Have to reallocate memory - counters.overTime_MAX += OVERTIMEALLOCSTEP; - logg_struct_resize("overTime",counters.overTime_MAX,OVERTIMEALLOCSTEP); - overTime = realloc(overTime, counters.overTime_MAX*sizeof(overTimeDataStruct)); - if(overTime == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - } - break; - case WILDCARD: - // Definitely enlarge wildcard entry - // Enlarge wildcarddomains pointer array - logg_struct_resize("wildcards", (counters.wildcarddomains+1), 1); - wildcarddomains = realloc(wildcarddomains, (counters.wildcarddomains+1)*sizeof(*wildcarddomains)); - if(wildcarddomains == NULL) - { - logg("FATAL: Memory allocation failed! Exiting"); - exit(EXIT_FAILURE); - } - break; - default: - /* That cannot happen */ - logg("Fatal error in memory_check(%i)", which); - exit(EXIT_FAILURE); - break; - } -}