1
votes

I am trying to write an echo server\client model in C. My code compiles but throws a segmentation fault error at run-time [I believe on the server side process]. When testing in CLion debug environment, the server process is able to execute the accept() system call and enter into a a waiting state until a client connects. Therefore, I believe that the segmentation fault error happens after the client makes the connect() system call.

Here are the relevant snippets of code (only the last part - not full program):

/* [6] LISTEN FOR CONNECTIONS ON BOUND SOCKET===================================================================== */
struct sockaddr_storage ample;    /* from Beej Guide 5.6 accept() */
socklen_t ample_sz = sizeof(ample);
fd_activeSock = accept(fd_listenSock, (struct sockaddr *)&established_SERV_param, &ample_sz);

if (fd_activeSock == -1)    /* Error checking */
{
    fprintf(stderr, "\nNo forum for communication...\nTERMINATING PROCESS");
    exit(EXIT_FAILURE);
}

printf("\nCommunication Established! What's your sign??");
freeaddrinfo(established_SERV_param);    /* free up memory */

/* [7] ACCEPT A CONNECTION (BLOCKING)============================================================================= */

/* MAIN LOOP====================================================================================================== */
while(1)
{
    bzero(msg_incoming, 16);
    recv(fd_activeSock, msg_incoming, 16, 0);
    printf("%s", msg_incoming);
    send(fd_activeSock, msg_incoming, 16, 0);
}

When I run both programs in separate terminals (server process first, of course), the last print statement that runs before the error is:

printf("\nCommunication Established! What's your sign??");

The error is output to the server terminal. There is a core dump; for future issues, could someone suggest a beginners tutorial on combing through core dump files. Also, I have run the code with the freeaddrinfo() call commented out and still get a segmentation fault error so I do not believe that this is the issue. Why run it at all? I do not want memory leaks. Thank you for your help.

2

2 Answers

4
votes

recv() does not explicitly place a null terminator at the end of the buffer, but printf() expects one.

In the statements:

bzero(msg_incoming, 16);
recv(fd_activeSock, msg_incoming, 16, 0);
printf("%s", msg_incoming); 

Although msg_incoming has been zeroed, when it is populated in the recv call, if all 16 elements are populated, there is no guaranteed that the last element of the array was populated with '\0', leaving the buffer as a non-null terminated array. If that happens, A segfault is likely when printf() is called. Or worse, a segfault may not occur, leading you to believe your code works fine. (AKA undefined behavior)

The fix is to check the return value of recv():

ssize_t bytes = recv(fd_activeSock, msg_incoming, 16, 0);
if(bytes <= 0)
{
    //handle error/end of message condition
}
else
{
    msg_incoming[bytes] = '\0';
    printf("%s", msg_incoming);
}

Additional material on Reading data with a socket.

2
votes
freeaddrinfo(established_SERV_param)

Should be called when established_SERV_param is obtained by getaddrinfo. Here established_SERV_param is probably a stack variable. Hence, you are trying to free a pointer to stack variable.

Umm something is wrong in your program. Since, freeaddrinfo expects a pointer but it is a variable since you use & in call to accept. Removing the call to freeaddrinfo may fix it.

If above is not enough then it is important to see how msg_incoming is defined/allocated. It should not be a const char array or initialised by a string literal making it a const. If it is a pointer it should be adequately allocated memory using malloc.

Analysing core dump:

Compile your code with debug On and optimisation Off

gcc -g -O0

Then open the core file in gdb as

gdb <executable> <core file>
(gdb) bt 

Above, bt will show you the back trace where the program crashed. You can go the function it crashed by command fr 0 and print some variables. A tutorial for gdb can found here