简体   繁体   中英

How do I interpret Valgrind to fix this program's segmentation fault?

I'm kind of new to programming and I was writing a program to recover JPEG files present in "card.raw" by comparing the 4 continuous bytes. If they demarcated a JPEG the program had to copy a block of 512 bytes into a new file saved a xxx.jpg (000.jpg, 001.jpg, etc). If after the block had been copied, the start of a new JPEG was found, the current file would be closed and the next file would be opened to copy the next JPG. Else the next block would be copied in the same file.

Code (UPDATED):
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>




int main(int argc, char *argv[])
{
    int counter = 0;
    if(argc != 2)
    {
        printf("Usage: ./recover image\n");
        return 1;
    }
    typedef uint8_t BYTE;
    FILE *recover = fopen(argv[1], "r");
    if(recover == NULL)
    {
        printf("ERRNO 1 IS %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    printf("ERRNO 1 IS %s\n", strerror(errno));
    fseek(recover, 0L, SEEK_END);
    long long int size = ftell(recover);
    BYTE *CHUNK = (BYTE*)malloc(size * sizeof(BYTE));
    fread(CHUNK, sizeof(BYTE), size, recover);      //Break recover into bytes
    int j = 0;
    char *file = NULL;
    FILE *f = NULL;
    int s = 0;
    write1:
    while(j + 3 < size)
    {
        if(CHUNK[j] == 0xff && CHUNK[j + 1] == 0xd8 && CHUNK[j + 2] == 0xff && j + 512 < size)                           //Check if byte is JPEG format 1st byte
        {
                    switch(CHUNK[j + 3])               //Check if byte is JPEG format 1st byte
                    {
                        case 0xe0:
                        case 0xe1:
                        case 0xe2:
                        case 0xe3:
                        case 0xe4:
                        case 0xe5:
                        case 0xe6:
                        case 0xe7:
                        case 0xe8:
                        case 0xe9:
                        case 0xea:
                        case 0xeb:
                        case 0xec:
                        case 0xed:
                        case 0xee:
                        case 0xef:
                        {
                            if(s == 0)
                            {
                                if(f != NULL)
                                {
                                    f = NULL;
                                }
                                sprintf(file ,"%03d.jpg",counter);   //Create custom file of format xxx.jpg
                                f = fopen(file,"w");
                                printf("ERRNO 2 IS %s\n", strerror(errno));
                                if(f == NULL)
                                {
                                    printf("ERRNO 2 is %s\n", strerror(errno));
                                    exit(EXIT_FAILURE);
                                }
                                //printf("ERRNO 2 IS %s\n", strerror(errno));
                                for(int k = 0; k < 512; k++)
                                {
                                    fwrite(&CHUNK[j + k], 512, sizeof(BYTE), f);  //Copy 512 bytes from start of JPEG file as 512 bytes form one 'block'
                                }
                                j += 512; //Increment to check initial bytes of next 'block'
                                s++;
                            }
                            else
                            {
                                fclose(f);
                                counter++;
                                goto write1;
                            }
                        }
                        default : goto write2;
                    }
        }
        else    //Else continue searching
        {
            write2:
            for(int k = 0; k < 512; k++)
            {
                fwrite(&CHUNK[j + k], 512, sizeof(BYTE), f);  //Copy 512 bytes from start of JPEG file as 512 bytes form one 'block'
            }
            j += 512;
        }
    }
    fclose(recover);
}

This program gives me a segmentation fault. I tried using Valgrind to find why the fault was occurring but I have no idea of what Valgrind's error message says.

UPDATED ERRROR MESSAGE:

==217== Memcheck, a memory error detector
==217== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==217== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==217== Command: ./recover card.raw
==217== 
ERRNO 1 IS Success
==217== Warning: client switching stacks?  SP change: 0x1fff000490 --> 0x1ffd36e490
==217==          to suppress, use: --max-stackframe=29958144 or greater
==217== Invalid write of size 8
==217==    at 0x400A0A: main (recover.c:29)
==217==  Address 0x1ffd36e490 is on thread 1's stack
==217== 
==217== 
==217== Process terminating with default action of signal 11 (SIGSEGV)
==217==  Access not within mapped region at address 0x1FFD36E490
==217==    at 0x400A0A: main (recover.c:29)
==217==  If you believe this happened as a result of a stack
==217==  overflow in your program's main thread (unlikely but
==217==  possible), you can try to increase the size of the
==217==  main thread stack using the --main-stacksize= flag.
==217==  The main thread stack size used in this run was 8388608.
==217== Invalid write of size 8
==217==    at 0x4A2A650: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==217==  Address 0x1ffd36e488 is on thread 1's stack
==217== 
==217== 
==217== Process terminating with default action of signal 11 (SIGSEGV)
==217==  Access not within mapped region at address 0x1FFD36E488
==217==    at 0x4A2A650: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==217==  If you believe this happened as a result of a stack
==217==  overflow in your program's main thread (unlikely but
==217==  possible), you can try to increase the size of the
==217==  main thread stack using the --main-stacksize= flag.
==217==  The main thread stack size used in this run was 8388608.
==217== 
==217== HEAP SUMMARY:
==217==     in use at exit: 4,648 bytes in 2 blocks
==217==   total heap usage: 2 allocs, 0 frees, 4,648 bytes allocated
==217== 
==217== 552 bytes in 1 blocks are still reachable in loss record 1 of 2
==217==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==217==    by 0x5694EB9: __fopen_internal (iofopen.c:65)
==217==    by 0x5694EB9: fopen@@GLIBC_2.2.5 (iofopen.c:89)
==217==    by 0x400940: main (recover.c:17)
==217== 
==217== 4,096 bytes in 1 blocks are still reachable in loss record 2 of 2
==217==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==217==    by 0x56941FB: _IO_file_doallocate (filedoalloc.c:101)
==217==    by 0x56A43E8: _IO_doallocbuf (genops.c:365)
==217==    by 0x56A0D92: _IO_file_seekoff@@GLIBC_2.2.5 (fileops.c:960)
==217==    by 0x569DD48: fseek (fseek.c:36)
==217==    by 0x4009B4: main (recover.c:24)
==217== 
==217== LEAK SUMMARY:
==217==    definitely lost: 0 bytes in 0 blocks
==217==    indirectly lost: 0 bytes in 0 blocks
==217==      possibly lost: 0 bytes in 0 blocks
==217==    still reachable: 4,648 bytes in 2 blocks
==217==         suppressed: 0 bytes in 0 blocks
==217== 
==217== For counts of detected and suppressed errors, rerun with: -v
==217== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault

