golang/go

x/sys/unix: IoctlGetInt broken on 64-bit big-endian platforms

Open

#60,429 opened on May 25, 2023

View on GitHub
 (5 comments) (0 reactions) (0 assignees)Go (19,008 forks)batch import
NeedsFixcompiler/runtimehelp wanted

Repository metrics

Stars
 (133,883 stars)
PR merge metrics
 (No merged PRs in 30d)

Description

What version of Go are you using (go version)?

What operating system and processor architecture are you using (go env)?

GOARCH=s390x
GOOS=linux

(using QEMU on an amd64 host)

What did you do?

package main

import (
	"fmt"
	"os"

	"golang.org/x/sys/unix"
)

func main() {
	tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
	if err != nil {
		panic(err)
	}
	pgid, err := unix.IoctlGetInt(int(tty.Fd()), unix.TIOCGPGRP)
	fmt.Println(pgid, unix.Getpgrp())
}

What did you expect to see?

Same number twice, as on amd64:

$ go run pgrp.go
101981 101981

What did you see instead?

$ GOARCH=s390x go run pgrp.go
438060894388224 101994

The first number is 101994<<32. I believe this happens because IoctlGetInt passes a pointer to a Go int to the system call:

func IoctlGetInt(fd int, req uint) (int, error) {
        var value int
        err := ioctlPtr(fd, req, unsafe.Pointer(&value))
        return value, err
}

The ioctl system call expects a pointer to a C int. Passing a Go int means it stores the pgid in the upper 32 bits.

Note that IoctlSetPointerInt passes a pointer to an int32:

func IoctlSetPointerInt(fd int, req uint, value int) error {
        v := int32(value)
        return ioctlPtr(fd, req, unsafe.Pointer(&v))
}

Contributor guide