4

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)
leolong
  • 172
  • 1
  • 6
  • Does your program die when you run it on Valgrind? – CocoCrisp Jun 29 '17 at 05:26
  • 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`. – user2722968 Jun 29 '17 at 06:21
  • @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 `Test`because there's no `delete` against your `new'? – Azeem Jun 29 '17 at 07:01
  • @Milind Sorry for the late reply. No, the program didn't die no matter running with Valgrind or not. – leolong Jul 03 '17 at 07:01
  • @user2722968 Sorry for the late reply. I compiled with '-O0'. – leolong Jul 03 '17 at 07:02
  • @Azeem 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. – leolong Jul 03 '17 at 07:04
  • @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. – Azeem Jul 03 '17 at 07:10
  • 1
    @Azeem 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. – leolong Jul 03 '17 at 07:46

2 Answers2

5

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]'
doqtor
  • 8,414
  • 2
  • 20
  • 36
  • 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. – leolong Jul 03 '17 at 07:10
  • 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. – leolong Jul 03 '17 at 09:51
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.

code_worker
  • 384
  • 3
  • 16