containers: detect overlapping mappings

when the container specifies some mappings to be applied, verify that
they are not overlapping and give a clearer error message.

Closes: https://github.com/containers/storage/issues/1127

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2022-02-07 10:47:35 +01:00
parent 7877039211
commit 7e610596cf
No known key found for this signature in database
GPG Key ID: 67E38F7A8BA21772
5 changed files with 87 additions and 0 deletions

View File

@ -324,6 +324,12 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
fmt.Sprintf("the container name \"%s\" is already in use by \"%s\". You have to remove that container to be able to reuse that name.", name, r.byname[name].ID))
}
}
if err := hasOverlappingRanges(options.UIDMap); err != nil {
return nil, err
}
if err := hasOverlappingRanges(options.GIDMap); err != nil {
return nil, err
}
if err == nil {
container = &Container{
ID: id,

View File

@ -55,4 +55,6 @@ var (
ErrStoreIsReadOnly = types.ErrStoreIsReadOnly
// ErrNotSupported is returned when the requested functionality is not supported.
ErrNotSupported = types.ErrNotSupported
// ErrInvalidMappings is returned when the specified mappings are invalid.
ErrInvalidMappings = types.ErrInvalidMappings
)

View File

@ -1,6 +1,9 @@
package storage
import (
"fmt"
"strings"
"github.com/containers/storage/pkg/idtools"
"github.com/google/go-intervals/intervalset"
"github.com/pkg/errors"
@ -218,3 +221,45 @@ func maxInt(a, b int) int {
}
return a
}
func hasOverlappingRanges(mappings []idtools.IDMap) error {
hostIntervals := intervalset.Empty()
containerIntervals := intervalset.Empty()
var conflicts []string
for _, m := range mappings {
c := interval{start: m.ContainerID, end: m.ContainerID + m.Size}
h := interval{start: m.HostID, end: m.HostID + m.Size}
added := false
overlaps := false
containerIntervals.IntervalsBetween(c, func(x intervalset.Interval) bool {
overlaps = true
return false
})
if overlaps {
conflicts = append(conflicts, fmt.Sprintf("%v:%v:%v", m.ContainerID, m.HostID, m.Size))
added = true
}
containerIntervals.Add(intervalset.NewSet([]intervalset.Interval{c}))
hostIntervals.IntervalsBetween(h, func(x intervalset.Interval) bool {
overlaps = true
return false
})
if overlaps && !added {
conflicts = append(conflicts, fmt.Sprintf("%v:%v:%v", m.ContainerID, m.HostID, m.Size))
}
hostIntervals.Add(intervalset.NewSet([]intervalset.Interval{h}))
}
if conflicts != nil {
if len(conflicts) == 1 {
return errors.Wrapf(ErrInvalidMappings, "the specified UID and/or GID mapping %s conflicts with other mappings", conflicts[0])
}
return errors.Wrapf(ErrInvalidMappings, "the specified UID and/or GID mappings %s conflict with other mappings", strings.Join(conflicts, ", "))
}
return nil
}

View File

@ -922,3 +922,35 @@ func TestIntervalEncompass(t *testing.T) {
})
}
}
func TestOverlappingMappings(t *testing.T) {
mappings := []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}, {ContainerID: 0, HostID: 1000, Size: 65536}}
if err := hasOverlappingRanges(mappings); err == nil {
t.Errorf("mappings = %v, expected to be overlapping", mappings)
}
mappings = []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}, {ContainerID: 65536, HostID: 5000, Size: 65536}}
if err := hasOverlappingRanges(mappings); err == nil {
t.Errorf("mappings = %v, expected to be overlapping", mappings)
}
mappings = []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}, {ContainerID: 0, HostID: 5000 + 65536, Size: 65536}}
if err := hasOverlappingRanges(mappings); err == nil {
t.Errorf("mappings = %v, expected to be overlapping", mappings)
}
mappings = []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}, {ContainerID: 0, HostID: 1000 + 65536, Size: 65536}}
if err := hasOverlappingRanges(mappings); err == nil {
t.Errorf("mappings = %v, expected to be overlapping", mappings)
}
mappings = []idtools.IDMap{{ContainerID: 0, HostID: 0, Size: 65536}, {ContainerID: 65536, HostID: 65536, Size: 65536}}
if err := hasOverlappingRanges(mappings); err != nil {
t.Errorf("mappings = %v, expected to not be overlapping", mappings)
}
mappings = []idtools.IDMap{{ContainerID: 0, HostID: 0, Size: 65536}, {ContainerID: 1 * 65536, HostID: 1 * 65536, Size: 65536}, {ContainerID: 2 * 65536, HostID: 2 * 65536, Size: 65536}}
if err := hasOverlappingRanges(mappings); err != nil {
t.Errorf("mappings = %v, expected to not be overlapping", mappings)
}
}

View File

@ -55,4 +55,6 @@ var (
ErrStoreIsReadOnly = errors.New("called a write method on a read-only store")
// ErrNotSupported is returned when the requested functionality is not supported.
ErrNotSupported = errors.New("not supported")
// ErrInvalidMappings is returned when the specified mappings are invalid.
ErrInvalidMappings = errors.New("invalid mappings specified")
)