Search code examples
c++socketsmemorymemset

using memset and freeaddrinfo causes the double free or corruption error


src.cpp

#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main(){
  
    struct addrinfo hints, *servinfo;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    getaddrinfo(NULL, "1560", &hints, &servinfo);
    freeaddrinfo(&hints);


}

I used gdb to find out where the error occurs and it occurs at the memset but when I comment out the call to freeaddrinfo(), it gives no error and works!

I am running Ubuntu 20.04.2 kernel 5.8.0-55-generic

edit: as you pointed out that I should give freeaddrinfo a list provided by getaddrinfo, when I pass servinfo to freeaddrinfo, it gives the following error

double free or corruption (out)
[1]    4123 abort (core dumped)  ./a.out

edit 2: thanks, when editing the question I forgot to replace the &hints with servinfo, and thought that I had already replaced it, sorry for the inconvinience, I deserve every bit of downvote for my stupidity.


Solution

  • freeaddrinfo(&hints);
    

    This is expected behaviour (in the way that it the behaviour is undefined; not in the way that you could rely on this behaviour). You may only pass structure created by getaddrinfo into freeaddrinfo. You didn't create &hints with getaddrinfo. Don't pass hints into freeaddrinfo.

    A fixed example:

    addrinfo hints {
        .ai_flags    = AI_PASSIVE,
        .ai_family   = AF_UNSPEC,
        .ai_socktype = SOCK_STREAM,
    };
    addrinfo* result;
    int err = getaddrinfo(nullptr, "12345", &hints, &result);
    if (err) {
        std::cout << "getaddrinfo: " << gai_strerror(err);
        std::exit(EXIT_FAILURE);
    } else {
        // use it here
    
        freeaddrinfo(result); // pay particular attention to this
    }
    

    I replaced it with freeaddrinfo(servinfo);

    Your new example still has freeaddrinfo(&hints) but also freeaddrinfo(servinfo). If you replace the former with the latter, then you're calling freeaddrinfo(servinfo) twice. You may not use the invalidated servinfo after you've freed it, including passing it into freeaddrinfo a second time. Similar rules apply as would apply to std::free.