Hacker News new | ask | show | jobs
by trealira 918 days ago
You can also cause a segfault in Go by dereferencing a null pointer. That's another example of not being entirely memory safe.
1 comments

In this case it's not memory unsafe. It is guaranteed to crash the program (or get caught). It's closer to a NullReferenceException than it is to reading from a null pointer in C. There's no memory exploitation you can pull off from this bug being in a Go program, but you could in a C program
It's only guaranteed because of the operating system's sandboxing.

> It's closer to a NullReferenceException than it is to reading from a null pointer in C.

No, it's exactly the same as a null pointer dereference in C, because it is literally reading from a null pointer in Go as well. In Java, the compiler inserts null checks before every single dereference and throws an exception for null references.

> There's no memory exploitation you can pull off from this bug being in a Go program, but you could in a C program

Provided the OS sends a SEGV signal for null pointer dereferences, I don't see there being a difference in security between C and Golang in this respect. It's a bigger problem when you're running without an operating system.

In huge number of cases the null dereference is not from accessing 0x0 but some offset to it (ie. accessing a struct member or array element that's not the first one). Of course in practice most of the offsets are below the limit where nothing is ever mapped (on Linux vm.mmap_min_addr and seems 64k by default for me) but it's still very possible to have such dereference to not segfault in C. That should not be possible in Go/Java (if it is, it would almost certainly be considered a bug in the compiler/VM).
Why isn't it possible in Go? If you can use pointers to structures in both Go and C, and you can access the fields of a structure through a pointer in both, then I don't understand why reading a structure field through a null pointer wouldn't cause the dereference of an address like 0x8 in both languages.
Unbounded/large offsets are the critical part. Minimum unit where memory protection can be set is one page (4096 bytes on x86), so compiler could reasonably assume that offsets 0-4095 are always safe to dereference (in the sense that SIGSEGV is guaranteed, which can be then turned into a NullPointerException in the SIGSEGV signal handler) without a NULL check. For anything larger or array accesses, add a explicit check for NULL before dereference.
> In Java, the compiler inserts null checks before every single dereference and throws an exception for null references.

Doesn't OpenJDK install a SIGSEGV handler, and generate the exception from that on a null dereference?

(AFAIK, a lot of runtimes for GC'd languages that support thread-based parallelism do so anyway, because they can use mprotect to do a write barrier in hardware.)

> Doesn't OpenJDK install a SIGSEGV handler, and generate the exception from that on a null dereference?

I thought I had read that they explicitly don't do that, but I can't find it anymore. You may be right. I should have checked before saying that.

> (AFAIK, a lot of runtimes for GC'd languages that support thread-based parallelism do so anyway, because they can use mprotect to do a write barrier in hardware.)

That's true. I guess those implementations must do something more advanced than "throw a NullPointerException if the program segfaults," given their garbage collector runtimes also rely on that signal.

> I thought I had read that they explicitly don't do that

This is exactly what they do: https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-che...

When this happens it'll cause a deoptimization and recompilation of the code to include the null check rather than rely on the signal handler repeatedly.

Thanks for the link!