Finding errors where indexes goes beyond their boudaries

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Finding errors where indexes goes beyond their boudaries

Post by JBNielsen »

My compiler DevCpp shows there is "An access Violation (Segmentation Fault)" in my chessprogram Dabbaba.

But it is caused by some wrong input - something has been corrupted earlier in the calculations.

I have made a little test to see if DevCpp can detect such things like this:


char testIdx;
char test50a[50];
char test50b[50];


testIdx=75; // outside the array

// ok to read from outside the array?
test50a[25]=test50a[testIdx];

// ok to write outside the array?
test50a[testIdx]=test50a[25];


But DevCpp does not complain about this - everything happens within the program.
And I think this is the way how some of my data is corrupted.

I have tried CppCheck - and it can spot the errors in my little test.
But in a complex program the index is not set to a wrong value right before it is used.

I wonder if there exists an compiler option, that makes it generate code so the program always checks the index boundaries?

Or is there only the hard way to find the error....
User avatar
Dan Honeycutt
Posts: 5258
Joined: Mon Feb 27, 2006 4:31 pm
Location: Atlanta, Georgia

Re: Finding errors where indexes goes beyond their boudaries

Post by Dan Honeycutt »

Can you not use an assert for some reason?

Best
Dan H.
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Finding errors where indexes goes beyond their boudaries

Post by Sven »

Code: Select all

char testIdx;
char test50a[50];
char test50b[50];


testIdx=75; // outside the array

// ok to read from outside the array?
test50a[25]=test50a[testIdx];

// ok to write outside the array?
test50a[testIdx]=test50a[25];
1. For "testIdx", I'd better use "unsigned int" instead of "char".

2. The compiler can only find such array bounds errors if a variable like "testIdx" is declared as "const", or (maybe) if it is "static" and the compiler is able to see that there is no other code in the same compilation unit that changes its value. In almost all other cases the compiler has no chance to detect this kind of array bounds violation since it has no knowledge about the value of the array index when compiling the array access code.

3. I don't know which compiler you are using with "DevCpp". GCC for instance has -Warray-bounds which "warns about subscripts to arrays that are always out of bounds" (but in real programs this is hard to find). The runtime option "-fbounds-check" seems to be for Java and Fortran only. Compilers like MSVC(++) or Intel C(++) have an option to check for array bounds violation at runtime. Typically you switch it off for an optimized version. Still no chance, though, for a compile time detection, at least in a more complex "real world" case.

4. The "right" solution, as already mentioned, is to use something like the standard assert() macro, as in this silly example:

Code: Select all

#include <assert.h>

#define ARRAY_SIZE&#40;a&#41; &#40;sizeof&#40;a&#41; / sizeof&#40;a&#91;0&#93;))

char test50a&#91;50&#93;;
char test50b&#91;50&#93;;

inline char getTest50A&#40;unsigned int idx&#41;
&#123;
    assert&#40;idx < ARRAY_SIZE&#40;test50a&#41;);
    return test50a&#91;idx&#93;;
&#125;

inline char getTest50B&#40;unsigned int idx&#41;
&#123;
    assert&#40;idx < ARRAY_SIZE&#40;test50b&#41;);
    return test50b&#91;idx&#93;;
&#125;
Only the debug version will actually do the bounds check so you can use it for thorough testing, while the release version, compiled with the "NDEBUG" preprocessor switch, will omit it. This concept is more general than any specific array bounds checking option and may have some other usability advantages, so I'd always prefer it.

5. There are various tools for static code analysis, some of them also open source.

6. Why are you using DevCpp? ;-)

Sven
User avatar
Jim Ablett
Posts: 1385
Joined: Fri Jul 14, 2006 7:56 am
Location: London, England
Full name: Jim Ablett

Re: Finding errors where indexes goes beyond their boudaries

Post by Jim Ablett »

6. Why are you using DevCpp? Wink
He's using the new actively maintained, resurrected DevCpp project >

http://sourceforge.net/projects/orwelldevcpp/

Jim.
User avatar
JuLieN
Posts: 2949
Joined: Mon May 05, 2008 12:16 pm
Location: Bordeaux (France)
Full name: Julien Marcel

Re: Finding errors where indexes goes beyond their boudaries

Post by JuLieN »

Jim Ablett wrote:
6. Why are you using DevCpp? Wink
He's using the new actively maintained, resurrected DevCpp project >

http://sourceforge.net/projects/orwelldevcpp/

