libmicrohttpd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] double free or corruption


From: Laurence Rochfort
Subject: [libmicrohttpd] double free or corruption
Date: Wed, 17 Feb 2016 10:53:09 +0000

Hi,

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.

Cheers,
Laurence.

Code listing:

#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:

 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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]