LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

CVI Error: Over Array Bounds

When the attached C-File is compiled and executed in Debug mode CVI fails with an Over Array Bounds error while dereferencing a valid char * pointer. The function 'LogEnetData ()' is passed a pointer to over a thousand bytes of valid data and yet CVI declares an error when the loop index is 2.  I believe that this violates standard C behaviour.
 
When I single-stepped through the code and monitored the value of pData after it had been incremented, I got the following results:
 
          i                              pData
-------------------           -----------------
          0                        0x0012FA6D                        This is OK
          1                       "Over Array Bounds"          
0 Kudos
Message 1 of 7
(5,250 Views)
Hello hurst,

Looks like you should be passing Message.Data instead of &Message.CsciSource in this line in main():
LogEnetData (fp, Message.MsgSize, (unsigned char *)&Message.CsciSource);
Message.CsciSource is declared as an unsigned short, which is why your loop would have problems after the first iteration.

Mert A.
National Instruments

0 Kudos
Message 2 of 7
(5,247 Views)

The code runs without error when compiled in Release mode, so why does it yield a FATAL error in Debug mode?

I am casting the address to a char *, so why does CVI insist on treating it otherwise. This seems to be a GROSS violation of the C Standard. The function LogEnetData sees the data pointer as an unsigned char * and should treat it as such. Only the function main () really knows what this pointer really is. So, what you're telling me is that the LogEnetData function knows what is going on in main and bases its execution on that knowledge. As I said, this is a GROSS violation of the C Standard.

Hurst C.

0 Kudos
Message 3 of 7
(5,237 Views)

It's not a question of the C standard; what you are seeing is the user protection CVI inserts in debug mode (to protect you from accessing invalid memory addresses, etc).

The simple fix to your problem is to change your call to LogEnetData like this:

  LogEnetData (fp, Message.MsgSize, ((unsigned char *)&Message) + sizeof(int));

Since the object you are taking the address of is the entire struct, the pointer is passed with that context information and will not complain until you try to access outside of the struct bounds.

Admittedly, the fatal error you are seeing is not truly 'correct', but based on the context that it has to go on (address being passed is that of a 'short'), it is.

Alternatively, you can disable runtime checks from the Build Options dialog, which will prevent this error from happening.

Hope this helps,

-alex

0 Kudos
Message 4 of 7
(5,231 Views)

I'm not interested in disabling runtime checks in the whole DLL just in this one section of code. I've tried using DisableBreakOnLibraryErrors () and EnableBreakOnLibraryErrors () around the loop but that doesn't  help. Is there a clean way of just disabling the run-time checks for the loop?

I don't care how you sugar coat this anomaly, the fact remains that LogEnetData () receives a unsigned char * pointer and since there cannot be any alignment restrictions for a byte it violates the C Standard by treating it as anything else but an unsigned char *.  This is exactly the purpose of a cast, to change the representaion of a variable. The observed behaviour in Release mode and Debug mode should be identical. The fact that a fatal error is elicited prevents the program from running to completion in Debug mode.

Hurst C.

0 Kudos
Message 5 of 7
(5,219 Views)
As I said in my previous post, the 'correct' fix for this is to change the call to LogEnetData to something like this:
 
  LogEnetData (fp, Message.MsgSize, ((unsigned char *)&Message) + offsetof(GenericMessageType, CsciSource));
 
Making this change causes the program to run fine in debug mode on my machine.
 
One of the major purposes of user protection checks is to ensure that you are accessing valid memory addresses, and the mechanism that is uses to accomplish this is the invisibly pass on the size of variables passed to functions (independent of the actual type used) so that the debugger can determine if the size of that variable has been exceeded. It has nothing to do with the correctness of the program, you are simply confusing the debugger. In this case, you are taking the address of a short, so the passed size will be two bytes. If instead, you take the address of the structure (as shown above) and offset to the member you are interested in, the size checks will be correct.
 
Hope this is more clear...
 
-alex
Message 6 of 7
(5,215 Views)

This isn't done very well at all.   Consider the following...

 

unsigned char  _foo[] = {'F','O','O','D','F','E','E','D',0};
typedef struct { unsigned char  _foo[8]; } foo_type;
union {
 int num;
 foo_type foo;
} _un = {'F','O','O','D','F','E','E','D',0};
foo_type _bar = {'F','O','O','D','F','E','E','D',0};
char* foo[] =
{
 (char*)&_foo,(char*)&_bar, (char*)&_un,
};

 

 

You can NOT reference foo[2][1]  but you can reference foo[1][1].  These are the same thing   The union confuses the array checking   Also since we are talking about array checking..  the initialized for _un shouldn't work at all since its passing in 9 values, instead of 8, but it compiles just fine..

 

To make things even worse you can reference foo[1][1000] and it works.  Seems there is no bounds checking on the struct, but there is on the union, and its wrong.    The array _foo does get the correct bounds of 9 btw.

 

By the way this can all be undone by casting back to some large array type.. say struct{ char foo[99999] }; 

 

The checking appears to (poorly) just look at the size of what got passed in.  It sees the union as 1 item, while it sees foo_type an unknown quantity, and it sees _foo as 9

 

0 Kudos
Message 7 of 7
(4,254 Views)