Joubarbe wrote: Wed Oct 23, 2024 4:36 pm
EDIT: I've just done an interesting debug: print all the memory addresses of all my views, then print them again just before it crashes. Result: it's the same addresses.
Yup, that's feasible. Consider this code:
Code: Select all
Structure THING
AnInt.i
ALong.l
AByte.b
EndStructure
Define x.i
NewList Things.THING()
AddElement(Things())
Things()\AnInt = 1
Things()\ALong = 1
Things()\AByte = 1
AddElement(Things())
Things()\AnInt = #MAXDWORD
Things()\ALong = #MAXDWORD
Things()\AByte = #MAXBYTE
x = ListSize(Things())
ForEach Things()
Debug @Things()
Next Things()
ClearList(Things())
The (shortened) assembly for this looks like:
Code: Select all
; ...
;
; Structure THING
; AnInt.i
; ALong.l
; AByte.b
; EndStructure
;
; Define x.i
;
; NewList Things.THING()
MOV r9,7
XOR r8,r8
LEA rdx,[t_Things]
MOV rcx,13
CALL PB_NewList
;
; AddElement(Things())
PUSH qword [t_Things]
POP rcx
CALL PB_AddElement
; Things()\AnInt = 1
MOV rbp,qword [t_Things+8]
MOV qword [rbp+16],1
; Things()\ALong = 1
MOV dword [rbp+24],1
; Things()\AByte = 1
MOV byte [rbp+28],1
;
; AddElement(Things())
PUSH qword [t_Things]
POP rcx
CALL PB_AddElement
; Things()\AnInt = #MAXDWORD
MOV rbp,qword [t_Things+8]
MOV rax,4294967295
MOV qword [rbp+16],rax
; Things()\ALong = #MAXDWORD
MOV rax,4294967295
MOV dword [rbp+24],eax
; Things()\AByte = #MAXBYTE
MOV byte [rbp+28],255
;
; x = ListSize(Things())
PUSH qword [t_Things]
POP rcx
CALL PB_ListSize
MOV qword [v_x],rax
;
; ForEach Things()
MOV rcx,qword [t_Things]
CALL PB_ResetList
_ForEach1:
MOV rcx,qword [t_Things]
CALL PB_NextElement
OR rax,rax
JZ _Next1
; Debug @Things()
; Next Things()
JMP _ForEach1
_Next1:
;
; ClearList(Things())
PUSH qword [t_Things]
POP rcx
CALL PB_ClearList
;
; ...
;
section '.bss' readable writeable
_PB_BSSSection:
pb_bssalign 8
;
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rq 1
_PB_Instance:
PB_Instance: rq 1
PB_ExitCode: rq 1
;
pb_bssalign 8
PB_DataPointer rq 1
v_x rq 1
pb_bssalign 8
pb_bssalign 8
pb_bssalign 8
pb_bssalign 8
t_Things rq 2
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
In each list library function call the base address of the list is passed to the function via RCX. The memory address of the base of the list is not passed directly to the function. If something manages to alter the value of RCX between the setup and the function call the function will get a corrupt parameter.
For example if I were to insert an INC RCX at line 66 in the assembly after the push/pop, PB_ClearList would get a corrupt pointer to the list. (OK I know this isn't what's actually happening to you, I'm just trying to illustrate a problem.)
This is a primitive example, I've not used a procedure call. Where procedure calls are used there is a second potential problem area because the stack will get involved too so it's possible that the stack data could have been incorrectly modified by another actor.
The underlying list data structure could remain intact in both of these cases.