Page 1 of 2

Freeing Strings

Posted: Thu Aug 10, 2006 12:11 am
by Xombie
What's the generally accepted method for completely destroying the memory used by strings in a structure or elsewhere?

For example - this code ...

Code: Select all

Structure s_Test
   a.s
   b.l
EndStructure
*HoldTest.s_Test = AllocateMemory(SizeOf(s_Test))
*HoldTest\a = "Hello World"
*HoldTest\b = 1978
q.l = @*HoldTest\a
FreeMemory(*HoldTest)
Debug PeekS(q)
... how should I completely erase the memory used by 'q' ? And I mean completely erase, not just ...

Code: Select all

*HoldTest\a = ""
... unless that's the way to do it. Help?

Posted: Thu Aug 10, 2006 9:36 am
by Psychophanta
In the case (source) you have wrote, there is no doubt you really have freed the string buffer, because there is supposed the string is existent (allocated) by the program only in the case its pointer exists. If the string pointer is freed, the pointed string should automatically be vanished (i.e. its resources be freed) by the program (or by OS).
Keep in mind that even the contents of a mem buffer are not refilled with other data, it doesn't mean it has not been freed; i.e. FreeMemory() doesn´t fill freed heap with any data.

Besides i have seen now this:

Code: Select all

df$="1234567890"
Debug MemorySize(@df$)
Debug Len(df$)
As you can see, the buffer always reports 5 bytes bigger than the lenght of the string. Which means that there is allocated the size of the string (i.e. len()+1) plus a longword which is a pointer, i think.
So not sure, but it seems that you can free any string doing like this:

Code: Select all

FreeMemory(@df$)
This is the proof:

Code: Select all

df$="1234567890"
Debug df$
Debug MemorySize(@df$)
Debug Len(df$)

;FreeMemory(@df$); <- uncomment to see the effect

Debug df$
Debug MemorySize(@df$)
Debug Len(df$)

Posted: Thu Aug 10, 2006 9:54 am
by helpy

Posted: Thu Aug 10, 2006 11:08 am
by Psychophanta
helpy wrote:Have a look at this topic: http://www.purebasic.fr/english/viewtopic.php?p=133623
Opps, then, if the memory allocated by strings (or whatever) inside a structure is no deallocated when freeing the variable of the mentioned structure, there is a big big big fault. :!:

Posted: Thu Aug 10, 2006 11:13 am
by helpy
Psychophanta wrote:
helpy wrote:Have a look at this topic: http://www.purebasic.fr/english/viewtopic.php?p=133623
Opps, then, if the memory allocated by strings (or whatever) inside a structure is no deallocated when freeing the variable of the mentioned structure, there is a big big big fault. :!:
This happens only, if you dynamically create structures (AllocateMemory and pointer to structures). It does not happen with regular structured variables!

Posted: Thu Aug 10, 2006 11:15 am
by Psychophanta
helpy wrote:This happens only, if you dynamically create structures (AllocateMemory and pointer to structures). It does not happen with regular structured variables!
Aha! then there are some more of logic :)

Re: Freeing Strings

Posted: Thu Aug 10, 2006 12:23 pm
by sverson
Xombie wrote:What's the generally accepted method for completely destroying the memory used by strings in a structure or elsewhere?
...exactly what i wanted to ask today...

My solution:

Code: Select all

Structure MyTest
  *MyPointer
  MyLong.l
  MyQuad.q
  MyString.s
EndStructure

*MyMem.MyTest = AllocateMemory(SizeOf(MyTest))
If *MyMem
  *MyMem\MyPointer = *MyMem
  *MyMem\MyLong    = $7FFFFFFF
  *MyMem\MyQuad    = $7FFFFFFFFFFFFFFF
  *MyMem\MyString  = "*MyMem\MyString Test"

  ptrDebug.l = @*MyMem\MyString
  Debug "-----------------"
  Debug *MyMem\MyPointer
  Debug *MyMem\MyLong   
  Debug *MyMem\MyQuad   
  Debug @*MyMem\MyString 
  Debug "1: "+PeekS(ptrDebug)
  

  ; clear and free all allocated memory
  RtlZeroMemory_(@*MyMem\MyString,Len(*MyMem\MyString))
  FreeMemory(@*MyMem\MyString)
  RtlZeroMemory_(*MyMem,SizeOf(MyTest))
  FreeMemory(*MyMem)
  ; finished...

  Debug "-----------------"
  Debug *MyMem\MyPointer
  Debug *MyMem\MyLong   
  Debug *MyMem\MyQuad   
  Debug @*MyMem\MyString 
  Debug "2: "+PeekS(ptrDebug)
  Debug "-----------------"
  
