Recent

Author Topic: How to find out if an object points to invalid data  (Read 2636 times)

stoffman

  • New Member
  • *
  • Posts: 43
How to find out if an object points to invalid data
« on: December 07, 2021, 04:53:04 pm »
I've the following code, how do can I find out that B points to invalid location?

Code: Pascal  [Select][+][-]
  1. procedure TMyApplication.DoRun;
  2. var
  3.   A,B : TTestObject;
  4. begin
  5.   WriteLn('Welcome');
  6.  
  7.   A := TTestObject.Create();
  8.   A.Number := 30;
  9.   B := A;
  10.  
  11.   if Assigned(A) then
  12.     WriteLn('A assigned');
  13.  
  14.   FreeAndNil(A);
  15.  
  16.   if Assigned(A) then
  17.     WriteLn('A assigned? That cannot be');
  18.  
  19.   if Assigned(B) then
  20.     WriteLn('B assigned');
  21.  
  22.   ReadLn();
  23.   Terminate();
  24. end;  

The output is:
Quote
Welcome
A assigned
B assigned

And of course accesing B will result in spectacular error. So how do I find that it is invalid before accessing it?

Thanks

Handoko

  • Hero Member
  • *****
  • Posts: 4431
  • My goal: build my own game engine using Lazarus
Re: How to find out if an object points to invalid data
« Reply #1 on: December 07, 2021, 05:34:25 pm »
Sorry, I can't answer your question.

But what I know is, you can avoid pointer that points to invalid location if you write your code carefully and properly planned. And that's not easy. That includes using correct algorithm for the problem, fully understanding the commands you're using, know what are the good habits for secure programming, properly format the source code, using descriptive name for variables, and much more.

https://wiki.lazarus.freepascal.org/Secure_programming

GetMem

  • Hero Member
  • *****
  • Posts: 3637
Re: How to find out if an object points to invalid data
« Reply #2 on: December 07, 2021, 05:42:52 pm »
@stoffman
Quote
So how do I find that it is invalid before accessing it?
You can't. This is a typical case of dangling pointers. When you call FreeAndNil(A) the memory occupied by A is freed and the variable reference cleared. As a side note(not related to subject), instead of FreeAndNil(A) I prefer:
Code: Pascal  [Select][+][-]
  1.   A.Free;   //free memory
  2.   A := nil; //clear variable refrence

Assigned(B) only checks if the variable reference is still assigned, which is clearly true, although B points to a memory that is no longer valid, hence the exception if you try to accessing it.

Long story short after FreeAndNil(A), you have to set B to nill;
Code: Pascal  [Select][+][-]
  1.   FreeAndNil(A);
  2.   B := nil;
  3.  
« Last Edit: December 07, 2021, 05:52:18 pm by GetMem »

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How to find out if an object points to invalid data
« Reply #3 on: December 07, 2021, 05:53:35 pm »
You can't, but you can do really funny things with it:
Code: Pascal  [Select][+][-]
  1. var
  2.   A, B, C: TStringList;
  3. begin
  4.   A := TStringList.Create;
  5.   B := A;
  6.   A.Text := 'I am A';
  7.   WriteLn(B.Text);
  8.   A.Free;
  9.   C := TStringList.Create;
  10.   C.Text := 'I am C';
  11.   WriteLn(B.Text);
  12.   C.Free;
  13. end;
Result:
Quote
I am A

I am C


So you better don't make any memory management mistakes, because they can lead to very unexpected side effects, and there is not much you can do to protect yourself against this
« Last Edit: December 07, 2021, 05:55:41 pm by Warfley »

howardpc

  • Hero Member
  • *****
  • Posts: 3858
Re: How to find out if an object points to invalid data
« Reply #4 on: December 07, 2021, 06:03:48 pm »
Knowing that B is merely a reference to A and not an independent instance  you should frame your "if" question of B to include A (to which it refers):
Code: Pascal  [Select][+][-]
  1. procedure TestAssignments;
  2. var
  3.   A, B: TTestObject;
  4. begin
  5.   WriteLn('Welcome');
  6.  
  7.   A := TTestObject.Create(Nil, 'A');
  8.   A.Number := 30;
  9.   B := A;
  10.  
  11.   if Assigned(A) then
  12.     WriteLn('A assigned');
  13.  
  14.   FreeAndNil(A);
  15.  
  16.   if Assigned(A) then
  17.     WriteLn('A assigned? That cannot be');
  18.  
  19.   if Assigned(B) and Assigned(A) then
  20.     WriteLn('B assigned')
  21.   else WriteLn('B is no longer a valid reference to A');
  22.  
  23.   ReadLn();
  24. end;
         

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 7686
  • Debugger - SynEdit - and more
    • wiki
