diff options
author | Rahat Mahmood <rahat@google.com> | 2018-12-12 13:09:10 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-12-12 13:09:59 -0800 |
commit | 75e39eaa74c65b6f7cfb95addb6ac0cbcc7d951a (patch) | |
tree | 1a6b64491851e002727a08546f1168c89b9c32aa /pkg | |
parent | a2c868a098fcb51dcdf629045c5f5c0f68c2766f (diff) |
Pass information about map writableness to filesystems.
This is necessary to implement file seals for memfds.
PiperOrigin-RevId: 225239394
Change-Id: Ib3f1ab31385afc4b24e96cd81a05ef1bebbcbb70
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/sentry/fs/binder/binder.go | 6 | ||||
-rw-r--r-- | pkg/sentry/fs/copy_up.go | 8 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/inode_cached.go | 12 | ||||
-rw-r--r-- | pkg/sentry/fs/fsutil/inode_cached_test.go | 8 | ||||
-rw-r--r-- | pkg/sentry/fs/overlay.go | 18 | ||||
-rw-r--r-- | pkg/sentry/fs/tmpfs/inode_file.go | 12 | ||||
-rw-r--r-- | pkg/sentry/kernel/shm/shm.go | 6 | ||||
-rw-r--r-- | pkg/sentry/memmap/mapping_set.go | 18 | ||||
-rw-r--r-- | pkg/sentry/memmap/mapping_set_test.go | 102 | ||||
-rw-r--r-- | pkg/sentry/memmap/memmap.go | 17 | ||||
-rw-r--r-- | pkg/sentry/mm/aio_context.go | 6 | ||||
-rw-r--r-- | pkg/sentry/mm/lifecycle.go | 2 | ||||
-rw-r--r-- | pkg/sentry/mm/mm.go | 4 | ||||
-rw-r--r-- | pkg/sentry/mm/special_mappable.go | 6 | ||||
-rw-r--r-- | pkg/sentry/mm/syscalls.go | 4 | ||||
-rw-r--r-- | pkg/sentry/mm/vma.go | 4 |
16 files changed, 162 insertions, 71 deletions
diff --git a/pkg/sentry/fs/binder/binder.go b/pkg/sentry/fs/binder/binder.go index 42b9e8b26..e642c7f22 100644 --- a/pkg/sentry/fs/binder/binder.go +++ b/pkg/sentry/fs/binder/binder.go @@ -302,7 +302,7 @@ func (bp *Proc) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArgum } // AddMapping implements memmap.Mappable.AddMapping. -func (bp *Proc) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (bp *Proc) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, _ bool) error { bp.mu.Lock() defer bp.mu.Unlock() if bp.mapped.Length() != 0 { @@ -320,12 +320,12 @@ func (bp *Proc) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar userm } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (bp *Proc) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (*Proc) RemoveMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, uint64, bool) { // Nothing to do. Notably, we don't free bp.mapped to allow another mmap. } // CopyMapping implements memmap.Mappable.CopyMapping. -func (bp *Proc) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { +func (bp *Proc) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, _ bool) error { // Nothing to do. Notably, this is one case where CopyMapping isn't // equivalent to AddMapping, as AddMapping would return EBUSY. return nil diff --git a/pkg/sentry/fs/copy_up.go b/pkg/sentry/fs/copy_up.go index d65dc74bf..6d4ebaaa4 100644 --- a/pkg/sentry/fs/copy_up.go +++ b/pkg/sentry/fs/copy_up.go @@ -270,13 +270,13 @@ func copyUpLocked(ctx context.Context, parent *Dirent, next *Dirent) error { for seg := next.Inode.overlay.mappings.FirstSegment(); seg.Ok(); seg = seg.NextSegment() { added := make(memmap.MappingsOfRange) for m := range seg.Value() { - if err := upperMappable.AddMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start()); err != nil { + if err := upperMappable.AddMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start(), m.Writable); err != nil { for m := range added { - upperMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start()) + upperMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start(), m.Writable) } for mr, mappings := range allAdded { for m := range mappings { - upperMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, mr.Start) + upperMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, mr.Start, m.Writable) } } return err @@ -301,7 +301,7 @@ func copyUpLocked(ctx context.Context, parent *Dirent, next *Dirent) error { if lowerMappable != nil { for seg := next.Inode.overlay.mappings.FirstSegment(); seg.Ok(); seg = seg.NextSegment() { for m := range seg.Value() { - lowerMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start()) + lowerMappable.RemoveMapping(ctx, m.MappingSpace, m.AddrRange, seg.Start(), m.Writable) } } } diff --git a/pkg/sentry/fs/fsutil/inode_cached.go b/pkg/sentry/fs/fsutil/inode_cached.go index b0af44ddd..707ca76d2 100644 --- a/pkg/sentry/fs/fsutil/inode_cached.go +++ b/pkg/sentry/fs/fsutil/inode_cached.go @@ -686,10 +686,10 @@ func (rw *inodeReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error } // AddMapping implements memmap.Mappable.AddMapping. -func (c *CachingInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (c *CachingInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error { // Hot path. Avoid defers. c.mapsMu.Lock() - mapped := c.mappings.AddMapping(ms, ar, offset) + mapped := c.mappings.AddMapping(ms, ar, offset, writable) // Do this unconditionally since whether we have c.backingFile.FD() >= 0 // can change across save/restore. for _, r := range mapped { @@ -705,10 +705,10 @@ func (c *CachingInodeOperations) AddMapping(ctx context.Context, ms memmap.Mappi } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) { // Hot path. Avoid defers. c.mapsMu.Lock() - unmapped := c.mappings.RemoveMapping(ms, ar, offset) + unmapped := c.mappings.RemoveMapping(ms, ar, offset, writable) for _, r := range unmapped { c.hostFileMapper.DecRefOn(r) } @@ -739,8 +739,8 @@ func (c *CachingInodeOperations) RemoveMapping(ctx context.Context, ms memmap.Ma } // CopyMapping implements memmap.Mappable.CopyMapping. -func (c *CachingInodeOperations) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { - return c.AddMapping(ctx, ms, dstAR, offset) +func (c *CachingInodeOperations) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error { + return c.AddMapping(ctx, ms, dstAR, offset, writable) } // Translate implements memmap.Mappable.Translate. diff --git a/pkg/sentry/fs/fsutil/inode_cached_test.go b/pkg/sentry/fs/fsutil/inode_cached_test.go index e388ec3d7..ce5201a40 100644 --- a/pkg/sentry/fs/fsutil/inode_cached_test.go +++ b/pkg/sentry/fs/fsutil/inode_cached_test.go @@ -305,7 +305,7 @@ func TestRead(t *testing.T) { // be cached. var ms noopMappingSpace ar := usermem.AddrRange{usermem.PageSize, 2 * usermem.PageSize} - if err := iops.AddMapping(ctx, ms, ar, usermem.PageSize); err != nil { + if err := iops.AddMapping(ctx, ms, ar, usermem.PageSize, true); err != nil { t.Fatalf("AddMapping got %v, want nil", err) } mr := memmap.MappableRange{usermem.PageSize, 2 * usermem.PageSize} @@ -334,7 +334,7 @@ func TestRead(t *testing.T) { // Delete the memory mapping and expect it to cause the cached page to be // uncached. - iops.RemoveMapping(ctx, ms, ar, usermem.PageSize) + iops.RemoveMapping(ctx, ms, ar, usermem.PageSize, true) if cached := iops.cache.Span(); cached != 0 { t.Fatalf("Span got %d, want 0", cached) } @@ -363,10 +363,10 @@ func TestWrite(t *testing.T) { // Translate to force them to be cached. var ms noopMappingSpace ar := usermem.AddrRange{usermem.PageSize, 3 * usermem.PageSize} - if err := iops.AddMapping(ctx, ms, ar, usermem.PageSize); err != nil { + if err := iops.AddMapping(ctx, ms, ar, usermem.PageSize, true); err != nil { t.Fatalf("AddMapping got %v, want nil", err) } - defer iops.RemoveMapping(ctx, ms, ar, usermem.PageSize) + defer iops.RemoveMapping(ctx, ms, ar, usermem.PageSize, true) mr := memmap.MappableRange{usermem.PageSize, 3 * usermem.PageSize} if _, err := iops.Translate(ctx, mr, mr, usermem.Read); err != nil { t.Fatalf("Translate got %v, want nil", err) diff --git a/pkg/sentry/fs/overlay.go b/pkg/sentry/fs/overlay.go index 8ace4ee64..f3e2d5cbe 100644 --- a/pkg/sentry/fs/overlay.go +++ b/pkg/sentry/fs/overlay.go @@ -259,32 +259,32 @@ func (o *overlayEntry) isMappableLocked() bool { } // AddMapping implements memmap.Mappable.AddMapping. -func (o *overlayEntry) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (o *overlayEntry) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error { o.mapsMu.Lock() defer o.mapsMu.Unlock() - if err := o.inodeLocked().Mappable().AddMapping(ctx, ms, ar, offset); err != nil { + if err := o.inodeLocked().Mappable().AddMapping(ctx, ms, ar, offset, writable); err != nil { return err } - o.mappings.AddMapping(ms, ar, offset) + o.mappings.AddMapping(ms, ar, offset, writable) return nil } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (o *overlayEntry) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (o *overlayEntry) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) { o.mapsMu.Lock() defer o.mapsMu.Unlock() - o.inodeLocked().Mappable().RemoveMapping(ctx, ms, ar, offset) - o.mappings.RemoveMapping(ms, ar, offset) + o.inodeLocked().Mappable().RemoveMapping(ctx, ms, ar, offset, writable) + o.mappings.RemoveMapping(ms, ar, offset, writable) } // CopyMapping implements memmap.Mappable.CopyMapping. -func (o *overlayEntry) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { +func (o *overlayEntry) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error { o.mapsMu.Lock() defer o.mapsMu.Unlock() - if err := o.inodeLocked().Mappable().CopyMapping(ctx, ms, srcAR, dstAR, offset); err != nil { + if err := o.inodeLocked().Mappable().CopyMapping(ctx, ms, srcAR, dstAR, offset, writable); err != nil { return err } - o.mappings.AddMapping(ms, dstAR, offset) + o.mappings.AddMapping(ms, dstAR, offset, writable) return nil } diff --git a/pkg/sentry/fs/tmpfs/inode_file.go b/pkg/sentry/fs/tmpfs/inode_file.go index 42a7d7b9c..e0181c52c 100644 --- a/pkg/sentry/fs/tmpfs/inode_file.go +++ b/pkg/sentry/fs/tmpfs/inode_file.go @@ -426,23 +426,23 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) } // AddMapping implements memmap.Mappable.AddMapping. -func (f *fileInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (f *fileInodeOperations) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error { f.mapsMu.Lock() defer f.mapsMu.Unlock() - f.mappings.AddMapping(ms, ar, offset) + f.mappings.AddMapping(ms, ar, offset, writable) return nil } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (f *fileInodeOperations) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (f *fileInodeOperations) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) { f.mapsMu.Lock() defer f.mapsMu.Unlock() - f.mappings.RemoveMapping(ms, ar, offset) + f.mappings.RemoveMapping(ms, ar, offset, writable) } // CopyMapping implements memmap.Mappable.CopyMapping. -func (f *fileInodeOperations) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { - return f.AddMapping(ctx, ms, dstAR, offset) +func (f *fileInodeOperations) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error { + return f.AddMapping(ctx, ms, dstAR, offset, writable) } // Translate implements memmap.Mappable.Translate. diff --git a/pkg/sentry/kernel/shm/shm.go b/pkg/sentry/kernel/shm/shm.go index 4343dee13..2f400cbba 100644 --- a/pkg/sentry/kernel/shm/shm.go +++ b/pkg/sentry/kernel/shm/shm.go @@ -375,7 +375,7 @@ func (s *Shm) Msync(context.Context, memmap.MappableRange) error { } // AddMapping implements memmap.Mappable.AddMapping. -func (s *Shm) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (s *Shm) AddMapping(ctx context.Context, _ memmap.MappingSpace, _ usermem.AddrRange, _ uint64, _ bool) error { s.mu.Lock() defer s.mu.Unlock() s.attachTime = ktime.NowFromContext(ctx) @@ -390,7 +390,7 @@ func (s *Shm) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (s *Shm) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (s *Shm) RemoveMapping(ctx context.Context, _ memmap.MappingSpace, _ usermem.AddrRange, _ uint64, _ bool) { s.mu.Lock() defer s.mu.Unlock() // TODO: RemoveMapping may be called during task exit, when ctx @@ -411,7 +411,7 @@ func (s *Shm) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar user } // CopyMapping implements memmap.Mappable.CopyMapping. -func (s *Shm) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { +func (*Shm) CopyMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, usermem.AddrRange, uint64, bool) error { return nil } diff --git a/pkg/sentry/memmap/mapping_set.go b/pkg/sentry/memmap/mapping_set.go index 33cf16f91..bd07e9aac 100644 --- a/pkg/sentry/memmap/mapping_set.go +++ b/pkg/sentry/memmap/mapping_set.go @@ -40,6 +40,7 @@ type MappingsOfRange map[MappingOfRange]struct{} type MappingOfRange struct { MappingSpace MappingSpace AddrRange usermem.AddrRange + Writable bool } func (r MappingOfRange) invalidate(opts InvalidateOpts) { @@ -92,6 +93,7 @@ func (mappingSetFunctions) Merge(r1 MappableRange, val1 MappingsOfRange, r2 Mapp Start: k1.AddrRange.End, End: k1.AddrRange.End + usermem.Addr(r2.Length()), }, + Writable: k1.Writable, } if _, ok := val2[k2]; !ok { return nil, false @@ -104,6 +106,7 @@ func (mappingSetFunctions) Merge(r1 MappableRange, val1 MappingsOfRange, r2 Mapp Start: k1.AddrRange.Start, End: k2.AddrRange.End, }, + Writable: k1.Writable, }] = struct{}{} } @@ -129,6 +132,7 @@ func (mappingSetFunctions) Split(r MappableRange, val MappingsOfRange, split uin Start: k.AddrRange.Start, End: k.AddrRange.Start + offset, }, + Writable: k.Writable, } m1[k1] = struct{}{} @@ -138,6 +142,7 @@ func (mappingSetFunctions) Split(r MappableRange, val MappingsOfRange, split uin Start: k.AddrRange.Start + offset, End: k.AddrRange.End, }, + Writable: k.Writable, } m2[k2] = struct{}{} } @@ -152,7 +157,7 @@ func (mappingSetFunctions) Split(r MappableRange, val MappingsOfRange, split uin // indicating that ms maps addresses [0x4000, 0x6000) to MappableRange [0x0, // 0x2000). Then for subsetRange = [0x1000, 0x2000), subsetMapping returns a // MappingOfRange for which AddrRange = [0x5000, 0x6000). -func subsetMapping(wholeRange, subsetRange MappableRange, ms MappingSpace, addr usermem.Addr) MappingOfRange { +func subsetMapping(wholeRange, subsetRange MappableRange, ms MappingSpace, addr usermem.Addr, writable bool) MappingOfRange { if !wholeRange.IsSupersetOf(subsetRange) { panic(fmt.Sprintf("%v is not a superset of %v", wholeRange, subsetRange)) } @@ -165,6 +170,7 @@ func subsetMapping(wholeRange, subsetRange MappableRange, ms MappingSpace, addr Start: start, End: start + usermem.Addr(subsetRange.Length()), }, + Writable: writable, } } @@ -172,7 +178,7 @@ func subsetMapping(wholeRange, subsetRange MappableRange, ms MappingSpace, addr // previously had no mappings. // // Preconditions: As for Mappable.AddMapping. -func (s *MappingSet) AddMapping(ms MappingSpace, ar usermem.AddrRange, offset uint64) []MappableRange { +func (s *MappingSet) AddMapping(ms MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) []MappableRange { mr := MappableRange{offset, offset + uint64(ar.Length())} var mapped []MappableRange seg, gap := s.Find(mr.Start) @@ -180,7 +186,7 @@ func (s *MappingSet) AddMapping(ms MappingSpace, ar usermem.AddrRange, offset ui switch { case seg.Ok() && seg.Start() < mr.End: seg = s.Isolate(seg, mr) - seg.Value()[subsetMapping(mr, seg.Range(), ms, ar.Start)] = struct{}{} + seg.Value()[subsetMapping(mr, seg.Range(), ms, ar.Start, writable)] = struct{}{} seg, gap = seg.NextNonEmpty() case gap.Ok() && gap.Start() < mr.End: @@ -199,7 +205,7 @@ func (s *MappingSet) AddMapping(ms MappingSpace, ar usermem.AddrRange, offset ui // MappableRanges that now have no mappings. // // Preconditions: As for Mappable.RemoveMapping. -func (s *MappingSet) RemoveMapping(ms MappingSpace, ar usermem.AddrRange, offset uint64) []MappableRange { +func (s *MappingSet) RemoveMapping(ms MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) []MappableRange { mr := MappableRange{offset, offset + uint64(ar.Length())} var unmapped []MappableRange @@ -213,7 +219,7 @@ func (s *MappingSet) RemoveMapping(ms MappingSpace, ar usermem.AddrRange, offset // Remove this part of the mapping. mappings := seg.Value() - delete(mappings, subsetMapping(mr, seg.Range(), ms, ar.Start)) + delete(mappings, subsetMapping(mr, seg.Range(), ms, ar.Start, writable)) if len(mappings) == 0 { unmapped = append(unmapped, seg.Range()) @@ -231,7 +237,7 @@ func (s *MappingSet) Invalidate(mr MappableRange, opts InvalidateOpts) { for seg := s.LowerBoundSegment(mr.Start); seg.Ok() && seg.Start() < mr.End; seg = seg.NextSegment() { segMR := seg.Range() for m := range seg.Value() { - region := subsetMapping(segMR, segMR.Intersect(mr), m.MappingSpace, m.AddrRange.Start) + region := subsetMapping(segMR, segMR.Intersect(mr), m.MappingSpace, m.AddrRange.Start, m.Writable) region.invalidate(opts) } } diff --git a/pkg/sentry/memmap/mapping_set_test.go b/pkg/sentry/memmap/mapping_set_test.go index 49ee34548..45d1d4688 100644 --- a/pkg/sentry/memmap/mapping_set_test.go +++ b/pkg/sentry/memmap/mapping_set_test.go @@ -40,7 +40,7 @@ func TestAddRemoveMapping(t *testing.T) { set := MappingSet{} ms := &testMappingSpace{} - mapped := set.AddMapping(ms, usermem.AddrRange{0x10000, 0x12000}, 0x1000) + mapped := set.AddMapping(ms, usermem.AddrRange{0x10000, 0x12000}, 0x1000, true) if got, want := mapped, []MappableRange{{0x1000, 0x3000}}; !reflect.DeepEqual(got, want) { t.Errorf("AddMapping: got %+v, wanted %+v", got, want) } @@ -49,7 +49,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x10000, 0x12000) => [0x1000, 0x3000) t.Log(&set) - mapped = set.AddMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000) + mapped = set.AddMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000, true) if len(mapped) != 0 { t.Errorf("AddMapping: got %+v, wanted []", mapped) } @@ -59,7 +59,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x11000, 0x12000) and [0x20000, 0x21000) => [0x2000, 0x3000) t.Log(&set) - mapped = set.AddMapping(ms, usermem.AddrRange{0x30000, 0x31000}, 0x4000) + mapped = set.AddMapping(ms, usermem.AddrRange{0x30000, 0x31000}, 0x4000, true) if got, want := mapped, []MappableRange{{0x4000, 0x5000}}; !reflect.DeepEqual(got, want) { t.Errorf("AddMapping: got %+v, wanted %+v", got, want) } @@ -70,7 +70,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x30000, 0x31000) => [0x4000, 0x5000) t.Log(&set) - mapped = set.AddMapping(ms, usermem.AddrRange{0x12000, 0x15000}, 0x3000) + mapped = set.AddMapping(ms, usermem.AddrRange{0x12000, 0x15000}, 0x3000, true) if got, want := mapped, []MappableRange{{0x3000, 0x4000}, {0x5000, 0x6000}}; !reflect.DeepEqual(got, want) { t.Errorf("AddMapping: got %+v, wanted %+v", got, want) } @@ -83,7 +83,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x14000, 0x15000) => [0x5000, 0x6000) t.Log(&set) - unmapped := set.RemoveMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0x1000) + unmapped := set.RemoveMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0x1000, true) if got, want := unmapped, []MappableRange{{0x1000, 0x2000}}; !reflect.DeepEqual(got, want) { t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) } @@ -95,7 +95,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x14000, 0x15000) => [0x5000, 0x6000) t.Log(&set) - unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000) + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000, true) if len(unmapped) != 0 { t.Errorf("RemoveMapping: got %+v, wanted []", unmapped) } @@ -106,7 +106,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x14000, 0x15000) => [0x5000, 0x6000) t.Log(&set) - unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x11000, 0x15000}, 0x2000) + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x11000, 0x15000}, 0x2000, true) if got, want := unmapped, []MappableRange{{0x2000, 0x4000}, {0x5000, 0x6000}}; !reflect.DeepEqual(got, want) { t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) } @@ -115,7 +115,7 @@ func TestAddRemoveMapping(t *testing.T) { // [0x30000, 0x31000) => [0x4000, 0x5000) t.Log(&set) - unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x30000, 0x31000}, 0x4000) + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x30000, 0x31000}, 0x4000, true) if got, want := unmapped, []MappableRange{{0x4000, 0x5000}}; !reflect.DeepEqual(got, want) { t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) } @@ -125,7 +125,7 @@ func TestInvalidateWholeMapping(t *testing.T) { set := MappingSet{} ms := &testMappingSpace{} - set.AddMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0) + set.AddMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0, true) // Mappings: // [0x10000, 0x11000) => [0, 0x1000) t.Log(&set) @@ -139,7 +139,7 @@ func TestInvalidatePartialMapping(t *testing.T) { set := MappingSet{} ms := &testMappingSpace{} - set.AddMapping(ms, usermem.AddrRange{0x10000, 0x13000}, 0) + set.AddMapping(ms, usermem.AddrRange{0x10000, 0x13000}, 0, true) // Mappings: // [0x10000, 0x13000) => [0, 0x3000) t.Log(&set) @@ -153,8 +153,8 @@ func TestInvalidateMultipleMappings(t *testing.T) { set := MappingSet{} ms := &testMappingSpace{} - set.AddMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0) - set.AddMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000) + set.AddMapping(ms, usermem.AddrRange{0x10000, 0x11000}, 0, true) + set.AddMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000, true) // Mappings: // [0x10000, 0x11000) => [0, 0x1000) // [0x12000, 0x13000) => [0x2000, 0x3000) @@ -170,8 +170,8 @@ func TestInvalidateOverlappingMappings(t *testing.T) { ms1 := &testMappingSpace{} ms2 := &testMappingSpace{} - set.AddMapping(ms1, usermem.AddrRange{0x10000, 0x12000}, 0) - set.AddMapping(ms2, usermem.AddrRange{0x20000, 0x22000}, 0x1000) + set.AddMapping(ms1, usermem.AddrRange{0x10000, 0x12000}, 0, true) + set.AddMapping(ms2, usermem.AddrRange{0x20000, 0x22000}, 0x1000, true) // Mappings: // ms1:[0x10000, 0x12000) => [0, 0x2000) // ms2:[0x11000, 0x13000) => [0x1000, 0x3000) @@ -184,3 +184,77 @@ func TestInvalidateOverlappingMappings(t *testing.T) { t.Errorf("Invalidate: ms1: got %+v, wanted %+v", got, want) } } + +func TestMixedWritableMappings(t *testing.T) { + set := MappingSet{} + ms := &testMappingSpace{} + + mapped := set.AddMapping(ms, usermem.AddrRange{0x10000, 0x12000}, 0x1000, true) + if got, want := mapped, []MappableRange{{0x1000, 0x3000}}; !reflect.DeepEqual(got, want) { + t.Errorf("AddMapping: got %+v, wanted %+v", got, want) + } + + // Mappings: + // [0x10000, 0x12000) writable => [0x1000, 0x3000) + t.Log(&set) + + mapped = set.AddMapping(ms, usermem.AddrRange{0x20000, 0x22000}, 0x2000, false) + if got, want := mapped, []MappableRange{{0x3000, 0x4000}}; !reflect.DeepEqual(got, want) { + t.Errorf("AddMapping: got %+v, wanted %+v", got, want) + } + + // Mappings: + // [0x10000, 0x11000) writable => [0x1000, 0x2000) + // [0x11000, 0x12000) writable and [0x20000, 0x21000) readonly => [0x2000, 0x3000) + // [0x21000, 0x22000) readonly => [0x3000, 0x4000) + t.Log(&set) + + // Unmap should fail because we specified the readonly map address range, but + // asked to unmap a writable segment. + unmapped := set.RemoveMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000, true) + if len(unmapped) != 0 { + t.Errorf("RemoveMapping: got %+v, wanted []", unmapped) + } + + // Readonly mapping removed, but writable mapping still exists in the range, + // so no mappable range fully unmapped. + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x20000, 0x21000}, 0x2000, false) + if len(unmapped) != 0 { + t.Errorf("RemoveMapping: got %+v, wanted []", unmapped) + } + + // Mappings: + // [0x10000, 0x12000) writable => [0x1000, 0x3000) + // [0x21000, 0x22000) readonly => [0x3000, 0x4000) + t.Log(&set) + + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x11000, 0x12000}, 0x2000, true) + if got, want := unmapped, []MappableRange{{0x2000, 0x3000}}; !reflect.DeepEqual(got, want) { + t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) + } + + // Mappings: + // [0x10000, 0x12000) writable => [0x1000, 0x3000) + // [0x21000, 0x22000) readonly => [0x3000, 0x4000) + t.Log(&set) + + // Unmap should fail since writable bit doesn't match. + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x10000, 0x12000}, 0x1000, false) + if len(unmapped) != 0 { + t.Errorf("RemoveMapping: got %+v, wanted []", unmapped) + } + + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x10000, 0x12000}, 0x1000, true) + if got, want := unmapped, []MappableRange{{0x1000, 0x2000}}; !reflect.DeepEqual(got, want) { + t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) + } + + // Mappings: + // [0x21000, 0x22000) readonly => [0x3000, 0x4000) + t.Log(&set) + + unmapped = set.RemoveMapping(ms, usermem.AddrRange{0x21000, 0x22000}, 0x3000, false) + if got, want := unmapped, []MappableRange{{0x3000, 0x4000}}; !reflect.DeepEqual(got, want) { + t.Errorf("RemoveMapping: got %+v, wanted %+v", got, want) + } +} diff --git a/pkg/sentry/memmap/memmap.go b/pkg/sentry/memmap/memmap.go index 05349a77f..28e2bed9b 100644 --- a/pkg/sentry/memmap/memmap.go +++ b/pkg/sentry/memmap/memmap.go @@ -36,16 +36,22 @@ type Mappable interface { // AddMapping notifies the Mappable of a mapping from addresses ar in ms to // offsets [offset, offset+ar.Length()) in this Mappable. // + // The writable flag indicates whether the backing data for a Mappable can + // be modified through the mapping. Effectively, this means a shared mapping + // where Translate may be called with at.Write == true. This is a property + // established at mapping creation and must remain constant throughout the + // lifetime of the mapping. + // // Preconditions: offset+ar.Length() does not overflow. - AddMapping(ctx context.Context, ms MappingSpace, ar usermem.AddrRange, offset uint64) error + AddMapping(ctx context.Context, ms MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) error // RemoveMapping notifies the Mappable of the removal of a mapping from // addresses ar in ms to offsets [offset, offset+ar.Length()) in this // Mappable. // // Preconditions: offset+ar.Length() does not overflow. The removed mapping - // must exist. - RemoveMapping(ctx context.Context, ms MappingSpace, ar usermem.AddrRange, offset uint64) + // must exist. writable must match the corresponding call to AddMapping. + RemoveMapping(ctx context.Context, ms MappingSpace, ar usermem.AddrRange, offset uint64, writable bool) // CopyMapping notifies the Mappable of an attempt to copy a mapping in ms // from srcAR to dstAR. For most Mappables, this is equivalent to @@ -56,8 +62,9 @@ type Mappable interface { // MappingSpace; it is analogous to Linux's vm_operations_struct::mremap. // // Preconditions: offset+srcAR.Length() and offset+dstAR.Length() do not - // overflow. The mapping at srcAR must exist. - CopyMapping(ctx context.Context, ms MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error + // overflow. The mapping at srcAR must exist. writable must match the + // corresponding call to AddMapping. + CopyMapping(ctx context.Context, ms MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, writable bool) error // Translate returns the Mappable's current mappings for at least the range // of offsets specified by required, and at most the range of offsets diff --git a/pkg/sentry/mm/aio_context.go b/pkg/sentry/mm/aio_context.go index 87942af0e..5e86d3b49 100644 --- a/pkg/sentry/mm/aio_context.go +++ b/pkg/sentry/mm/aio_context.go @@ -244,7 +244,7 @@ func (m *aioMappable) Msync(ctx context.Context, mr memmap.MappableRange) error } // AddMapping implements memmap.Mappable.AddMapping. -func (m *aioMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (m *aioMappable) AddMapping(_ context.Context, _ memmap.MappingSpace, ar usermem.AddrRange, offset uint64, _ bool) error { // Don't allow mappings to be expanded (in Linux, fs/aio.c:aio_ring_mmap() // sets VM_DONTEXPAND). if offset != 0 || uint64(ar.Length()) != aioRingBufferSize { @@ -254,11 +254,11 @@ func (m *aioMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (m *aioMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (m *aioMappable) RemoveMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, uint64, bool) { } // CopyMapping implements memmap.Mappable.CopyMapping. -func (m *aioMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { +func (m *aioMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64, _ bool) error { // Don't allow mappings to be expanded (in Linux, fs/aio.c:aio_ring_mmap() // sets VM_DONTEXPAND). if offset != 0 || uint64(dstAR.Length()) != aioRingBufferSize { diff --git a/pkg/sentry/mm/lifecycle.go b/pkg/sentry/mm/lifecycle.go index b248b76e7..1613ce11d 100644 --- a/pkg/sentry/mm/lifecycle.go +++ b/pkg/sentry/mm/lifecycle.go @@ -81,7 +81,7 @@ func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) { vmaAR := srcvseg.Range() // Inform the Mappable, if any, of the new mapping. if vma.mappable != nil { - if err := vma.mappable.AddMapping(ctx, mm2, vmaAR, vma.off); err != nil { + if err := vma.mappable.AddMapping(ctx, mm2, vmaAR, vma.off, vma.isMappableAsWritable()); err != nil { mm2.removeVMAsLocked(ctx, mm2.applicationAddrRange()) return nil, err } diff --git a/pkg/sentry/mm/mm.go b/pkg/sentry/mm/mm.go index aab697f9e..b1e39e898 100644 --- a/pkg/sentry/mm/mm.go +++ b/pkg/sentry/mm/mm.go @@ -349,6 +349,10 @@ func (v *vma) loadRealPerms(b int) { } } +func (v *vma) isMappableAsWritable() bool { + return !v.private && v.maxPerms.Write +} + // pma represents a platform mapping area. // // +stateify savable diff --git a/pkg/sentry/mm/special_mappable.go b/pkg/sentry/mm/special_mappable.go index e511472f4..64d0dd3f6 100644 --- a/pkg/sentry/mm/special_mappable.go +++ b/pkg/sentry/mm/special_mappable.go @@ -76,16 +76,16 @@ func (m *SpecialMappable) Msync(ctx context.Context, mr memmap.MappableRange) er } // AddMapping implements memmap.Mappable.AddMapping. -func (m *SpecialMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) error { +func (*SpecialMappable) AddMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, uint64, bool) error { return nil } // RemoveMapping implements memmap.Mappable.RemoveMapping. -func (m *SpecialMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar usermem.AddrRange, offset uint64) { +func (*SpecialMappable) RemoveMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, uint64, bool) { } // CopyMapping implements memmap.Mappable.CopyMapping. -func (m *SpecialMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR usermem.AddrRange, offset uint64) error { +func (*SpecialMappable) CopyMapping(context.Context, memmap.MappingSpace, usermem.AddrRange, usermem.AddrRange, uint64, bool) error { return nil } diff --git a/pkg/sentry/mm/syscalls.go b/pkg/sentry/mm/syscalls.go index 1a46c2105..daaae4da1 100644 --- a/pkg/sentry/mm/syscalls.go +++ b/pkg/sentry/mm/syscalls.go @@ -443,7 +443,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr usermem.Addr, oldSi return 0, syserror.EINVAL } // Inform the Mappable, if any, of the new mapping. - if err := vma.mappable.CopyMapping(ctx, mm, oldAR, newAR, vseg.mappableOffsetAt(oldAR.Start)); err != nil { + if err := vma.mappable.CopyMapping(ctx, mm, oldAR, newAR, vseg.mappableOffsetAt(oldAR.Start), vma.isMappableAsWritable()); err != nil { return 0, err } } @@ -498,7 +498,7 @@ func (mm *MemoryManager) MRemap(ctx context.Context, oldAddr usermem.Addr, oldSi // Now that pmas have been moved to newAR, we can notify vma.mappable that // oldAR is no longer mapped. if vma.mappable != nil { - vma.mappable.RemoveMapping(ctx, mm, oldAR, vma.off) + vma.mappable.RemoveMapping(ctx, mm, oldAR, vma.off, vma.isMappableAsWritable()) } return newAR.Start, nil diff --git a/pkg/sentry/mm/vma.go b/pkg/sentry/mm/vma.go index dafdbd0e4..5c2c802f6 100644 --- a/pkg/sentry/mm/vma.go +++ b/pkg/sentry/mm/vma.go @@ -65,7 +65,7 @@ func (mm *MemoryManager) createVMALocked(ctx context.Context, opts memmap.MMapOp // Inform the Mappable, if any, of the new mapping. if opts.Mappable != nil { - if err := opts.Mappable.AddMapping(ctx, mm, ar, opts.Offset); err != nil { + if err := opts.Mappable.AddMapping(ctx, mm, ar, opts.Offset, !opts.Private && opts.MaxPerms.Write); err != nil { return vmaIterator{}, usermem.AddrRange{}, err } } @@ -332,7 +332,7 @@ func (mm *MemoryManager) removeVMAsLocked(ctx context.Context, ar usermem.AddrRa vmaAR := vseg.Range() vma := vseg.ValuePtr() if vma.mappable != nil { - vma.mappable.RemoveMapping(ctx, mm, vmaAR, vma.off) + vma.mappable.RemoveMapping(ctx, mm, vmaAR, vma.off, vma.isMappableAsWritable()) } if vma.id != nil { vma.id.DecRef() |