EndIf
I hope this is the correct way...

;-) sverson

[EDIT] see also SecureZeroMemory. (http://msdn.microsoft.com/library/defau ... memory.asp)

Posted: Thu Aug 10, 2006 4:01 pm
by helpy
I tried your code in a loop WITHOUT debugger! 5 loops are ok, but in the 6th the program crashes.

Code: Select all

Structure MyTest
  *MyPointer
  MyLong.l
  MyQuad.q
  MyString.s
EndStructure

For i = 1 To 6
	*MyMem.MyTest = AllocateMemory(SizeOf(MyTest))
	If *MyMem
	  *MyMem\MyPointer = *MyMem
	  *MyMem\MyLong    = $7FFFFFFF
	  *MyMem\MyQuad    = $7FFFFFFFFFFFFFFF
	  *MyMem\MyString  = "*MyMem\MyString Test"
	
	  ptrDebug.l = @*MyMem\MyString
	  Debug "-----------------"
	  Debug *MyMem\MyPointer
	  Debug *MyMem\MyLong   
	  Debug *MyMem\MyQuad   
	  Debug @*MyMem\MyString
	  Debug "1: "+PeekS(ptrDebug)
	 
	
	  ; clear and free all allocated memory
	  RtlZeroMemory_(@*MyMem\MyString,Len(*MyMem\MyString))
	  FreeMemory(@*MyMem\MyString)
	  RtlZeroMemory_(*MyMem,SizeOf(MyTest))
	  FreeMemory(*MyMem)
	  ; finished...
	
	  Debug "-----------------"
	  Debug *MyMem\MyPointer
	  Debug *MyMem\MyLong   
	  Debug *MyMem\MyQuad   
	  Debug @*MyMem\MyString
	  Debug "2: "+PeekS(ptrDebug)
	  Debug "-----------------"
	 
	EndIf
Next i

Posted: Thu Aug 10, 2006 4:54 pm
by sverson
@helpy: same here!* :? (please take the above code as a question)

5..7 loops -> crash!

That's why i still look for a proper way to free strings inside structures.

FreeMemory(@*MyMem\MyString) does not work properly.

Your "workaround with a Macro" seems to be the only way ATM. :cry:
http://www.purebasic.fr/english/viewtopic.php?p=134805

@Fred:

Code: Select all

While famous_TODO_list[FreeStructureStrings]<top
  PrintN("please...") 
Wend
PrintN("Thanks ;-)") 
;-) sverson

Posted: Thu Aug 10, 2006 6:07 pm
by Xombie
That's ... slightly worrisome. It means that nearly all of my code leaks memory like a sieve. I used the allocation of structures to create a kind of array/linked list hybrid and use it in a lot of my stuff. I'll see about using helpy's macro examble while waiting for something official. Thanks, helpy.

So another question would be - why does ...

Code: Select all

Structure s_test 
   a.s 
   b.l 
EndStructure 
*HoldTest.s_test = AllocateMemory(SizeOf(s_test)) 
*HoldTest\a = "Hello World" 
*HoldTest\b = 1978 
q.l = @*HoldTest\a 
FreeMemory(*HoldTest) 
Debug PeekS(q)
Debug *HoldTest\b
... return the 1978 for the second debug? If the memory is freed, the value should exist anywhere in memory, right?

Posted: Thu Aug 10, 2006 10:47 pm
by freak
> ... return the 1978 for the second debug? If the memory is freed, the value should exist anywhere in memory, right?

By freeing the memory, you just tell the OS that you do not need it anymore. What the OS
then does with it cannot predicted.
The the memory (and its content) can remain for quite a while, or be invalidated
immediately. You may even receive the exact same pointer in a future AllocateMemory() call.

