//===-- asan_errors.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 error structures.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ERRORS_H
#define ASAN_ERRORS_H

#include "asan_descriptions.h"
#include "asan_scariness_score.h"
#include "sanitizer_common/sanitizer_common.h"

namespace __asan {

// (*) VS2013 does not implement unrestricted unions, so we need a trivial
// default constructor explicitly defined for each particular error.

// None of the error classes own the stack traces mentioned in them.

struct ErrorBase {
  ScarinessScoreBase scariness;
  u32 tid;

  ErrorBase() = default;  // (*)
  explicit ErrorBase(u32 tid_) : tid(tid_) {}
  ErrorBase(u32 tid_, int initial_score, const char *reason) : tid(tid_) {
    scariness.Clear();
    scariness.Scare(initial_score, reason);
  }
};

struct ErrorDeadlySignal : ErrorBase {
  SignalContext signal;

  ErrorDeadlySignal() = default;  // (*)
  ErrorDeadlySignal(u32 tid, const SignalContext &sig)
      : ErrorBase(tid),
        signal(sig) {
    scariness.Clear();
    if (signal.IsStackOverflow()) {
      scariness.Scare(10, "stack-overflow");
    } else if (!signal.is_memory_access) {
      scariness.Scare(10, "signal");
    } else if (signal.is_true_faulting_addr &&
               signal.addr < GetPageSizeCached()) {
      scariness.Scare(10, "null-deref");
    } else if (signal.addr == signal.pc) {
      scariness.Scare(60, "wild-jump");
    } else if (signal.write_flag == SignalContext::Write) {
      scariness.Scare(30, "wild-addr-write");
    } else if (signal.write_flag == SignalContext::Read) {
      scariness.Scare(20, "wild-addr-read");
    } else {
      scariness.Scare(25, "wild-addr");
    }
  }
  void Print();
};

struct ErrorDoubleFree : ErrorBase {
  const BufferedStackTrace *second_free_stack;
  HeapAddressDescription addr_description;

  ErrorDoubleFree() = default;  // (*)
  ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr)
      : ErrorBase(tid, 42, "double-free"),
        second_free_stack(stack) {
    CHECK_GT(second_free_stack->size, 0);
    GetHeapAddressInformation(addr, 1, &addr_description);
  }
  void Print();
};

struct ErrorNewDeleteTypeMismatch : ErrorBase {
  const BufferedStackTrace *free_stack;
  HeapAddressDescription addr_description;
  uptr delete_size;
  uptr delete_alignment;

  ErrorNewDeleteTypeMismatch() = default;  // (*)
  ErrorNewDeleteTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
                             uptr delete_size_, uptr delete_alignment_)
      : ErrorBase(tid, 10, "new-delete-type-mismatch"),
        free_stack(stack),
        delete_size(delete_size_),
        delete_alignment(delete_alignment_) {
    GetHeapAddressInformation(addr, 1, &addr_description);
  }
  void Print();
};

struct ErrorFreeNotMalloced : ErrorBase {
  const BufferedStackTrace *free_stack;
  AddressDescription addr_description;

  ErrorFreeNotMalloced() = default;  // (*)
  ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr)
      : ErrorBase(tid, 40, "bad-free"),
        free_stack(stack),
        addr_description(addr, /*shouldLockThreadRegistry=*/false) {}
  void Print();
};

struct ErrorAllocTypeMismatch : ErrorBase {
  const BufferedStackTrace *dealloc_stack;
  AllocType alloc_type, dealloc_type;
  AddressDescription addr_description;

  ErrorAllocTypeMismatch() = default;  // (*)
  ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
                         AllocType alloc_type_, AllocType dealloc_type_)
      : ErrorBase(tid, 10, "alloc-dealloc-mismatch"),
        dealloc_stack(stack),
        alloc_type(alloc_type_),
        dealloc_type(dealloc_type_),
        addr_description(addr, 1, false) {}
  void Print();
};

