0

Whenever I try and give a file like /home/..../TulsaQueen.mp3 the program gives a segmentation fault. gdb says it has something to do with strlen() but I cannot figure it out. why is it happening? I am creating a server and need to handle GET request my .jpg works everytime but .mp3 doesn't.

void *connectionThread(void *socket_desc){

    char buffer[1024];
    int newsockfd = *(int*)socket_desc;
    int n;
    magic_t myt = magic_open(MAGIC_ERROR|MAGIC_MIME_TYPE);
    magic_load(myt,NULL);
    bzero(buffer,256);
    FILE * picture;
    int size;
    char *str_size = malloc(100);
    struct stat sb;
    int fd = open("TulsaQueen.mp3", O_RDONLY );
    fstat( fd, &sb );

 while (1)
     {memset(buffer, 0, 1024);n = read(newsockfd,buffer,1024);
     if (n < 0){ printf("ERROR reading from socket"); close(newsockfd);pthread_exit(NULL);}
     printf("%s",buffer);


    char *token = strtok(buffer," ");
    if(token != NULL)
    token = strtok(NULL, " ");

    printf("%s\n",token);

    if(strcmp(magic_file(myt,token),"audio/mpeg") == 0){

        write(newsockfd,"HTTP/1.1 200 OK\r\n",strlen("HTTP/1.1 200 OK\r\n"));
        write(newsockfd,"Content-Length: ",strlen("Content-Length: ")); 
        snprintf(str_size,100,"%li",sb.st_size);
        write(newsockfd,str_size,strlen(str_size));
        write(newsockfd,"\r\n",strlen("\r\n"));
        write(newsockfd,"Content-Type: ",strlen("Content-type: "));
        printf("magic output: '%s'\n",magic_file(myt,token));
        write(newsockfd,magic_file(myt,token),strlen(magic_file(myt,token))); 
        write(newsockfd,"\r\nConnection: keep-alive",strlen("\r\nConnection: keep-alive"));
        write(newsockfd,"\r\n\r\n",strlen("\r\n\r\n"));

        write( newsockfd, &sb.st_size, sizeof( sb.st_size ) );
        sendfile( newsockfd, fd, 0, sb.st_size );
    }
    if(strcmp(magic_file(myt,token),"image/jpeg") == 0){
    picture = fopen (token, "r");
    fseek(picture,0,SEEK_END);
    size = ftell(picture);
    fseek(picture,0,SEEK_SET);
    char file_buf[size];
    snprintf(str_size,16,"%d",size);

    //Header for HTTP standards
    write(newsockfd,"HTTP/1.1 200 OK\r\n",strlen("HTTP/1.1 200 OK\r\n"));
    write(newsockfd,"Content-Length: ",strlen("Content-Length: "));
    write(newsockfd,str_size,strlen(str_size));
    write(newsockfd,"\r\n",strlen("\r\n"));
    write(newsockfd,"Content-Type: ",strlen("Content-type: "));
    write(newsockfd,magic_file(myt,token),strlen(magic_file(myt,token))); //get Content-type
    write(newsockfd,"\r\nConnection: keep-alive",strlen("\r\nConnection: keep-alive"));
    write(newsockfd,"\r\n\r\n",strlen("\r\n\r\n"));

    rewind(picture);
    while(!feof(picture)){
    fread(file_buf, sizeof(char), sizeof(file_buf), picture);
        write(newsockfd, file_buf, sizeof(file_buf));
    bzero(file_buf, sizeof(file_buf));
    }
    }

     }  
    close(fd);
    free(buffer);
    fclose(picture);
    close(newsockfd);
    magic_close(myt);
    pthread_exit(NULL);
    return 0;
}

Here is the error I got

Thread 2 "a.out" received signal SIGSEGV, Segmentation fault. (gdb) where (0) strlen () at ../sysdeps/x86_64/strlen.S:106 (1) 0x00007ffff764069c in _IO_puts (str=0x0) at ioputs.c:35 (2) 0x0000000000401623 in connectionThread () (3) 0x00007ffff7bc16ba in start_thread (arg=0x7ffff73b6700) at pthread_create.c:333 (4) 0x00007ffff76d782d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

