summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/loader/elf.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/loader/elf.go')
-rw-r--r--pkg/sentry/loader/elf.go118
1 files changed, 62 insertions, 56 deletions
diff --git a/pkg/sentry/loader/elf.go b/pkg/sentry/loader/elf.go
index 97e32c8ba..900236531 100644
--- a/pkg/sentry/loader/elf.go
+++ b/pkg/sentry/loader/elf.go
@@ -223,13 +223,8 @@ func parseHeader(ctx context.Context, f *fs.File) (elfInfo, error) {
// mapSegment maps a phdr into the Task. offset is the offset to apply to
// phdr.Vaddr.
func mapSegment(ctx context.Context, m *mm.MemoryManager, f *fs.File, phdr *elf.ProgHeader, offset usermem.Addr) error {
- // Alignment of vaddr and offset must match. We'll need to map on the
- // page boundary.
+ // We must make a page-aligned mapping.
adjust := usermem.Addr(phdr.Vaddr).PageOffset()
- if adjust != usermem.Addr(phdr.Off).PageOffset() {
- ctx.Infof("Alignment of vaddr %#x != off %#x", phdr.Vaddr, phdr.Off)
- return syserror.ENOEXEC
- }
addr, ok := offset.AddLength(phdr.Vaddr)
if !ok {
@@ -239,17 +234,11 @@ func mapSegment(ctx context.Context, m *mm.MemoryManager, f *fs.File, phdr *elf.
}
addr -= usermem.Addr(adjust)
- fileOffset := phdr.Off - adjust
fileSize := phdr.Filesz + adjust
if fileSize < phdr.Filesz {
ctx.Infof("Computed segment file size overflows: %#x + %#x", phdr.Filesz, adjust)
return syserror.ENOEXEC
}
- memSize := phdr.Memsz + adjust
- if memSize < phdr.Memsz {
- ctx.Infof("Computed segment mem size overflows: %#x + %#x", phdr.Memsz, adjust)
- return syserror.ENOEXEC
- }
ms, ok := usermem.Addr(fileSize).RoundUp()
if !ok {
ctx.Infof("fileSize %#x too large", fileSize)
@@ -257,51 +246,64 @@ func mapSegment(ctx context.Context, m *mm.MemoryManager, f *fs.File, phdr *elf.
}
mapSize := uint64(ms)
- prot := progFlagsAsPerms(phdr.Flags)
- mopts := memmap.MMapOpts{
- Length: mapSize,
- Offset: fileOffset,
- Addr: addr,
- Fixed: true,
- // Linux will happily allow conflicting segments to map over
- // one another.
- Unmap: true,
- Private: true,
- Perms: prot,
- MaxPerms: usermem.AnyAccess,
- }
- defer func() {
- if mopts.MappingIdentity != nil {
- mopts.MappingIdentity.DecRef()
- }
- }()
- if err := f.ConfigureMMap(ctx, &mopts); err != nil {
- ctx.Infof("File is not memory-mappable: %v", err)
- return err
- }
- if _, err := m.MMap(ctx, mopts); err != nil {
- ctx.Infof("Error mapping PT_LOAD segment %+v at %#x: %v", phdr, addr, err)
- return err
- }
-
- // We need to clear the end of the last page that exceeds fileSize so
- // we don't map part of the file beyond fileSize.
- //
- // Note that Linux *does not* clear the portion of the first page
- // before phdr.Off.
- if mapSize > fileSize {
- zeroAddr, ok := addr.AddLength(fileSize)
- if !ok {
- panic(fmt.Sprintf("successfully mmaped address overflows? %#x + %#x", addr, fileSize))
+ if mapSize > 0 {
+ // This must result in a page-aligned offset. i.e., the original
+ // phdr.Off must have the same alignment as phdr.Vaddr. If that is not
+ // true, MMap will reject the mapping.
+ fileOffset := phdr.Off - adjust
+
+ prot := progFlagsAsPerms(phdr.Flags)
+ mopts := memmap.MMapOpts{
+ Length: mapSize,
+ Offset: fileOffset,
+ Addr: addr,
+ Fixed: true,
+ // Linux will happily allow conflicting segments to map over
+ // one another.
+ Unmap: true,
+ Private: true,
+ Perms: prot,
+ MaxPerms: usermem.AnyAccess,
}
- zeroSize := int64(mapSize - fileSize)
- if zeroSize < 0 {
- panic(fmt.Sprintf("zeroSize too big? %#x", uint64(zeroSize)))
+ defer func() {
+ if mopts.MappingIdentity != nil {
+ mopts.MappingIdentity.DecRef()
+ }
+ }()
+ if err := f.ConfigureMMap(ctx, &mopts); err != nil {
+ ctx.Infof("File is not memory-mappable: %v", err)
+ return err
}
- if _, err := m.ZeroOut(ctx, zeroAddr, zeroSize, usermem.IOOpts{IgnorePermissions: true}); err != nil {
- ctx.Warningf("Failed to zero end of page [%#x, %#x): %v", zeroAddr, zeroAddr+usermem.Addr(zeroSize), err)
+ if _, err := m.MMap(ctx, mopts); err != nil {
+ ctx.Infof("Error mapping PT_LOAD segment %+v at %#x: %v", phdr, addr, err)
return err
}
+
+ // We need to clear the end of the last page that exceeds fileSize so
+ // we don't map part of the file beyond fileSize.
+ //
+ // Note that Linux *does not* clear the portion of the first page
+ // before phdr.Off.
+ if mapSize > fileSize {
+ zeroAddr, ok := addr.AddLength(fileSize)
+ if !ok {
+ panic(fmt.Sprintf("successfully mmaped address overflows? %#x + %#x", addr, fileSize))
+ }
+ zeroSize := int64(mapSize - fileSize)
+ if zeroSize < 0 {
+ panic(fmt.Sprintf("zeroSize too big? %#x", uint64(zeroSize)))
+ }
+ if _, err := m.ZeroOut(ctx, zeroAddr, zeroSize, usermem.IOOpts{IgnorePermissions: true}); err != nil {
+ ctx.Warningf("Failed to zero end of page [%#x, %#x): %v", zeroAddr, zeroAddr+usermem.Addr(zeroSize), err)
+ return err
+ }
+ }
+ }
+
+ memSize := phdr.Memsz + adjust
+ if memSize < phdr.Memsz {
+ ctx.Infof("Computed segment mem size overflows: %#x + %#x", phdr.Memsz, adjust)
+ return syserror.ENOEXEC
}
// Allocate more anonymous pages if necessary.
@@ -321,9 +323,13 @@ func mapSegment(ctx context.Context, m *mm.MemoryManager, f *fs.File, phdr *elf.
Addr: anonAddr,
// Fixed without Unmap will fail the mmap if something is
// already at addr.
- Fixed: true,
- Private: true,
- Perms: progFlagsAsPerms(phdr.Flags),
+ Fixed: true,
+ Private: true,
+ // N.B. Linux uses vm_brk to map these pages, ignoring
+ // the segment protections, instead always mapping RW.
+ // These pages are not included in the final brk
+ // region.
+ Perms: usermem.ReadWrite,
MaxPerms: usermem.AnyAccess,
}); err != nil {
ctx.Infof("Error mapping PT_LOAD segment %v anonymous memory: %v", phdr, err)