Home > Software engineering >  Nil Pointer Dereference
Nil Pointer Dereference

Time:09-17

So I'm trying something new today, and this code gives me a nil pointer dereference. I know why I'm getting one. However what I want to do is see if the pointer is nil before I dereference it and due to my use of unsafe.pointer I can't figure out how to do that.

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    test := readByte(0x34534)
    fmt.Println(test)
}

func readByte(address uintptr) byte {
    byteAddr := (*byte)(unsafe.Pointer(address))
    if byteAddr == nil {
        return 0
    }
    return *byteAddr
}

CodePudding user response:

That isn't a nil pointer and doesn't give a nil-pointer-deref-error. (It gives you a more generic error.)

There is no general method in Go to validate an arbitrary integer-treated-as-pointer, and in fact, sometimes there's no machine-specific method either. To do this sort of thing at a very low level—e.g., in assembly code—tends to require extremely specialized tricks. Some instruction sets have a "test accessibility of address" instruction, for instance:

probe (r1),#read,r2

sets r2 to a true/false value depending on whether the address stored in r1 is readable. Others don't even allow this, and instead require that you catch the fault that occurs when you attempt the access, which leads to code of this form, which I've translated into pseudo-Go:

p = sysmagic.GetMagic()
didfault := false
p.trapfault = &didfault // tell fault handler to set didfault and skip the load
p.allowedpc = getPC()   // provided the program counter is near here
v := *((*byte)unsafe.Pointer(testaddr))
p.trapfault = nil
if didfault {
    // address was bad
} else {
    // v holds the byte that was accessed
}

(although this sort of thing is more commonly written in C or assembly these days). The fault-catching code, part of the OS, then checks to see if we're in the special "catch and skip a fault" mode and advances the program counter to move past the failing load instruction, which must be within close range of the annotated program counter (otherwise any load instruction that fails, e.g., in an NMI handler, might set didfault to true).

(Another method that is used here is to mark, in a table, the address of "no fault load" instructions, and if a fault occurs, to check to see if we've tried to execute one of those marked instructions and if so, set the "resume" PC to the address stored as a companion in the table. This is useful on machines that have variable-length instructions, and lets us write special "copy string into kernel" functions and the like.)

The short version, though, is that you cannot do this portably. There usually is some method for doing it, on any given machine, but it may be restricted. You must consult the hardware and software documentation for your machine and/or OS and see if such faults are recoverable. Given that (a) OS authors generally have already done this for you to whatever extent is possible and (b) Go tends to use this to panic here, it's possible that you can catch the Go panic and recover—but on some machines, bus faults of various kinds can make the machine itself rather erratic, so that your program becomes unstable.

CodePudding user response:

You can use the if statement and return the true or false value.

package main
  
import (
    "fmt"
)
  
type Temp struct {
}
  
func main() {
    var pnt *Temp // pointer
    var x = "123"
    var pnt1 *string = &x
  
    if pnt == nil {
    fmt.Println("True")
    } else if pnt1 == nil {
        fmt.Println("False")
    }

    fmt.Printf("pnt is a nil pointer: %v\n", pnt == nil)
    fmt.Printf("pnt1 is a nil pointer: %v\n", pnt1 == nil)
}
  •  Tags:  
  • go
  • Related