Der Aufruf von sets aus Go gibt EINVAL für den mnt-Namespace zurück

8

Der C-Code funktioniert einwandfrei und tritt korrekt in den Namespace ein, aber der Go-Code scheint EINVAL immer vom Aufruf setns zurückzugeben, um den Namespace mnt einzugeben. Ich habe eine Reihe von Permutationen (einschließlich eingebetteten C-Code mit cgo und externen .so ) auf Go 1.2 , 1.3 und den aktuellen Tipp versucht.

Beim Durchlaufen des Codes in gdb wird angezeigt, dass beide Sequenzen setns in libc genauso aufrufen (oder so erscheint es mir).

Ich habe gekocht, was scheint das Problem bis auf den Code unten. Was mache ich falsch?

Einrichtung

Ich habe einen Shell-Alias ​​zum Starten von schnellen Busybox-Containern:

%Vor%

Nach dem Ausführen startet startbb einen Container und gibt seine PID aus.

lxc-checkconfig outputs:

%Vor%

uname -a erzeugt:

%Vor%

Arbeitender C-Code

Der folgende C-Code funktioniert einwandfrei:

%Vor%

Nach dem Kompilieren mit gcc -o checkns checkns.c lautet die Ausgabe von sudo ./checkns <PID> :

%Vor%

Fehlgeschlagener Go-Code

Umgekehrt funktioniert der folgende Go-Code (der identisch sein sollte) nicht ganz so gut:

%Vor%

Stattdessen führt sudo go run main.go <PID> Folgendes aus:

%Vor%     
ilowe 06.09.2014, 20:56
quelle

1 Antwort

5

(Es gibt ein Problem, das beim Go-Projekt eingereicht wurde )

Die Antwort auf diese Frage lautet also, dass Sie setns aus einem single-threaded Kontext aufrufen müssen. Dies ist sinnvoll, da setns den aktuellen Thread dem Namespace zuordnen soll. Da Go multi-threaded ist, müssen Sie den Aufruf setns vornehmen, bevor die Go-Laufzeitthreads starten.

Ich denke das liegt daran, dass der Thread, in dem der Aufruf von syscall.RawSyscall ausgeführt wird, nicht der Hauptthread ist - selbst mit runtime.LockOSThread das Ergebnis ist nicht was Sie würden erwarten (dh, dass die Goroutine zum Haupt-C-Thread "gesperrt" ist und daher äquivalent zu dem Konstruktortrick ist, der unten erläutert wird).

Die Antwort, die ich nach der Einreichung des Problems erhielt, schlug vor, den " cgo Konstruktortrick" zu verwenden. Ich konnte keine "richtige" Dokumentation zu diesem "Trick" finden, aber es wird in nsinit von Docker / Michael Crosby verwendet und obwohl ich diesen Code Zeile für Zeile durchgegangen bin, habe ich nicht versucht, ihn auf diese Weise auszuführen (siehe unten für Frustration).

Der "Trick" ist im Grunde, dass Sie cgo erhalten können, um eine C-Funktion auszuführen, bevor Sie die Go-Laufzeit starten.

Fügen Sie dazu das Makro __attribute__((constructor)) hinzu, um die Funktion zu dekorieren, die vor dem Start von Go ausgeführt werden soll:

%Vor%

Als Template habe ich checkns.go folgendermaßen geändert:

%Vor%

Dieser Code funktioniert, erfordert aber, dass PID fest codiert ist, da er nicht korrekt von der Kommandozeileneingabe gelesen wird, aber er veranschaulicht die Idee (und funktioniert, wenn Sie ein PID von einem wie oben beschrieben gestarteten Container bereitstellen) .

Es ist frustrierend, weil ich setns mehrmals aufrufen wollte, aber da dieser C-Code vor dem Start der Go-Laufzeit ausgeführt wird, ist kein Go-Code verfügbar.

Update: In den Kernel-Mailinglisten herumzublättern bietet dieser Link zu einer Konversation, die dies dokumentiert. Ich kann es nicht in wirklich veröffentlichten Manpages finden, aber hier ist das Zitat von einem Patch zu setns(2) , bestätigt von Eric Biederman:

  

Ein Prozess kann möglicherweise nicht mit einem neuen Mount-Namespace reassoziiert werden   es ist multi-threaded. Das Ändern des Mount-Namespace erfordert   dass der Aufrufer sowohl CAP_SYS_CHROOT als auch CAP_SYS_ADMIN besitzt   Funktionen in einem eigenen Benutzernamespace und CAP_SYS_ADMIN in der   Ziel-Mount-Namespace.

    
ilowe 07.09.2014 04:12
quelle

Tags und Links