Jump to page: 1 28  
Page
Thread overview
November 19
When I was first playing with D, I managed to create a segfault by doing `SomeClass c;` and then trying do something with the object I thought I had default-created, by analogy with C++ syntax. Seasoned D programmers will recognise that I did nothing of the sort and instead created c is null and my program ended up dereferencing a null pointer.

I'm not the only one who has done this. I can't find it right now, but I've seen at least one person open a bug report because they misunderstood this as a bug in dmd.

I have been told a couple of times that this isn't something that needs to be patched in the language, but I don't understand. It seems like a very easy way to generate a segfault (and not a NullPointerException or whatever).

What's the reasoning for allowing this?
November 19
On Monday, 19 November 2018 at 21:23:31 UTC, Jordi Gutiérrez Hermoso wrote:
> What's the reasoning for allowing this?

The mistake is immediately obvious when you run the program, so I just don't see it as a big deal. You lose a matter of seconds, realize the mistake, and fix it.

What is your proposal for handling it? The ones usually put around are kinda a pain to use.
November 19
On 11/19/18 4:23 PM, Jordi Gutiérrez Hermoso wrote:
> When I was first playing with D, I managed to create a segfault by doing `SomeClass c;` and then trying do something with the object I thought I had default-created, by analogy with C++ syntax. Seasoned D programmers will recognise that I did nothing of the sort and instead created c is null and my program ended up dereferencing a null pointer.
> 
> I'm not the only one who has done this. I can't find it right now, but I've seen at least one person open a bug report because they misunderstood this as a bug in dmd.
> 
> I have been told a couple of times that this isn't something that needs to be patched in the language, but I don't understand. It seems like a very easy way to generate a segfault (and not a NullPointerException or whatever).
> 
> What's the reasoning for allowing this?

A null pointer dereference is an immediate error, and it's also a safe error. It does not cause corruption, and it is free (the MMU is doing it for you).

Note, you can get a null pointer exception on Linux by using etc.linux.memoryerror: https://github.com/dlang/druntime/blob/master/src/etc/linux/memoryerror.d

The worst part about a null-pointer segfault is when it's intermittent and you get no information about where it happens. Then it can be annoying to track down. But it can't be used as an exploit.

Consistent segfaults are generally easy to figure out.

-Steve
November 19
On Mon, 19 Nov 2018 21:23:31 +0000, Jordi Gutiérrez Hermoso wrote:
> When I was first playing with D, I managed to create a segfault by doing `SomeClass c;` and then trying do something with the object I thought I had default-created, by analogy with C++ syntax. Seasoned D programmers will recognise that I did nothing of the sort and instead created c is null and my program ended up dereferencing a null pointer.

Programmers coming from nearly any language other than C++ would find it expected and intuitive that declaring a class instance variable leaves it null.

The compiler *could* give you a warning that you're using an uninitialized variable in a way that will lead to a segfault, but that sort of flow analysis gets hard fast.

If you wanted the default constructor to be called implicitly, that would make @nogc functions behave significantly differently (they'd forbid declarations without explicit initialization or would go back to default null), and it would be a problem for anything that doesn't have a no-args constructor (again, this would either be illegal or go back to null).

Easier for everything to be consistent and everything to be initialized to null.
November 20
On Monday, 19 November 2018 at 21:52:47 UTC, Steven Schveighoffer wrote:

> A null pointer dereference is an immediate error, and it's also a safe error. It does not cause corruption, and it is free (the MMU is doing it for you).

Is this always true for all arches that D can compile to? I remember back in the DOS days with no memory protection you really could read OS data around the beginning.

> Consistent segfaults are generally easy to figure out.

I think I would still prefer a stack trace like other kinds of D errors. Is this too difficult?
November 19
On 11/19/18 7:21 PM, Jordi Gutiérrez Hermoso wrote:
> On Monday, 19 November 2018 at 21:52:47 UTC, Steven Schveighoffer wrote:
> 
>> A null pointer dereference is an immediate error, and it's also a safe error. It does not cause corruption, and it is free (the MMU is doing it for you).
> 
> Is this always true for all arches that D can compile to? I remember back in the DOS days with no memory protection you really could read OS data around the beginning.

It's true for all OSes that D supports, and for most modern operating systems, that run in protected mode.

It would NOT necessarily be true for kernel modules or an OS kernel, so that is something to be concerned about.

>> Consistent segfaults are generally easy to figure out.
> 
> I think I would still prefer a stack trace like other kinds of D errors. Is this too difficult?

Yes and no. It's good to remember that this is a HARDWARE generated exception, and each OS handles it differently. It's also important to remember that a segmentation fault is NOT necessarily the result of a simple error like forgetting to initialize a variable. It could be a serious memory corruption error. Generating stack traces can be dangerous in this kind of state.

As I said, on Linux you can enable a "hack" that generates an error for a null dereference. On Windows, I believe that it already generates an exception without any modification.

On other OSes you may be out of luck until someone figures out a nice clever hack for it.

And if it's repeatable, you can always run in a debugger to see where the error is occurring.

-Steve
November 20
On Monday, 19 November 2018 at 21:57:11 UTC, Neia Neutuladh wrote:

> Programmers coming from nearly any language other than C++ would find it expected and intuitive that declaring a class instance variable leaves it null.

What do you think about making the syntax slightly more explicit and warn or possibly error out if you don't do it that way? Either

  SomeClass c = null;

or

  SomeClass c = new SomeClass();

and nothing else.

> The compiler *could* give you a warning that you're using an uninitialized variable in a way that will lead to a segfault, but that sort of flow analysis gets hard fast.

Nulls/Nones are always a big gap in a language's type system. A common alternative is to have some Option/Maybe type like Rust or Haskell or D's Variant. How about making that required to plug the null gap?

> If you wanted the default constructor to be called implicitly,

Yeah, maybe this bit of C++ syntax isn't the best idea. What about other alternatives?
November 20
On Tuesday, 20 November 2018 at 00:30:44 UTC, Jordi Gutiérrez Hermoso wrote:
>
> Yeah, maybe this bit of C++ syntax isn't the best idea. What about other alternatives?

You could try testing for null before dereferencing ;-)