struct ErrorMallocUsableSizeNotOwned : ErrorBase {
  const BufferedStackTrace *stack;
  AddressDescription addr_description;

  ErrorMallocUsableSizeNotOwned() = default;  // (*)
  ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr)
      : ErrorBase(tid, 10, "bad-malloc_usable_size"),
        stack(stack_),
        addr_description(addr, /*shouldLockThreadRegistry=*/false) {}
  void Print();
};

struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase {
  const BufferedStackTrace *stack;
  AddressDescription addr_description;

  ErrorSanitizerGetAllocatedSizeNotOwned() = default;  // (*)
  ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_,
                                         uptr addr)
      : ErrorBase(tid, 10, "bad-__sanitizer_get_allocated_size"),
        stack(stack_),
        addr_description(addr, /*shouldLockThreadRegistry=*/false) {}
  void Print();
};

struct ErrorCallocOverflow : ErrorBase {
  const BufferedStackTrace *stack;
  uptr count;
  uptr size;

  ErrorCallocOverflow() = default;  // (*)
  ErrorCallocOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_,
                      uptr size_)
      : ErrorBase(tid, 10, "calloc-overflow"),
        stack(stack_),
        count(count_),
        size(size_) {}
  void Print();
};

struct ErrorReallocArrayOverflow : ErrorBase {
  const BufferedStackTrace *stack;
  uptr count;
  uptr size;

  ErrorReallocArrayOverflow() = default;  // (*)
  ErrorReallocArrayOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_,
                            uptr size_)
      : ErrorBase(tid, 10, "reallocarray-overflow"),
        stack(stack_),
        count(count_),
        size(size_) {}
  void Print();
};

struct ErrorPvallocOverflow : ErrorBase {
  const BufferedStackTrace *stack;
  uptr size;

  ErrorPvallocOverflow() = default;  // (*)
  ErrorPvallocOverflow(u32 tid, BufferedStackTrace *stack_, uptr size_)
      : ErrorBase(tid, 10, "pvalloc-overflow"),
        stack(stack_),
        size(size_) {}
  void Print();
};

struct ErrorInvalidAllocationAlignment : ErrorBase {
  const BufferedStackTrace *stack;
  uptr alignment;

  ErrorInvalidAllocationAlignment() = default;  // (*)
  ErrorInvalidAllocationAlignment(u32 tid, BufferedStackTrace *stack_,
                                  uptr alignment_)
      : ErrorBase(tid, 10, "invalid-allocation-alignment"),
        stack(stack_),
        alignment(alignment_) {}
  void Print();
};

struct ErrorInvalidAlignedAllocAlignment : ErrorBase {
  const BufferedStackTrace *stack;
  uptr size;
  uptr alignment;

  ErrorInvalidAlignedAllocAlignment() = default;  // (*)
  ErrorInvalidAlignedAllocAlignment(u32 tid, BufferedStackTrace *stack_,
                                    uptr size_, uptr alignment_)
      : ErrorBase(tid, 10, "invalid-aligned-alloc-alignment"),
        stack(stack_),
        size(size_),
        alignment(alignment_) {}
  void Print();
};

struct ErrorInvalidPosixMemalignAlignment : ErrorBase {
  const BufferedStackTrace *stack;
  uptr alignment;

  ErrorInvalidPosixMemalignAlignment() = default;  // (*)
  ErrorInvalidPosixMemalignAlignment(u32 tid, BufferedStackTrace *stack_,
                                     uptr alignment_)
      : ErrorBase(tid, 10, "invalid-posix-memalign-alignment"),
        stack(stack_),
        alignment(alignment_) {}
  void Print();
};

struct ErrorAllocationSizeTooBig : ErrorBase {
  const BufferedStackTrace *stack;
  uptr user_size;
  uptr total_size;
  uptr max_size;