I'd like to know why the segmentation fault occurs or atleast what Valgrind's error message means.

UPDATE: Program output:

ERRNO 1 IS Success
Segmentation fault

UPDATE: Changed CHUNK from BYTE* to BYTE .

UPDATE: Used malloc to declare CHUNK array to prevent stack overflow.

UPDATE: Changed fread(&CHUNK, sizeof(BYTE), size, recover); to fread(CHUNK, sizeof(BYTE), size, recover); to prevent stack problems.

UPDATE: Eliminated while(!(feof)) to prevent a seemingly infinite loop that was occurring.

Some problems noted below, in no particular order.

  •  BYTE CHUNK[size];

    [ EDIT ] This was fixed in OP's subsequent edit. This allocates size bytes on the stack, where size is the filesize of the argv[1] file. If that filesize is too large, the CHUNK array can easily overflow the default stack allocation. See why not to Declare large array on Stack .

  •  while(j < size) { if(CHUNK[j] == 0xff && CHUNK[j + 1] == 0xd8 && CHUNK[j + 2] == 0xff) { switch(CHUNK[j + 3])

    The loop counter j goes all the way to the end of the CHUNK buffer, but the if statement also reads CHUNK[j + 1] , CHUNK[j + 2] then CHUNK[j + 3] . When j = size - 1 for example, all three reads go past the end of the array and cause undefined behavior .

  •  write: while(j < size) {... else { counter++; goto write; }

    There is a code path which jumps back to the write: label without modifying j , which causes an infinite loop (not the only such path, the implicit default case of the switch is another).

  •  else //Else continue searching { for(int k = 0; k < 512; k++) { fwrite(&CHUNK[j + k], 512, sizeof(BYTE), f);

    There is a code path where this fwrite is reached with f == NULL . Even when f is not NULL , reading 512 bytes starting from &CHUNK[j + k] can potentially go past the end of the array, which is UB again.


[ EDIT ] A few additional notes.

  •  char *file = NULL; ... sprintf(file,"%03d.jpg",counter);

    The file pointer is never allocated, so sprintf will write to a NULL pointer, resulting in UB.

  •  long long int size = ftell(recover);

    ftell returns a long so there is no need to use a long long to store it.

  •  fseek(recover, 0L, SEEK_END); long long int size = ftell(recover);

    The return values of fseek and ftell are neither checked, so any errors will pass unnoticed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM