Thread overview | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
August 02, 2010 null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
The following code fails with a "Bus error" (OSX speak for "Segfault," if I understand correctly). // types.d import std.stdio; class A { int x = 42; } void fail_sometimes(int n) { A a; if (n == 0) { a = new A; // clearly a contrived example } assert(a.x == 42, "Wrong x value"); } void main() { fail_sometimes(1); } It's even worse if I do a 'dmd -run types.d', it just fails without even the minimalistic "Bus error." Is this correct behavior? I searched the archives & looked at the FAQ & found workarounds (registering a signal handler), but not a justification, and the threads were from a couple years ago. Wondering if maybe something has changed and there's a problem with my system? -- rwsims |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ryan W Sims | Even better, you can annotate fail_sometimes with @safe, and it'll still access out-of-bounds memory. Take the following with a grain of salt since I'm really new to the language. gdb says: Reason: KERN_PROTECTION_FAILURE at address: 0x00000008 0x00001e52 in D4test14fail_sometimesFiZv () which indicates that 'a' is getting initialized to null (possibly by process startup 0ing out the stack), and then x is being read out of it. You can get exactly the same crashes in C++ by reading member variables out of null pointers. The D compiler is supposed to catch the uninitialized variable ("It is an error to use a local variable without first assigning it a value." in http://www.digitalmars.com/d/2.0/function.html), but clearly it's missing this one. I haven't actually found where in the language spec it says that class variables are pointers, or what their default values are. I'd expect to find this in http://www.digitalmars.com/d/2.0/type.html, but no luck. Looking through the bug tracker ... Walter's response to http://d.puremagic.com/issues/show_bug.cgi?id=671 seems to indicate that he isn't serious about uninitialized use being an error. It's just undefined behavior like in C++. In any case, the fix for your problem will be to initialize 'a' before using it. On Sun, Aug 1, 2010 at 9:59 PM, Ryan W Sims <rwsims@gmail.com> wrote: > The following code fails with a "Bus error" (OSX speak for "Segfault," if I > understand correctly). > > // types.d > import std.stdio; > > class A { > int x = 42; > } > > void fail_sometimes(int n) { > A a; > if (n == 0) { > a = new A; // clearly a contrived example > } > assert(a.x == 42, "Wrong x value"); > } > > void main() { > fail_sometimes(1); > } > > It's even worse if I do a 'dmd -run types.d', it just fails without even the minimalistic "Bus error." Is this correct behavior? I searched the archives & looked at the FAQ & found workarounds (registering a signal handler), but not a justification, and the threads were from a couple years ago. Wondering if maybe something has changed and there's a problem with my system? > > -- > rwsims > |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
On Monday 02 August 2010 00:05:40 Jeffrey Yasskin wrote:
> Even better, you can annotate fail_sometimes with @safe, and it'll still access out-of-bounds memory.
>
> Take the following with a grain of salt since I'm really new to the language.
>
> gdb says:
> Reason: KERN_PROTECTION_FAILURE at address: 0x00000008
> 0x00001e52 in D4test14fail_sometimesFiZv ()
>
> which indicates that 'a' is getting initialized to null (possibly by process startup 0ing out the stack), and then x is being read out of it. You can get exactly the same crashes in C++ by reading member variables out of null pointers. The D compiler is supposed to catch the uninitialized variable ("It is an error to use a local variable without first assigning it a value." in http://www.digitalmars.com/d/2.0/function.html), but clearly it's missing this one.
>
> I haven't actually found where in the language spec it says that class variables are pointers, or what their default values are. I'd expect to find this in http://www.digitalmars.com/d/2.0/type.html, but no luck.
>
> Looking through the bug tracker ... Walter's response to http://d.puremagic.com/issues/show_bug.cgi?id=671 seems to indicate that he isn't serious about uninitialized use being an error. It's just undefined behavior like in C++.
>
> In any case, the fix for your problem will be to initialize 'a' before using it.
_All_ variables in D are initialized with a default value. There should be _no_ undefined behavior with regards to initializations. D is very concientious about avoiding undefined behavior. In the case of references and pointers, they are initialized to null. There's not really such a thing as using a variable without initializing it, because variables are default initialized if you don't initialize them yourself. The _one_ exception would be if you explicitly initialized a variable to void:
int[] a = void;
In that case, you are _explicitly_ telling the compiler not to default initialize the variable. That _can_ lead to undefined behavior and is definitely unsafe. As such, it is intended solely for the purposes of optimizing code where absolutely necessary. So, you really shouldn't have any variables in your code that weren't initialized, even if you didn't initialize them explicitly.
The pages that you're looking at there need to be updated for clarity.
- Jonathan M Davis
|
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ryan W Sims | On Sunday 01 August 2010 21:59:42 Ryan W Sims wrote: > The following code fails with a "Bus error" (OSX speak for "Segfault," > if I understand correctly). > > // types.d > import std.stdio; > > class A { > int x = 42; > } > > void fail_sometimes(int n) { > A a; > if (n == 0) { > a = new A; // clearly a contrived example > } > assert(a.x == 42, "Wrong x value"); > } > > void main() { > fail_sometimes(1); > } > > It's even worse if I do a 'dmd -run types.d', it just fails without even the minimalistic "Bus error." Is this correct behavior? I searched the archives & looked at the FAQ & found workarounds (registering a signal handler), but not a justification, and the threads were from a couple years ago. Wondering if maybe something has changed and there's a problem with my system? > > -- > rwsims You are getting a segmentation fault because you are dereferencing a null reference. All references are default initialized to null. So, if you fail to explicitly initialize them or to assign to them, then they stay null, and in such a case, you will get a segfault if you try to dereference them. If you changed your code to import std.stdio; class A { int x = 42; } void fail_sometimes(int n) { A a; if (n == 0) { a = new A; // clearly a contrived example } assert(a !is null, "a shouldn't be null"); assert(a.x == 42, "Wrong x value"); } void main() { fail_sometimes(1); } you would get output something like this core.exception.AssertError@types.d(12): a shouldn't be null ---------------- ./types() [0x804b888] ./types() [0x8049360] ./types() [0x8049399] ./types() [0x804ba54] ./types() [0x804b9b9] ./types() [0x804ba91] ./types() [0x804b9b9] ./types() [0x804b968] /opt/lib32/lib/libc.so.6(__libc_start_main+0xe6) [0xf760bc76] ./types() [0x8049261] Unlike Java, there is no such thing as a NullPointerException in D. You just get segfaults - just like you would in C++. So, if you don't want segfaults from derefencing null references, you need to make sure that they aren't null when you dereference them. - Jonathan M Davis |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | Jonathan M Davis: > _All_ variables in D are initialized with a default value. There should be _no_ undefined behavior with regards to initializations. D is very concientious about avoiding undefined behavior. See also: http://d.puremagic.com/issues/show_bug.cgi?id=3820 Bye, bearophile |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ryan W Sims | On Mon, 02 Aug 2010 00:59:42 -0400, Ryan W Sims <rwsims@gmail.com> wrote:
> The following code fails with a "Bus error" (OSX speak for "Segfault," if I understand correctly).
>
> // types.d
> import std.stdio;
>
> class A {
> int x = 42;
> }
>
> void fail_sometimes(int n) {
> A a;
> if (n == 0) {
> a = new A; // clearly a contrived example
> }
> assert(a.x == 42, "Wrong x value");
> }
>
> void main() {
> fail_sometimes(1);
> }
>
> It's even worse if I do a 'dmd -run types.d', it just fails without even the minimalistic "Bus error." Is this correct behavior? I searched the archives & looked at the FAQ & found workarounds (registering a signal handler), but not a justification, and the threads were from a couple years ago. Wondering if maybe something has changed and there's a problem with my system?
I'm not familiar with dmd -run, but you should be aware that asserts are not compiled into release code.
Try changing the assert to this:
if(a.x != 42) writeln("Wrong x value");
FWIW, D does not have null pointer exceptions, even in debug mode. It's an oft-debated subject, but Walter hasn't ever budged on it. His view is that you should use a debugger to see where your code is failing. We have pointed out countless times that often it's not possible to have a debugger at hand, or even be able to reproduce the issue that caused the segfault while in a different environment. I don't know if we'll ever see null pointer exceptions, but I'd love them in debug mode only, or at least to see a stack trace when it occurs. The latter can be done without Phobos/dmd help if someone can write such a signal handler function. I don't know enough about stack traces to understand how to do it.
-Steve
|
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 8/2/10 1:56 AM, Jonathan M Davis wrote: > On Sunday 01 August 2010 21:59:42 Ryan W Sims wrote: >> The following code fails with a "Bus error" (OSX speak for "Segfault," >> if I understand correctly). >> >> // types.d >> import std.stdio; >> >> class A { >> int x = 42; >> } >> >> void fail_sometimes(int n) { >> A a; >> if (n == 0) { >> a = new A; // clearly a contrived example >> } >> assert(a.x == 42, "Wrong x value"); >> } >> >> void main() { >> fail_sometimes(1); >> } >> >> It's even worse if I do a 'dmd -run types.d', it just fails without even >> the minimalistic "Bus error." Is this correct behavior? I searched the >> archives& looked at the FAQ& found workarounds (registering a signal >> handler), but not a justification, and the threads were from a couple >> years ago. Wondering if maybe something has changed and there's a >> problem with my system? >> >> -- >> rwsims > > You are getting a segmentation fault because you are dereferencing a null > reference. All references are default initialized to null. So, if you fail to > explicitly initialize them or to assign to them, then they stay null, and in > such a case, you will get a segfault if you try to dereference them. Yes, I know *why* I'm getting a segfault, thank you - I set up the example explicitly to defeat the compiler's null checking to test the behavior. I was startled that there wasn't an exception thrown w/ a stack trace. [snip] > > > Unlike Java, there is no such thing as a NullPointerException in D. You just get > segfaults - just like you would in C++. So, if you don't want segfaults from > derefencing null references, you need to make sure that they aren't null when > you dereference them. > > - Jonathan M Davis That was my question, thanks. It seemed like such an un-D thing to have happen; I was surprised. I guess w/o the backing of a full virtual machine, it's tricker to catch null dereferences on the fly, but boy it'd be nice to have. Don't want to re-fire the debate here, though. -- rwsims |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
On Mon, Aug 2, 2010 at 1:49 AM, Jonathan M Davis <jmdavisprog@gmail.com> wrote: > On Monday 02 August 2010 00:05:40 Jeffrey Yasskin wrote: >> Even better, you can annotate fail_sometimes with @safe, and it'll still access out-of-bounds memory. >> >> Take the following with a grain of salt since I'm really new to the language. >> >> gdb says: >> Reason: KERN_PROTECTION_FAILURE at address: 0x00000008 >> 0x00001e52 in D4test14fail_sometimesFiZv () >> >> which indicates that 'a' is getting initialized to null (possibly by process startup 0ing out the stack), and then x is being read out of it. You can get exactly the same crashes in C++ by reading member variables out of null pointers. The D compiler is supposed to catch the uninitialized variable ("It is an error to use a local variable without first assigning it a value." in http://www.digitalmars.com/d/2.0/function.html), but clearly it's missing this one. >> >> I haven't actually found where in the language spec it says that class variables are pointers, or what their default values are. I'd expect to find this in http://www.digitalmars.com/d/2.0/type.html, but no luck. >> >> Looking through the bug tracker ... Walter's response to http://d.puremagic.com/issues/show_bug.cgi?id=671 seems to indicate that he isn't serious about uninitialized use being an error. It's just undefined behavior like in C++. >> >> In any case, the fix for your problem will be to initialize 'a' before using it. > > _All_ variables in D are initialized with a default value. There should be _no_ undefined behavior with regards to initializations. D is very concientious about avoiding undefined behavior. In the case of references and pointers, they are initialized to null. That's good to know. Unfortunately, reading through a null pointer does cause undefined behavior: it's not a guaranteed segfault. Consider an object with a large array at the beginning, which pushes later members past the empty pages at the beginning of the address space. I don't suppose the D compiler watches for such large objects and emits actual null checks before indexing into them? > The pages that you're looking at there need to be updated for clarity. Nice use of the passive voice. Who needs to update them? Is their source somewhere you or I could send a patch? |
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jeffrey Yasskin | Jeffrey Yasskin:
> That's good to know. Unfortunately, reading through a null pointer does cause undefined behavior: it's not a guaranteed segfault. Consider an object with a large array at the beginning, which pushes later members past the empty pages at the beginning of the address space. I don't suppose the D compiler watches for such large objects and emits actual null checks before indexing into them?
I am not expert enough to give you a good answer about this, but do some tests :-) And later if you want you may say the same things in the main D newsgroup.
Bye,
bearophile
|
August 02, 2010 Re: null dereference exception vs. segfault? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ryan W Sims | Am 02.08.2010 16:50, schrieb Ryan W Sims:
> On 8/2/10 1:56 AM, Jonathan M Davis wrote:
>> On Sunday 01 August 2010 21:59:42 Ryan W Sims wrote:
>>> The following code fails with a "Bus error" (OSX speak for "Segfault,"
>>> if I understand correctly).
>>>
>>> // types.d
>>> import std.stdio;
>>>
>>> class A {
>>> int x = 42;
>>> }
>>>
>>> void fail_sometimes(int n) {
>>> A a;
>>> if (n == 0) {
>>> a = new A; // clearly a contrived example
>>> }
>>> assert(a.x == 42, "Wrong x value");
>>> }
>>>
>>> void main() {
>>> fail_sometimes(1);
>>> }
>>>
>>> It's even worse if I do a 'dmd -run types.d', it just fails without even
>>> the minimalistic "Bus error." Is this correct behavior? I searched the
>>> archives& looked at the FAQ& found workarounds (registering a signal
>>> handler), but not a justification, and the threads were from a couple
>>> years ago. Wondering if maybe something has changed and there's a
>>> problem with my system?
>>>
>>> --
>>> rwsims
>>
>> You are getting a segmentation fault because you are dereferencing a null
>> reference. All references are default initialized to null. So, if you
>> fail to
>> explicitly initialize them or to assign to them, then they stay null,
>> and in
>> such a case, you will get a segfault if you try to dereference them.
>
> Yes, I know *why* I'm getting a segfault, thank you - I set up the
> example explicitly to defeat the compiler's null checking to test the
> behavior. I was startled that there wasn't an exception thrown w/ a
> stack trace.
>
> [snip]
>
>>
>>
>> Unlike Java, there is no such thing as a NullPointerException in D.
>> You just get
>> segfaults - just like you would in C++. So, if you don't want
>> segfaults from
>> derefencing null references, you need to make sure that they aren't
>> null when
>> you dereference them.
>>
>> - Jonathan M Davis
>
> That was my question, thanks. It seemed like such an un-D thing to have
> happen; I was surprised. I guess w/o the backing of a full virtual
> machine, it's tricker to catch null dereferences on the fly, but boy
> it'd be nice to have. Don't want to re-fire the debate here, though.
>
> --
> rwsims
If you want a NullPointerException as part of your program flow, you can use enforce() (in std.contracts I think). I don't think catching a NullPointerException in a big code block where you don't know which dereferencing should fail is good style.
Mafi
|
Copyright © 1999-2021 by the D Language Foundation