/*
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Copyright © 2025 Keith Packard
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __UBSAN_H_
#define __UBSAN_H_

#include <inttypes.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdarg.h>

enum { type_kind_int = 0, type_kind_float = 1, type_unknown = 0xffff };

enum {
    type_check_load_of,
    type_check_store_to,
    type_check_reference_binding_to,
    type_check_member_access_within,
    type_check_member_call_on,
    type_check_constructor_call_on,
    type_check_downcast_of_0,
    type_check_downcast_of_1,
    type_check_upcast_of,
    type_check_cast_to_virtual_base_of,
    type_check_nonnull_binding_to,
    type_check_dynamic_operation_on
};

enum {
    builtin_check_kind_ctz_passed_zero,
    builtin_check_kind_clz_passed_zero,
    builtin_check_kind_assume_passed_false,
};

enum {
    implicit_conversion_integer_truncation = 0,
    implicit_conversion_unsigned_integer_truncation = 1,
    implicit_conversion_signed_integer_truncation = 2,
    implicit_conversion_integer_sign_change = 3,
    implicit_conversion_signed_integer_truncation_or_sign_change = 4,
};

enum {
    cfi_type_check_v_call,
    cfi_type_check_nv_call,
    cfi_type_check_derived_cast,
    cfi_type_check_unrelated_cast,
    cfi_type_check_i_call,
    cfi_type_check_nvmf_call,
    cfi_type_check_vmf_call,
};

struct type_descriptor {
    uint16_t type_kind;
    uint16_t type_info;
    char     type_name[];
};

static inline bool
type_is_signed(struct type_descriptor *type)
{
    return !!(type->type_info & 1);
}

static inline unsigned int
type_int_width(struct type_descriptor *type)
{
    return 1U << (type->type_info >> 1);
}

static inline unsigned int
type_float_width(struct type_descriptor *type)
{
    return type->type_info;
}

struct source_location {
    const char  *file_name;
    unsigned int line;
    unsigned int column;
};

struct overflow_data {
    struct source_location  location;
    struct type_descriptor *type;
};

struct pointer_overflow_data {
    struct source_location location;
};

struct float_cast_overflow_data {
    struct source_location  location;
    struct type_descriptor *from_type;
    struct type_descriptor *to_type;
};

struct dynamic_type_cache_miss_data {
    struct source_location  location;
    struct type_descriptor *type;
    void                   *type_info;
    unsigned char           type_check_kind;
};

struct type_mismatch_data {
    struct source_location  location;
    struct type_descriptor *type;
    unsigned long           alignment;
    unsigned char           type_check_kind;
};

struct type_mismatch_data_v1 {
    struct source_location  location;
    struct type_descriptor *type;
    unsigned char           log_alignment;
    unsigned char           type_check_kind;
};

struct function_type_mismatch_data {
    struct source_location  location;
    struct type_descriptor *type;
};

struct type_mismatch_data_common {
    struct source_location *location;
    struct type_descriptor *type;
    unsigned long           alignment;
    unsigned char           type_check_kind;
};

struct nonnull_arg_data {
    struct source_location location;
    struct source_location attr_location;
    int                    arg_index;
};

struct nonnull_return_data {
    struct source_location location;
};

struct out_of_bounds_data {
    struct source_location  location;
    struct type_descriptor *array_type;
    struct type_descriptor *index_type;
};

struct shift_out_of_bounds_data {
    struct source_location  location;
    struct type_descriptor *lhs_type;
    struct type_descriptor *rhs_type;
};

struct invalid_builtin_data {
    struct source_location location;
    unsigned char          kind;
};

struct invalid_objc_cast_data {
    struct source_location  location;
    struct type_descriptor *expected_type;
};

struct unreachable_data {
    struct source_location location;
};

struct implicit_conversion_data {
    struct source_location  location;
    struct type_descriptor *from_type;
    struct type_descriptor *to_type;
    unsigned char           kind;
};

struct invalid_value_data {
    struct source_location  location;
    struct type_descriptor *type;
};

struct alignment_assumption_data {
    struct source_location  location;
    struct source_location  assumption_location;
    struct type_descriptor *type;
};

struct cfi_check_fail_data {
    unsigned char           cfi_type_check_kind;
    struct source_location  location;
    struct type_descriptor *type;
};

struct vla_bound_data {
    struct source_location  location;
    struct type_descriptor *type;
};

void __ubsan_handle_add_overflow(void *data, void *lhs, void *rhs);

void __ubsan_handle_alignment_assumption(void *data, void *ptr, void *align, void *offset);

void __ubsan_handle_builtin_unreachable(void *data);

void __ubsan_handle_cfi_bad_type(void *data, void *vtable, void *valid_vtable, void *opts);

void __ubsan_handle_cfi_check_fail(void *data, void *function, void *vtable_is_valid);

void __ubsan_handle_divrem_overflow(void *data, void *lhs, void *rhs);

void __ubsan_handle_dynamic_type_cache_miss(void *data, void *pointer, void *hash);

void __ubsan_handle_float_cast_overflow(void *data, void *from);

void __ubsan_handle_function_type_mismatch(void *data, void *ptr);

void __ubsan_handle_implicit_conversion(void *data, void *src, void *dst);

void __ubsan_handle_invalid_builtin(void *data);

void __ubsan_handle_invalid_objc_cast(void *data, void *pointer);

void __ubsan_handle_load_invalid_value(void *data, void *val);

void __ubsan_handle_local_out_of_bounds();

void __ubsan_handle_missing_return(void *data);

void __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);

void __ubsan_handle_negate_overflow(void *data, void *val);

void __ubsan_handle_nonnull_arg(void *data);

void __ubsan_handle_nonnull_return(void *data);

void __ubsan_handle_nonnull_return_v1(void *data, void *location);

void __ubsan_handle_nullability_arg(void *data);

void __ubsan_handle_nullability_return(void *data);

void __ubsan_handle_nullability_return_v1(void *data, void *location);

void __ubsan_handle_out_of_bounds(void *data, void *index);

void __ubsan_handle_pointer_overflow(void *data, void *val, void *result);

void __ubsan_handle_shift_out_of_bounds(void *data, void *lhs, void *rhs);

void __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs);

void __ubsan_handle_type_mismatch(void *data, void *ptr);

void __ubsan_handle_type_mismatch_v1(void *data, void *ptr);

void __ubsan_handle_vla_bound_not_positive(void *data, void *bound);

void __noreturn __picolibc_format(printf, 3, 4)
__ubsan_error(struct source_location *source, const char *fail, const char *fmt, ...);

void __picolibc_format(printf, 3, 4) __ubsan_warning(struct source_location *source,
                                                     const char *fail, const char *fmt, ...);

void __ubsan_message(struct source_location *source, const char *msg, const char *fail,
                     const char *fmt, va_list ap);

#define VAL_STR_LEN 32

void __ubsan_val_to_string(char str[static VAL_STR_LEN], struct type_descriptor *type, void *value);

intmax_t    __ubsan_val_to_imax(struct type_descriptor *type, void *value, intmax_t def);

uintmax_t   __ubsan_val_to_umax(struct type_descriptor *type, void *value, uintmax_t def);

const char *__ubsan_type_check_to_string(unsigned char type_check_kind);

const char *__ubsan_cfi_type_check_to_string(unsigned char cfi_type_check_kind);

#endif /* _UBSAN_H_ */
