Debugging a segfault — program always does as it should but always segfaults - c

This program is supposed to print a webpage to terminal
usage: ./prog www.page.com 80 /folder/index.html
program always does as it should but always seg-faults at the end
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <termios.h>
#define HOST "localhost"
#define PAGE "/"
#define PORT "80"
#define USERAGENT "HTML 1.1"
char *build_get_query(char *host,char *page){
char *query;
char *getpage = page;
char *tpl = "GET /%s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n";
if(getpage[0]=='/'){
getpage=getpage + 1; /*Removing leading / */
}
/* -5 is to consider the %s %s %s in tpl and the ending \0 */
query=malloc(strlen(host)+strlen(getpage)+strlen(USERAGENT)+strlen(tpl)-5);
sprintf(query, tpl, getpage, host, USERAGENT);
return query;
}
int main(int argc, char **argv){
int sockfd;
struct addrinfo hints, *servinfo, *p;
int s;
char *page;
char *stringA;
int tmpres;
char *sstr;
if(argc<3){
fprintf(stderr,"\n\tUSAGE:\n\t%s hostname/ip PORTnr /PAGE/index.shtml \n\n",argv[0]);
fprintf(stderr,"\texample:\n\t%s www.anypage.com 80 /folder/index.shtml \n\n",argv[0]);
exit(1);
}
memset(&hints,0,sizeof(struct addrinfo));
hints.ai_family=AF_INET; /* Allow IPv4 or IPv6 */
hints.ai_socktype=SOCK_STREAM; /* stream socket */
hints.ai_flags=0;
hints.ai_protocol=0; /* Any protocol */
if((s=getaddrinfo(argv[1],PORT,&hints,&servinfo))!=0){
fprintf(stderr,"\n\tgetaddrinfo: %s\n",gai_strerror(s));
exit(EXIT_FAILURE);
}
for(p=servinfo; p!=NULL; p=p->ai_next){
if((sockfd=socket(p->ai_family,p->ai_socktype,p->ai_protocol))==-1){
fprintf(stderr,"\n\tSOCKET FAIL\n\n");
continue;
}
if((connect(sockfd,p->ai_addr,p->ai_addrlen))<0){
close(sockfd);
fprintf(stderr,"\n\tCONECTION FAIL\n\n");
continue;
}
break;
}
if(p==NULL){
fprintf(stderr,"\n\tCONECTION FAIL\n\n");
exit(2);
}
freeaddrinfo(servinfo);
if(argc > 3){
page=argv[3];
}else{
page=PAGE;
}
if((stringA=malloc(BUFSIZ))==NULL){
printf("\n\tMEMORY ERROR\n\n");
exit(1);
}
stringA=build_get_query(argv[1],page);
if((write(sockfd,stringA,strlen(stringA)))<0){
printf("\n\tERROR ON SEND\n\n");
}
memset(stringA, 0, sizeof(stringA));
while((tmpres=read(sockfd,stringA,BUFSIZ))>0){
if((sstr=strstr(stringA,"<!DOCTYPE "))!=NULL){
stringA=sstr;
}
if((sstr=strstr(stringA,"</html>"))!=NULL){
strncpy(sstr+7,"\n",(BUFSIZ-strlen(stringA))+5);
}
fprintf(stdout,"%s",stringA);
}
if(tmpres<0){
perror("READ ERROR");
}
close(sockfd);
return 0;
}
gdb info
Program received signal SIGSEGV, Segmentation fault.
_dl_fini () at dl-fini.c:183
183 dl-fini.c: No such file or directory.
in dl-fini.c
(gdb) bt
#0 _dl_fini () at dl-fini.c:183
#1 0x0015d1ef in __run_exit_handlers (status=0, listp=0x289324, run_list_atexit=true) at exit.c:78
#2 0x0015d25f in *__GI_exit (status=0) at exit.c:100
#3 0x00144bde in __libc_start_main (main=0x804892d <main>, argc=4, ubp_av=0xbffff454, init=0x8048d00 <__libc_csu_init>,
fini=0x8048cf0 <__libc_csu_fini>, rtld_fini=0x11e080 <_dl_fini>, stack_end=0xbffff44c) at libc-start.c:258
#4 0x08048811 in _start ()
(gdb)
I am intrigued with gdb information because I have installed sudo apt-get install build-essential about a year ago and never had problems until today.