Re: How to find out if an object points to invalid data
« Reply #5 on: December 07, 2021, 06:43:31 pm »
You can't. Because the memory of B at this time is random. It could be:
- still untouched and look exactly like before
- something new, that either gives unexpected results, or crashes
- inaccessible

If you have code (a bigger project) that has this issue:

1) You can use -gh and set the environment "keepreleased"

2) On Linux: use valgrind --tool=memcheck.
valgrind will tell you when, where, .... all of it.

Warfley

  • Hero Member
  • *****
  • Posts: 654
Re: How to find out if an object points to invalid data
« Reply #6 on: December 07, 2021, 08:06:29 pm »
But, there is a very easy way to avoid all of these problems associated with manual memory management, you can make use of COM interfaces to perform reference counting:
Code: Pascal  [Select][+][-]
  1. unit SharedTypes;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.  
  12.   { SharedClass }
  13.  
  14.   generic SharedClass<T> = interface
  15.     function Get: T;
  16.     procedure SetValue(AValue: T);
  17.     function Assigned: Boolean;
  18.     property Value: T read Get write SetValue;
  19.   end;
  20.  
  21.   { TSharedClassInstance }
  22.  
  23.   generic TSharedClassInstance<T> = class(TInterfacedObject, specialize SharedClass<T>)
  24.   private
  25.     FReference: T;
  26.   public
  27.     constructor Create(AReference: T);
  28.     destructor Destroy; override;
  29.     function Get: T; inline;
  30.     procedure SetValue(AValue: T); inline;
  31.     function Assigned: Boolean; inline;
  32.   end;
  33.        
  34. generic function Shared<T>(AValue: T): specialize SharedClass<T>; inline;
  35. implementation
  36.  
  37. generic function Shared<T>(AValue: T): specialize SharedClass<T>;
  38. begin
  39.   Result := specialize TSharedClassInstance<T>.Create(AValue);
  40. end;
  41.  
  42. { TSharedClassInstance }
  43.  
  44. constructor TSharedClassInstance.Create(AReference: T);
  45. begin
  46.   inherited Create;
  47.   FReference := AReference;
  48. end;
  49.  
  50. destructor TSharedClassInstance.destroy;
  51. begin
  52.   FReference.Free;
  53.   inherited Destroy;
  54. end;
  55.  
  56. function TSharedClassInstance.Get: T;
  57. begin
  58.   Result := FReference;
  59. end;
  60.  
  61. procedure TSharedClassInstance.SetValue(AValue: T);
  62. begin
  63.   FReference := AValue;
  64. end;
  65.  
  66. function TSharedClassInstance.Assigned: Boolean;
  67. begin
  68.   Result := FReference <> nil;
  69. end;
  70.  
  71. end.

Usage:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode Delphi}{$H+}
  4.  
  5. uses
  6.   classes, SharedTypes;
  7.  
  8. var
  9.   sl1, sl2: SharedClass<TStringList>;
  10. begin
  11.   sl1 := Shared<TStringList>(TStringList.Create);
  12.   sl2 := sl1;
  13.  
  14.   sl1.Get.Text := 'Hello World!';
  15.   WriteLn(sl1.Get.Text);
  16.  
  17.   // Set to nil: still sl2 active so nothing happens
  18.   sl1 := nil;
  19.   WriteLn(sl2.Get.Text);
  20.   // Set sl2 to nil -> no more references -> Free memory
  21.   sl2 := nil;
  22.   // No memory leaks, no dangling pointers, no use after free possible with reference counting
  23. end.

It is still a little bit cumbersome because Interfaces do not allow for static class methods and there are no class operators to overload the explicit and/or implicit cast between the SharedClass and T.

Thats why there needs to be this generic function "Shared". Sadly generic functions are still very buggy in FPC, meaning that I can't restrict the generics to classes (by using <T: TObject> as generic parameter) because this makes the FPC throw one error after another
« Last Edit: December 07, 2021, 08:08:07 pm by Warfley »

 

TinyPortal © 2005-2018