4

Consider the following program:

struct abc
{
    int x[5];
    int y[5];
};

int main()
{
    struct abc test;
    test.y[0] = 10;
    printf("%d", test.x[5]);
}

(borrowed from Is it legal to overrun one element of a struct to view another?)

BoundsChecker does not detect this as an overflow. Are there any programs that will detect this type of programming error?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
user265445
  • 341
  • 1
  • 4
  • 11

2 Answers2

6

clang does, even with no special flags turned on:

$ clang example.c -o example
example.c:13:18: warning: array index of '5' indexes past the end of an array
      (that contains 5 elements) [-Warray-bounds]
    printf("%d", test.x[5]);
                 ^      ~
example.c:5:5: note: array 'x' declared here
    int x[5];
    ^
1 warning generated.

The same warning is printed when compiling as C++.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • Unfortunately the sample was too simplified. The index into test.x will not be a constant but a variable only known at run time. Does clang do that kind of validation or is more for compile time validations? – user265445 Dec 16 '11 at 22:27
  • 1
    @user265445, yes, if you pass the `-fcatch-undefined-behavior` flag to clang when compiling, it will detect these kinds of errors at runtime and trap. If you'd prefer, you can also have it call a function of your own choosing rather than just crashing, using `-ftrap-function`. – Carl Norum Dec 16 '11 at 23:38
  • I was able to get the compile time warning messages but it didn't produce any errors at run-time (even for the memory overwrites caught by the compile time warnings). Is there a special option that has to be specified for the program execution? – user265445 Dec 27 '11 at 22:18
  • @user265445, if you use `-fcatch-undefined-behavior` you should at least get some runtime checking. You should be able to see the differences in the output binary. – Carl Norum Dec 27 '11 at 23:49
  • @cal-norum What should the runtime output look like? Should it be similar to the compile time warnings? In one of my test programs I just get "Segmentation fault". – user265445 Dec 29 '11 at 16:04
  • In our project, `ud2` undefined instruction opcodes get emitted and are executed when runtime undefined behaviour occurs. – Carl Norum Dec 29 '11 at 18:03
  • Upon further testing, I found that my segmentation fault was actually my unintentional error that was output from both gcc and clang compiled programs. So when you get a ud2 code does it look like the compile time clang warnings that I am seeing which display the source line, etc? – user265445 Dec 29 '11 at 19:34
  • No, it's just a program crash. – Carl Norum Dec 29 '11 at 19:59
2

What we are seeing here is a nasty, little known secret about BoundsChecker: it gives you no visibility into structure members at all. If you declare an array as part of a structure, whether that structure is allocated automatically or dynamically, BoundsChecker sees the structure as a blob. If you overrun the boundaries of that structure, it will report the problem. Otherwise, no.

That said, several years ago we wrote some code which ships with the product currently, and which may be invoked with a hidden configuration option. If you were to manually insert the line EnableStructureMemberEnumeration=1 into the [BC MemoryTracker] section of the project configuration file (.dpbcd), then you would find that the product can see a structure's members (structures and arrays, principally), as long as that structure is allocated automatically (on the stack).

This option is not fully ready for prime-time, though. It has issues with very complex classes and structures such as are found in STL. Furthermore, in the specific test case given above, there is another problem. It cannot distinguish in general between an array that starts a structure/class, and the class object itself. Both have the same address in memory. But consider this code:

struct _Type {
  int Array[5];
} Objects[10];

struct _Type *pObject = &Objects[5];
int *pInt = &Objects[0].Array[5];

In both cases, we see the same address, the first time with an offset of 100, the second time with an offset of 20. The first, if evaluated with respect to Array, is invalid, but if evaluated with respect to Objects, is valid. What's a poor program to do?

Oh, and one more thing: BoundsChecker generally ignores access to the first element past the end of the array. This is because so much code exists out there where an iterator will go just one element past the end. This was a huge source of false errors, so somebody altered it to complain only if the overrun went to the next location (and beyond). The problem here is, you've incremented the iterator, but are you really going to use it?

So, the answer, for the moment, to the question is that BoundsChecker does indeed support this kind of check, but not without using an undocumented "feature" and not without problems still.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Rick Papo
  • 369
  • 1
  • 2
  • 10
  • One last note: this kind of buffer overrun will only be detected if the target application is instrumented for error detection. Overruns of dynamically allocated memory are detected an entirely different way when instrumentation is not involved. – Rick Papo Dec 14 '11 at 16:10
  • Do you know which version of BoundsChecker implemented this option? I am running VC++ BoundsChecker Version 8.2 (2007). I tried putting the option in the config file but the file gets rewritten when I execute and it removes the line. – user265445 Dec 15 '11 at 14:06
  • Version 10.5, which came out February 2011. The latest version available for download is 10.5.3, build 1120. Relative to 8.2, the latest version supports Visual Studios 2008 and 2010, Windows 7, and true X64 applications. Integration with Visual Studio 6 and 2003 was dropped. – Rick Papo Dec 15 '11 at 14:18
  • I couldn't find a link to it, is there a trial version of BoundsChecker? – user265445 Dec 15 '11 at 16:59
  • Yes, though I don't think you can get the very latest build there, but rather probably 10.5.2 from July 2011. That has the feature you want, though. See the forum posting at: http://community.microfocus.com/Forums/9_DevPartner/2852_DevPartner_Studio_SuiteWideFree_Evaluations_Restored – Rick Papo Dec 16 '11 at 01:11
  • I ended up purchasing a copy to try it. I did the work in VS2010. I found that if I modified the config file it already had the line: EnableStructureMemberEnumeration=0 so I just modified the value to =1. The build went OK but when I tried to run the program in BoundsChecker it modified the file and restored the original value =0. Is there anyway to prevent this from happening? btw: it didn't detect the array overrun within the array in the structure. – user265445 Dec 16 '11 at 19:05
  • I presume you bought the $99 "web edition"? Did you modify the file after you had already started BC? The file only gets loaded once, when you load the project. Modifying it by hand at that point, it will be ignored during the current session and overwritten at the end of the session. Please shut down Visual Studio or the BC Standalone, modify the file with an editor, and try again afterwards. And don't forget that there's still the weakness where it cannot distinguish between a structure and the first member of the structure, and favors the structure over the member. – Rick Papo Dec 17 '11 at 01:14
  • Thanks, once I restarted VS then the modified config file stuck. Are there any plans to update this functionality. Most of my variables are on the heap so I am limited to only overruns of the entire structure. The fact that it doesn't differentiate between the first element and the structure and the need to overrun by more than 1 byte are real limitations. I have been using BC for years and it otherwise works great for memory leaks and detecting uninitialized memory. The fact that it doesn't detect overruns of elements in structures was an unpleasant surprise when a customer found it! – user265445 Dec 19 '11 at 13:57
  • The problem was reported to us by a user back in 2008, and I threw together a mockup of how to solve it at that time. Unfortunately, the problem was handed to somebody who didn't really understand it, and the fix languished for a long time while I was working on the X64 conversion. Worse, problems with Microsoft's PDB information made some of the logic shaky. Finishing this particular piece of functionality is definitely on our list of things to do . . . along with quite a few other things. We are really short staffed, so nothing happens quickly. – Rick Papo Dec 19 '11 at 15:28
  • An update on this particular problem: the next build of the product we let out the door will also support the enumeration of members of static objects. I finished testing the new code today. Previously, it only supported automatics, and we still have to do something about dynamically allocated objects. – Rick Papo Dec 31 '11 at 19:31