devicemapper: remove container rootfs mountPath after umount
| ... | ... |
@@ -2428,6 +2428,18 @@ func (devices *DeviceSet) UnmountDevice(hash, mountPath string) error {
|
| 2428 | 2428 |
} |
| 2429 | 2429 |
logrus.Debug("devmapper: Unmount done")
|
| 2430 | 2430 |
|
| 2431 |
+ // Remove the mountpoint here. Removing the mountpoint (in newer kernels) |
|
| 2432 |
+ // will cause all other instances of this mount in other mount namespaces |
|
| 2433 |
+ // to be killed (this is an anti-DoS measure that is necessary for things |
|
| 2434 |
+ // like devicemapper). This is necessary to avoid cases where a libdm mount |
|
| 2435 |
+ // that is present in another namespace will cause subsequent RemoveDevice |
|
| 2436 |
+ // operations to fail. We ignore any errors here because this may fail on |
|
| 2437 |
+ // older kernels which don't have |
|
| 2438 |
+ // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. |
|
| 2439 |
+ if err := os.Remove(mountPath); err != nil {
|
|
| 2440 |
+ logrus.Debugf("devmapper: error doing a remove on unmounted device %s: %v", mountPath, err)
|
|
| 2441 |
+ } |
|
| 2442 |
+ |
|
| 2431 | 2443 |
return devices.deactivateDevice(info) |
| 2432 | 2444 |
} |
| 2433 | 2445 |
|
| ... | ... |
@@ -5,12 +5,15 @@ package devmapper |
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"os" |
| 8 |
+ "os/exec" |
|
| 8 | 9 |
"syscall" |
| 9 | 10 |
"testing" |
| 10 | 11 |
"time" |
| 11 | 12 |
|
| 12 | 13 |
"github.com/docker/docker/daemon/graphdriver" |
| 13 | 14 |
"github.com/docker/docker/daemon/graphdriver/graphtest" |
| 15 |
+ "github.com/docker/docker/pkg/parsers/kernel" |
|
| 16 |
+ "golang.org/x/sys/unix" |
|
| 14 | 17 |
) |
| 15 | 18 |
|
| 16 | 19 |
func init() {
|
| ... | ... |
@@ -150,3 +153,53 @@ func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) {
|
| 150 | 150 |
case <-doneChan: |
| 151 | 151 |
} |
| 152 | 152 |
} |
| 153 |
+ |
|
| 154 |
+// Ensure that mounts aren't leakedriver. It's non-trivial for us to test the full |
|
| 155 |
+// reproducer of #34573 in a unit test, but we can at least make sure that a |
|
| 156 |
+// simple command run in a new namespace doesn't break things horribly. |
|
| 157 |
+func TestDevmapperMountLeaks(t *testing.T) {
|
|
| 158 |
+ if !kernel.CheckKernelVersion(3, 18, 0) {
|
|
| 159 |
+ t.Skipf("kernel version <3.18.0 and so is missing torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe.")
|
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ driver := graphtest.GetDriver(t, "devicemapper", "dm.use_deferred_removal=false", "dm.use_deferred_deletion=false").(*graphtest.Driver).Driver.(*graphdriver.NaiveDiffDriver).ProtoDriver.(*Driver) |
|
| 163 |
+ defer graphtest.PutDriver(t) |
|
| 164 |
+ |
|
| 165 |
+ // We need to create a new (dummy) device. |
|
| 166 |
+ if err := driver.Create("some-layer", "", nil); err != nil {
|
|
| 167 |
+ t.Fatalf("setting up some-layer: %v", err)
|
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ // Mount the device. |
|
| 171 |
+ _, err := driver.Get("some-layer", "")
|
|
| 172 |
+ if err != nil {
|
|
| 173 |
+ t.Fatalf("mounting some-layer: %v", err)
|
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ // Create a new subprocess which will inherit our mountpoint, then |
|
| 177 |
+ // intentionally leak it and stick around. We can't do this entirely within |
|
| 178 |
+ // Go because forking and namespaces in Go are really not handled well at |
|
| 179 |
+ // all. |
|
| 180 |
+ cmd := exec.Cmd{
|
|
| 181 |
+ Path: "/bin/sh", |
|
| 182 |
+ Args: []string{
|
|
| 183 |
+ "/bin/sh", "-c", |
|
| 184 |
+ "mount --make-rprivate / && sleep 1000s", |
|
| 185 |
+ }, |
|
| 186 |
+ SysProcAttr: &syscall.SysProcAttr{
|
|
| 187 |
+ Unshareflags: syscall.CLONE_NEWNS, |
|
| 188 |
+ }, |
|
| 189 |
+ } |
|
| 190 |
+ if err := cmd.Start(); err != nil {
|
|
| 191 |
+ t.Fatalf("starting sub-command: %v", err)
|
|
| 192 |
+ } |
|
| 193 |
+ defer func() {
|
|
| 194 |
+ unix.Kill(cmd.Process.Pid, unix.SIGKILL) |
|
| 195 |
+ cmd.Wait() |
|
| 196 |
+ }() |
|
| 197 |
+ |
|
| 198 |
+ // Now try to "drop" the device. |
|
| 199 |
+ if err := driver.Put("some-layer"); err != nil {
|
|
| 200 |
+ t.Fatalf("unmounting some-layer: %v", err)
|
|
| 201 |
+ } |
|
| 202 |
+} |
| ... | ... |
@@ -232,10 +232,12 @@ func (d *Driver) Put(id string) error {
|
| 232 | 232 |
if count := d.ctr.Decrement(mp); count > 0 {
|
| 233 | 233 |
return nil |
| 234 | 234 |
} |
| 235 |
+ |
|
| 235 | 236 |
err := d.DeviceSet.UnmountDevice(id, mp) |
| 236 | 237 |
if err != nil {
|
| 237 |
- logrus.Errorf("devmapper: Error unmounting device %s: %s", id, err)
|
|
| 238 |
+ logrus.Errorf("devmapper: Error unmounting device %s: %v", id, err)
|
|
| 238 | 239 |
} |
| 240 |
+ |
|
| 239 | 241 |
return err |
| 240 | 242 |
} |
| 241 | 243 |
|