github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/mm/vma.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mm
    16  
    17  import (
    18  	"fmt"
    19  	"sync/atomic"
    20  
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/limits"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap"
    29  )
    30  
    31  // Caller provides the droppedIDs slice to collect dropped mapping
    32  // identities. The caller must drop the references on these identities outside a
    33  // mm.mappingMu critical section. droppedIDs has append-like semantics, multiple
    34  // calls to functions that drop mapping identities within a scope should reuse
    35  // the same slice.
    36  //
    37  // Preconditions:
    38  //   - mm.mappingMu must be locked for writing.
    39  //   - opts must be valid as defined by the checks in MMap.
    40  func (mm *MemoryManager) createVMALocked(ctx context.Context, opts memmap.MMapOpts, droppedIDs []memmap.MappingIdentity) (vmaIterator, hostarch.AddrRange, []memmap.MappingIdentity, error) {
    41  	if opts.MaxPerms != opts.MaxPerms.Effective() {
    42  		panic(fmt.Sprintf("Non-effective MaxPerms %s cannot be enforced", opts.MaxPerms))
    43  	}
    44  
    45  	// Find a usable range.
    46  	addr, err := mm.findAvailableLocked(opts.Length, findAvailableOpts{
    47  		Addr:     opts.Addr,
    48  		Fixed:    opts.Fixed,
    49  		Unmap:    opts.Unmap,
    50  		Map32Bit: opts.Map32Bit,
    51  	})
    52  	if err != nil {
    53  		// Can't force without opts.Unmap and opts.Fixed.
    54  		if opts.Force && opts.Unmap && opts.Fixed {
    55  			addr = opts.Addr
    56  		} else {
    57  			return vmaIterator{}, hostarch.AddrRange{}, droppedIDs, err
    58  		}
    59  	}
    60  	ar, _ := addr.ToRange(opts.Length)
    61  
    62  	// Check against RLIMIT_AS.
    63  	newUsageAS := mm.usageAS + opts.Length
    64  	if opts.Unmap {
    65  		newUsageAS -= uint64(mm.vmas.SpanRange(ar))
    66  	}
    67  	if limitAS := limits.FromContext(ctx).Get(limits.AS).Cur; newUsageAS > limitAS {
    68  		return vmaIterator{}, hostarch.AddrRange{}, droppedIDs, linuxerr.ENOMEM
    69  	}
    70  
    71  	if opts.MLockMode != memmap.MLockNone {
    72  		// Check against RLIMIT_MEMLOCK.
    73  		if creds := auth.CredentialsFromContext(ctx); !creds.HasCapabilityIn(linux.CAP_IPC_LOCK, creds.UserNamespace.Root()) {
    74  			mlockLimit := limits.FromContext(ctx).Get(limits.MemoryLocked).Cur
    75  			if mlockLimit == 0 {
    76  				return vmaIterator{}, hostarch.AddrRange{}, droppedIDs, linuxerr.EPERM
    77  			}
    78  			newLockedAS := mm.lockedAS + opts.Length
    79  			if opts.Unmap {
    80  				newLockedAS -= mm.mlockedBytesRangeLocked(ar)
    81  			}
    82  			if newLockedAS > mlockLimit {
    83  				return vmaIterator{}, hostarch.AddrRange{}, droppedIDs, linuxerr.EAGAIN
    84  			}
    85  		}
    86  	}
    87  
    88  	// Remove overwritten mappings. This ordering is consistent with Linux:
    89  	// compare Linux's mm/mmap.c:mmap_region() => do_munmap(),
    90  	// file->f_op->mmap().
    91  	var vgap vmaGapIterator
    92  	if opts.Unmap {
    93  		vgap, droppedIDs = mm.unmapLocked(ctx, ar, droppedIDs)
    94  	} else {
    95  		vgap = mm.vmas.FindGap(ar.Start)
    96  	}
    97  
    98  	// Inform the Mappable, if any, of the new mapping.
    99  	if opts.Mappable != nil {
   100  		// The expression for writable is vma.canWriteMappableLocked(), but we
   101  		// don't yet have a vma.
   102  		if err := opts.Mappable.AddMapping(ctx, mm, ar, opts.Offset, !opts.Private && opts.MaxPerms.Write); err != nil {
   103  			return vmaIterator{}, hostarch.AddrRange{}, droppedIDs, err
   104  		}
   105  	}
   106  
   107  	// Take a reference on opts.MappingIdentity before inserting the vma since
   108  	// vma merging can drop the reference.
   109  	if opts.MappingIdentity != nil {
   110  		opts.MappingIdentity.IncRef()
   111  	}
   112  
   113  	// Finally insert the vma.
   114  	v := vma{
   115  		mappable:       opts.Mappable,
   116  		off:            opts.Offset,
   117  		realPerms:      opts.Perms,
   118  		effectivePerms: opts.Perms.Effective(),
   119  		maxPerms:       opts.MaxPerms,
   120  		private:        opts.Private,
   121  		growsDown:      opts.GrowsDown,
   122  		mlockMode:      opts.MLockMode,
   123  		numaPolicy:     linux.MPOL_DEFAULT,
   124  		id:             opts.MappingIdentity,
   125  		hint:           opts.Hint,
   126  	}
   127  
   128  	vseg := mm.vmas.Insert(vgap, ar, v)
   129  	mm.usageAS += opts.Length
   130  	if v.isPrivateDataLocked() {
   131  		mm.dataAS += opts.Length
   132  	}
   133  	if opts.MLockMode != memmap.MLockNone {
   134  		mm.lockedAS += opts.Length
   135  	}
   136  
   137  	return vseg, ar, droppedIDs, nil
   138  }
   139  
   140  type findAvailableOpts struct {
   141  	// These fields are equivalent to those in memmap.MMapOpts, except that:
   142  	//
   143  	//	- Addr must be page-aligned.
   144  	//
   145  	//	- Unmap allows existing guard pages in the returned range.
   146  
   147  	Addr     hostarch.Addr
   148  	Fixed    bool
   149  	Unmap    bool
   150  	Map32Bit bool
   151  }
   152  
   153  // map32Start/End are the bounds to which MAP_32BIT mappings are constrained,
   154  // and are equivalent to Linux's MAP32_BASE and MAP32_MAX respectively.
   155  const (
   156  	map32Start = 0x40000000
   157  	map32End   = 0x80000000
   158  )
   159  
   160  // findAvailableLocked finds an allocatable range.
   161  //
   162  // Preconditions: mm.mappingMu must be locked.
   163  func (mm *MemoryManager) findAvailableLocked(length uint64, opts findAvailableOpts) (hostarch.Addr, error) {
   164  	if opts.Fixed {
   165  		opts.Map32Bit = false
   166  	}
   167  	allowedAR := mm.applicationAddrRange()
   168  	if opts.Map32Bit {
   169  		allowedAR = allowedAR.Intersect(hostarch.AddrRange{map32Start, map32End})
   170  	}
   171  
   172  	// Does the provided suggestion work?
   173  	if ar, ok := opts.Addr.ToRange(length); ok {
   174  		if allowedAR.IsSupersetOf(ar) {
   175  			if opts.Unmap {
   176  				return ar.Start, nil
   177  			}
   178  			// Check for the presence of an existing vma or guard page.
   179  			if vgap := mm.vmas.FindGap(ar.Start); vgap.Ok() && vgap.availableRange().IsSupersetOf(ar) {
   180  				return ar.Start, nil
   181  			}
   182  		}
   183  	}
   184  
   185  	// Fixed mappings accept only the requested address.
   186  	if opts.Fixed {
   187  		return 0, linuxerr.ENOMEM
   188  	}
   189  
   190  	// Prefer hugepage alignment if a hugepage or more is requested.
   191  	alignment := uint64(hostarch.PageSize)
   192  	if length >= hostarch.HugePageSize {
   193  		alignment = hostarch.HugePageSize
   194  	}
   195  
   196  	if opts.Map32Bit {
   197  		return mm.findLowestAvailableLocked(length, alignment, allowedAR)
   198  	}
   199  	if mm.layout.DefaultDirection == arch.MmapBottomUp {
   200  		return mm.findLowestAvailableLocked(length, alignment, hostarch.AddrRange{mm.layout.BottomUpBase, mm.layout.MaxAddr})
   201  	}
   202  	return mm.findHighestAvailableLocked(length, alignment, hostarch.AddrRange{mm.layout.MinAddr, mm.layout.TopDownBase})
   203  }
   204  
   205  func (mm *MemoryManager) applicationAddrRange() hostarch.AddrRange {
   206  	return hostarch.AddrRange{mm.layout.MinAddr, mm.layout.MaxAddr}
   207  }
   208  
   209  // Preconditions: mm.mappingMu must be locked.
   210  func (mm *MemoryManager) findLowestAvailableLocked(length, alignment uint64, bounds hostarch.AddrRange) (hostarch.Addr, error) {
   211  	for gap := mm.vmas.LowerBoundGap(bounds.Start); gap.Ok() && gap.Start() < bounds.End; gap = gap.NextLargeEnoughGap(hostarch.Addr(length)) {
   212  		if gr := gap.availableRange().Intersect(bounds); uint64(gr.Length()) >= length {
   213  			// Can we shift up to match the alignment?
   214  			if offset := uint64(gr.Start) % alignment; offset != 0 {
   215  				if uint64(gr.Length()) >= length+alignment-offset {
   216  					// Yes, we're aligned.
   217  					return gr.Start + hostarch.Addr(alignment-offset), nil
   218  				}
   219  			}
   220  
   221  			// Either aligned perfectly, or can't align it.
   222  			return gr.Start, nil
   223  		}
   224  	}
   225  	return 0, linuxerr.ENOMEM
   226  }
   227  
   228  // Preconditions: mm.mappingMu must be locked.
   229  func (mm *MemoryManager) findHighestAvailableLocked(length, alignment uint64, bounds hostarch.AddrRange) (hostarch.Addr, error) {
   230  	for gap := mm.vmas.UpperBoundGap(bounds.End); gap.Ok() && gap.End() > bounds.Start; gap = gap.PrevLargeEnoughGap(hostarch.Addr(length)) {
   231  		if gr := gap.availableRange().Intersect(bounds); uint64(gr.Length()) >= length {
   232  			// Can we shift down to match the alignment?
   233  			start := gr.End - hostarch.Addr(length)
   234  			if offset := uint64(start) % alignment; offset != 0 {
   235  				if gr.Start <= start-hostarch.Addr(offset) {
   236  					// Yes, we're aligned.
   237  					return start - hostarch.Addr(offset), nil
   238  				}
   239  			}
   240  
   241  			// Either aligned perfectly, or can't align it.
   242  			return start, nil
   243  		}
   244  	}
   245  	return 0, linuxerr.ENOMEM
   246  }
   247  
   248  // Preconditions: mm.mappingMu must be locked.
   249  func (mm *MemoryManager) mlockedBytesRangeLocked(ar hostarch.AddrRange) uint64 {
   250  	var total uint64
   251  	for vseg := mm.vmas.LowerBoundSegment(ar.Start); vseg.Ok() && vseg.Start() < ar.End; vseg = vseg.NextSegment() {
   252  		if vseg.ValuePtr().mlockMode != memmap.MLockNone {
   253  			total += uint64(vseg.Range().Intersect(ar).Length())
   254  		}
   255  	}
   256  	return total
   257  }
   258  
   259  // getVMAsLocked ensures that vmas exist for all addresses in ar, and support
   260  // access of type (at, ignorePermissions). It returns:
   261  //
   262  //   - An iterator to the vma containing ar.Start. If no vma contains ar.Start,
   263  //     the iterator is unspecified.
   264  //
   265  //   - An iterator to the gap after the last vma containing an address in ar. If
   266  //     vmas exist for no addresses in ar, the iterator is to a gap that begins
   267  //     before ar.Start.
   268  //
   269  //   - An error that is non-nil if vmas exist for only a subset of ar.
   270  //
   271  // Preconditions:
   272  //   - mm.mappingMu must be locked for reading; it may be temporarily unlocked.
   273  //   - ar.Length() != 0.
   274  func (mm *MemoryManager) getVMAsLocked(ctx context.Context, ar hostarch.AddrRange, at hostarch.AccessType, ignorePermissions bool) (vmaIterator, vmaGapIterator, error) {
   275  	if checkInvariants {
   276  		if !ar.WellFormed() || ar.Length() == 0 {
   277  			panic(fmt.Sprintf("invalid ar: %v", ar))
   278  		}
   279  	}
   280  
   281  	// Inline mm.vmas.LowerBoundSegment so that we have the preceding gap if
   282  	// !vbegin.Ok().
   283  	vbegin, vgap := mm.vmas.Find(ar.Start)
   284  	if !vbegin.Ok() {
   285  		vbegin = vgap.NextSegment()
   286  		// vseg.Ok() is checked before entering the following loop.
   287  	} else {
   288  		vgap = vbegin.PrevGap()
   289  	}
   290  
   291  	addr := ar.Start
   292  	vseg := vbegin
   293  	for vseg.Ok() {
   294  		// Loop invariants: vgap = vseg.PrevGap(); addr < vseg.End().
   295  		vma := vseg.ValuePtr()
   296  		if addr < vseg.Start() {
   297  			// TODO(jamieliu): Implement vma.growsDown here.
   298  			return vbegin, vgap, linuxerr.EFAULT
   299  		}
   300  
   301  		perms := vma.effectivePerms
   302  		if ignorePermissions {
   303  			perms = vma.maxPerms
   304  		}
   305  		if !perms.SupersetOf(at) {
   306  			return vbegin, vgap, linuxerr.EPERM
   307  		}
   308  
   309  		addr = vseg.End()
   310  		vgap = vseg.NextGap()
   311  		if addr >= ar.End {
   312  			return vbegin, vgap, nil
   313  		}
   314  		vseg = vgap.NextSegment()
   315  	}
   316  
   317  	// Ran out of vmas before ar.End.
   318  	return vbegin, vgap, linuxerr.EFAULT
   319  }
   320  
   321  // getVecVMAsLocked ensures that vmas exist for all addresses in ars, and
   322  // support access to type of (at, ignorePermissions). It returns the subset of
   323  // ars for which vmas exist. If this is not equal to ars, it returns a non-nil
   324  // error explaining why.
   325  //
   326  // Preconditions: mm.mappingMu must be locked for reading; it may be
   327  // temporarily unlocked.
   328  //
   329  // Postconditions: ars is not mutated.
   330  func (mm *MemoryManager) getVecVMAsLocked(ctx context.Context, ars hostarch.AddrRangeSeq, at hostarch.AccessType, ignorePermissions bool) (hostarch.AddrRangeSeq, error) {
   331  	for arsit := ars; !arsit.IsEmpty(); arsit = arsit.Tail() {
   332  		ar := arsit.Head()
   333  		if ar.Length() == 0 {
   334  			continue
   335  		}
   336  		if _, vend, err := mm.getVMAsLocked(ctx, ar, at, ignorePermissions); err != nil {
   337  			return truncatedAddrRangeSeq(ars, arsit, vend.Start()), err
   338  		}
   339  	}
   340  	return ars, nil
   341  }
   342  
   343  // vma extension will not shrink the number of unmapped bytes between the start
   344  // of a growsDown vma and the end of its predecessor non-growsDown vma below
   345  // guardBytes.
   346  //
   347  // guardBytes is equivalent to Linux's stack_guard_gap after upstream
   348  // 1be7107fbe18 "mm: larger stack guard gap, between vmas".
   349  const guardBytes = 256 * hostarch.PageSize
   350  
   351  // unmapLocked unmaps all addresses in ar and returns the resulting gap in
   352  // mm.vmas.
   353  //
   354  // Caller provides the droppedIDs slice to collect dropped mapping
   355  // identities. The caller must drop the references on these identities outside a
   356  // mm.mappingMu critical section. droppedIDs has append-like semantics, multiple
   357  // calls to functions that drop mapping identities within a scope should reuse
   358  // the same slice.
   359  //
   360  // Preconditions:
   361  //   - mm.mappingMu must be locked for writing.
   362  //   - ar.Length() != 0.
   363  //   - ar must be page-aligned.
   364  func (mm *MemoryManager) unmapLocked(ctx context.Context, ar hostarch.AddrRange, droppedIDs []memmap.MappingIdentity) (vmaGapIterator, []memmap.MappingIdentity) {
   365  	if checkInvariants {
   366  		if !ar.WellFormed() || ar.Length() == 0 || !ar.IsPageAligned() {
   367  			panic(fmt.Sprintf("invalid ar: %v", ar))
   368  		}
   369  	}
   370  
   371  	// AddressSpace mappings and pmas must be invalidated before
   372  	// mm.removeVMAsLocked() => memmap.Mappable.RemoveMapping().
   373  	mm.Invalidate(ar, memmap.InvalidateOpts{InvalidatePrivate: true})
   374  	return mm.removeVMAsLocked(ctx, ar, droppedIDs)
   375  }
   376  
   377  // removeVMAsLocked removes vmas for addresses in ar and returns the
   378  // resulting gap in mm.vmas.
   379  //
   380  // Caller provides the droppedIDs slice to collect dropped mapping
   381  // identities. The caller must drop the references on these identities outside a
   382  // mm.mappingMu critical section. droppedIDs has append-like semantics, multiple
   383  // calls to functions that drop mapping identities within a scope should reuse
   384  // the same slice.
   385  //
   386  // Preconditions:
   387  //   - mm.mappingMu must be locked for writing.
   388  //   - ar.Length() != 0.
   389  //   - ar must be page-aligned.
   390  func (mm *MemoryManager) removeVMAsLocked(ctx context.Context, ar hostarch.AddrRange, droppedIDs []memmap.MappingIdentity) (vmaGapIterator, []memmap.MappingIdentity) {
   391  	if checkInvariants {
   392  		if !ar.WellFormed() || ar.Length() == 0 || !ar.IsPageAligned() {
   393  			panic(fmt.Sprintf("invalid ar: %v", ar))
   394  		}
   395  	}
   396  	vseg, vgap := mm.vmas.Find(ar.Start)
   397  	if vgap.Ok() {
   398  		vseg = vgap.NextSegment()
   399  	}
   400  	for vseg.Ok() && vseg.Start() < ar.End {
   401  		vseg = mm.vmas.Isolate(vseg, ar)
   402  		vmaAR := vseg.Range()
   403  		vma := vseg.ValuePtr()
   404  		if vma.mappable != nil {
   405  			vma.mappable.RemoveMapping(ctx, mm, vmaAR, vma.off, vma.canWriteMappableLocked())
   406  		}
   407  		if vma.id != nil {
   408  			droppedIDs = append(droppedIDs, vma.id)
   409  		}
   410  		mm.usageAS -= uint64(vmaAR.Length())
   411  		if vma.isPrivateDataLocked() {
   412  			mm.dataAS -= uint64(vmaAR.Length())
   413  		}
   414  		if vma.mlockMode != memmap.MLockNone {
   415  			mm.lockedAS -= uint64(vmaAR.Length())
   416  		}
   417  		vgap = mm.vmas.Remove(vseg)
   418  		vseg = vgap.NextSegment()
   419  	}
   420  	return vgap, droppedIDs
   421  }
   422  
   423  // canWriteMappableLocked returns true if it is possible for vma.mappable to be
   424  // written to via this vma, i.e. if it is possible that
   425  // vma.mappable.Translate(at.Write=true) may be called as a result of this vma.
   426  // This includes via I/O with usermem.IOOpts.IgnorePermissions = true, such as
   427  // PTRACE_POKEDATA.
   428  //
   429  // canWriteMappableLocked is equivalent to Linux's VM_SHARED.
   430  //
   431  // Preconditions: mm.mappingMu must be locked.
   432  func (v *vma) canWriteMappableLocked() bool {
   433  	return !v.private && v.maxPerms.Write
   434  }
   435  
   436  // isPrivateDataLocked identify the data segments - private, writable, not stack
   437  //
   438  // Preconditions: mm.mappingMu must be locked.
   439  func (v *vma) isPrivateDataLocked() bool {
   440  	return v.realPerms.Write && v.private && !v.growsDown
   441  }
   442  
   443  // vmaSetFunctions implements segment.Functions for vmaSet.
   444  type vmaSetFunctions struct{}
   445  
   446  func (vmaSetFunctions) MinKey() hostarch.Addr {
   447  	return 0
   448  }
   449  
   450  func (vmaSetFunctions) MaxKey() hostarch.Addr {
   451  	return ^hostarch.Addr(0)
   452  }
   453  
   454  func (vmaSetFunctions) ClearValue(vma *vma) {
   455  	vma.mappable = nil
   456  	vma.id = nil
   457  	vma.hint = ""
   458  	atomic.StoreUintptr(&vma.lastFault, 0)
   459  }
   460  
   461  func (vmaSetFunctions) Merge(ar1 hostarch.AddrRange, vma1 vma, ar2 hostarch.AddrRange, vma2 vma) (vma, bool) {
   462  	if vma1.mappable != vma2.mappable ||
   463  		(vma1.mappable != nil && vma1.off+uint64(ar1.Length()) != vma2.off) ||
   464  		vma1.realPerms != vma2.realPerms ||
   465  		vma1.maxPerms != vma2.maxPerms ||
   466  		vma1.private != vma2.private ||
   467  		vma1.growsDown != vma2.growsDown ||
   468  		vma1.mlockMode != vma2.mlockMode ||
   469  		vma1.numaPolicy != vma2.numaPolicy ||
   470  		vma1.numaNodemask != vma2.numaNodemask ||
   471  		vma1.dontfork != vma2.dontfork ||
   472  		vma1.id != vma2.id ||
   473  		vma1.hint != vma2.hint {
   474  		return vma{}, false
   475  	}
   476  
   477  	if vma2.id != nil {
   478  		// This DecRef() will never be the final ref, since the vma1 is
   479  		// currently holding a ref to the same mapping identity. Thus, we don't
   480  		// need to worry about whether we're in a mm.mappingMu critical section.
   481  		vma2.id.DecRef(context.Background())
   482  	}
   483  	return vma1, true
   484  }
   485  
   486  func (vmaSetFunctions) Split(ar hostarch.AddrRange, v vma, split hostarch.Addr) (vma, vma) {
   487  	v2 := v
   488  	if v2.mappable != nil {
   489  		v2.off += uint64(split - ar.Start)
   490  	}
   491  	if v2.id != nil {
   492  		v2.id.IncRef()
   493  	}
   494  	return v, v2
   495  }
   496  
   497  // Preconditions:
   498  //   - vseg.ValuePtr().mappable != nil.
   499  //   - vseg.Range().Contains(addr).
   500  func (vseg vmaIterator) mappableOffsetAt(addr hostarch.Addr) uint64 {
   501  	if checkInvariants {
   502  		if !vseg.Ok() {
   503  			panic("terminal vma iterator")
   504  		}
   505  		if vseg.ValuePtr().mappable == nil {
   506  			panic("Mappable offset is meaningless for anonymous vma")
   507  		}
   508  		if !vseg.Range().Contains(addr) {
   509  			panic(fmt.Sprintf("addr %v out of bounds %v", addr, vseg.Range()))
   510  		}
   511  	}
   512  
   513  	vma := vseg.ValuePtr()
   514  	vstart := vseg.Start()
   515  	return vma.off + uint64(addr-vstart)
   516  }
   517  
   518  // Preconditions: vseg.ValuePtr().mappable != nil.
   519  func (vseg vmaIterator) mappableRange() memmap.MappableRange {
   520  	return vseg.mappableRangeOf(vseg.Range())
   521  }
   522  
   523  // Preconditions:
   524  //   - vseg.ValuePtr().mappable != nil.
   525  //   - vseg.Range().IsSupersetOf(ar).
   526  //   - ar.Length() != 0.
   527  func (vseg vmaIterator) mappableRangeOf(ar hostarch.AddrRange) memmap.MappableRange {
   528  	if checkInvariants {
   529  		if !vseg.Ok() {
   530  			panic("terminal vma iterator")
   531  		}
   532  		if vseg.ValuePtr().mappable == nil {
   533  			panic("MappableRange is meaningless for anonymous vma")
   534  		}
   535  		if !ar.WellFormed() || ar.Length() == 0 {
   536  			panic(fmt.Sprintf("invalid ar: %v", ar))
   537  		}
   538  		if !vseg.Range().IsSupersetOf(ar) {
   539  			panic(fmt.Sprintf("ar %v out of bounds %v", ar, vseg.Range()))
   540  		}
   541  	}
   542  
   543  	vma := vseg.ValuePtr()
   544  	vstart := vseg.Start()
   545  	return memmap.MappableRange{vma.off + uint64(ar.Start-vstart), vma.off + uint64(ar.End-vstart)}
   546  }
   547  
   548  // Preconditions:
   549  //   - vseg.ValuePtr().mappable != nil.
   550  //   - vseg.mappableRange().IsSupersetOf(mr).
   551  //   - mr.Length() != 0.
   552  func (vseg vmaIterator) addrRangeOf(mr memmap.MappableRange) hostarch.AddrRange {
   553  	if checkInvariants {
   554  		if !vseg.Ok() {
   555  			panic("terminal vma iterator")
   556  		}
   557  		if vseg.ValuePtr().mappable == nil {
   558  			panic("MappableRange is meaningless for anonymous vma")
   559  		}
   560  		if !mr.WellFormed() || mr.Length() == 0 {
   561  			panic(fmt.Sprintf("invalid mr: %v", mr))
   562  		}
   563  		if !vseg.mappableRange().IsSupersetOf(mr) {
   564  			panic(fmt.Sprintf("mr %v out of bounds %v", mr, vseg.mappableRange()))
   565  		}
   566  	}
   567  
   568  	vma := vseg.ValuePtr()
   569  	vstart := vseg.Start()
   570  	return hostarch.AddrRange{vstart + hostarch.Addr(mr.Start-vma.off), vstart + hostarch.Addr(mr.End-vma.off)}
   571  }
   572  
   573  // seekNextLowerBound returns mm.vmas.LowerBoundSegment(addr), but does so by
   574  // scanning linearly forward from vseg.
   575  //
   576  // Preconditions:
   577  //   - mm.mappingMu must be locked.
   578  //   - addr >= vseg.Start().
   579  func (vseg vmaIterator) seekNextLowerBound(addr hostarch.Addr) vmaIterator {
   580  	if checkInvariants {
   581  		if !vseg.Ok() {
   582  			panic("terminal vma iterator")
   583  		}
   584  		if addr < vseg.Start() {
   585  			panic(fmt.Sprintf("can't seek forward to %#x from %#x", addr, vseg.Start()))
   586  		}
   587  	}
   588  	for vseg.Ok() && addr >= vseg.End() {
   589  		vseg = vseg.NextSegment()
   590  	}
   591  	return vseg
   592  }
   593  
   594  // availableRange returns the subset of vgap.Range() in which new vmas may be
   595  // created without MMapOpts.Unmap == true.
   596  func (vgap vmaGapIterator) availableRange() hostarch.AddrRange {
   597  	ar := vgap.Range()
   598  	next := vgap.NextSegment()
   599  	if !next.Ok() || !next.ValuePtr().growsDown {
   600  		return ar
   601  	}
   602  	// Exclude guard pages.
   603  	if ar.Length() < guardBytes {
   604  		return hostarch.AddrRange{ar.Start, ar.Start}
   605  	}
   606  	ar.End -= guardBytes
   607  	return ar
   608  }