I'm writing an app that provides a file listing via HTTP. Note that this is my first C app in about 10 years, so I may well be doing something dumb.
I'm receiving the following error when cleaning up a struct that contains a string pointer handed to MHD_create_response_from_buffer.
*** Error in `./lgserver': double free or corruption (!prev): 0x00007f956c02d6c0 ***
Advice appreciated.
Laurence.
#include "../lookglas.h"
struct Tree_args {
char *paths[2];
time_t modified_since;
char *listing;
};
static int walk_tree(struct Tree_args *);
static int order_files(const FTSENT**, const FTSENT**);
static int process_http_request(void *cls,
struct MHD_Connection *,
const char *,
const char *,
const char *,
const char *, size_t *, void **);
static void *cleanup_http_request(void *,
struct MHD_Connection *,
void **,
enum MHD_RequestTerminationCode);
static struct Tree_args *create_tree_args(char *path, size_t path_len, time_t modified_since);
static void destroy_tree_args(struct Tree_args *tree_args);
#define LISTING_BUF_SIZE 4096;
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Usage: %s PORT\n", argv[0]);
exit(EXIT_FAILURE);
}
errno = 0;
unsigned int port = strtoul(argv[1], NULL, 0);
if (0 != errno) {
fprintf(stderr, "%s is not a valid port number.\n", argv[1]);
exit(EXIT_FAILURE);
}
struct MHD_Daemon *daemon;
daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, port, NULL, NULL,
&process_http_request, NULL,
MHD_OPTION_NOTIFY_COMPLETED, &cleanup_http_request, NULL, MHD_OPTION_END);
if (NULL == daemon) {
perror("Could not start libmicrohttpd");
exit(EXIT_FAILURE);
}
// TODO: Replace with signal handling
getchar();
MHD_stop_daemon(daemon);
exit(EXIT_SUCCESS);
}
static int walk_tree(struct Tree_args *tree_args)
{
FTS *ftsp;
FTSENT *cur_node;
char ftype;
time_t mtime;
int line_len = 0;
size_t str_len = 0;
size_t listing_buf_size = 0;
const size_t buf_malloc = (LG_MAX_PATH_LENGTH + 14) * 128;
const char *line_fstr = "%c\t\"%s\"\t%ld\n\n";
assert(tree_args);
assert(tree_args->paths);
assert(tree_args->modified_since >= 0);
ftsp = fts_open(tree_args->paths, FTS_PHYSICAL, &order_files);
if (NULL != ftsp) {
printf("Processing tree: %s\n", tree_args->paths[0]);
while ((cur_node = fts_read(ftsp)) != NULL) { // Walk tree with each call
mtime = cur_node->fts_statp->st_mtime;
if ((cur_node->fts_info == FTS_D || cur_node->fts_info == FTS_F)
&& (mtime > tree_args->modified_since)) {
switch (cur_node->fts_info) {
case FTS_D:
ftype = 'D';
break;
case FTS_F:
ftype = 'F';
break;
default:
break;
}
line_len = snprintf(NULL, 0,
line_fstr, ftype, cur_node->fts_path, mtime);
if (str_len + line_len > listing_buf_size) {
void *tmp = realloc(tree_args->listing, listing_buf_size + buf_malloc);
if (tmp) {
tree_args->listing = tmp;
listing_buf_size += buf_malloc;
} else {
perror("Could not allocate memory for directory listing.");
return -1;
}
}
line_len = snprintf(tree_args->listing + str_len,
line_len,
line_fstr, ftype, cur_node->fts_path, mtime);
if (line_len < 0) {
perror("Could not generate directory listing.");
return -1;
}
str_len += line_len - 1;
}
}
if (0 != errno) {
// TODO: Format error string
perror("Could read info for file.");
return -1;
}
}
fts_close(ftsp);
return 0;
}
static int order_files(const FTSENT *one[], const FTSENT *two[])
{
return (strcmp((*one)->fts_name, (*two)->fts_name));
}
static int process_http_request(void *cls,
struct MHD_Connection *connection,
const char *url,
const char *method,
const char *version,
const char *upload_data,
size_t *upload_data_size,
void **ptr)
{
const char *list_path = "/list";
/* const char *download_path = "/download"; */
const char *mod_since_query_parm = "modified_since";
static int aptr;
int ret;
struct MHD_Response *response;
if (0 != strcmp (method, "GET"))
return MHD_NO; /* Unsupported HTTP method */
if (&aptr != *ptr) {
/* Don't respond on first call so as to allow http streaming */
*ptr = &aptr;
return MHD_YES;
}
*ptr = NULL; /* Reset connection history */
if (strncasecmp(url, list_path, strlen(list_path)) == 0) {
// TODO: Add check for valid path.
// TODO: Check for perms on path
if (strnlen(url, LG_MAX_PATH_LENGTH) == LG_MAX_PATH_LENGTH + 1) {
fprintf(stderr, "Supplied URL is longer than max path length on this box: %s\n", url);
return MHD_NO;
}
time_t mod_since = 0;
char *strtolTmp;
const char *mod_since_str = MHD_lookup_connection_value(connection,
MHD_GET_ARGUMENT_KIND,
mod_since_query_parm);
if (NULL != mod_since_str) {
errno = 0;
mod_since = (time_t)strtol(mod_since_str, &strtolTmp, 10);
if ((0 != errno || '\0' != *strtolTmp) || mod_since < 0) {
fprintf(stderr, "Supplied \"modified since\" time was not a valid time.\n");
// Replace with nice HTTP error message response
return MHD_NO;
}
}
struct Tree_args *tree_args = create_tree_args((char *)url + strlen(list_path),
strlen(url) - strlen(list_path),
mod_since);
if (walk_tree(tree_args) == -1) {
destroy_tree_args(tree_args);
// Replace with nice HTTP error message response
return MHD_NO;
}
// *ptr is supplied to &cleanup_http_request when MHD has processed this request.
*ptr = tree_args;
response = MHD_create_response_from_buffer(strlen(tree_args->listing),
tree_args->listing, MHD_RESPMEM_MUST_FREE);
if (NULL == response) {
destroy_tree_args(tree_args);
return MHD_NO;
}
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
return ret;
/* } else if (strncasecmp(url, download_path, strlen(download_path)) == 0) { */
// TODO: Copy path length and mod time checks here
// TODO: Add check for valid path.
// TODO: Check for perms on path
} else {
// TODO: return 404 or access denied?
return MHD_NO;
}
}
static void *cleanup_http_request(void *cls,
struct MHD_Connection *connection,
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
destroy_tree_args(*con_cls);
return NULL;
}
static struct Tree_args *create_tree_args(char *path, size_t path_len, time_t modified_since)
{
struct Tree_args *tree_args = malloc(sizeof(*tree_args));
if (NULL == tree_args)
return NULL;
tree_args->paths[0] = strndup(path, path_len);
if (NULL == tree_args->paths[0]) {
destroy_tree_args(tree_args);
return NULL;
}
tree_args->paths[1] = NULL;
tree_args->modified_since = modified_since;
tree_args->listing = NULL;
return tree_args;
}
static void destroy_tree_args(struct Tree_args *tree_args)
{
free(tree_args->paths[0]);
free(tree_args->listing);
free(tree_args);
}
valgrind --leak-check=full --show-leak-kinds=all ./lgserver 8888
==20592== Memcheck, a memory error detector
==20592== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==20592== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==20592== Command: ./lgserver 8888
==20592==
Processing tree: /home/laurence/Music
==20592== Thread 2:
==20592== Invalid free() / delete / delete[] / realloc()
==20592== at 0x4C2CE2B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x401769: destroy_tree_args (server.c:254)
==20592== by 0x4016A1: cleanup_http_request (server.c:230)
==20592== by 0x4E3E877: MHD_connection_handle_idle (connection.c:2749)
==20592== by 0x4E42E58: MHD_run_from_select (daemon.c:2297)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592== Address 0x66d84a0 is 0 bytes inside a block of size 91,392 free'd
==20592== at 0x4C2CE2B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E45A69: MHD_destroy_response (response.c:589)
==20592== by 0x4E3E47B: MHD_connection_handle_idle (connection.c:2744)
==20592== by 0x4E42E58: MHD_run_from_select (daemon.c:2297)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592== Block was alloc'd at
==20592== at 0x4C2DD9F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x401292: walk_tree (server.c:99)
==20592== by 0x4015C0: process_http_request (server.c:195)
==20592== by 0x4E3CB93: call_connection_handler (connection.c:1585)
==20592== by 0x4E3DBCB: MHD_connection_handle_idle (connection.c:2624)
==20592== by 0x4E42E58: MHD_run_from_select (daemon.c:2297)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592==
Internal application error, closing connection.
==20592== Invalid read of size 8
==20592== at 0x40174F: destroy_tree_args (server.c:253)
==20592== by 0x4016A1: cleanup_http_request (server.c:230)
==20592== by 0x4E3C892: MHD_connection_close_ (connection.c:487)
==20592== by 0x4E3CBB6: connection_close_error (connection.c:510)
==20592== by 0x4E3CBB6: call_connection_handler (connection.c:1594)
==20592== by 0x4E3DBCB: MHD_connection_handle_idle (connection.c:2624)
==20592== by 0x4E42E58: MHD_run_from_select (daemon.c:2297)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==20592==
==20592==
==20592== Process terminating with default action of signal 11 (SIGSEGV)
==20592== Access not within mapped region at address 0x0
==20592== at 0x40174F: destroy_tree_args (server.c:253)
==20592== by 0x4016A1: cleanup_http_request (server.c:230)
==20592== by 0x4E3C892: MHD_connection_close_ (connection.c:487)
==20592== by 0x4E3CBB6: connection_close_error (connection.c:510)
==20592== by 0x4E3CBB6: call_connection_handler (connection.c:1594)
==20592== by 0x4E3DBCB: MHD_connection_handle_idle (connection.c:2624)
==20592== by 0x4E42E58: MHD_run_from_select (daemon.c:2297)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592== If you believe this happened as a result of a stack
==20592== overflow in your program's main thread (unlikely but
==20592== possible), you can try to increase the size of the
==20592== main thread stack using the --main-stacksize= flag.
==20592== The main thread stack size used in this run was 8720384.
==20592==
==20592== HEAP SUMMARY:
==20592== in use at exit: 68,008 bytes in 11 blocks
==20592== total heap usage: 2,027 allocs, 2,017 frees, 8,667,392 bytes allocated
==20592==
==20592== Thread 1:
==20592== 32 bytes in 2 blocks are still reachable in loss record 1 of 7
==20592== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E4038C: internal_add_connection (daemon.c:1433)
==20592== by 0x4E41A05: MHD_accept_connection (daemon.c:2015)
==20592== by 0x4E42F9F: MHD_run_from_select (daemon.c:2255)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592==
==20592== 80 bytes in 2 blocks are still reachable in loss record 2 of 7
==20592== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E44F12: MHD_pool_create (memorypool.c:91)
==20592== by 0x4E4035C: internal_add_connection (daemon.c:1414)
==20592== by 0x4E41A05: MHD_accept_connection (daemon.c:2015)
==20592== by 0x4E42F9F: MHD_run_from_select (daemon.c:2255)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592==
==20592== 488 bytes in 1 blocks are still reachable in loss record 3 of 7
==20592== at 0x4C2DB95: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E43B06: MHD_start_daemon_va (daemon.c:3702)
==20592== by 0x4E449C9: MHD_start_daemon (daemon.c:3053)
==20592== by 0x4010A0: main (server.c:41)
==20592==
==20592== 560 bytes in 1 blocks are possibly lost in loss record 4 of 7
==20592== at 0x4C2DB95: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4013504: allocate_dtv (dl-tls.c:322)
==20592== by 0x4013504: _dl_allocate_tls (dl-tls.c:544)
==20592== by 0x54210D2: allocate_stack (allocatestack.c:588)
==20592== by 0x54210D2: pthread_create@@GLIBC_2.2.5 (pthread_create.c:537)
==20592== by 0x4E3F39F: create_thread (daemon.c:1230)
==20592== by 0x4E443F9: MHD_start_daemon_va (daemon.c:4212)
==20592== by 0x4E449C9: MHD_start_daemon (daemon.c:3053)
==20592== by 0x4010A0: main (server.c:41)
==20592==
==20592== 576 bytes in 1 blocks are still reachable in loss record 5 of 7
==20592== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E43C80: MHD_start_daemon_va (daemon.c:3821)
==20592== by 0x4E449C9: MHD_start_daemon (daemon.c:3053)
==20592== by 0x4010A0: main (server.c:41)
==20592==
==20592== 736 bytes in 2 blocks are still reachable in loss record 6 of 7
==20592== at 0x4C2DB95: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E4033A: internal_add_connection (daemon.c:1397)
==20592== by 0x4E41A05: MHD_accept_connection (daemon.c:2015)
==20592== by 0x4E42F9F: MHD_run_from_select (daemon.c:2255)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592==
==20592== 65,536 bytes in 2 blocks are still reachable in loss record 7 of 7
==20592== at 0x4C2BBCF: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20592== by 0x4E44F2F: MHD_pool_create (memorypool.c:110)
==20592== by 0x4E4035C: internal_add_connection (daemon.c:1414)
==20592== by 0x4E41A05: MHD_accept_connection (daemon.c:2015)
==20592== by 0x4E42F9F: MHD_run_from_select (daemon.c:2255)
==20592== by 0x4E4320A: MHD_select (daemon.c:2440)
==20592== by 0x4E43381: MHD_select_thread (daemon.c:2999)
==20592== by 0x54206A9: start_thread (pthread_create.c:333)
==20592==
==20592== LEAK SUMMARY:
==20592== definitely lost: 0 bytes in 0 blocks
==20592== indirectly lost: 0 bytes in 0 blocks
==20592== possibly lost: 560 bytes in 1 blocks
==20592== still reachable: 67,448 bytes in 10 blocks
==20592== suppressed: 0 bytes in 0 blocks
==20592==
==20592== For counts of detected and suppressed errors, rerun with: -v
==20592== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Killed