简体   繁体   English

如何解释 Valgrind 以修复该程序的分段错误?

[英]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.我对编程有点陌生,我正在编写一个程序来通过比较 4 个连续字节来恢复“card.raw”中存在的 JPEG 文件。 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).如果他们划分 JPEG,程序必须将 512 字节的块复制到保存为 xxx.jpg(000.jpg、001.jpg 等)的新文件中。 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.如果在复制块后,找到新 JPEG 的开头,则关闭当前文件并打开下一个文件以复制下一个 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.我尝试使用 Valgrind 来找出故障发生的原因,但我不知道 Valgrind 的错误消息是什么意思。

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.我想知道为什么会发生分段错误或至少 Valgrind 的错误消息是什么意思。

UPDATE: Program output:更新:程序 output:

ERRNO 1 IS Success
Segmentation fault

UPDATE: Changed CHUNK from BYTE* to BYTE .更新:将CHUNKBYTE*更改为BYTE

UPDATE: Used malloc to declare CHUNK array to prevent stack overflow.更新:使用malloc声明CHUNK数组以防止堆栈溢出。

UPDATE: Changed fread(&CHUNK, sizeof(BYTE), size, recover);更新:改变fread(&CHUNK, sizeof(BYTE), size, recover); to fread(CHUNK, sizeof(BYTE), size, recover);fread(CHUNK, sizeof(BYTE), size, recover); to prevent stack problems.以防止堆栈问题。

UPDATE: Eliminated while(!(feof)) to prevent a seemingly infinite loop that was occurring.更新:消除while(!(feof))以防止发生看似无限的循环。

Some problems noted below, in no particular order.下面列出了一些问题,排名不分先后。

  •  BYTE CHUNK[size];

    [ EDIT ] This was fixed in OP's subsequent edit. [编辑] 这已在 OP 的后续编辑中修复。 This allocates size bytes on the stack, where size is the filesize of the argv[1] file.这会在堆栈上分配size个字节,其中sizeargv[1]文件的文件大小。 If that filesize is too large, the CHUNK array can easily overflow the default stack allocation.如果该文件大小太大,则CHUNK数组很容易溢出默认堆栈分配。 See why not to Declare large array on Stack .看看为什么不在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] .循环计数器j一直到CHUNK缓冲区的末尾,但if语句也读取CHUNK[j + 1]CHUNK[j + 2]然后CHUNK[j + 3] When j = size - 1 for example, all three reads go past the end of the array and cause undefined behavior .例如,当j = size - 1时,所有三个都读取 go 超出数组末尾并导致未定义行为

  •  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).有一个代码路径跳回write: label 而不修改j ,这会导致无限循环(不是唯一这样的路径, switch的隐式default情况是另一种)。

  •  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 .有一个代码路径,可以通过f == NULL到达此fwrite 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.即使f不是NULL ,从&CHUNK[j + k]开始读取 512 个字节也可能 go 超过数组的末尾,这又是 UB。


[ 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. file指针永远不会分配,因此sprintf将写入NULL指针,从而导致 UB。

  •  long long int size = ftell(recover);

    ftell returns a long so there is no need to use a long long to store it. ftell返回一个long ,因此无需使用long long来存储它。

  •  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. fseekftell的返回值都没有被检查,所以任何错误都会被忽略。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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