access violation?

classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

access violation?

Ryan Joseph
I was doing some testing today (Mac) and discovered that calling Free on a nil object didn’t cause a EAccessViolation exception. Why doesn’t that crash?

var
        c: TObject;
begin
        c := nil;
        c.Free;



Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Michael Van Canneyt


On Thu, 26 Jul 2018, Ryan Joseph wrote:

> I was doing some testing today (Mac) and discovered that calling Free on a nil object didn’t cause a EAccessViolation exception. Why doesn’t that crash?

It is by design.

And - OMG !! - it is even documented:

https://www.freepascal.org/docs-html/current/rtl/system/tobject.free.html

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Victor Campillo
On 27/07/18 07:36, Michael Van Canneyt wrote:
> It is by design.
>
> And - OMG !! - it is even documented:
>
> https://www.freepascal.org/docs-html/current/rtl/system/tobject.free.html
>
There is no need to search the docs, it is even more quick just jump to
the implementation of free and see what it does :)

--
Victor Campillo

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph
In reply to this post by Michael Van Canneyt


> On Jul 26, 2018, at 11:36 PM, Michael Van Canneyt <[hidden email]> wrote:
>
> It is by design.
>
> And - OMG !! - it is even documented:

I never use Free directly so I had no idea. How is that even accomplished in the language? I thought it was just a method and behaved accordingly.

If it is magic then how do we call other methods on nil objects? There are times that would be handy if I could control it.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

dmitry boyarintsev
On Fri, Jul 27, 2018 at 11:15 AM, Ryan Joseph <[hidden email]> wrote:
I never use Free directly so I had no idea. How is that even accomplished in the language? I thought it was just a method and behaved accordingly.

If it is magic then how do we call other methods on nil objects? There are times that would be handy if I could control it.

It's not a magic, it's explicit check for the value of "Self" within a method.
Self would point to an actual instance of an object, if it's nil. Free method won't do anything.

type
  TRobust = class(TObject)
  public
    v : Integer;
    procedure RobustMethod;
  end;

procedure TRobust.RobustMethod;
begin
  if Self = nil then Exit; // remove or comment out this line to start getting AVs
  v := 5;
end;

var
  r : TRobust;
begin
  r := nil;
  r.RobustMethod;
end.

You could consider this a sanity check, similar to checking any other input parameters.

thanks,
Dmitry

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph


> On Jul 27, 2018, at 10:16 AM, Dmitry Boyarintsev <[hidden email]> wrote:
>
> type
>   TRobust = class(TObject)
>   public
>     v : Integer;
>     procedure RobustMethod;
>   end;
>
> procedure TRobust.RobustMethod;
> begin
>   if Self = nil then Exit; // remove or comment out this line to start getting AVs
>   v := 5;
> end;
>
> var
>   r : TRobust;
> begin
>   r := nil;
>   r.RobustMethod;
> end.
>

I had no idea you could do that!

I’m totally confused now. Since when was it ok to call methods on nil objects? According to this test not only can you call methods on nil objects but it calls the method statically (like a class method), i.e the test prints “DoThis”. How is that possible?

type
        TMyClass = class
                procedure DoThis;
        end;

procedure TMyClass.DoThis;
begin
        writeln('DoThis');
end;

var
        obj: TMyClass;
begin
        obj := nil;
        obj.DoThis;


Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Mattias Gaertner
On Fri, 27 Jul 2018 11:06:11 -0600
Ryan Joseph <[hidden email]> wrote:

>[...]
> I’m totally confused now. Since when was it ok to call methods on nil objects?

You can't if you compile with -CR.
The RTL is not compiled with objectchecks, so it works there.

> According to this test not only can you call methods on nil objects but
> it calls the method statically (like a class method), i.e the test
> prints “DoThis”. How is that possible?

Self on x86_64 target is just a hidden argument.
It won't work with virtual methods.

Mattias

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph


> On Jul 27, 2018, at 11:24 AM, Mattias Gaertner <[hidden email]> wrote:
>
> You can't if you compile with -CR.
> The RTL is not compiled with objectchecks, so it works there.
>
>> According to this test not only can you call methods on nil objects but
>> it calls the method statically (like a class method), i.e the test
>> prints “DoThis”. How is that possible?
>
> Self on x86_64 target is just a hidden argument.
> It won't work with virtual methods.

