Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/sys: trimmed string socket opt on linux #63217

Closed
KSDaemon opened this issue Sep 25, 2023 · 5 comments
Closed

x/sys: trimmed string socket opt on linux #63217

KSDaemon opened this issue Sep 25, 2023 · 5 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@KSDaemon
Copy link

KSDaemon commented Sep 25, 2023

Bug description

Func GetsockoptString from x/sys/unix package always removes the last byte from the buffer, which in some cases leads to a truncated option value.

From unix man page:

              The security context string may include a terminating null
              character in the returned length, but is not guaranteed to
              do so: a security context "foo" might be represented as
              either {'f','o','o'} of length 3 or {'f','o','o','\0'} of
              length 4, which are considered to be interchangeable.  The
              string is printable, does not contain non-terminating null
              characters, and is in an unspecified encoding (in
              particular, it is not guaranteed to be ASCII or UTF-8).

But the implementation (this patch) always cuts the last byte without checking it for the null character.

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

$ go version
go version go1.21.1

Does this issue reproduce with the latest release?

Yes.

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

I'm using intel mac, but building for arm linux (GOARCH=arm64 GOOS=linux)

go env Output
$ go env
go env
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/Users/kostik/Library/Caches/go-build'
GOENV='/Users/kostik/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/kostik/go/pkg/mod'
GOOS='darwin'
GOPATH='/Users/kostik/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/Cellar/go/1.21.1/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/Cellar/go/1.21.1/libexec/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/3p/1hh1wbcx7hjgl_79qxfdbqtr0000gq/T/go-build2506492106=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

The service code is running on the linux with apparmor enabled. It just reads the app armor label from the Unix socket connection by calling:

var getSocketSecurityLabel = func(rc syscall.RawConn) (string, error) {
	var label string
	var readLabelError error
	err := rc.Control(func(fd uintptr) {
		// Read the apparmor label from the socket option SO_PEERSEC
		label, readLabelError = unix.GetsockoptString(int(fd), syscall.SOL_SOCKET, unix.SO_PEERSEC)
	})

	if err != nil {
		return "", err
	}
	if readLabelError != nil {
		return "", readLabelError
	}

	return label, nil
}

What did you expect to see?

  • For unconfined application the expected output is unconfined
  • For snap running in development mode the expected output is appname (complain)
  • For a labeled app the expected output is full app name

What did you see instead?

  • For unconfined application the real output is unconfine
  • For snap running in development mode the real output is appname (complain
  • For a labeled app the real output miss the last char of the name
@gopherbot gopherbot added this to the Unreleased milestone Sep 25, 2023
@KSDaemon
Copy link
Author

The actual fix seems to be pretty short:

// GetsockoptString returns the string value of the socket option opt for the
// socket associated with fd at the given socket level.
func GetsockoptString(fd, level, opt int) (string, error) {
	buf := make([]byte, 256)
	vallen := _Socklen(len(buf))
	err := getsockopt(fd, level, opt, unsafe.Pointer(&buf[0]), &vallen)
	if err != nil {
		if err == ERANGE {
			buf = make([]byte, vallen)
			err = getsockopt(fd, level, opt, unsafe.Pointer(&buf[0]), &vallen)
		}
		if err != nil {
			return "", err
		}
	}
	if buf[vallen-1] == 0 {
		return string(buf[:vallen-1]), nil
	} else {
		return string(buf[:vallen]), nil
	}
}

If this looks good I can submit a patch!

@thanm thanm added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Sep 25, 2023
@thanm
Copy link
Contributor

thanm commented Sep 25, 2023

@golang/runtime

@ianlancetaylor
Copy link
Contributor

Please do submit a patch, and if at all possible include a test that fails without the patch. Thanks.

@gopherbot
Copy link

Change https://go.dev/cl/531117 mentions this issue: x/sys: fix trimmed socket opt string in unix/syscall_linux

@gopherbot
Copy link

Change https://go.dev/cl/530897 mentions this issue: unix: fix trimmed socket opt string in GetsockoptString

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants