Jump to page: 1 2
Thread overview
null dereference
Mar 15, 2014
luka8088
Mar 15, 2014
bearophile
Mar 15, 2014
luka8088
Mar 15, 2014
Ary Borenszweig
Mar 15, 2014
luka8088
Mar 15, 2014
Jonathan M Davis
Mar 15, 2014
Adam D. Ruppe
Mar 15, 2014
luka8088
Mar 15, 2014
Jonathan M Davis
Mar 16, 2014
luka8088
March 15, 2014
I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.

One possible reason that comes to mind is that it somewhat affects performance but should this really be an issue in version(assert)/debug build? Especially given the benefit of having file/line number and stack information outputted.




module program;

import std.stdio;

void main () {

  A a1 = new A();

  // auto inserted by the compiler before accessing object member
  // on version(assert) (or maybe on debug?)
  version(assert)
    if (a1 is null)
      throw new NullDereferenceError("Null Dereference");

  a1.f();

  A a2;

  // auto inserted by the compiler before accessing object member
  // on version(assert) (or maybe on debug?)
  version(assert)
    if (a2 is null)
      throw new NullDereferenceError("Null Dereference");

  a1.f();

}

class A {

  void f () {
    writeln("A.f called");
  }

}

class NullDereferenceError : Error {
  this (string msg, string file = __FILE__, size_t line = __LINE__) {
    super(msg, file, line);
  }
}
March 15, 2014
luka8088:

> I was thinking and I am not sure about the reason for not having some
> king of safeguard for null dereferencing in version(assert)/debug builds.

Eventually reference deference in D will be guarded by an assert in non-release builds. This desire is a raising tide that eventually can't be stopped.

Bye,
bearophile
March 15, 2014
On 15.3.2014. 12:25, bearophile wrote:
> luka8088:
> 
>> I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.
> 
> Eventually reference deference in D will be guarded by an assert in non-release builds. This desire is a raising tide that eventually can't be stopped.
> 
> Bye,
> bearophile

I am very glad to hear that! It is very frustrating for a program to segfault without giving any information (on linux) even in debug mode.


March 15, 2014
On 3/15/14, 8:25 AM, bearophile wrote:
> luka8088:
>
>> I was thinking and I am not sure about the reason for not having some
>> king of safeguard for null dereferencing in version(assert)/debug builds.
>
> Eventually reference deference in D will be guarded by an assert in
> non-release builds. This desire is a raising tide that eventually can't
> be stopped.
>
> Bye,
> bearophile

Really? I thought Walter was against this. He always says you can fire up a debugger and check where the dereference occurred.
March 15, 2014
There is a hidden module on Linux which can activate this, sort of:

void main() {
   // these two lines turn it on
   import etc.linux.memoryerror;
   registerMemoryErrorHandler();

   Object o = null;
   o.toString(); // trigger it here
}

etc.linux.memoryerror.NullPointerError@src/etc/linux/memoryerror.d(325):
----------------
./test56(void etc.linux.memoryerror.sigsegvDataHandler()+0xb) [0x805d44b]
./test56(_Dmain+0xa) [0x805c8ea]
./test56(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll().void __lambda1()+0x10) [0x805cb58]
./test56(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x18) [0x805cad0]
./test56(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll()+0x27) [0x805cb1f]
./test56(void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate())+0x18) [0x805cad0]
./test56(_d_run_main+0x117) [0x805ca67]
./test56(main+0x14) [0x805c90c]
/lib/libc.so.6(__libc_start_main+0xe6) [0xf75f3b86]
./test56() [0x805c831]




As you can see there, the top line doesn't really help much, it just lists the  druntime module, but the stack trace can help: it shows _Dmain in there, and if you add other functions, you can see them too.

So still not quite a file+line number for the exact place (you can get that in a debugger fairly easily though) but helps find where it is, especially if you have fairly small functions.
March 15, 2014
On 15.3.2014. 16:37, Adam D. Ruppe wrote:
> There is a hidden module on Linux which can activate this, sort of:
> 
> void main() {
>    // these two lines turn it on
>    import etc.linux.memoryerror;
>    registerMemoryErrorHandler();
> 
>    Object o = null;
>    o.toString(); // trigger it here
> }
> 
> etc.linux.memoryerror.NullPointerError@src/etc/linux/memoryerror.d(325):
> ----------------
> ../test56(void etc.linux.memoryerror.sigsegvDataHandler()+0xb) [0x805d44b]
> ../test56(_Dmain+0xa) [0x805c8ea]
> ../test56(void rt.dmain2._d_run_main(int, char**, extern (C) int
> function(char[][])*).runAll().void __lambda1()+0x10) [0x805cb58]
> ../test56(void rt.dmain2._d_run_main(int, char**, extern (C) int
> function(char[][])*).tryExec(scope void delegate())+0x18) [0x805cad0]
> ../test56(void rt.dmain2._d_run_main(int, char**, extern (C) int
> function(char[][])*).runAll()+0x27) [0x805cb1f]
> ../test56(void rt.dmain2._d_run_main(int, char**, extern (C) int
> function(char[][])*).tryExec(scope void delegate())+0x18) [0x805cad0]
> ../test56(_d_run_main+0x117) [0x805ca67]
> ../test56(main+0x14) [0x805c90c]
> /lib/libc.so.6(__libc_start_main+0xe6) [0xf75f3b86]
> ../test56() [0x805c831]
> 
> 
> 
> 
> As you can see there, the top line doesn't really help much, it just lists the  druntime module, but the stack trace can help: it shows _Dmain in there, and if you add other functions, you can see them too.
> 
> So still not quite a file+line number for the exact place (you can get that in a debugger fairly easily though) but helps find where it is, especially if you have fairly small functions.

