Search code examples
cvalgrindcs50

cs50 recover sill reachable but no valgrind error


I have been struggling with the 'recover' program for a few hours and cannot seem to find the issue.

I thought I had already closed all the files, and I haven't used the 'malloc' syntax for memory allocation.

Could you please provide me with some hints or guidance? Thank you very much!

I've also tried using Valgrind, and it's showing something like this:

==34245== Memcheck, a memory error detector
==34245== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==34245== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==34245== Command: ./recover card.raw
==34245== 
==34245== 
==34245== HEAP SUMMARY:
==34245==     in use at exit: 944 bytes in 2 blocks
==34245==   total heap usage: 104 allocs, 102 frees, 233,912 bytes allocated
==34245== 
==34245== 944 bytes in 2 blocks are still reachable in loss record 1 of 1
==34245==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==34245==    by 0x4A076CD: __fopen_internal (iofopen.c:65)
==34245==    by 0x4A076CD: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==34245==    by 0x1092A9: main (recover.c:48)
==34245== 
==34245== LEAK SUMMARY:
==34245==    definitely lost: 0 bytes in 0 blocks
==34245==    indirectly lost: 0 bytes in 0 blocks
==34245==      possibly lost: 0 bytes in 0 blocks
==34245==    still reachable: 944 bytes in 2 blocks
==34245==         suppressed: 0 bytes in 0 blocks
==34245== 
==34245== For lists of detected and suppressed errors, rerun with: -s
==34245== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

My codes below:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


int main(int argc, char *argv[])
{
    // Ensure proper usage
    if (argc != 2)
    {
        printf("Usage: ./recover card\n");
        return 1;
    }

    // If the forensic image cannot be opened for reading, your program should inform the user as much, and main should return 1.
    // Remember filenames
    char *infile = argv[1];
    // Open input file
    FILE *inptr = fopen(infile, "r");

    if (inptr == NULL)
    {
        printf("Could not open %s.\n", infile);
        fclose(inptr);
        return 1;
    }

    // block size is 512 bytes
    typedef uint8_t BYTE;

    int BLOCK_SIZE = sizeof(BYTE) * 512;

    BYTE buffer[BLOCK_SIZE];

    int index = 0;

    FILE *img = NULL;

    // The files you generate should each be named ###.jpg, where ### is a three-digit decimal number, starting with 000 for the first image and counting up.
    while (fread(buffer, 1, BLOCK_SIZE, inptr) == BLOCK_SIZE)
    {
        char filename[8];

        // is first pic.
        if (index == 0) {

            sprintf(filename, "%03i.jpg", index);
            img = fopen(filename, "w");

            if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0) {
                if (img != NULL) {
                    fwrite(buffer, 1, BLOCK_SIZE, img);
                }
                index++;
            }
        } else {
            //is not first pic
            if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0) {
                //is a new img
                //close prev img
                if (img != NULL) {
                    fclose(img);
                }
                // open a new img
                sprintf(filename, "%03i.jpg", index);
                img = fopen(filename, "w");
                fwrite(buffer, 1, BLOCK_SIZE, img);
                index++;
            } else {
                //keep writing
                fwrite(buffer, 1, BLOCK_SIZE, img);
            }
        }
    //Your program, if it uses malloc, must not leak any memory.
    }
    if (img != NULL) {
        fclose(img);
    }
    fclose(inptr);
    return (0);
}

Solution

  • There are some problems in the code:

    • the files must be opened in binary mode with "rb" or "wb".
    • if fopen fails and returns NULL, you must not call fclose(inptr);
    • if the first block does not have an image signature, index is not incremented and the in next iteration you will reopen the same file without an intervening fclose() and keep doing this for each block until a JPG signature is found. The memory allocated by fopen() is lost, which explains the valgrind output. You should only create the file when you find a signature.

    Here is a modified version:

    #include <errno.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
        // Ensure proper usage
        if (argc != 2) {
            fprintf(stderr, "Usage: ./recover card\n");
            return 1;
        }
    
        // If the forensic image cannot be opened for reading, your program should inform the user as much, and main should return 1.
        // Remember filenames
        char *infile = argv[1];
        // Open input file
        FILE *inptr = fopen(infile, "rb");
        if (inptr == NULL) {
            fprintf(stderr, "Could not open file %s: %s\n", infile, strerror(errno));
            return 1;
        }
    
        // block size is 512 bytes
        enum { BLOCK_SIZE = 512 };
        uint8_t buffer[BLOCK_SIZE];
    
        int index = 0;
    
        FILE *img = NULL;
    
        // The files you generate should each be named ###.jpg, where ### is a three-digit decimal number, starting with 000 for the first image and counting up.
        while (fread(buffer, 1, BLOCK_SIZE, inptr) == BLOCK_SIZE) {
            char filename[20];
    
            //is there is a picture signature, create a new file
            if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0) {
                //close prev img if any
                if (img != NULL) {
                    fclose(img);
                }
                // open a new img
                snprintf(filename, sizeof filename, "%03i.jpg", index++);
                img = fopen(filename, "wb");
                if (img == NULL) {
                    fprintf(stderr, "Could not open image file %s: %s\n", filename, strerror(errno));
                }
            }
            if (img != NULL) {
                // write the block to the current image file
                fwrite(buffer, 1, BLOCK_SIZE, img);
            }
        }
        if (img != NULL) {
            fclose(img);
        }
        fclose(inptr);
        return 0;
    }