Repository: llvm/llvm-project
File: compiler-rt/lib/scudo/standalone/secondary.h (C/C++)

Code before:
      // to include a page where the header lives.
      const uptr NewMapEnd =
          CommitBase + PageSize + roundUp(Size, PageSize) + getGuardPageSize();
      DCHECK_LE(NewMapEnd, MapEnd);
      if (NewMapEnd != MapEnd) {
        MemMap.unmap(NewMapEnd, MapEnd - NewMapEnd);
        MapEnd = NewMapEnd;
      }
    }
  }

  const uptr CommitSize = MapEnd - getGuardPageSize() - CommitBase;
  const uptr AllocPos = roundDown(CommitBase + CommitSize - Size, Alignment);
  if (!mapSecondary<Config>(Options, CommitBase, CommitSize, AllocPos, 0,
                            MemMap)) {
    unmap(MemMap);
    return nullptr;
  }
  const uptr HeaderPos = AllocPos - getHeadersSize();
  // Make sure that the header is not in the guard page or before the base.
  DCHECK_GE(HeaderPos, MapBase + getGuardPageSize());
  LargeBlock::Header *H = reinterpret_cast<LargeBlock::Header *>(
      LargeBlock::addHeaderTag<Config>(HeaderPos));
  if (useMemoryTagging<Config>(Options))
    storeTags(LargeBlock::addHeaderTag<Config>(roundDown(
                  reinterpret_cast<uptr>(H), archMemoryTagGranuleSize())),
              reinterpret_cast<uptr>(H + 1));
  H->CommitBase = CommitBase;
  H->CommitSize = CommitSize;
  H->MemMap = MemMap;
  if (BlockEndPtr)
    *BlockEndPtr = CommitBase + CommitSize;
  {
    ScopedLock L(Mutex);
    InUseBlocks.push_back(H);
    AllocatedBytes += CommitSize;
    FragmentedBytes += H->MemMap.getCapacity() - CommitSize;
    if (LargestSize < CommitSize)
      LargestSize = CommitSize;
    NumberOfAllocs++;
    Stats.add(StatAllocated, CommitSize);
    Stats.add(StatMapped, H->MemMap.getCapacity());
  }
  return reinterpret_cast<void *>(HeaderPos + LargeBlock::getHeaderSize());
}

template <typename Config>
void MapAllocator<Config>::deallocate(const Options &Options, void *Ptr)
    EXCLUDES(Mutex) {
  LargeBlock::Header *H = LargeBlock::getHeader<Config>(Ptr);
  const uptr CommitSize = H->CommitSize;

Review comment (bug):
This still aborts. If you change the reinterpret_cast<uptr>(H) to HeaderPos, those aborts disappear because HeaderPos isn't tagged, but H is.

However, you get MTE failures when a cache entry is reused, and the leftover tag seems to be left alive in the the actual allocation.

Specifically, running ScudoCombinedDeathTestBasicCombined16_AndroidConfig.BasicCombined16 shows this problem. I think you need to clear the previous tag in the tryAllocateFromCache function.

Code after:
      // to include a page where the header lives.
      const uptr NewMapEnd =
          CommitBase + PageSize + roundUp(Size, PageSize) + getGuardPageSize();
      DCHECK_LE(NewMapEnd, MapEnd);
      if (NewMapEnd != MapEnd) {
        MemMap.unmap(NewMapEnd, MapEnd - NewMapEnd);
        MapEnd = NewMapEnd;
      }
    }
  }

  const uptr CommitSize = MapEnd - getGuardPageSize() - CommitBase;
  const uptr AllocPos = roundDown(CommitBase + CommitSize - Size, Alignment);
  if (!mapSecondary<Config>(Options, CommitBase, CommitSize, AllocPos, 0,
                            MemMap)) {
    unmap(MemMap);
    return nullptr;
  }
  const uptr HeaderPos = AllocPos - getHeadersSize();
  // Make sure that the header is not in the guard page or before the base.
  DCHECK_GE(HeaderPos, MapBase + getGuardPageSize());
  LargeBlock::Header *H = reinterpret_cast<LargeBlock::Header *>(
      LargeBlock::addHeaderTag<Config>(HeaderPos));
  if (useMemoryTagging<Config>(Options))
    storeTags(reinterpret_cast<uptr>(H), reinterpret_cast<uptr>(H + 1));
  H->CommitBase = CommitBase;
  H->CommitSize = CommitSize;
  H->MemMap = MemMap;
  if (BlockEndPtr)
    *BlockEndPtr = CommitBase + CommitSize;
  {
    ScopedLock L(Mutex);
    InUseBlocks.push_back(H);
    AllocatedBytes += CommitSize;
    FragmentedBytes += H->MemMap.getCapacity() - CommitSize;
    if (LargestSize < CommitSize)
      LargestSize = CommitSize;
    NumberOfAllocs++;
    Stats.add(StatAllocated, CommitSize);
    Stats.add(StatMapped, H->MemMap.getCapacity());
  }
  return reinterpret_cast<void *>(HeaderPos + LargeBlock::getHeaderSize());
}

template <typename Config>
void MapAllocator<Config>::deallocate(const Options &Options, void *Ptr)
    EXCLUDES(Mutex) {
  LargeBlock::Header *H = LargeBlock::getHeader<Config>(Ptr);
  const uptr CommitSize = H->CommitSize;
  {
    ScopedLock L(Mutex);