Jim.
No wonder this compiler produces buggy code if their maintainers can't even get the description right ^^
Description
A maintained verison of Dev-C++ which features an updated MinGW compiler and updated code.
This is just an easy joke and I feel quite ashamed. That's indeed an enormous work they're doing! :)
"The only good bug is a dead bug." (Don Dailey)
[Blog: http://tinyurl.com/predateur ] [Facebook: http://tinyurl.com/fbpredateur ] [MacEngines: http://tinyurl.com/macengines ]
jbell

Re: Finding errors where indexes goes beyond their boudaries

Post by jbell »

You could try running your code with an interpreter. For simple code, I have used the EiC interpreter. It catches array bounds violations at runtime, but the program runs much much slower than the compiled version. Also, EiC doesn't support 64-bit integers. Apparently, EiC is no longer being developed, but I found a copy at http://twkm.freeshell.org. I don't have experience with any other interpreters.
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: Finding errors where indexes goes beyond their boudaries

Post by JBNielsen »

Sven; I have not expressed myself clearly.
Of course the error should be detected at runtime.
The compiler should generate the code to do that with a switch, so it can be turned off in the final compile.
And from what you write it is possible with some compilers.

I do not know about assert(), but it seems to be a possibility?!
But I have +50 arrays in dabbaba to change...

I am only an amateur in C programming - dabbaba is the only program I have written in C.
And all the 12.000 lines of code is in one single file...
My proffession is being a mainframe-programmer, though (cobol, cics, db2, tso...).

Since 2008 I have used the Pelle compiler.
But I think it may produce wrong code, and I have never learnt how to debug with it.
I would also like to try performance profiling.
So I changed to DevCpp a week ago.
And it found the access violation I was not aware of - dabbaba seems to run ok with it...

Could an interpreter detect this?!

DevCpp found the "An access Violation (Segmentation Fault)" here:
void change_to_pili( char *pili, char *sqfr, char *sqto)
{ char *p, p=pili;
loop: if (*p!=*sqfr) {++p; goto loop;}
*p=*sqto; <--------------------------------------------------------------------
}

But the real error is, that pili does not contain the value *sqfr.
I assume it has been overwritten by an index going beyond its boundaries.

It may take some time for the interpreter.
The access violation happens after 500.000 nodes - 5 seconds.

Thanks for your helpful posts!
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Finding errors where indexes goes beyond their boudaries

Post by Sven »

JBNielsen wrote:

Code: Select all

void change_to_pili&#40; char *pili,  char *sqfr,  char *sqto&#41;
&#123; char *p, p=pili;
 loop&#58;       if (*p!=*sqfr&#41; &#123;++p; goto loop;&#125;  
       *p=*sqto; <--------------------------------------------------------------------
&#125;
But the real error is, that pili does not contain the value *sqfr.
I assume it has been overwritten by an index going beyond its boundaries.
You could write it like this to track the error:

Code: Select all

#include <assert.h>

// release version
void change_to_pili&#40;char *pili, char sqfr, char sqto&#41;
&#123;
    assert&#40;pili != 0&#41;;

    char *p = pili;
    while (*p != sqfr&#41; &#123;
        ++p;
    &#125;
    *p = sqto;
&#125;

// debug version with different signature
bool change_to_pili_safe&#40;char *pili, char sqfr, char sqto, size_t maxlen&#41;
&#123;
    assert&#40;pili != 0&#41;;

    char *p = pili;
    size_t len = 0;
    while (*p != sqfr && len <= maxlen&#41; &#123;
        ++p;
        ++len;
    &#125;
    if (*p == sqfr&#41; &#123;
        *p = sqto;
        return true; // OK
    &#125; else &#123;
        assert&#40;len > maxlen&#41;; // just for clarification
        return false; // maxlen exceeded but 'sqfr' not found
    &#125;
&#125;
The first version is a "better" version of the code snippet you posted. "sqfr" and "sqto" do not need to be pointers since they are always dereferenced, so you can pass their values. NOTE: change the calling code appropriately, e.g. write "change_to_pili(pili, from, to)" instead of "change_to_pili(pili, &from, &to)"! If you can't or don't want to change the calling code then leave both parameters as pointers, of course. Instead of "goto" use a normal "for" or "while" loop. To deactivate the "assert" macro for a release version compile with the NDEBUG preprocessor switch.

Now the real thing: to spot your error I would use the second version of the function for debugging, change the calling code by adding a reasonable value for the additional size parameter (usually the max size of the array), and set a debugger break point on the "return false;" line to see under which conditions you get the failure. It will stop when "sqfr" is not found.

EDIT: this may point you to a situation where the corruption has already occurred. What you actually need is where the corruption happens. But you may get some indication. Setting memory breakpoints might help, too, if your IDE supports them.

Sven
JBNielsen
Posts: 267
Joined: Thu Jul 07, 2011 10:31 pm
Location: Denmark

Re: Finding errors where indexes goes beyond their boudaries

Post by JBNielsen »

Hi Sven

Thanks for your helpful advices. I will be more aware of asserts in the future.

After many hours of debugging I have found the error in something I have restructured recently.
An array was defined too small, and then the index exceeded the boundary.

PS. If you install DevCpp and intend to use gcc, be sure to get at more recent version of gcc.
I had another strange error, and Jim Ablett helped me go get the Ruben-Vb Gcc 4.7.0 version.
He wrote:

"Replace your Gcc installation (4.6.2) in devcpp with this one(4.7.0) (just overwrite it - it's a direct replacement) >

http://sourceforge.net/projects/mingw-w ... z/download

This is what I use and it compiles correctly."