golang / go

The Go programming language

Home Page:https://go.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Calling setns from Go returns EINVAL for mnt namespace

gopherbot opened this issue · comments

by iain.lowe:

The C code works fine and correctly enters the namespace, but the Go code always seems
to return EINVAL from the `setns` call to enter the `mnt` namespace. I've tried a number
of permutations (including embedded C code with cgo and external `.so`) on Go `1.2`,
`1.3` and the current tip.

Stepping through the code in `gdb` shows that both sequences are calling `setns` in
`libc` the exact same way (or so it appears to me).

I have boiled what seems to be the issue down to the code below. What am I doing wrong?

## Setup

I have a shell alias for starting quick busybox containers:

    alias startbb='docker inspect --format "{{ .State.Pid }}" $(docker run -d busybox sleep 1000000)'

After running this, `startbb` will start a container and output it's PID.

`lxc-checkconfig` outputs:

    Found kernel config file /boot/config-3.8.0-44-generic
    --- Namespaces ---
    Namespaces: enabled
    Utsname namespace: enabled
    Ipc namespace: enabled
    Pid namespace: enabled
    User namespace: missing
    Network namespace: enabled
    Multiple /dev/pts instances: enabled
    
    --- Control groups ---
    Cgroup: enabled
    Cgroup clone_children flag: enabled
    Cgroup device: enabled
    Cgroup sched: enabled
    Cgroup cpu account: enabled
    Cgroup memory controller: missing
    Cgroup cpuset: enabled
    
    --- Misc ---
    Veth pair device: enabled
    Macvlan: enabled
    Vlan: enabled
    File capabilities: enabled

`uname -a` produces:

    Linux gecko 3.8.0-44-generic #66~precise1-Ubuntu SMP Tue Jul 15 04:01:04 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

## Working C code

The attached C code works fine.

After compiling with `gcc -o checkns checkns.c`, the output of `sudo ./checkns
<PID>` is:

    setns on ipc namespace succeeded
    setns on uts namespace succeeded
    setns on net namespace succeeded
    setns on pid namespace succeeded
    setns on mnt namespace succeeded

## Failing Go code

Conversely, the attached Go code (which should be identical) doesn't work quite as well.

Instead, running `sudo go run main.go <PID>` produces:

    setns on ipc namespace succeeded
    setns on uts namespace succeeded
    setns on net namespace succeeded
    setns on pid namespace succeeded
    setns on mnt namespace failed: invalid argument

Attachments:

  1. checkns.c (786 bytes)
  2. checkns.go (730 bytes)

Comment 1 by iain.lowe:

Adding calls to runtime.LockOSThread have no effect.

Comment 2:

is it possible that this is something to do with Go programs being multithreaded?
Try using the cgo constructor trick to do this.
/*
__attribute__((constructor)) void enter_namespace(void) {
   // set ns here
}
*/
import "C"
this should make sure the C code runs before Go's main, thus
it will enter the namespace before Go runtime starts new threads.

Comment 3 by iain.lowe:

Thanks, that was it.

Comment 4:

Status changed to WontFix.