‘unshare’ funktioniert in C api nicht wie erwartet

Lesezeit: 8 Minuten

Benutzer-Avatar
ocirocir

Diese Befehlsfolge funktioniert:

unshare --fork --pid --mount 
umount /proc
mount -t proc proc /proc
umount /dev/pts
mount -t devpts devpts /dev/pts

Das entsprechende C-Programm funktioniert jedoch nicht wie erwartet (es scheint, dass es das vorherige /proc nicht unmountet, und es bietet auch EBUSY, das versucht, die devpts zu unmounten):

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("My pid: %i\n", getpid()); // It prints 1 as expected

umount("/proc"); // Returns 0

system("mount"); // Should print error on mtab, but it prints the previous mounted filesystems

mount("proc", "/proc", "proc",
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL));  // Returns 0

umount("/dev/pts");  // Returns -1 errno = 0 (??)

mount("devpts", "/dev/pts", "devpts", 
      MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_NODEV,
      NULL) ); // Returns -1 errno = EBUSY

Ich habe hier auf eine Fehlerprüfung auf Lesbarkeit verzichtet

Ich denke, dass Unshare oder Unmount nicht wie erwartet funktioniert: Selbst wenn es Null zurückgibt, scheint es, dass /proc nicht unmountet wird (wenn ich versuche, a system("mount") danach druckt es die gemounteten Dateisysteme).

  • Verwenden Sie perror anstelle von printf – es gibt Informationen über ERRNO

    – Jassen

    19. Januar 2016 um 9:14 Uhr

  • Ok, jedenfalls denke ich, dass das EBUSY an ist mount devpts wird durch den “stillen Ausfall” von verursacht umount/mount proz

    – ocirocir

    19. Januar 2016 um 9:17 Uhr

  • Dein umounts scheitern bei mir, also habe ich sie durch ersetzt umount2("<mountpoint>", MNT_DETACH). Dies hat das Problem jedoch nicht ganz behoben: es hat /proc und /dev/pts global ausgehängt (und wieder eingehängt)! Was zum Teufel?

    – Benutzer3035772

    21. Januar 2016 um 11:13 Uhr

  • Warum sollte Mount Ihrer Meinung nach einen Fehler auf mtab drucken?

    – nsilent22

    23. Januar 2016 um 15:36 Uhr

  • Weil Sie /proc umounten, und /etc/mtab/ ist ein Link zu /proc/self/mounts

    – ocirocir

    23. Januar 2016 um 16:23 Uhr

Benutzer-Avatar
John Bollinger

Trotz Ihres Kommentars

“manchmal” umount gibt 0 “manchmal” -1 zurück, aber am Ende wird es nicht ausgehängt /proc überhaupt

in 10000 Versuchen des Codes aus Ihrem Pastebin, umount() stets für mich gescheitert, Rückkehr -1 und nicht aushängen /proc. Ich bin nicht geneigt, das zu glauben umount() jemals zurückkehrt 0 obwohl das angeforderte Unmounten nicht durchgeführt wurde, aber wenn dies jemals der Fall ist, würde dies einen Fehler darstellen umount(). Wenn Sie einen solchen Fehler tatsächlich belegen können, wäre die Antwort der Community, einen Fehlerbericht gegen glibc einzureichen.


Die Frage wird dann, warum und wie Ihr bash Skript verhält sich anders. Tatsächlich scheint es aber nicht zu gehen.

Erstens haben Sie die falsche Erwartung an die unshare(1) Befehl. im Gegensatz zu den unshare(2) Funktion, die unshare Der Befehl wirkt sich nicht auf die Shell aus, in der er ausgeführt wird. Stattdessen startet es ein separater Prozess das über seine eigenen privaten Kopien der angegebenen Namespaces verfügt. Normalerweise würden Sie den Befehl angeben, um diesen Prozess auf dem zu starten unshare Befehlszeile, und tatsächlich weist die Handbuchseite des Programms darauf hin, dass dies obligatorisch ist.

Empirisch finde ich, dass, wenn ich einen solchen Befehl nicht angebe – wie Sie es tun – dann unshare startet eine neue Shell als Zielprozess. Insbesondere wenn ich Ihr Skript ausführe (mit ausreichenden Rechten zur Verwendung unshare), erhalte ich sofort einen neuen Prompt, aber es ist der Prompt der neuen Shell, der im Vordergrund läuft. Dies ist mir sofort klar, weil die Eingabeaufforderung anders ist (Ihre Eingabeaufforderung könnte unter diesen Umständen jedoch nicht anders sein). Es kommt keine Fehlermeldung etc. aus umount damals weil es ist noch nicht gelaufen. Wenn ich es manuell versuche umount proc im (unshared) Subshell, es schlägt mit “Gerät ist beschäftigt” fehl – das ist das Analogon zu dem, was Ihr C-Programm versucht.

Wenn ich die Subshell verlasse, wird der Rest des Skripts mit beiden ausgeführt umounts und beides mountscheitert. Das ist zu erwarten, da das Hauptskript seinen Mount-Namensraum teilt.


Das ist durchaus plausibel /proc ist wirklich ausgelastet und kann daher nicht ausgehängt werden, selbst für einen Prozess, der eine private Kopie des Mount-Namensraums hat. Es ist wahrscheinlich, dass ein solcher Prozess selbst seine private Kopie des Mounts von verwendet /proc. Dagegen finde ich, dass ich kann erfolgreich aushängen /dev/pts in einem Prozess mit einem nicht gemeinsam genutzten Mount-Namespace, aber nicht in einem Prozess, der die Systemkopie dieses Namespace gemeinsam nutzt.

  • Ich stimme Ihrer Analyse zu, aber in meinem System kann ich die Bereitstellung aufheben /proc und ich verwende kein Skript, um diese Befehle auszuführen: imagebin.ca/v/2Uw7vXG71WhQ

    – ocirocir

    27. Januar 2016 um 20:17 Uhr

  • Ich habe jedoch gerade gesehen, dass das erneute Einhängen von /dev/pts alle Systeme betrifft und nicht nur die private Kopie, ich weiß nicht warum.

    – ocirocir

    27. Januar 2016 um 20:20 Uhr

  • @FedericoReghenzani, wie ich in meiner Antwort geschrieben habe, wenn ich es versuche umount /proc von innen ein unshared-Subshell schlägt sie fehl, genau wie der Versuch, dasselbe vom C-Programm aus zu tun. Dies hängt nicht davon ab, ob die Befehle als Skript ausgeführt werden. Das Verhalten ist für mich konsistent, und wenn es für Sie wirklich inkonsistent ist, würde ich meinen, dass Sie einen Fehler in Ihrer Distribution haben, den ich in meiner nicht habe.

    – Johannes Bollinger

    27. Januar 2016 um 22:38 Uhr


  • @FedericoReghenzani, auch, unmounten /dev/pts innerhalb eines unshared Subshell scheint für mich genau so zu funktionieren, wie ich es erwarten würde. Das Dateisystem wird in der Subshell erfolgreich ausgehängt – die Auflistung des Einhängepunktverzeichnisses zeigt keinen Inhalt – aber es bleibt eingehängt, soweit alle anderen Prozesse betroffen sind.

    – Johannes Bollinger

    27. Januar 2016 um 22:41 Uhr


  • Ok, ich habe es in einem anderen System versucht. Versuchen Sie (vor dem Unshare) umount (mit -lf) und remount /proc. Jetzt sollte es möglich sein, /proc in einer nicht freigegebenen Umgebung zu umounten. Es scheint jedoch, dass umount /proc im nicht freigegebenen Namespace das gesamte System betrifft.

    – ocirocir

    28. Januar 2016 um 5:35 Uhr

Ich habe das Problem beim Überprüfen des Quellcodes des Befehls zum Aufheben der Freigabe gefunden. Das /proc muss mit demontiert werden MS_PRIVATE | MS_REC und ohne sie gemountet werden, dient dies im Wesentlichen dazu, sicherzustellen, dass der Mount nur im aktuellen (dem neuen) Namensraum wirksam ist. Das zweite Problem ist, dass ein umount nicht möglich ist /dev/pts ohne Auswirkungen auf den globalen Namensraum (dies wird durch die interne Routine des devpts-Treibers verursacht). Um ein privates /dev/pts zu haben, besteht die einzige Möglichkeit darin, es mit dem dedizierten zu mounten -o newinstance Möglichkeit. Endlich /dev/ptmx sollte auch bind-remountet werden.

Daher ist dies wie erwartet der C-Arbeitscode:

unshare(CLONE_NEWPID | CLONE_NEWNS );
int pid = fork();
if (pid != 0) {
    int status;
    waitpid(-1, &status, 0);
    return status;
}

printf("New PID after unshare is %i", getpid());

if (mount("none", "/proc", NULL, MS_PRIVATE|MS_REC, NULL)) {
    printf("Cannot umount proc! errno=%i", errno);
    exit(1);
}

if (mount("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL)) {
    printf("Cannot mount proc! errno=%i", errno);
    exit(1);
}


if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL | MS_NOSUID | MS_NOEXEC, "newinstance") ) {
    printf("Cannot mount pts! errno=%i", errno);
    exit(1);
}

if (mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_MGC_VAL | MS_NOSUID | MS_NOEXEC | MS_BIND, NULL) ) {
    printf("Cannot mount ptmx! errno=%i", errno);
    exit(1);
}

Ich denke, das Problem liegt beim System (“mount”), das eine Shell erzeugt und das umount nicht überträgt. Versuchen Sie, nach dem umount eine Datei in /proc/ zu öffnen, und sehen Sie, dass sie wie erwartet funktioniert.

Sieh dir das an –

unshare(CLONE_NEWPID | CLONE_NEWNS );
int rc = 0;
int pid = fork();
if (pid != 0) {
        int status;
        waitpid(-1, &status, 0);
        return status;
}

printf(">>> My pid: %d\n", getpid()); // It prints 1 as expected
rc = umount2("/proc", MNT_FORCE); // Returns 0
printf(">>> umount returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

rc = open("/proc/cpuinfo", O_RDONLY);
printf(">>> open returned %d. errno = %d, desc = (%s)\n", rc, errno, strerror(errno));

Benutzer-Avatar
Maquefel

unshare bash != unshare c

unshare – führt ein Programm mit einigen Namensräumen aus, die von den Eltern nicht geteilt wurden

Also im Grunde mit –fork forken Sie von /bin/sh von /bin/bash (was auch immer Sie Ihr Skript ausführen) mit den Optionen –pid und –mount. „fork“ gefolgt von „unshare“

unshare – trennt Teile des Prozessausführungskontextes (aktueller Prozess) Sie trennen die Freigabe von init und forken dann.

CLONE_NEWPID ist “Klonen”-Flag, nicht “Teilen aufheben”

Je nachdem, was Sie erreichen möchten, nehme ich an, dass Sie versuchen, “/proc” und “/dev/pts” exklusiv für untergeordnete Prozesse zu machen.

Hier ist ein kleines Beispiel mit mount –bind local folders:

# mkdir mnt point
# touch point/point.txt
# mount --bind point mnt
# ls mnt
point.txt

# ./unshare
My pid: 28377
Child:
point.txt
Parent:

# ls mnt

Code:

#define _GNU_SOURCE
#include <sched.h>

int main(int argc, char *argv[])
{
        /** umount global */
        system("umount mnt/");
        int pid = fork();
        if (pid != 0) {
                int status;
                waitpid(-1, &status, 0);
                printf("Parent:\n");
                /* and here we don't */
                system("ls mnt/");
                return status;
        }
        /* unshare */
        unshare(CLONE_FS | CLONE_NEWNS);
        printf("My pid: %i\n", getpid()); // It prints 1 as expected
        /* mount exclusively */
        system("mount --bind point/ mnt/");
        printf("Child:\n");
        /* here we see it */
        system("ls mnt/"); 

        return 0;
}

Es gibt auch ein schönes Beispiel mit bash:
http://karelzak.blogspot.ru/2009/12/unshare1.html

Fortsetzung:

mount hängt von /etc/mtab ab, was nicht immer ein symbolischer Link zu /proc/mounts ist

Überprüfen Sie also /etc/mtab mit ls -la.

Überprüfen Sie auch den Code für umount auf /dev/pts mit:

int ret = umount("/dev/pts");
int errsv = errno;
if(ret == -1) {
  printf("Error on umount: %s\n", strerror(errsv));
}

Ich bin mir ziemlich sicher, dass es verwendet wird – überprüfen Sie es mit fuser /dev/pts/

** BEARBEITET **

Schließlich – ich bin mir nicht sicher, ob Sie procfs nur im Namespace umounten können (ich denke, es ist unmöglich)

Sie können jedoch Ihre eigene Kopie von procfs in Ihrem Namespace mounten:

# mount -t proc proc /proc/

Jetzt ist nur noch Ihr Prozess über ps -e sichtbar.

1228360cookie-check‘unshare’ funktioniert in C api nicht wie erwartet

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy