2

I'm trying to debug code that eventually throws

*** glibc detected *** ./build/smonitor: free(): invalid pointer:

Its challenging because I don't use free... I've seen the other SO posts that have examples replicating the issue.. I need help on how to debug. First off, I'm a C/C++ n00b so my pointer skills are in-development but I'm not doing much dynamic memory allocation (I think).

I'm starting to write my own 'security' application where I take snapshots from cameras and write them to a NFS share, I'll eventually have a display of each camera's snapshot. Right now, I take captures from 1 camera and cycle them through my display window (using opencv). I can run for a while (~hour) before I get the core dump. I create a thread to run the window, I should loop until my run flag gets set to false and then I call cvReleaseImage.. I have no idea why this is failing, any guidance is greatly appreciated!

// will be replaced with camera X filename on NFS share
std::string generate_filename() 
{
    static const char alphanum[] =
                "0123456789"
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                "abcdefghijklmnopqrstuvwxyz";

    std::string filename = "";
    std::stringstream ss;
    for (int i = 0; i < 10; i++)
    {
        ss << alphanum[rand() % (sizeof(alphanum) - 1)];
    }
    ss << ".jpg";
    printf("Generated filename: %s\n", ss.str().c_str());
    return ss.str();
}

std::string generate_file_path()
{
    std::stringstream ss;

    ss << CAPTURES_PATH << generate_filename();
    return ss.str();
}


void capture_photo(std::string& filepath)
{
    time_t now;
    time_t end;
    double seconds;
    bool cancelCapture = false;
    int count = 0;

    CvCapture* capture = cvCreateCameraCapture(0);
    printf(“Opened camera capture\n");
    IplImage* frame;
    while(1)
    {
        frame = cvQueryFrame(capture);
        if (!frame)
        {
            fprintf(stderr, "Could not read frame from video stream\n\n");
        } else
        {
            cvShowImage(WINDOW, frame);
            cvWaitKey(100);
            if (get_snapshot_enabled()) // simulate delay between snapshots
            {
                filepath = generate_file_path();
                printf("Saving image\n");
                cvSaveImage(filepath.c_str(), frame);
                break;
            }
        }
    }
    printf("Ending camera capture\n");
    cvReleaseCapture(&capture);
}

void* manage_window(void* arg)
{
    time_t now;
    time_t end;
    double seconds = 0;
    double stateSec;

    int i = 0;
    int rem = 0;

    IplImage* images[10];

    time_t lastUpdate;
    time_t tDiff; // time diff

    cvNamedWindow(WINDOW, CV_WINDOW_FREERATIO);
    cvSetWindowProperty(WINDOW, CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);

    std::string filepath;
    time(&now);
    int lastPos = 0;
    while (1)
    {
        if (get_snapshot_enabled())
        {
            write_console_log("Going to capture photo\n");
            // camera was selected
            filepath = generate_file_path();
            printf("Generated filepath: %s\n", filepath.c_str());
            capture_photo(filepath);

            if (!filepath.empty())
            {
                printf("Received filepath: %s\n", filepath.c_str());
                time(&now);

                images[lastPos] = cvLoadImage(filepath.c_str());
                cvShowImage(WINDOW, images[lastPos]);
                cvWaitKey(100);
                if (lastPos == 10) lastPos = 0;
                else lastPos++;
            } 
        }

        time(&end);
        seconds = difftime(end, now);
        if (seconds >= 5)
        {
            cvShowImage(WINDOW, images[ i % 10])
            cvWaitKey(100);

            i++;
            time(&now);
        }

        // check if we're running
        if (!get_running())
        {
            // log some error we're not running...
            write_logs("Window thread exiting, not running...");
            break;
        }
    }

    for (i=0; i < 10; i++)
        cvReleaseImage(&images[i]);

    pthread_exit(NULL);
}
Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
Crushing
  • 487
  • 1
  • 9
  • 24
  • 2
    Don't tag `C` if you're using `C++`. – PaulMcKenzie May 06 '16 at 22:49
  • 2
    Have you run your program under your development environment's debugger? Should help you narrow down where the bad `free` occurs. – user4581301 May 06 '16 at 22:49
  • 3
    This `if (lastPos == 10) lastPos = 0; else lastPos++` seems to lead to `images[10] = ...` i.e. invalid write beyond the end of array. – J.J. Hakala May 06 '16 at 23:56
  • JJ is right, you probably actually want to increment before the check for 1-past the end: `if (++lastPos == 10) lastPos = 0;` (without any `else...`) – Christopher Oicles May 07 '16 at 04:30
  • Oh man @J.J.Hakala you're spot on, that one slipped right by me... Why didn't the app crash a lot earlier? – Crushing May 07 '16 at 12:04
  • @Crushing It doesn't crash earlier because accessing an array out-of-bounds is undefined behavior. Unlike other languages, C++ doesn't automatically give you some sort of assert or "stack trace" when you access an item out-of-bounds of an array. You want to write (or read) `images[10]`, C++ says "yes sir" and does it. What will result -- we don't know. – PaulMcKenzie May 07 '16 at 16:25

1 Answers1

1

In void* manage_window(void* arg) there are lines

IplImage* images[10];

and

images[lastPos] = cvLoadImage(filepath.c_str());

if (lastPos == 10) lastPos = 0;
else lastPos++;

where lastPos can be incremented to 10, leading to

images[10] = cvLoadImage(filepath.c_str());

i.e. invalid write beyond the end of array. I think something like valgrind would have detected this.

J.J. Hakala
  • 6,136
  • 6
  • 27
  • 61
  • Before trying Valgrind, give [Address Sanitizer](http://clang.llvm.org/docs/AddressSanitizer.html) a try. It runs much faster and catches most of the same bugs. Of course, Valgrind remains the gold standard for this sort of thing. – Benjamin Barenblat May 07 '16 at 13:58