Valgrind array overrun within objects
Asked Answered
O

2

5

I have a simple program as below.

struct Test
{
    int a[5];
    int b;
};

int main()
{
    Test* t = new Test;
    t->b = 1;
    t->a[5] = 5;          //This is an illegal write
    cout << t->b << endl; //Output is 5
    return 0;
}

Running it with Valgrind Memcheck didn't report the illegal memory write.

I noticed that Valgrind claims the Memcheck tool cannot detect global or stack array overrun, but this array is in heap, right? It's just that the array is in an object.

Is it that Valgrind really cannot detect this kind of error or just I did something wrong? If the former is true, then is there any other tool that can detect this type of error?

==========================================================================

Update:

The compilation command I used was g++ -O0 -g main.cc. The valgrind command was simply valgrind ./a.out, which should invoke the memcheck tool by default.

The compiler version is gcc version 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC), and valgrind version is valgrind-3.5.0.

Valgrind output when running this program:

==7759== Memcheck, a memory error detector
==7759== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==7759== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==7759== Command: ./a.out
==7759== 
5
==7759== 
==7759== HEAP SUMMARY:
==7759==     in use at exit: 24 bytes in 1 blocks
==7759==   total heap usage: 1 allocs, 0 frees, 24 bytes allocated
==7759== 
==7759== LEAK SUMMARY:
==7759==    definitely lost: 24 bytes in 1 blocks
==7759==    indirectly lost: 0 bytes in 0 blocks
==7759==      possibly lost: 0 bytes in 0 blocks
==7759==    still reachable: 0 bytes in 0 blocks
==7759==         suppressed: 0 bytes in 0 blocks
==7759== Rerun with --leak-check=full to see details of leaked memory
==7759== 
==7759== For counts of detected and suppressed errors, rerun with: -v
==7759== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
Occipital answered 29/6, 2017 at 2:55 Comment(8)
Does your program die when you run it on Valgrind?Rosas
Valgrind should be able to detect this as t get's allocated on the stack. Are you compiling on '-O0' ? On '-O1' and above, the compiler will remove the whole Test-thing and go straight to putting the character "5" to cout.Trucking
@leolong: Kindly update your question with the compiler and valgrind commands along with all the options? Didn't you get any message against your allocation of Testbecause there's no delete against your `new'?Zebapda
@Milind Sorry for the late reply. No, the program didn't die no matter running with Valgrind or not.Occipital
@Trucking Sorry for the late reply. I compiled with '-O0'.Occipital
@Zebapda Sorry for the late reply. I've modified the post as you suggested. I did get "definitely lost" message from Valgrind for the not deleted Test object.Occipital
@leolong: Right. It should. And, for the invalid write, when I compiled your code with clang it gave me a warning for out of bounds write.Zebapda
@Zebapda Thank you. But I think the compilation time warning won't be a solution for me because I'm guessing that it cannot warn about more complicated cases when the index is a variable whose value can only be determined at run time.Occipital
R
6

I think the below sentence, which you've already found:

Memcheck cannot detect every memory error your program has. For example, it can't detect out-of-range reads or writes to arrays that are allocated statically or on the stack. But it should detect many errors that could crash your program (eg. cause a segmentation fault).

in case of your class definition should be interpreted this way: although class object is allocated dynamically, the array itself is allocated statically.

I've verified few cases:

Valgrind will report invalid write if array is dynamically allocated:

struct Test
{
    int *a;
    int b;
};

int main()
{
    Test* t = new Test;
    t->a = new int[5];
    t->b = 1;
    t->a[5] = 5;          //This is an illegal write
    cout << t->b << endl; //Output is 5
    delete [] t->a;
    delete t;
    return 0;
}

Also error will be reported if you change the order of the members to:

struct Test
{
  int b;  
  int a[5];
};

This is because when attempting to write to a[5] we will be already behind dynamically allocated object.

With original class definition if you attempt to write to a[6] - because then we are behind b so behind dynamically allocated object.


Update: gcc sanitizer (I suspect clang too) detects this out of bound access at run time by compiling:

g++ -fno-omit-frame-pointer -fsanitize=bounds m.cpp

Output:

m.cpp:15:7: runtime error: index 5 out of bounds for type 'int [5]'
Rapine answered 29/6, 2017 at 9:14 Comment(2)
Thanks for the reply. I also did these tests, so it looked to me that Valgrind cannot detect array overrun 'with' an object. Do you know any tool which can accomplish this task then? Thank you.Occipital
Thank you very much. Looks like sanitizers have to be the way to go. I had been worrying about their performance impact. But at least the impact is much smaller than Valgrind. I guess it is the best option then. Thanks.Occipital
M
0

Valgrind seems has newer tool called SGCheck for detecting stack array overrun

It seems needs version larger than 3.8

check this link

It said

SGCheck is a tool for finding overruns of stack and global arrays. It works by using a heuristic approach derived from an observation about the likely forms of stack and global array accesses.

Monocarpic answered 3/8, 2022 at 3:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.