This is all news to me. I had no idea I was possibly compiling programs where I could call nil objects.

How do I disable this? I just ran this test program with -CR and I still calls the method statically.

Ryans-MacBook-Pro-2:~ ryanjoseph$ fpc -CR /Users/ryanjoseph/Desktop/macro_test/obj_test.pas
Free Pascal Compiler version 3.0.4 [2017/11/26] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Darwin for x86_64
Compiling /Users/ryanjoseph/Desktop/macro_test/obj_test.pas
Assembling (pipe) /Users/ryanjoseph/Desktop/macro_test/obj_test.s
Linking /Users/ryanjoseph/Desktop/macro_test/obj_test
19 lines compiled, 0.1 sec
Ryans-MacBook-Pro-2:~ ryanjoseph$ /Users/ryanjoseph/Desktop/macro_test/obj_test
DoThis


{$mode objfpc}

program obj_test;

type
        TMyClass = class
                procedure DoThis;
        end;

procedure TMyClass.DoThis;
begin
        writeln('DoThis');
end;

var
        obj: TMyClass;
begin
        obj := nil;
        obj.DoThis;
end.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

dmitry boyarintsev
In reply to this post by Ryan Joseph
On Fri, Jul 27, 2018 at 1:06 PM, Ryan Joseph <[hidden email]> wrote:
I had no idea you could do that!

Obviously, it speaks high of your abilities ;)
You've been able to accomplish your tasks without use of hacks (of any kind), playing strict by the rules.


I’m totally confused now. Since when was it ok to call methods on nil objects? According to this test not only can you call methods on nil objects but it calls the method statically (like a class method), i.e the test prints “DoThis”. How is that possible?

From the low-level (virtual/physical memory, CPU) perspective you can do that at any time on either nil-ed or uninitialized object as long as the method doesn't try to access and invalid memory.

The example (compiled for i386 and not using -CR works as expected)

type
  TRobust = class(TObject)
  public
    v : Integer;
    function Max(a,b: Integer): Integer;
  end;

function TRobust.Max(a,b: Integer): Integer;
begin
  if a>b then Result:=a
  else Result:=b;
end;

var
  r : TRobust;
begin
  r := nil;
  writeln(r.Max(5,10));
end.  
 
However such behavior, would be RTL specific - it's all about how the compiler would generate such call. If it would attempt to access any field (i.e. VMT) related to the instance of the object itself (rather than the class), there's a high chance it would fail.

And that's why having -CR enabled during development is generally a very good idea.

From high-level (OOP) such actions are not welcomed (and are enforced using -CR in case of FPC).

In order to have robust, maintainable and portable (to either other platform or even language) a developer should respect high-level rules and never depend on low-level rules to be the same on any platform.

thanks,
Dmitry

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Michael Van Canneyt


On Fri, 27 Jul 2018, Dmitry Boyarintsev wrote:

>> From high-level (OOP) such actions are not welcomed (and are enforced using
> -CR in case of FPC).
>
> In order to have robust, maintainable and portable (to either other
> platform or even language) a developer should respect high-level rules and
> never depend on low-level rules to be the same on any platform.

Do I have permission to quote you the next time someone starts about how
the lifetimes of interfaces are managed slightly differently in FPC and Delphi ?

:)

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

dmitry boyarintsev
On Fri, Jul 27, 2018 at 3:22 PM, Michael Van Canneyt <[hidden email]> wrote:
In order to have robust, maintainable and portable (to either other
platform or even language) a developer should respect high-level rules and
never depend on low-level rules to be the same on any platform.

Do I have permission to quote you the next time someone starts about how the lifetimes of interfaces are managed slightly differently in FPC and Delphi ?

:)

Permission granted. 
I thought all (COM) interfaces have been resolved, back in 2.6?

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Mattias Gaertner
In reply to this post by Ryan Joseph
On Fri, 27 Jul 2018 11:41:51 -0600
Ryan Joseph <[hidden email]> wrote:

>[...]
> How do I disable this? I just ran this test program with -CR and I still calls the method statically.

You are right. I just tested myself.
Either I'm confusing the flag or it has been changed.
Once upon a time Lazarus used the same trick and I had to change the
code as some users wanted use the flag.