jhowe
  • 3
  • 1
  • 3
  • 2
    If you encounter a segfault in `strlen()`, then it almost certainly arises from one of two causes: (1) you pass an invalid pointer to the function, or (2) the char array the argument points to is not null terminated. – John Bollinger Apr 24 '17 at 21:28
  • It's unclear where exactly your segfault occurs, but I observe several instances of `strlen(magic_file(myt,token))`. These are dangerous because the `magic_file()` function of libmagic returns `NULL` in the event of error. Your approach changes recoverable errors into undefined behavior (which likely manifest as segfaults, though not *necessarily* so, and not necessarily the particular segfaults that are troubling you). – John Bollinger Apr 24 '17 at 21:35
  • If you compile your program with the `-g` argument to the compiler (and no optimization) the stack trace you get will include the line number in your own code. This will help you better find the error, and if you also tell us which of your 16 calls to strlen() that breaks, it will also be easier for us to track down your bug, – nos Apr 26 '17 at 09:32
  • Also, this is wrong: `while(!feof(picture)) ...`. No, do not do that. See [Why is “while ( !feof (file) )” always wrong?](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Andrew Henle Apr 26 '17 at 09:45

1 Answers1

0

One error in the code, that might explain the issue, has to do with the buffer size, memset and read...

The buffer is exactly 1024 bytes long. You set all these bytes to 0 and then you read up to 1024 bytes.

But, what happens when you read exactly 1024 bytes without any NUL (0) bytes at the end? ...

... if this happens than strlen will overflow while searching for the NUL byte (on byte 1025 and onwards), resulting is a segmentation fault.

One way to resolve this specific issue would be to read 1023 bytes... i.e.:

memset(buffer, 0, 1024);
n = read(newsockfd,buffer,1023);

A better way to resolve this specific issue (as suggested in the comments) would be to use n to set the NUL byte value. i.e.:

n = read(newsockfd,buffer,1023);
if (n < 0) {
   // ...
}
buffer[n] = 0;

This way you make sure a NUL byte is always present within the allocated buffer.

I should point out that tokenizing the string with strtok, as well as using str* functions to parse the incoming data, is somewhat ineffective and might produce unexpected errors / results...

...especially when you're in a multi-threaded environment...

For example, strtok is not thread-safe... and even though there are thread-safe variations, it's hardly ideal.

Moreover, it might corrupt incoming data unintentionally - after all, you're only using the first "token", so I assume you expect the rest of the data to remain unaltered.

Good Luck!

P.S.

  • strlen(magic_file(myt,token)) - what if magic_file(myt,token) is an invalid string (i.e. NULL)?

  • strlen("\r\n\r\n") is always 4... why do you use a function call for this?

  • free(buffer) should probably have been free(str_size)

Myst
  • 18,516
  • 2
  • 45
  • 67
  • 1
    Another way is to not bother pointlessly setting a kilobyte of data to 0's, just to blast over potentially all of them (less one) in the ensuing read. That `n` returned from `read` is remarkably helpful in knowing where to set `0` to terminate whatever was just read. – WhozCraig Apr 24 '17 at 22:06
  • 1
    The null byte is pointless anyway 'cos the embedded nulls in m3 encoded data. – ThingyWotsit Apr 24 '17 at 22:26
  • 1
    @ThingyWotsit totally concur. I was just commenting that, when reading data you want to have terminated, it seems a bit overt to fire memory fills when a single octet does the job (in general; as mp3 data I wouldn't do any of this regardless). – WhozCraig Apr 24 '17 at 22:30
  • @WhozCraig indeed. That cargo-cult memset/bzero is all over examples on the net. Sometime in the early 70's some daft dev wrote an example with bzero in it, and it's been copied ever since:( – ThingyWotsit Apr 24 '17 at 22:32
  • @ThingyWotsit , @WhozCraig - I thought your comments were enough, but I guess I'll update the answer to reflect the... however... even if you don't like the code in the answer, it still exposes one of the main issues the OP experienced. The "cargo-cult" complaining will not help younger programmers grow and there's a limit to how much learning a single person could do at a time... you might as well rant about the use of a thread-per-socket server design, which is by far more evil than an excessive `memset`. – Myst Apr 25 '17 at 00:49
  • That, ^^^ is why such things continue. It is not against SO policy to point out bad practices, as has been discussed and agreed on meta several times. I did not suggest the issue as an answer, (because it it not), nor did I downvote this answer. 'complaining will not help younger programmers grow' - well, we differ strongly. – ThingyWotsit Apr 25 '17 at 11:35
  • @ThingyWotsit , I guess you're right... I guess I was just reacting to what I felt was an unfair down-vote. However, I still think that practical comments are better than rants ;-) – Myst Apr 26 '17 at 07:19
  • `magic_file()` may return non-zero invalid memory address which is very difficult to debug. – Ham Jul 12 '22 at 09:52