Description
In bcm283x/gpio.go, the driverGPIO.Init() method has a variable shadowing bug that silently swallows errors from pmem.MapGPIO(), causing the driver to initialise with gpioMemory == nil even when /dev/gpiomem is accessible and mmappable.
At line ~1447:
m, err := pmem.MapGPIO()
if err != nil {
var err2 error
m, err2 = pmem.Map(uint64(d.gpioBaseAddr), 4096)
var err error // <-- shadows outer err with a new nil error
if err2 != nil {
if distro.IsRaspbian() {
if os.IsNotExist(err) && os.IsPermission(err2) { // err is always nil here
return true, fmt.Errorf(...)
}
}
if os.IsPermission(err2) {
return true, fmt.Errorf("need more access, try as root: %v", err) // err is nil
}
return true, err // returns nil — Init "succeeds" with gpioMemory unset
}
}
Line 1453 declares var err error inside the if err2 != nil block, shadowing the outer err from line 1447. This means:
os.IsNotExist(err) on line 1458 always checks a nil error
return true, err on line 1465 returns nil — the driver Init succeeds without setting gpioMemory
Impact
When MapGPIO() fails and Map() also fails (non-root user without /dev/mem access), the Init silently succeeds with gpioMemory == nil. All subsequent Pin.Read() calls fall through to the gpioioctl path, which has a separate issue where it reconfigures output pins (see #75).
We confirmed this on a Raspberry Pi 3 Model A+ (BCM2837). /dev/gpiomem has correct permissions (root:gpio rw-rw----) and we proved via a standalone test program that mmapping it works correctly as a non-root user. Despite this, periph.io's MapGPIO() fails — and the error is swallowed by the shadowing bug, so the driver falls back to gpioioctl without any error indication.
On BCM2711 (Raspberry Pi 4B), MapGPIO() succeeds, so this bug is not triggered. The BCM2837-specific MapGPIO() failure may be a third issue, but the shadowing bug masks it — fixing the shadowing would at least surface the real error.
Suggested fix
Remove the shadowing declaration at line 1453. The outer err from line 1447 should flow through to the error checks:
m, err := pmem.MapGPIO()
if err != nil {
var err2 error
m, err2 = pmem.Map(uint64(d.gpioBaseAddr), 4096)
// removed: var err error
if err2 != nil {
// now err contains the original MapGPIO() error
...
}
}
Environment
- periph.io/x/host/v3 v3.8.5
- Raspberry Pi 3 Model A+ (BCM2837), Raspberry Pi OS (kernel 6.12.75+rpt-rpi-v8)
- Running as non-root systemd service user with gpio group membership
Related
Description
In
bcm283x/gpio.go, thedriverGPIO.Init()method has a variable shadowing bug that silently swallows errors frompmem.MapGPIO(), causing the driver to initialise withgpioMemory == nileven when/dev/gpiomemis accessible and mmappable.At line ~1447:
Line 1453 declares
var err errorinside theif err2 != nilblock, shadowing the outererrfrom line 1447. This means:os.IsNotExist(err)on line 1458 always checks a nil errorreturn true, erron line 1465 returns nil — the driver Init succeeds without settinggpioMemoryImpact
When
MapGPIO()fails andMap()also fails (non-root user without/dev/memaccess), the Init silently succeeds withgpioMemory == nil. All subsequentPin.Read()calls fall through to thegpioioctlpath, which has a separate issue where it reconfigures output pins (see #75).We confirmed this on a Raspberry Pi 3 Model A+ (BCM2837).
/dev/gpiomemhas correct permissions (root:gpio rw-rw----) and we proved via a standalone test program that mmapping it works correctly as a non-root user. Despite this, periph.io'sMapGPIO()fails — and the error is swallowed by the shadowing bug, so the driver falls back togpioioctlwithout any error indication.On BCM2711 (Raspberry Pi 4B),
MapGPIO()succeeds, so this bug is not triggered. The BCM2837-specificMapGPIO()failure may be a third issue, but the shadowing bug masks it — fixing the shadowing would at least surface the real error.Suggested fix
Remove the shadowing declaration at line 1453. The outer
errfrom line 1447 should flow through to the error checks:Environment
Related