Mattias
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph


> On Jul 27, 2018, at 2:17 PM, Mattias Gaertner <[hidden email]> wrote:
>
> You are right. I just tested myself.
> Either I'm confusing the flag or it has been changed.
> Once upon a time Lazarus used the same trick and I had to change the
> code as some users wanted use the flag.

Do you need me to file a bug report? This caused me a big headache this morning that I would like to avoid in the future. Off the top of my head I’m not aware of any time I’d want a call to nil to not fire an exception, except for calling Free perhaps since this is the documented behavior.

Thanks for straitening this out for me, I thought I was losing my mind. :)

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
Am 27.07.2018 um 19:41 schrieb Ryan Joseph:

>
>> On Jul 27, 2018, at 11:24 AM, Mattias Gaertner <[hidden email]> wrote:
>>
>> You can't if you compile with -CR.
>> The RTL is not compiled with objectchecks, so it works there.
>>
>>> According to this test not only can you call methods on nil objects but
>>> it calls the method statically (like a class method), i.e the test
>>> prints “DoThis”. How is that possible?
>> Self on x86_64 target is just a hidden argument.
>> It won't work with virtual methods.
> This is all news to me. I had no idea I was possibly compiling programs where I could call nil objects.
Normal, non-virtual methods are essentially ordinary
functions/procedures. In contrast to static functions/procedures they
have a hidden parameter that contains the value of Self. As Self is
merely a pointer there is no problem if Self should be Nil as long as it
isn't dereferenced (which happens if you access a field or a call a
virtual method).
Calling a virtual method would fail immediately.
> How do I disable this? I just ran this test program with -CR and I still calls the method statically.
The method is always called statically, -CR doesn't change anything
there. Calling a virtual method however would fail with EObjectCheck
instead of EAccessViolation.
The method you provided below would not fail anyway, because it doesn't
dereference Self. If you'd however access for example a field of your
class then a check should be inserted inside the method. For some reason
it isn't however and *that* is the bug.

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph


> On Jul 28, 2018, at 2:03 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> The method you provided below would not fail anyway, because it doesn't dereference Self. If you'd however access for example a field of your class then a check should be inserted inside the method. For some reason it isn't however and *that* is the bug.

Just getting back to this now.

Thanks for the description, I didn’t know FPC behaved that way but it’s good to know.

However, I’ve never had a use for this (outside of calling Free as I learned and the way I’ve always programmed Pascal, accessing nil is an error or undefined behavior so I avoid it meticulously.

How can I disable this so calling a method on nil gives an error?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Free Pascal - General mailing list
Ryan Joseph <[hidden email]> schrieb am Mo., 30. Juli 2018, 18:05:


> On Jul 28, 2018, at 2:03 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> The method you provided below would not fail anyway, because it doesn't dereference Self. If you'd however access for example a field of your class then a check should be inserted inside the method. For some reason it isn't however and *that* is the bug.

Just getting back to this now.

Thanks for the description, I didn’t know FPC behaved that way but it’s good to know.

However, I’ve never had a use for this (outside of calling Free as I learned and the way I’ve always programmed Pascal, accessing nil is an error or undefined behavior so I avoid it meticulously.

How can I disable this so calling a method on nil gives an error?

If the method doesn't access Self then there is nothing you can do. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Ryan Joseph


> On Jul 30, 2018, at 11:08 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> If the method doesn't access Self then there is nothing you can do.

Is this something the compiler team would be interested in adding? I’d like to get an error when calling methods on nil. It’s seems like pretty basic type safety stuff to me.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: access violation?

Free Pascal - General mailing list
Ryan Joseph <[hidden email]> schrieb am Di., 31. Juli 2018, 18:15:


> On Jul 30, 2018, at 11:08 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> If the method doesn't access Self then there is nothing you can do.

Is this something the compiler team would be interested in adding? I’d like to get an error when calling methods on nil. It’s seems like pretty basic type safety stuff to me.

No, because calling methods like Free would break as well and there is enough code out there that relies on that behavior. Also for methods that don't access Self it really is not important whether Self is valid or not. And for the other cases (which are the majority btw) there is functionality for that even if it is currently buggy. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal