Skip to content

Commit 32d433c

Browse files
committed
merge #3990 into opencontainers/runc:main
Aleksa Sarai (1): configs: validate: add validation for bind-mount fsflags LGTMs: kolyshkin AkihiroSuda
2 parents a2ba985 + 669f4db commit 32d433c

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

libcontainer/configs/validate/validator.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,26 @@ func cgroupsCheck(config *configs.Config) error {
275275
return nil
276276
}
277277

278+
func checkBindOptions(m *configs.Mount) error {
279+
if !m.IsBind() {
280+
return nil
281+
}
282+
// We must reject bind-mounts that also have filesystem-specific mount
283+
// options, because the kernel will completely ignore these flags and we
284+
// cannot set them per-mountpoint.
285+
//
286+
// It should be noted that (due to how the kernel caches superblocks), data
287+
// options could also silently ignored for other filesystems even when
288+
// doing a fresh mount, but there is no real way to avoid this (and it
289+
// matches how everything else works). There have been proposals to make it
290+
// possible for userspace to detect this caching, but this wouldn't help
291+
// runc because the behaviour wouldn't even be desirable for most users.
292+
if m.Data != "" {
293+
return errors.New("bind mounts cannot have any filesystem-specific options applied")
294+
}
295+
return nil
296+
}
297+
278298
func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
279299
if !m.IsIDMapped() {
280300
return nil
@@ -313,6 +333,9 @@ func mountsWarn(config *configs.Config) error {
313333

314334
func mountsStrict(config *configs.Config) error {
315335
for _, m := range config.Mounts {
336+
if err := checkBindOptions(m); err != nil {
337+
return fmt.Errorf("invalid mount %+v: %w", m, err)
338+
}
316339
if err := checkIDMapMounts(config, m); err != nil {
317340
return fmt.Errorf("invalid mount %+v: %w", m, err)
318341
}

libcontainer/configs/validate/validator_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,49 @@ func TestValidateMounts(t *testing.T) {
421421
}
422422
}
423423

424+
func TestValidateBindMounts(t *testing.T) {
425+
testCases := []struct {
426+
isErr bool
427+
flags int
428+
data string
429+
}{
430+
{isErr: false, flags: 0, data: ""},
431+
{isErr: false, flags: unix.MS_RDONLY | unix.MS_NOSYMFOLLOW, data: ""},
432+
433+
{isErr: true, flags: 0, data: "idmap"},
434+
{isErr: true, flags: unix.MS_RDONLY, data: "custom_ext4_flag"},
435+
{isErr: true, flags: unix.MS_NOATIME, data: "rw=foobar"},
436+
}
437+
438+
for _, tc := range testCases {
439+
for _, bind := range []string{"bind", "rbind"} {
440+
bindFlag := map[string]int{
441+
"bind": unix.MS_BIND,
442+
"rbind": unix.MS_BIND | unix.MS_REC,
443+
}[bind]
444+
445+
config := &configs.Config{
446+
Rootfs: "/var",
447+
Mounts: []*configs.Mount{
448+
{
449+
Destination: "/",
450+
Flags: tc.flags | bindFlag,
451+
Data: tc.data,
452+
},
453+
},
454+
}
455+
456+
err := Validate(config)
457+
if tc.isErr && err == nil {
458+
t.Errorf("%s mount flags:0x%x data:%v, expected error, got nil", bind, tc.flags, tc.data)
459+
}
460+
if !tc.isErr && err != nil {
461+
t.Errorf("%s mount flags:0x%x data:%v, expected nil, got error %v", bind, tc.flags, tc.data, err)
462+
}
463+
}
464+
}
465+
}
466+
424467
func TestValidateIDMapMounts(t *testing.T) {
425468
mapping := []configs.IDMap{
426469
{

0 commit comments

Comments
 (0)