User:Pkmnfrk/Mystery SIGTRAPs in FreeBasic/redim foo(ubound(foo) + 1)

From OHRRPGCE-Wiki
Revision as of 22:26, 24 November 2008 by Pkmnfrk (talk | contribs) (wip)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The error

The first clue you have that you are experiencing this problem is a tell-tale warning message, hinting at stack corruption:

D:\ohrrpgce>gdb crash1
GNU gdb 5.2.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) r
Starting program: D:\ohrrpgce/crash1.exe
warning: HEAP[crash1.exe]:
warning: Invalid Address specified to RtlFreeHeap( 00320000, 0012FF80 )

Of course, your executable name will vary, as will the addresses in the message.

Debugging

Regardless, let's pull up a backtrace, shall we?

(gdb) bt
#0  0x7c901231 in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#1  0x7c96c943 in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#2  0x7c96cd80 in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#3  0x7c96df66 in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#4  0x7c94a5d0 in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#5  0x7c9268ad in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#6  0x77c2c2de in _C__prg_code_bas_FreeBASIC_lib_win32_def____libmsvcrt_dll_a_iname ()
#7  0x00401582 in hRedim ()
#8  0x0040161c in fb_ArrayRedimEx ()
#9  0x004012f5 in DOSOMETHING (FOO=
      {data = 0x12ff80, ptr = 0x12ff80, size = 12, elen = 4, dims = 1, dim1_elemns = 3, dim1_lbound = 0, dim1_ubound = 2}) at crash1.bas:3
#10 0x004013b6 in main (__FB_ARGC__=1, __FB_ARGV__=0x323da8) at crash1.bas:11

Oh my. The SIGTRAP was issued from deep within the runtime library. We're 9 frame pointers removed from the error! Surely it's not us... or, is it? Since we don't have the source for the runtime, let's backtrack to the last bit of code we executed:

(gdb) f 9
#9  0x004012f5 in DOSOMETHING (FOO=
      {data = 0x12ff80, ptr = 0x12ff80, size = 12, elen = 4, dims = 1, dim1_elemns = 3, dim1_lbound = 0, dim1_ubound = 2}) at crash1.bas:3
3               redim foo(ubound(foo) + 1)

Well, the last thing we did was to resize an array. This is a valid, well defined action. According to the bound info passed with the parameter, we're not overflowing anything. So, is this our culprit?

The answer is yes. Why? Let's dig deeper. Let's pull up the previous stack frame:

(gdb) f 10
#10 0x004013b6 in main (__FB_ARGC__=1, __FB_ARGV__=0x323da8) at crash1.bas:11
11      dosomething temp()

The cause

Ah, okay. We're passing the temp() array to DoSomething(), which resizes it. Again, still valid. So, why doesn't it like the array? Let's expand our view a bit, maybe find where temp() is declared.

(gdb) list
6               next
7       end sub
8
9       dim as integer temp(2)
10
11      dosomething temp()
12      for i as integer = lbound(temp) to ubound(temp)
13              print i
14      next

Well, already, I can see what the problem is. Can you? That's alright, I'll wait.

...

Done yet? No? Look closely at the line I highlighted:

9       dim as integer temp(2)

That's right, temp() is a static array. Rediming it directly is invalid, but when we pass it to DoSomething, it can't tell that it's static, and so allows the Redim. The problem is that temp() is located on the stack, which is not nearly as flexible as the heap (read: where dynamic arrays are stored). And, Redim tries to free the old memory (on the stack) as heap memory. Hilarity ensues.

By that, I mean that the operation will fail, and nothing will change. Further writes to the "new" array will likely scribble all over other important stack variables, and possibly crash (if you're lucky).

Anyway, how do you fix it? You have two options:

Resolution: Turn the static array into a dynamic array

Just change this:

dim as integer temp(2)

in to this:

redim as integer temp(2)

Two extra characters, no error message! It's win-win! This should be your default solution.

However, in certain cases, you cannot do this (for totally arbitrary reasons), eg an array in a UDT:

Type MyType
  myarray() as integer 'error 11: Expected constant, found ')' in 'myarray() as integer'
End Type

So, you might...

Resolution: Remove the Redim

If possible, rework your code so that the Redim is unnecessary. You might make the static arrays big enough that you need not resize them. Or... something. Really, it's hard to come up with a general suggestion for this case, as it depends on what your code does.

If this is not satisfactory (eg, you're working with arbitrarily large sets of data), then there is a third option, which is not for the faint of heart...

Resolution: Manage the memory yourself

Summary

Long story short, resizing a static array is a big no-no. If