So if you have sensitive Information (like a password), overwriting it with 0 might be a good
thing before freeing the memory.

As for freeing the structured memory: Fred promised a function, but we are still working on
Linux, and with the Mac inbetween the next Update for Windows will be a while.

Using FreeMemory() on the PB strings is something you should do. Yes, i know it seems to work. (at least on some Windows versions)
The truth is though, that PB uses a different Memory heap for AllocateMemory and for the string management.
So theoretically, freeing the one thing with the function for the other (according to the API docs) should not work.
The Memory management of Windows is known to be very forgiving in this respect, so usually it works, yes.
I do not know if this is the case for all Windows versions though. Also the string handling on Linux/Mac is different, so it probably won't work there either.

Here is a very simple trick, that does the job quite nicely. It works on all OS, in PB 3.94 as well as 4.00 and probably will with future Versions as well.
The perfect function so to say ;). Its all about tricking PB into freein the String:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
Use it like this:

Code: Select all

Structure MyStrings
  a.s
  b.s
  c.s
EndStructure

Procedure test()
  Protected *x.MyStrings = AllocateMemory(SizeOf(MyStrings))
  
  *x\a = Space(4000)
  *x\b = Space(4000)
  *x\c = Space(4000)
  
  FreePBString(@*x\a)
  FreePBString(@*x\b)
  FreePBString(@*x\c)
  
  FreeMemory(*x)
EndProcedure

CallDebugger

For i = 0 To 10000
  test()
Next i

CallDebugger

Posted: Thu Aug 10, 2006 11:30 pm
by helpy
That is really nice! Thank you, freak!

Posted: Fri Aug 11, 2006 12:13 am
by sverson
freak wrote:The perfect function so to say ;)
Great - thanks :)

After adding ZeroMemory it definitely is the perfect function!

Code: Select all

Procedure DeletePBString(*Address,delete.b=#False)
  Protected String.String                  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address)                 ; poke our strings address into the structure
  If delete
    RtlZeroMemory_(*Address,Len(String\s)) ; fill the String with zero 
  EndIf
EndProcedure                               ; when returning, PB's string free routine for the local structure will actually free the passed string.

Code: Select all

Structure MyStrings
  a.s
  b.s
  c.s
EndStructure

Procedure.l AvailPhysMem()
  Protected MemStat.MEMORYSTATUS
  GlobalMemoryStatus_(@MemStat)
  ProcedureReturn (MemStat\dwAvailPhys>>10)
EndProcedure

Procedure test()
  Protected *x.MyStrings = AllocateMemory(SizeOf(MyStrings))
  
  *x\a = Space(4000)
  *x\b = Space(4000)
  *x\c = Space(4000)
  
  ; DeletePBString(@*x\a,#True)
  ; DeletePBString(@*x\b,#True)
  ; DeletePBString(@*x\c,#True)
  
  FreeMemory(*x)
EndProcedure

Debug Str((4001*3*10000)>>10)+" Kb allocated!"

mem1.l = AvailPhysMem()
For i = 0 To 10000
  test()
Next i
mem2.l = AvailPhysMem()
Debug Str(mem1)+" Kb"
Debug Str(mem2)+" Kb"
Debug Str(mem1-mem2)+" Kb"
CallDebugger
Uncomment DeletePBString to to free and clear all strings.
There will remain just a little difference due to other programs allocate or free memory while the test is running.

;-) sverson

Posted: Fri Aug 11, 2006 9:55 am
by Psychophanta
freak wrote:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
:?:
I don't understand a thing :?
Can anybody explain that better? Or write the same code in ASM to undestand it?

Posted: Fri Aug 11, 2006 10:12 am
by helpy
Psychophanta wrote:
freak wrote:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
:?:
I don't understand a thing :?
Can anybody explain that better? Or write the same code in ASM to undestand it?
"String.String" a local structure variable with ONE string element. All local structure variables are freed by PB when the procedure will be finished.

With "PokeL(@String, *Address) the passed string address is assigned to the structure variable. And know you have access to the string, which is identified by the passed string pointer, with the local structure variable String.String.

==> side effect: when the procedure is finished PB will free/release all local variables ... and also the local structure variables, which points to the passed string pointer.

Hope you understand a german native speaker ;-)

cu, helpy