Page 1 of 2
Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 7:02 pm
by Joubarbe
UPDATE: Reproducible crash:
viewtopic.php?p=630156#p630156
Sorry for the silly title, it's hard to explain, and I cannot make a runnable code for you to experience the same thing. So please bear with me, I'm not asking for a magic solution, but just for a bit of light.
Code: Select all
If \x > 0
ProcedureReturn *table\items(\x - 1, \y)
ElseIf \y > 0
ProcedureReturn *table\items(ArraySize(*table\items(), 1), \y - 1) ; Crashes here 10/10, invalid memory access.
EndIf
This code is a snippet from a procedure where I use
With *table\selected_item\pos, where
selected_item is a pointer. I've tried without the With : EndWith, same result (someone mentioned that as a potential problem in a previous post).
Code: Select all
If \x > 0
ProcedureReturn *table\items(\x - 1, \y)
ElseIf \y > 0
Define debug_x = ArraySize(*table\items(), 1) ; DEBUG
Define debug_y = \y - 1 ; DEBUG
ProcedureReturn *table\items(debug_x, debug_y) ; Does not crash 0/10.
EndIf
Would anyone have any idea why I have no invalid memory access in the second situation? What is the difference?
Also, what extra information is the Purifier supposed to give? From all the errors I had, the Purifier never ever gave me something else than the standard debugger would give. Is there extra info somewhere else that I need to look?
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 7:41 pm
by SMaag
PureBasic Version
ASM or C-Backend
Did you check ASM or C output?
Try
Code: Select all
define ret = *table\items(ArraySize(*table\items(), 1), \y - 1)
ProcedureReturn ret
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 7:55 pm
by spikey
You'd have to examine the resultant assembler code to know the actual differences. In the first example I would expect the entire expression to be calculated via the stack whereas in the second the variable locations will be referenced as well in the calculation stages. This means that the compiler will emit slightly different assembler code in each case, even though the end result should be the same.
The purifier looks for overruns for various storages by tagging the ends of the storage allocation with a 'magic number'. If the storage's allocation is then subsequently overrun by some other action this will corrupt the 'magic number' and the purifier will notice this when it checks. For example, if a 'byte' size variable erroneously has a 'long' value written to its location, this would corrupt the variable and the 'magic number' following it.
What the purifier won't do is detect the modification of a pointer with a correct size but invalid semantic value because the 'magic number' will remain intact. I'm guessing that's what you are experiencing because a bad pointer value can lead to an IMA but, of course, I can't say for certain because I don't have a runnable case to test.
(However, please don't go away with the impression I'm saying you've a pointer problem in your source code - more or less everything is a pointer sooner or later at the assembly level).
As SMaag said - it would be useful to know if it's the calculation that causes the fault or the return.
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 9:31 pm
by Joubarbe
Thanks to both of you.
Code: Select all
Define ret = *table\items(ArraySize(*table\items(), 1), \y - 1) ; Crashes here.
ProcedureReturn ret
Doesn't crash with C backend.
PureBasic 6.12
I don't know ASM, so I couldn't really analyse the output, unfortunately. I had problems for a few weeks now, and my game is on Steam, so it's really frustrating. Maybe I should put a C version out, but I'm fairly certain that other errors will show at other places.
I don't do any manual memory allocation.
EDIT: I think I should ask: what are the things you can do wrong in PB with pointers? I mean something that can cause an IMA quite randomly. Empty pointers are detected, pointers with a wrong type are detected by PB (at least you have an error on the line when you call a property of a structure that don't belong to the pointer), I don't Peek/Poke, and I don't do arithmetic with them either. At this point, I don't really know where to look really.
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 9:58 pm
by #NULL
I don't have answers to your questions, but what happens if you leave the expression as is and use any/some/all of the following:
Code: Select all
; Debug \y
; Debug *table
; Debug *table\items()
; Debug ArraySize(*table\items(), 1)
; Debug \y - 1
; Debug ArraySize(*table\items(), 2)
ProcedureReturn *table\items(ArraySize(*table\items(), 1), \y - 1) ; Crashes here 10/10, invalid memory access.
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 10:07 pm
by Joubarbe
All debug lines pass, only the ProcedureReturn line crashes.
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 10:14 pm
by #NULL
And all the values are all right?
You don't use any threads?
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 10:18 pm
by Joubarbe
Code: Select all
7
2742047289200
2742042756752
1
6
7
I see no problem, the array is indeed 1, 7 of size.
I don't use any thread.
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 10:56 pm
by #NULL
Joubarbe wrote: Thu Oct 31, 2024 9:31 pm
pointers with a wrong type are detected by PB (at least you have an error on the line when you call a property of a structure that don't belong to the pointer)
It checks the property for the type/structure you are using syntactically, but the underlying data could be of a different (wrong) type:
Code: Select all
Structure s1
b1.b
b2.b
b3.b
b4.b
EndStructure
a.s1
a\b2 = 1
Structure s2
l.l
EndStructure
*l.s2 = @a
Debug *l\l ; 256
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 11:06 pm
by #NULL
Did you put PurifierGranularity(1, 1, 1, 1) before any other code?
Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 11:37 pm
by Joubarbe
#NULL wrote: Thu Oct 31, 2024 11:06 pm
Did you put PurifierGranularity(1, 1, 1, 1) before any other code?
No, I don't even know this command

Re: Decoupling an expression removes the crash (?)
Posted: Thu Oct 31, 2024 11:55 pm
by Quin
Joubarbe wrote: Thu Oct 31, 2024 11:37 pm
#NULL wrote: Thu Oct 31, 2024 11:06 pm
Did you put PurifierGranularity(1, 1, 1, 1) before any other code?
No, I don't even know this command
Whoa...me neither...

Type it in the PB IDE and press F1 and you'll get help for it, but I can't figure out where the main documentation actually links to it...
Edit: it's under the Debugger section.

Re: Decoupling an expression removes the crash (?)
Posted: Fri Nov 01, 2024 10:58 am
by SMaag
Define ret = *table\items(ArraySize(*table\items(), 1), \y - 1) ; Crashes here.
ProcedureReturn ret
PureBasic
Doesn't crash with C backend.
PureBasic 6.12
Now it seems more clear:
1. might be a problem of implicit type conversion, maybe one of the Array index isn't an Integer!
This would explain why your Debug code works.
2. possible bug in PB ASM
You didn't tell your defintitions:
how items() is defined?
Whats the type of \y?
Can you provide a demo code with all the definitions what shows the error?
Can you proivde the ASM Output code from that?
Re: Decoupling an expression removes the crash (?)
Posted: Fri Nov 01, 2024 11:52 am
by #NULL
SMaag wrote: Fri Nov 01, 2024 10:58 am
You didn't tell your defintitions:
how items() is defined?
Whats the type of \y?
Also, besides the type of \items(), does the procedure have a return type defined? And what's the expression where the function call and return value is used?
Another thing to try:
Code: Select all
Procedure pass(i.i)
; Debug i
ProcedureReturn i
EndProcedure
;...
ProcedureReturn pass(*table\items(ArraySize(*table\items(), 1), \y - 1))
Re: Decoupling an expression removes the crash (?)
Posted: Fri Nov 01, 2024 1:09 pm
by Joubarbe
Ok, thanks a lot guys.
@SMaag:
This is the main structures involved in that function: (the table is at the bottom)
Code: Select all
Structure _XY
x.i
y.i
EndStructure
Structure _Item
text$
text_without_tags$
*callback_submit.__Item
*callback_select.__Item
*callback_color.__Generic
*callback_text.__GenericText
custom_value_submit.i
custom_value_select.i
custom_value_color.i
custom_value_text.i
disabled.b
*page
color.i
background_color.i
align_x.i
no_process.b
underlined.b
List formatted_words._FormattedWord()
EndStructure
Structure _Control
type.i
origin_pos._XY
processed.b
EndStructure
Structure _TableItem Extends _Item
pos._XY
header.b
separator._Separator
no_truncation.b ; If #False, then the part of text$ that is beyond column width will be truncated with a "." character.
EndStructure
Structure _Table Extends _Control
centered.b
unselectable.b
cols.i
rows.i
separator$
vertical_layout.b
*selected_item._TableItem
Array col_sizes.i(0)
Array items._TableItem(0, 0)
items_background_color.i
; List "key browsing" purposes: (see FindNextTableItemFromHotkey())
char_pressed$
char_pressed_time_ms.i
char_pressed_index.i
char_pressed_item_index.i
char_pressed_same.b
EndStructure
So the items() array is defined as: Array items._TableItem(0, 0) that I "Dim" dynamically when needed. And \pos\y is an integer.
The function that crashes returns a pointer to a _TableItem. Here is the full function:
Code: Select all
Procedure.i GetPreviousTableItem(*table._Table) : With *table\selected_item\pos
If *table\selected_item = #Null : DebuggerError("The table has no selected element.") : EndIf
If *table\vertical_layout = #True
If \y > 0
ProcedureReturn *table\items(\x, \y - 1)
ElseIf \x > 0
ProcedureReturn *table\items(\x - 1, ArraySize(*table\items(), 2))
EndIf
Else
If \x > 0
ProcedureReturn *table\items(\x - 1, \y)
ElseIf \y > 0
ProcedureReturn *table\items(ArraySize(*table\items(), 1), \y - 1)
EndIf
EndIf
ProcedureReturn #Null
EndWith : EndProcedure
Please remind me how to get the ASM output? And how to isolate this piece of code particularly?
@#NULL
I think the above answers your first question.
The program crashes exactly at the same line, ie. at "ProcedureReturn pass(......)"
I'm going to try to make a runnable code that imitates the same structure. I don't think it will crash, but hopefully it will...