//===-- asan_descriptions.h -------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// ASan-private header for asan_descriptions.cpp.
// TODO(filcab): Most struct definitions should move to the interface headers.
//===----------------------------------------------------------------------===//
#ifndef ASAN_DESCRIPTIONS_H
#define ASAN_DESCRIPTIONS_H

#include "asan_allocator.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_report_decorator.h"

namespace __asan {

void DescribeThread(AsanThreadContext *context);
static inline void DescribeThread(AsanThread *t) {
  if (t) DescribeThread(t->context());
}

class AsanThreadIdAndName {
 public:
  explicit AsanThreadIdAndName(AsanThreadContext *t);
  explicit AsanThreadIdAndName(u32 tid);

  // Contains "T%tid (%name)" or "T%tid" if the name is empty.
  const char *c_str() const { return &name[0]; }

 private:
  void Init(u32 tid, const char *tname);

  char name[128];
};

class Decorator : public __sanitizer::SanitizerCommonDecorator {
 public:
  Decorator() : SanitizerCommonDecorator() {}
  const char *Access() { return Blue(); }
  const char *Location() { return Green(); }
  const char *Allocation() { return Magenta(); }

  const char *ShadowByte(u8 byte) {
    switch (byte) {
      case kAsanHeapLeftRedzoneMagic:
      case kAsanArrayCookieMagic:
        return Red();
      case kAsanHeapFreeMagic:
        return Magenta();
      case kAsanStackLeftRedzoneMagic:
      case kAsanStackMidRedzoneMagic:
      case kAsanStackRightRedzoneMagic:
        return Red();
      case kAsanStackAfterReturnMagic:
        return Magenta();
      case kAsanInitializationOrderMagic:
        return Cyan();
      case kAsanUserPoisonedMemoryMagic:
      case kAsanContiguousContainerOOBMagic:
      case kAsanAllocaLeftMagic:
      case kAsanAllocaRightMagic:
        return Blue();
      case kAsanStackUseAfterScopeMagic:
        return Magenta();
      case kAsanGlobalRedzoneMagic:
        return Red();
      case kAsanInternalHeapMagic:
        return Yellow();
      case kAsanIntraObjectRedzone:
        return Yellow();
      default:
        return Default();
    }
  }
};

enum ShadowKind : u8 {
  kShadowKindLow,
  kShadowKindGap,
  kShadowKindHigh,
};
static const char *const ShadowNames[] = {"low shadow", "shadow gap",
                                          "high shadow"};

struct ShadowAddressDescription {
  uptr addr;
  ShadowKind kind;
  u8 shadow_byte;

  void Print() const;
};

bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr);
bool DescribeAddressIfShadow(uptr addr);

enum AccessType {
  kAccessTypeLeft,
  kAccessTypeRight,
  kAccessTypeInside,
  kAccessTypeUnknown,  // This means we have an AddressSanitizer bug!
};

struct ChunkAccess {
  uptr bad_addr;
  sptr offset;
  uptr chunk_begin;
  uptr chunk_size;
  u32 user_requested_alignment : 12;
  u32 access_type : 2;
  u32 alloc_type : 2;
};

struct HeapAddressDescription {
  uptr addr;
  uptr alloc_tid;
  uptr free_tid;
  u32 alloc_stack_id;
  u32 free_stack_id;
  ChunkAccess chunk_access;

  void Print() const;
};

bool GetHeapAddressInformation(uptr addr, uptr access_size,
                               HeapAddressDescription *descr);
bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1);

struct StackAddressDescription {
  uptr addr;
  uptr tid;
  uptr offset;
  uptr frame_pc;
  uptr access_size;
  const char *frame_descr;

  void Print() const;
};

bool GetStackAddressInformation(uptr addr, uptr access_size,
                                StackAddressDescription *descr);

struct WildAddressDescription {
  uptr addr;
  uptr access_size;

  void Print() const;
};

struct GlobalAddressDescription {
  uptr addr;
  // Assume address is close to at most four globals.
  static const int kMaxGlobals = 4;
  __asan_global globals[kMaxGlobals];
  u32 reg_sites[kMaxGlobals];
  uptr access_size;
  u8 size;

  void Print(const char *bug_type = "") const;

  // Returns true when this descriptions points inside the same global variable
  // as other. Descriptions can have different address within the variable
  bool PointsInsideTheSameVariable(const GlobalAddressDescription &other) const;
};

bool GetGlobalAddressInformation(uptr addr, uptr access_size,
                                 GlobalAddressDescription *descr);
bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type);

// General function to describe an address. Will try to describe the address as
// a shadow, global (variable), stack, or heap address.
// bug_type is optional and is used for checking if we're reporting an
// initialization-order-fiasco
// The proper access_size should be passed for stack, global, and heap
// addresses. Defaults to 1.
// Each of the *AddressDescription functions has its own Print() member, which
// may take access_size and bug_type parameters if needed.
void PrintAddressDescription(uptr addr, uptr access_size = 1,
                             const char *bug_type = "");

enum AddressKind {
  kAddressKindWild,
  kAddressKindShadow,
  kAddressKindHeap,
  kAddressKindStack,
  kAddressKindGlobal,
};

class AddressDescription {
  struct AddressDescriptionData {
    AddressKind kind;
    union {
      ShadowAddressDescription shadow;
      HeapAddressDescription heap;
      StackAddressDescription stack;
      GlobalAddressDescription global;
      WildAddressDescription wild;
    };
  };

  AddressDescriptionData data;

 public:
  AddressDescription() = default;
  // shouldLockThreadRegistry allows us to skip locking if we're sure we already
  // have done it.
  explicit AddressDescription(uptr addr, bool shouldLockThreadRegistry = true)
      : AddressDescription(addr, 1, shouldLockThreadRegistry) {}
  AddressDescription(uptr addr, uptr access_size,
                     bool shouldLockThreadRegistry = true);

  uptr Address() const {
    switch (data.kind) {
      case kAddressKindWild:
        return data.wild.addr;
      case kAddressKindShadow:
        return data.shadow.addr;
      case kAddressKindHeap:
        return data.heap.addr;
      case kAddressKindStack:
        return data.stack.addr;
      case kAddressKindGlobal:
        return data.global.addr;
    }
    UNREACHABLE("AddressInformation kind is invalid");
  }
  void Print(const char *bug_descr = nullptr) const {
    switch (data.kind) {
      case kAddressKindWild:
        data.wild.Print();
        return;
      case kAddressKindShadow:
        return data.shadow.Print();
      case kAddressKindHeap:
        return data.heap.Print();
      case kAddressKindStack:
        return data.stack.Print();
      case kAddressKindGlobal:
        // initialization-order-fiasco has a special Print()
        return data.global.Print(bug_descr);
    }
    UNREACHABLE("AddressInformation kind is invalid");
  }

  void StoreTo(AddressDescriptionData *dst) const { *dst = data; }

  const ShadowAddressDescription *AsShadow() const {
    return data.kind == kAddressKindShadow ? &data.shadow : nullptr;
  }
  const HeapAddressDescription *AsHeap() const {
    return data.kind == kAddressKindHeap ? &data.heap : nullptr;
  }
  const StackAddressDescription *AsStack() const {
    return data.kind == kAddressKindStack ? &data.stack : nullptr;
  }
  const GlobalAddressDescription *AsGlobal() const {
    return data.kind == kAddressKindGlobal ? &data.global : nullptr;
  }
};

}  // namespace __asan

#endif  // ASAN_DESCRIPTIONS_H