The thing I see is that you allocate BUFSIZ bytes to the buffer, and you read up to BUFSIZ bytes into the buffer, but you are trying to use a strstr() and a strlen() on the buffer... which, if you have actually read BUFSIZ bytes into your buffer may well not be a null terminated string.
I would suggest you allocate BUFSIZ+1 and memset( stringA, 0, BUFSIZ+1 ) to guard against a random walk through memory by strlen() which would produce some pretty undefined behaviors in the while loop and could also result in the printf() within the loop behaving oddly, or at least use printf( "%.*s", BUFSIZ, stringA ); for the output.
char readBuffer[BUFSIZ+1] = { 0 };
stringA=build_get_query(argv[1],page);
if((write(sockfd,stringA,strlen(stringA)))<0){
printf("\n\tERROR ON SEND\n\n");
}
while((tmpres=read(sockfd,readBuffer,BUFSIZ))>0){
if((sstr=strstr(readBuffer,"<!DOCTYPE "))!=NULL){
strcpy( readBuffer, sstr );
}
if((sstr=strstr(readBuffer,"</html>"))!=NULL){
strncpy(sstr+7,"\n",(BUFSIZ-strlen(readBuffer))+5);
}
fprintf(stdout,"%s",readBuffer);
memset( readBuffer, 0 , BUFSIZ+1 );
}
PS: Actually, after reading the other response, I realize that you're making an even bigger mistake depending on stringA being a pointer to a BUFSIZ array after you have reassigned it to some other address (returned from the build query).

And the size of the buffer allocated to stringA isn't BUFSIZ. After all it receives the address of the buffer allocated in build_get_query, which could be even smaller than BUFSIZ.
I don't know why you use stringA=malloc(BUFSIZ) and in the next instruction you change the stringA pointer to another buffer. There are many memory leaks there.

Related

read fails with EFAULT