  ErrorAllocationSizeTooBig() = default;  // (*)
  ErrorAllocationSizeTooBig(u32 tid, BufferedStackTrace *stack_,
                            uptr user_size_, uptr total_size_, uptr max_size_)
      : ErrorBase(tid, 10, "allocation-size-too-big"),
        stack(stack_),
        user_size(user_size_),
        total_size(total_size_),
        max_size(max_size_) {}
  void Print();
};

struct ErrorRssLimitExceeded : ErrorBase {
  const BufferedStackTrace *stack;

  ErrorRssLimitExceeded() = default;  // (*)
  ErrorRssLimitExceeded(u32 tid, BufferedStackTrace *stack_)
      : ErrorBase(tid, 10, "rss-limit-exceeded"),
        stack(stack_) {}
  void Print();
};

struct ErrorOutOfMemory : ErrorBase {
  const BufferedStackTrace *stack;
  uptr requested_size;

  ErrorOutOfMemory() = default;  // (*)
  ErrorOutOfMemory(u32 tid, BufferedStackTrace *stack_, uptr requested_size_)
      : ErrorBase(tid, 10, "out-of-memory"),
        stack(stack_),
        requested_size(requested_size_) {}
  void Print();
};

struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase {
  const BufferedStackTrace *stack;
  uptr length1, length2;
  AddressDescription addr1_description;
  AddressDescription addr2_description;
  const char *function;

  ErrorStringFunctionMemoryRangesOverlap() = default;  // (*)
  ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_,
                                         uptr addr1, uptr length1_, uptr addr2,
                                         uptr length2_, const char *function_)
      : ErrorBase(tid),
        stack(stack_),
        length1(length1_),
        length2(length2_),
        addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false),
        addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false),
        function(function_) {
    char bug_type[100];
    internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
    scariness.Clear();
    scariness.Scare(10, bug_type);
  }
  void Print();
};

struct ErrorStringFunctionSizeOverflow : ErrorBase {
  const BufferedStackTrace *stack;
  AddressDescription addr_description;
  uptr size;

  ErrorStringFunctionSizeOverflow() = default;  // (*)
  ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_,
                                  uptr addr, uptr size_)
      : ErrorBase(tid, 10, "negative-size-param"),
        stack(stack_),
        addr_description(addr, /*shouldLockThreadRegistry=*/false),
        size(size_) {}
  void Print();
};

struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase {
  const BufferedStackTrace *stack;
  uptr beg, end, old_mid, new_mid;

  ErrorBadParamsToAnnotateContiguousContainer() = default;  // (*)
  // PS4: Do we want an AddressDescription for beg?
  ErrorBadParamsToAnnotateContiguousContainer(u32 tid,
                                              BufferedStackTrace *stack_,
                                              uptr beg_, uptr end_,
                                              uptr old_mid_, uptr new_mid_)
      : ErrorBase(tid, 10, "bad-__sanitizer_annotate_contiguous_container"),
        stack(stack_),
        beg(beg_),
        end(end_),
        old_mid(old_mid_),
        new_mid(new_mid_) {}
  void Print();
};

struct ErrorBadParamsToAnnotateDoubleEndedContiguousContainer : ErrorBase {
  const BufferedStackTrace *stack;
  uptr storage_beg, storage_end, old_container_beg, old_container_end,
      new_container_beg, new_container_end;

  ErrorBadParamsToAnnotateDoubleEndedContiguousContainer() = default;  // (*)
  ErrorBadParamsToAnnotateDoubleEndedContiguousContainer(
      u32 tid, BufferedStackTrace *stack_, uptr storage_beg_, uptr storage_end_,
      uptr old_container_beg_, uptr old_container_end_, uptr new_container_beg_,
      uptr new_container_end_)
      : ErrorBase(tid, 10,
                  "bad-__sanitizer_annotate_double_ended_contiguous_container"),
        stack(stack_),
        storage_beg(storage_beg_),
        storage_end(storage_end_),
        old_container_beg(old_container_beg_),
        old_container_end(old_container_end_),
        new_container_beg(new_container_beg_),
        new_container_end(new_container_end_) {}
  void Print();
};

struct ErrorODRViolation : ErrorBase {
  __asan_global global1, global2;
  u32 stack_id1, stack_id2;

  ErrorODRViolation() = default;  // (*)
  ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_,
                    const __asan_global *g2, u32 stack_id2_)
      : ErrorBase(tid, 10, "odr-violation"),
        global1(*g1),
        global2(*g2),
        stack_id1(stack_id1_),
        stack_id2(stack_id2_) {}
  void Print();
};

struct ErrorInvalidPointerPair : ErrorBase {
  uptr pc, bp, sp;
  AddressDescription addr1_description;
  AddressDescription addr2_description;

  ErrorInvalidPointerPair() = default;  // (*)
  ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1,
                          uptr p2)
      : ErrorBase(tid, 10, "invalid-pointer-pair"),
        pc(pc_),
        bp(bp_),
        sp(sp_),
        addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false),
        addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {}
  void Print();
};

struct ErrorGeneric : ErrorBase {
  AddressDescription addr_description;
  uptr pc, bp, sp;
  uptr access_size;
  const char *bug_descr;
  bool is_write;
  u8 shadow_val;

  ErrorGeneric() = default;  // (*)
  ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, bool is_write_,
               uptr access_size_);
  void Print();
};

// clang-format off
#define ASAN_FOR_EACH_ERROR_KIND(macro)                    \
  macro(DeadlySignal)                                      \
  macro(DoubleFree)                                        \
  macro(NewDeleteTypeMismatch)                             \
  macro(FreeNotMalloced)                                   \
  macro(AllocTypeMismatch)                                 \
  macro(MallocUsableSizeNotOwned)                          \
  macro(SanitizerGetAllocatedSizeNotOwned)                 \
  macro(CallocOverflow)                                    \
  macro(ReallocArrayOverflow)                              \
  macro(PvallocOverflow)                                   \
  macro(InvalidAllocationAlignment)                        \
  macro(InvalidAlignedAllocAlignment)                      \
  macro(InvalidPosixMemalignAlignment)                     \
  macro(AllocationSizeTooBig)                              \
  macro(RssLimitExceeded)                                  \
  macro(OutOfMemory)                                       \
  macro(StringFunctionMemoryRangesOverlap)                 \
  macro(StringFunctionSizeOverflow)                        \
  macro(BadParamsToAnnotateContiguousContainer)            \
  macro(BadParamsToAnnotateDoubleEndedContiguousContainer) \
  macro(ODRViolation)                                      \
  macro(InvalidPointerPair)                                \
  macro(Generic)
// clang-format on

#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name)                    \
  ErrorDescription(Error##name const &e) : kind(kErrorKind##name) { \
    internal_memcpy(&name, &e, sizeof(name));                       \
  }
#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
  case kErrorKind##name:                   \
    return name.Print();

enum ErrorKind {
  kErrorKindInvalid = 0,
  ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND)
};

struct ErrorDescription {
  ErrorKind kind;
  // We're using a tagged union because it allows us to have a trivially
  // copiable type and use the same structures as the public interface.
  //
  // We can add a wrapper around it to make it "more c++-like", but that would
  // add a lot of code and the benefit wouldn't be that big.
  union {
    ErrorBase Base;
    ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER)
  };

  ErrorDescription() { internal_memset(this, 0, sizeof(*this)); }
  explicit ErrorDescription(LinkerInitialized) {}
  ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR)

  bool IsValid() { return kind != kErrorKindInvalid; }
  void Print() {
    switch (kind) {
      ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT)
      case kErrorKindInvalid:
        CHECK(0);
    }
    CHECK(0);
  }
};

#undef ASAN_FOR_EACH_ERROR_KIND
#undef ASAN_DEFINE_ERROR_KIND
#undef ASAN_ERROR_DESCRIPTION_MEMBER
#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR
#undef ASAN_ERROR_DESCRIPTION_PRINT

}  // namespace __asan

#endif  // ASAN_ERRORS_H
