Skip to content

os: root operations require read permission on intermediate directories #81605

Open

Description

@crazy-max

Go version

go version go1.26.8 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/root/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/root/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2907829509=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
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'
GOTELEMETRY='local'
GOTELEMETRYDIR='/root/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.26.8'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Compared os.Mkdir with Root.Mkdir beneath a directory owned by the calling user with mode 0300 (write and search permission, but no read permission).

The standalone program below was built in the official golang:1.26.8 Linux container and run as UID/GID 1000:1000. The environment above is from the build step, which ran as root; the reproducer itself ran unprivileged.

Run as an unprivileged Linux user. Running with capabilities that bypass discretionary access checks can hide the problem.

package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	dir, err := os.MkdirTemp("", "root-permission-")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	parent := filepath.Join(dir, "parent")
	if err := os.Mkdir(parent, 0o700); err != nil {
		panic(err)
	}
	if err := os.Chmod(parent, 0o300); err != nil {
		panic(err)
	}
	defer os.Chmod(parent, 0o700)

	root, err := os.OpenRoot(dir)
	if err != nil {
		panic(err)
	}
	defer root.Close()

	fmt.Println("os.Mkdir:", os.Mkdir(filepath.Join(parent, "ordinary"), 0o700))
	fmt.Println("Root.Mkdir:", root.Mkdir("parent/rooted", 0o700))
}

What did you see happen?

os.Mkdir: <nil>
Root.Mkdir: mkdirat parent/rooted: permission denied

The paths are entirely inside the root, with no symlinks or concurrent filesystem changes. In separate probes, ordinary stat, file creation, symlink creation, hardlink creation, chmod, and timestamp operations also succeeded beneath the same directory permissions, while their os.Root equivalents failed with permission denied.

This affects archive extraction using os.Root: a preexisting write/search-only directory, or a mode-0300 directory created earlier in the same archive, prevents subsequent child extraction even though ordinary filesystem operations allow it.

The apparent cause is that rootOpenDir opens intermediate directories without O_PATH or an equivalent search-only mode. The access mode is therefore read-only. This code is present in the tested Go 1.26.8 toolchain and in the linked upstream revision.

What did you expect to see?

Both directory-creation operations should succeed. Creating an entry requires write and search permission on the parent, not permission to list its contents. Root.Mkdir should preserve these permission semantics while enforcing containment.

On Linux, a separate probe using an O_PATH|O_DIRECTORY|O_NOFOLLOW parent descriptor followed by mkdirat with a single-component child name succeeded without changing directory permissions. This suggests search-only directory handles may be relevant, although a general change would need to account for other platforms and operations that actually require directory reading.

Activity

  1. gopherbot commented on Sep 25, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/838745 mentions this issue: os: open intermediate Root directories with O_PATH on Linux

  2. prattmic commented on Sep 29, 2026

    @prattmic
    Member
  3. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Sep 29, 2026
  4. added this to the Backlog milestone on Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions