go: os/user: non-cgo implementation of LookupGroup and LookupGroupId can fail when large groups are present in /etc/group
What version of Go are you using (go version)?
$ go version go version go1.15.6 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build513596641=/tmp/go-build -gno-record-gcc-switches"
What did you do?
- Create a large Linux group in
/etc/group. - From a Go program built with
CGO_ENABLED=0, callLookupGroup()orLookupGroupId()from theos/userpackage.
The provided example is run in a Docker container in order to not pollute the local host’s users and groups. Note that the script executed to create a large group in /etc/group may take a few minutes to execute.
$ docker run -it golang:1.15.6-buster
# cat > setup_large_group.sh << EOF
#!/bin/bash
groupadd largegroup
for i in \$(seq 1 7500); do
user="user\${i}"
useradd "\${user}"
usermod -a -G largegroup "\${user}"
done
EOF
# chmod +x setup_large_group.sh
# ./setup_large_group.sh
# wc -L /etc/group
66410 /etc/group
# cat > lookup_group.go << EOF
package main
import (
"fmt"
"os"
"os/user"
)
func main() {
gid := os.Args[1]
group, err := user.LookupGroupId(gid)
if err != nil {
fmt.Fprintf(os.Stderr, "user.LookupGroupId: %v\n", err)
os.Exit(1)
}
fmt.Println(group)
}
EOF
# CGO_ENABLED=0 go build lookup_group.go
# export LARGEGROUPID=$(cut -d: -f3 < <(getent group largegroup))
# ./lookup_group "${LARGEGROUPID}"
What did you expect to see?
&{1000 largegroup}
What did you see instead?
user.LookupGroupId: bufio.Scanner: token too long
This appears to happen when the length of a line in /etc/group exceeds bufio.MaxScanTokenSize bytes (currently 65536 bytes) in length. The implementation in lookup_unix.go does not gracefully handle lines in /etc/group exceeding 65536 bytes in length in its usage of bufio.Scanner.
As far as I know, there are no documented limits to the size of a group in Linux, so ideally the Go implementations of LookupGroup() and LookupGroupId() should work regardless of whether the binary is compiled with cgo or not.
One possible solution could be to read in the file using a bufio.Reader by calling r.ReadLine().
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 4
- Comments: 18 (16 by maintainers)
for anyone trying at home: the setup can be just (so it doesn’t take forever):
This is fixed by I453321f1ab15fd4d0002f97fcec7d0789e1e0da5; should be available in the next Go release.
@rturner3 thanks for reporting! @ianlancetaylor thanks for the thorough reivew!
Andrey