If the following code in D, did what you'd reasonably expect it to do, then you could do this:

-----
module test;

import std.stdio;

class C { public int x; }

void main()
{
  try
  {
    C c = null;
    c.x = 100;
  }
  catch(Exception e)
  {
    writeln(e.msg);
  }

}
-----

November 20
On Tue, 20 Nov 2018 00:30:44 +0000, Jordi Gutiérrez Hermoso wrote:
> On Monday, 19 November 2018 at 21:57:11 UTC, Neia Neutuladh wrote:
> 
>> Programmers coming from nearly any language other than C++ would find it expected and intuitive that declaring a class instance variable leaves it null.
> 
> What do you think about making the syntax slightly more explicit and warn or possibly error out if you don't do it that way?

The prevailing idea is that warnings are either non-problems, in which case they shouldn't be emitted, or things you really need to fix, in which case they should be errors.

Things that are sometimes errors can be left to lint tools.

> Either
> 
>    SomeClass c = null;
> 
> or
> 
>    SomeClass c = new SomeClass();
> 
> and nothing else.

That would work, though it would be mildly tedious.

However, the general philosophy with D is that things should be implicitly initialized to a default state equal to the `.init` property of the type. That default state can be user-defined with structs, but with other types, it is generally an 'empty' state that has well-defined semantics. For floating point values, that is NaN. For integers, it's 0. For arrays, it's a null array with length 0. For objects and pointers, it's null.

> Nulls/Nones are always a big gap in a language's type system. A common alternative is to have some Option/Maybe type like Rust or Haskell or D's Variant.

Variant is about storing arbitrary values in the same variable. Nullable is the D2 equivalent of Option or Maybe.

> How about making that required to plug the null gap?

That's extremely unlikely to make it into D2 and rather unlikely to make it into a putative D3. However, if you feel strongly enough about it, you can write a DIP.

I've used Kotlin with its null safety, and I honestly haven't seen benefits from it. I have seen some NullPointerExceptions in slightly different places and some NullPointerExceptions instead of empty strings in log messages, but that's it.
November 19
On Monday, November 19, 2018 5:30:00 PM MST Steven Schveighoffer via Digitalmars-d-learn wrote:
> On 11/19/18 7:21 PM, Jordi Gutiérrez Hermoso wrote:
> > On Monday, 19 November 2018 at 21:52:47 UTC, Steven Schveighoffer wrote:
> >> A null pointer dereference is an immediate error, and it's also a safe error. It does not cause corruption, and it is free (the MMU is doing it for you).
> >
> > Is this always true for all arches that D can compile to? I remember back in the DOS days with no memory protection you really could read OS data around the beginning.
>
> It's true for all OSes that D supports, and for most modern operating systems, that run in protected mode.
>
> It would NOT necessarily be true for kernel modules or an OS kernel, so that is something to be concerned about.

For @safe to function properly, dereferencing null _must_ be guaranteed to be memory safe, and for dmd it is, since it will always segfault. Unfortunately, as understand it, it is currently possible with ldc's optimizer to run into trouble, since it'll do things like see that something must be null and therefore assume that it must never be dereferenced, since it would clearly be wrong to dereference it. And then when the code hits a point where it _does_ try to dereference it, you get undefined behavior. It's something that needs to be fixed in ldc, but based on discussions I had with Johan at dconf this year about the issue, I suspect that the spec is going to have to be updated to be very clear on how dereferencing null has to be handled before the ldc guys do anything about it. As long as the optimizer doesn't get involved everything is fine, but as great as optimizers can be at making code faster, they aren't really written with stuff like @safe in mind.

> >> Consistent segfaults are generally easy to figure out.
> >
> > I think I would still prefer a stack trace like other kinds of D errors. Is this too difficult?
>
> Yes and no. It's good to remember that this is a HARDWARE generated exception, and each OS handles it differently. It's also important to remember that a segmentation fault is NOT necessarily the result of a simple error like forgetting to initialize a variable. It could be a serious memory corruption error. Generating stack traces can be dangerous in this kind of state.
>
> As I said, on Linux you can enable a "hack" that generates an error for a null dereference. On Windows, I believe that it already generates an exception without any modification.
>
> On other OSes you may be out of luck until someone figures out a nice clever hack for it.
>
> And if it's repeatable, you can always run in a debugger to see where the error is occurring.

Also, if your OS supports core dumps, and you have them turned on, then it's trivial to get a stack trace - as well as a lot more of the program state.

- Jonathan M Davis




« First   ‹ Prev
1 2 3 4 5 6 7 8