I was not aware of this. Thanks!

March 15, 2014
On 15.3.2014. 14:34, Ary Borenszweig wrote:
> On 3/15/14, 8:25 AM, bearophile wrote:
>> luka8088:
>>
>>> I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.
>>
>> Eventually reference deference in D will be guarded by an assert in non-release builds. This desire is a raising tide that eventually can't be stopped.
>>
>> Bye,
>> bearophile
> 
> Really? I thought Walter was against this. He always says you can fire up a debugger and check where the dereference occurred.

Hm, that is true, but i think it should be a default behavior in
version(assert).

I saw many discussions on this topic and many arguments but I am still not able to digest that the following produces invalid memory operation:

void main () @safe {
  Object o = null;
  o.toHash();
}


March 15, 2014
On Saturday, March 15, 2014 10:34:39 Ary Borenszweig wrote:
> On 3/15/14, 8:25 AM, bearophile wrote:
> > luka8088:
> >> I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.
> > 
> > Eventually reference deference in D will be guarded by an assert in non-release builds. This desire is a raising tide that eventually can't be stopped.
> > 
> > Bye,
> > bearophile
> 
> Really? I thought Walter was against this. He always says you can fire up a debugger and check where the dereference occurred.

He is against it. Very much so. Maybe it'll happen at some point, but the entire basis for Bearophile's assertion that it's going to happen appears to be simply the fact that many folks want it. And yes, that might eventually sway Walter, but it's far from guaranteed, and honestly, I'd be surprised if it happened.

- Jonathan M Davis
March 15, 2014
On Saturday, March 15, 2014 11:05:42 luka8088 wrote:
> I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.
> 
> One possible reason that comes to mind is that it somewhat affects performance but should this really be an issue in version(assert)/debug build? Especially given the benefit of having file/line number and stack information outputted.

Essentially what it comes down to is the fact that because the OS already detects null pointer dereferences for you (hence the segfault or access violation that you get when it occurs), Walter considers it unnecessary. If you want more details, look at the resulting core dump in a debugger or run the program in a debugger to begin with. Now, that obviously doesn't always work, which is part of why many folks argue in favor of adding additional checks, but that's Walter's position.

I believe that there was some work done to make it so that druntime would detect a segfault and print a stacktrace when that happens, but it's not enabled normally, and I don't know quite what state it's in. That would probably be the ideal solution though, since it gives you the stacktrace without requiring additional checks.

- Jonathan M Davis
March 16, 2014
On 16.3.2014. 0:22, Jonathan M Davis wrote:
> On Saturday, March 15, 2014 11:05:42 luka8088 wrote:
>> I was thinking and I am not sure about the reason for not having some king of safeguard for null dereferencing in version(assert)/debug builds.
>>
>> One possible reason that comes to mind is that it somewhat affects performance but should this really be an issue in version(assert)/debug build? Especially given the benefit of having file/line number and stack information outputted.
> 
> Essentially what it comes down to is the fact that because the OS already detects null pointer dereferences for you (hence the segfault or access violation that you get when it occurs), Walter considers it unnecessary. If you want more details, look at the resulting core dump in a debugger or run the program in a debugger to begin with. Now, that obviously doesn't always work, which is part of why many folks argue in favor of adding additional checks, but that's Walter's position.
> 
> I believe that there was some work done to make it so that druntime would detect a segfault and print a stacktrace when that happens, but it's not enabled normally, and I don't know quite what state it's in. That would probably be the ideal solution though, since it gives you the stacktrace without requiring additional checks.
> 
> - Jonathan M Davis
> 


That is great. At least in theory, but at the end of the day the following code still performs invalid memory operation and ends up being killed by the os without any change of recovery.

void main () @safe {
  Object o = null;
  o.toHash();
}


I saw multiple failed attempts to implement handlers for such operation
and throwing exceptions instead. I don't remember why, and I can't find
the post, but there where claims that it will never be turned on by default.

etc.linux.memoryerror.registerMemoryErrorHandler(); that Adam pointed
out is great but somewhat looses it's point if it not turned on by default.


Consider a simple scenario. I am writing a vibed application, and I deploy it to a linux server. Now for testing purposes I make a non-release build and add:

try { ... } catch (Throwable t) { logCrash(t); }

Unfortunately null deference happened and I have no info why. For argument purposes let's say that this happens rarely and I am not able to reproduce it on my machine. Now I go to the docs looking for a way to get more info out of this. And there is nothing about it in the docs.

« First   ‹ Prev
1 2