Skip to content

Commit

Permalink
mountinfo: linux: use /proc/thread-self/mountinfo
Browse files Browse the repository at this point in the history
In a Go program with many threads, which goroutine is running on the
thread-group leader thread is not something programs can controls. Thus,
even if you use runtime.LockOSThread() for threads that have different
mount namespaces, it's possible that /proc/self will refer to the
"wrong" thread.

The solution is to simply use /proc/thread-self, which will always
provide the correct result for the calling thread. For pre-3.17 kernels
we use /proc/self/task/<tid> as a fallback.

The usage of /proc/self/mountinfo caused isuses for a patch to runc
which creates a thread to create id-mapped mounts (which requires
joining the container mount namespace).

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
  • Loading branch information
cyphar committed Oct 26, 2023
1 parent c0711cd commit 170c9be
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions mountinfo/mountinfo_linux.go
Expand Up @@ -5,15 +5,18 @@ import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"

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

// GetMountsFromReader retrieves a list of mounts from the
// reader provided, with an optional filter applied (use nil
// for no filter). This can be useful in tests or benchmarks
// that provide fake mountinfo data, or when a source other
// than /proc/self/mountinfo needs to be read from.
// than /proc/thread-self/mountinfo needs to be read from.
//
// This function is Linux-specific.
func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
Expand Down Expand Up @@ -128,7 +131,19 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
}

func parseMountTable(filter FilterFunc) ([]*Info, error) {
f, err := os.Open("/proc/self/mountinfo")
// We need to lock ourselves to the current OS thread in order to make sure
// that the thread referenced by /proc/thread-self stays alive until we
// finish parsing the file.
runtime.LockOSThread()
defer runtime.UnlockOSThread()

f, err := os.Open("/proc/thread-self/mountinfo")
if os.IsNotExist(err) {
// On pre-3.17 kernels (such as CentOS 7), we don't have
// /proc/thread-self/ so we need to manually construct
// /proc/self/task/<tid>/ as a fallback.
f, err = os.Open("/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/mountinfo")
}
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 170c9be

Please sign in to comment.