Browse code

Merge pull request #34573 from cyphar/dm-dos-prevention-remove-mountpoint

devicemapper: remove container rootfs mountPath after umount

Vincent Demeester authored on 2017/11/09 01:08:07
Showing 3 changed files
... ...
@@ -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