I am running the following C code, where trying to read in buffer which
is allocated on caller's stack, but fails with errno 14 (Bad Address).
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
void wrapper(int fd, char **buf)
{
int res = read(fd, *buf, 10);
printf("res: %d, errno: %d\n", res, errno);
printf("Buf: %s\n", *buf);
}
int main()
{
char buffer[10];
memset(buffer, 0, 10);
int fd = open("main.c", O_RDONLY);
wrapper(fd, (char **)&buffer);
return 0;
}
The output is
res: -1, errno: 14
Buf: (null)
I have been searching for explanation why it fails, whereas changing it to
void wrapper(int fd, char *buf)
...
wrapper(fd, (char *)buffer);
works, but without result so far.
why it fails
Arrays are not pointers. buffer is not a char*. Consequently, &buffer is not a char**, is not compatible with char**, and should not be cast to char**. If it is cast to char** and then dereferenced, the behaviour is undefined.
After analyzed your intention, of course it is possible to create something like a "wrapper" containing read string by read(2) syscall and use that buffer away from wrapper() function. You wanted to pass amount of characters which would be read from file being in a table of files whom index of the table (file descriptor) was return by open(2) syscall. But as n.m. said, arrays are not pointers and your solution cannot work properly.
Let me explain my simple fix to your code:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define AMOUNT 20
#define assert_msg(x) for ( ; !(x) ; assert(x) )
void
wrapper(int fd, char **buf, size_t size)
{
ssize_t res;
char *out;
out = calloc(size + 1, sizeof(char));
assert(out != NULL);
res = read(fd, out, size);
assert_msg(res != -1) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
out[size] = '\0';
fprintf(stdout, "Inside function: %s\n", out);
fprintf(stdout, "res: %d, size: %d, errno: (%d: %s)\n", res, size,
errno, strerror(errno));
*buf = out;
}
int
main(int argc, char **argv)
{
int fd;
char *buf;
buf = NULL;
assert(argc == 2);
errno = 0;
fd = open(argv[1], O_RDONLY);
assert_msg(fd != -1) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
wrapper(fd, &buf, AMOUNT);
fprintf(stdout, "Outside function: %s\n", buf);
free(buf);
return (EXIT_SUCCESS);
}
I pass a filename as an input argument. It was a bit easier for me instead of hardcoding the name.
As you can see, inside my wrapper() implementation I allocate memory for an out buffer which size I am passing by a value of size variable. I know that the same value as AMOUNT value defined as macro but it would be easy to change in any other solution.
Then, I read given amount of characters using read(2) syscall, from a file descriptor returned by open(2) syscall in main() function which I pass to wrapper().
At the end of that function I tell that I would like to save an address to the beginning of allocated out buffer and I would like that *buf indicates on that address. It is a buffer of size + 1 char elements, allocated on heap, not on a local stack. Therefore program cannot "reuse" that addresses during his execution. Every address for variables declared like int a;, struct type name; or char tab[10]; are "freed" automatically after the end of function and you do not have an access to it. To be clear, you may have an access (e.g. print data from address saved to indicator) but you cannot be sure that you would not lose the data being saved there. Space allocated manually still exist on a heap until calling free(3) function.
So if we would do something like:
void
wrapper(int fd, char **buf, const size_t size)
{
ssize_t res;
char out[size];
(...)
*buf = out;
}
you may lost your data being saved on a local stack during continuing program execution.
Additionally, in my solution I also defined my own macro assert_msg(x) which is able to run assert(3) function and shows a text message with error. But it is only a feature but thanks to that we are able to see string corresponding to an errno number.
Of course, my program need better handling errors but it had to present the idea only.
Furthermore, you should also specify file permissions during using open(2) syscall as a third argument. It looks similar to the second argument because it is a bitwise 'or' separated list of values. Example flags: S_IRUSR, S_IRGRP, S_IWOTH etc.
In that argument, you can also just write proper value describing permissions, for example 0755.

How to clean a buffer filled using read() and continue reading at the same buffer?

I have a project for the university where I have to implement an Iterative server.
The server uses a protocol given by the professor and in a few words the client has to send a message in a specific form and my server has to make some parsing in order to save some data in a global struct.
I use the function read() in order to receive the message and store it in a char array with fixed size. My problem is that the message some times might be bigger than the size of the buffer I use to store it. NOTE: I am not able to send the size of the message first in order to bypass this problem.
I would like to know if there is a way to make this happen.
Bellow is some of the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "keyvalue.h"
#include <errno.h>
int main ( int argc, char *argv[])
{
char keyvaluebuffer[4096];
int num;
//socket()
//bind()
//listen()
while(1)
{
//accept()
while((num = read(acceptfd, keyvaluebuffer, 4096) ) > 0 )
{
//At this point I use the keyvaluebuffer to parse it and store the data.
}//while (read > 0) end
if (num < 0){close(accept);}
close(accept);
}//while(1) end
close(socket);
}//main end
If the message is larger than the buffer size, then you will at least need to store the part of the message that has been retrieved before reading more of the message. One option would be to dynamically allocate a message buffer. After reading part of the message into the static buffer it is copied into the dynamic message buffer. If the dynamic message buffer has been filled, it is reallocated.
In the example code below, buffer[] is an array that holds BUF_SIZE chars. msg is a pointer to dynamically allocated memory, and initially points to BUF_SIZE bytes. msgbuff_sz keeps track of the size of the dynamic message allocation, while msg_sz keeps track of the actual message size. After each call to read(), the contents of buffer[] are copied to the appropriate location in msg, and msg_sz is updated. If the message size is the same as the message buffer size, the message buffer msg is reallocated. This continues until the end-of-file condition is reached (or until read() returns an error value).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#define BUF_SIZE 4096
int main(void)
{
int fd = open("message.txt", O_RDONLY);
if (fd < 0) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SIZE];
char *msg = malloc(BUF_SIZE);
size_t msgbuff_sz = BUF_SIZE;
size_t msg_sz = 0;
if (msg == NULL) {
perror("Buffer allocation error");
exit(EXIT_FAILURE);
}
ssize_t ret_val;
while ((ret_val = read(fd, buffer, sizeof buffer)) > 0) {
memcpy(msg + msg_sz, buffer, ret_val);
msg_sz += ret_val;
if (msg_sz == msgbuff_sz) {
msgbuff_sz += BUF_SIZE;
char *tmp = realloc(msg, msgbuff_sz);
if (tmp == NULL) {
perror("Buffer reallocation error");
exit(EXIT_FAILURE);
}
msg = tmp;
}
}
if (ret_val < 0) {
perror("Unable to read file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < msg_sz; i++) {
putchar(msg[i]);
}
putchar('\n');
free(msg);
return 0;
}

Segmentation Fault when moving binary outside directory

Hello guys this is my first post here.
My problem is stupid I think but I can't find any solution, hope you can help me!
So, me and a friend are coding a small system monitor (learn better/fun), the code has 2 sections: the daemon and the command line interface (for now), when I compile the CLI section all went great, the daemon is particular one, because when I compile and I execute it in the compile directory it works without error! Magically, when I move out of the compile directory it gives me a segmentation fault!
Compiler: GCC
Here is the repository: https://github.com/StefanoBelli/JASM
Makefile:
#!/usr/bin/make -f
SHELL=/bin/sh
#### CONFIGURATION ####
CC=gcc
DEBUG=-g
CFLAGS=-O2 -pipe -Wall -std=c11 $(DEBUG)
LIBS=
BINOUT=jasm
#### SOURCES & RULES ####
OBJS:=$(patsubst %.c,%.o,$(wildcard *.c))
install:$(OBJS)
$(CC) $(CFLAGS) $(LIBS) -o $(BINOUT) $(OBJS)
clean:
rm -fv *.o
.PHONY: install,clean
GDB Output:
(gdb) run
Starting program: /home/stefanozzz123/Devel/C.Cpp/JASM/bin/jasm
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a7db04 in vfprintf () from /usr/lib/libc.so.6
(gdb)
Thank all of you guys! :)
EDIT: As you requested here is code:
jasm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "queue.h"
#include "miscellaneous.h"
#include "ipc.h"
int main(int argc, char *argv[])
{
start_daemon();
start_server();
}
ipc.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include "ipc.h"
#include "miscellaneous.h"
#include "getter.h"
static void excecute_command(int fd, char *command)
{
/*
* if get* -> modulo get
* if start* -> modulo dei moduli
*/
// ************************** getter ***************************************
if(strncmp("get", command, 3)==0) { //ricevuto comando getter
int i;
//char buf[BUFSIZ];
strcpy(command, &command[3]);
for(i=0; i<NGETTER; i++) {
if(strcmp(getterName[i], command)==0) { //se esiste getter
log_string("getter found :)");
getterFunction[i](fd);
return;
}
}
log_error("getter NOT found :(");
write(fd, "null\0", 4);
return;
}
// ************************** starter **************************************
if(strncmp("start", command, 5)==0) { //ricevuto start modulo
log_error("starter NOT found :(");
write(fd, "null\0", 4);
return;
}
// ************************** miscellaneous ********************************
if(strcmp("halt", command)==0) { //spegne jasm
log_string("# halt and catch fire, done");
write(fd, "halt\0", 4);
exit(0);
}
/*if(strcmp("getVersion", command)==0) {
write(fd, (void *)VERSION, sizeof(VERSION));
log_string("server reply <version> with success");
return;
}*/
log_error("request not found");
write(fd, "null\0", 4);
}
void start_server()
{
int server_sockfd, client_sockfd;
int server_len;
socklen_t client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
int result;
fd_set readfds, testfds;
server_sockfd=socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family=AF_INET;
server_address.sin_addr.s_addr=htonl(INADDR_ANY);
server_address.sin_port=htons(SERVER_PORT);
server_len=sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
listen(server_sockfd, 5);
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
log_string("server started");
while(1) {
char buf[BUFSIZ];
char received[BUFSIZ];
int fd;
int nread;
testfds=readfds;
result=select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
if(result<1) {
log_error("server fail");
exit(1);
}
for(fd=0; fd<FD_SETSIZE; fd++) {
if(FD_ISSET(fd, &testfds)) {
if(fd==server_sockfd) {
client_len=sizeof(client_address);
client_sockfd=accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
FD_SET(client_sockfd, &readfds);
sprintf(buf, "adding client on fd %d", client_sockfd);
log_string(buf);
} else {
ioctl(fd, FIONREAD, &nread);
if(nread==0) {
close(fd);
FD_CLR(fd, &readfds);
sprintf(buf, "removing client on fd %d", fd);
log_string(buf);
} else {
read(fd, &received, BUFSIZ);
sprintf(buf, "received from fd %d command <%s>", fd, received);
log_string(buf);
excecute_command(fd, received);
}
}
}
}
}
}
miscellanous.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "miscellaneous.h"
char * getTime()
{
time_t curtime;
struct tm *loctime;
static char *ret;
curtime=time(NULL);
loctime=localtime(&curtime);
ret=asctime(loctime);
ret[24]='\0';
return ret;
}
void log_string(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] %s\n", getTime(), message);
fclose(fp);
}
void log_error(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] ERROR: %s!\n", getTime(), message);
fclose(fp);
}
void start_daemon()
{
pid_t pid;
char buf[BUFSIZ];
log_string("boot");
pid=fork();
switch(pid) {
case -1:
log_error("fork fail");
exit(1);
break;
case 0:
log_string("fork success");
break;
default:
exit(0);
break;
}
if(setsid()<0) {
log_error("setsid fail");
exit(1);
} else {
log_string("setsid success");
}
//chiude i file descriptor di stdin, stdout, stderr
close(0);
close(1);
close(2);
sprintf(buf, "jasm started with pid %d and ppid %d", getpid(), getppid());
log_string(buf);
}
Essentially these are main srcs...
GDB Backtrace says nothing as the program run stops immediately
Since the question was tagged gdb, let's see how gdb can help. In my case, I've installed the debuginfo files for libc, so that I can examine the arguments to C library functions, but you don't really need that in this case because we can find the bug by looking at the user's source code.
(gdb) run
Starting program: ./jasm
Program received signal SIGSEGV, Segmentation fault.
_IO_vfprintf_internal (s=0x0, format=0x4019b1 "[%s] %s\n",
ap=ap#entry=0x7fffffffbd38) at vfprintf.c:1295
1295 vfprintf.c: No such file or directory.
(gdb) bt
#0 _IO_vfprintf_internal (s=0x0, format=0x4019b1 "[%s] %s\n",
ap=ap#entry=0x7fffffffbd38) at vfprintf.c:1295
#1 0x00007ffff7a693f7 in __fprintf (stream=<optimized out>,
format=<optimized out>) at fprintf.c:32
#2 0x000000000040149d in log_string (message=0x4019cb "boot")
at miscellaneous.c:46
#3 0x000000000040151f in start_daemon () at miscellaneous.c:64
#4 0x0000000000401400 in main (argc=1, argv=0x7fffffffdf78) at jasm.c:31
The declaration for vfprintf is:
int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
Even though we don't have the source code for vfprintf installed, we can see that the first argument passed to it, s, is a NULL stream pointer, and that is likely the cause of the seg fault.
Let's look at something we have source code for: frame 2, log_string.
(gdb) frame 2
#2 0x000000000040149d in log_string (message=0x4019cb "boot")
at miscellaneous.c:46
46 fprintf(fp, "[%s] %s\n", getTime(), message);
(gdb) print fp
$2 = (FILE *) 0x0
There it is.
#define LOGPATH "../../../../data/log/jasm.log"
void log_string(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] %s\n", getTime(), message);
fclose(fp);
}
void log_error(const char *message)
{
FILE *fp;
fp=fopen(LOGPATH, "a+");
fprintf(fp, "[%s] ERROR: %s!\n", getTime(), message);
fclose(fp);
}
Check the return value from fopen. It may be NULL depending on what directory the program is run from. It may be better to use an absolute pathname, possibly settable in the Makefile for portability.
write(fd, "halt\0", 4);
All of these should have a count of 5, to include the trailing NUL. (And it isn't absolutely necessary to explicitly include \0 in the string literal, because C string literals implicitly have a \0 at the end.)

SIGXFSZ is sent by kernel unless something is printed to stdout?

I am learning "Advanced Programming in Unix Environment", and have a problem with exercise no.11 in chapter 10.
In my program, I set RLIMIT_FSIZE to 1024.
So the kernel should send SIGXFSZ to my program when write trying to exceed that limit.
But I found that SIGXFSZ is not send unless something is printed to stdout.
Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <signal.h>
#define BUFFSIZE 100
void xfsz_handler(int signo)
{
fprintf(stderr, "%d, %s\n", signo, strsignal(signo));
}
int main(int argc, char* argv[])
{
int n;
char buf[BUFFSIZE];
struct rlimit fsizeLimit;
fsizeLimit.rlim_cur=1024;
fsizeLimit.rlim_max=1024;
if(setrlimit(RLIMIT_FSIZE, &fsizeLimit) < 0)
{
perror("setrlimit error");
exit(-1);
}
if(signal(SIGXFSZ, xfsz_handler)==SIG_ERR)
{
fprintf(stderr, "set signal handler error for %d\n", SIGXFSZ);
exit(-1);
}
printf("what ever\n"); /* we need this to get SIGXFSZ sent */
while ( (n=read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
{
int byteWrite = 0;
if ( (byteWrite = write(STDOUT_FILENO, buf, n)) < 0)
{
perror("write error");
exit(-1);
}
if(byteWrite!=n)
{
fprintf(stderr, "byteWrite=%d, n=%d\n", byteWrite, n);
exit(-1);
}
}
if (n<0)
{
perror("read error");
exit(-1);
}
return 0;
}
if I comment out the following line in the code, kernel will not transmit SIGXFSZ.
printf("What ever . . . \n");
Why this happens? Thanks in advance.
[root#luaDevelopment ex11]# ./myCopy < /root/workspace/AdvanceProgrammingInTheUnixEnvironment.20140627.tar.bz2 >aa.tar.bz2
byteWrite=24, n=100
[root#luaDevelopment ex11]# make
gcc -o myCopy myCopy.c -std=gnu99 -I../../lib/ -L../../lib/ -lch10
[root#luaDevelopment ex11]# ./myCopy < /root/workspace/AdvanceProgrammingInTheUnixEnvironment.20140627.tar.bz2 >aa.tar.bz2
byteWrite=24, n=100
25, File size limit exceeded
[root#luaDevelopment ex11]#
user3693690 found the answer in Appendix C of the book:
10.11 Under Linux 3.2.0, Mac OS X 10.6.8, and Solaris 10, the signal handler for SIGXFSZ is never called because the loop exits the program on a short write, but write returns a count of 24 as soon as the file’s size reaches 1,024 bytes. When the file’s size has reached 1,000 bytes under FreeBSD 8.0, the signal handler is called on the next attempt to write 100 bytes, and the write call returns −1 with errno set to EFBIG("File too big"). On all four platforms, if we attempt an additional write at the current file offset (the end of the file), we will receive SIGXFSZ and write will fail, returning −1 with errno set to EFBIG.

In C, why does reading from a socket cause printw to give a segmentation fault?

I'm writing a program that connects to a server, gets the sent data and displays it to the screen. I'm working on creating a UI for the program using ncurses. I initialize the screen, connect to the server, read the socket, print the output, close the socket, end the screen and this gives me a segmentation fault.
If I skip the socket read, no fault is given and the screen displays; however, there is a pause before it displays. I wonder if it has to do with the fact that my socket is non blocking?
I have 3 files, main.c socket.c and screen.c. I'll truncate the files for the needed information.
main.c
#include "socket.h"
#include "screen.h"
int main(int argc, char *argv[]){
screenInit();
socketConnect(argv[1], argv[2]);
sleep(1);
char buffer[1024];
socketRead(buffer, sizeof buffer);//commenting this out lets the screen display
screenDraw(buffer);
socketClose();
screenEnd();
return 0;
}
socket.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
int sockfd;
int socketRead(char *output, int length){
memset(output, 0, length);
char buffer[1024];
memset(buffer, 0, sizeof buffer);
for(;;){
char *ch;
if(recv(sockfd, ch, 1, 0) == -1)
return -1;
if(strlen(buffer)+1 == sizeof buffer)
return -2;
strcat(buffer, ch);
if(*ch == '\n'){
if(strlen(buffer)+1 <= length){
strcpy(output, buffer);
return 0;
}else{
return -3;
}
}
}
}
int socketConnect(char *host, char *port){
//sockfd gets set here
fcntl(sockfd, F_SETFL, O_NONBLOCK);
return 0;
}
void socketClose(){
close(sockfd);
}
screen.c
#include <ncurses.h>
void screenInit(){
initscr();
cbreak();
noecho();
}
void screenDraw(char *string){
printw("%s", string);
refresh();
getch();
}
void screenEnd(){
endwin();
}
Here's the stack from gdb
#0 0x00007ffff7647caa in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff7649191 in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007ffff764b580 in malloc() from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007ffff7bce2de in _nc_printf_string () from /lib/x86_64-linux-gnu/libncurses.so.5
#4 0x00007ffff7bc93ef in vwprintw () from /lib/x86_64-linux-gnu/libncurses.so.5
#5 0x00007ffff7bca0e9 in printw () from /lib/x86_64-linux-gnu/libncurses.so.5
#6 0x000000000040111f in screenDraw ()
#7 0x0000000000400d56 in main ()
I thought I'd add my Makefile here as well if for some reason that had something to do with the problem.
Makefile
objects = main.o socket.o screen.o
program: $(objects)
cc -Wall -o program $(objects) -lncurses
.PHONY: clean
clean:
#-rm -f program
You have an error in socketRead() here:
char *ch;
if(recv(sockfd, ch, 1, 0) == -1)
return -1;
ch is only a pointer, you never allocated memory to store something in there - all you have is a pointer that was not initialized. You can't use it and pass it to recv, because recv will attempt to write to an undefined location. This is why your program crashes.
I think you want to use buffer here:
if (recv(sockfd, buffer, sizeof buffer, 0) == -1) { ... }
Or, alternatively, you can make ch point to a valid location before using it:
ch = buffer;
if (recv(sockfd, ch, sizeof buffer, 0) == -1) { ... }
Note, however, that in this case you can't use sizeof ch, since that would yield the size of a char *.
If you want to explicitly leave out space for a null terminating byte, then use sizeof(buffer) - 1:
if (recv(sockfd, buffer, sizeof buffer, 0) == -1) { ... }
These lines are also wrong, for the same reason: you never initialized ch
strcat(buffer, ch);
if (*ch == '\n') {
Note that recv(sockfd, buffer, sizeof(buffer)-1, 0) will read chunks of at most sizeof(buffer)-1 bytes. Your code conveys the idea that you want to read it char by char; in that case, you probably want to make ch a char instead of a char *. In that case, you just have to use the address of operator in the call to recv, because recv expects a pointer:
if (recv(sockfd, &ch, 1, 0) == -1)
But then of course, you can't use strcat with a char (strcat expects null terminated sequences of characters), and instead of if (*ch == '\n'), you must use if (ch == '\n').
The char pointer is not initialised to the buffer:
char *ch = buffer;
if(recv(sockfd, ch, 1, 0) == -1)
return -1;
Error handling is also needed to read the return code from SocketRead() - perhaps you left that out of your post for brevity.

Resources