/* SPDX-License-Identifier: copyleft-next-0.3.1 */
/* Copyright 2021 - 2022, Kim Kuparinen < kimi.h.kuparinen@gmail.com > */

/**
 * @file ubsan.c
 * Tiny undefined behaviour sanitizer, mostly lifted from
 * https://github.com/Abb1x/tinyubsan/blob/master/src/tinyubsan.c
 *
 * \todo Add in more runtime info.
 *
 * Note that this file's documentation is pretty vague, as I don't know the ins
 * and outs of the undefined behaviour sanitizer subsystem.
 */

#include <kmi/types.h>
#include <kmi/debug.h>

#if defined(DEBUG)
/* ubsan is only sensible if we're in debug mode. */

/**
 * Describes a source file location.
 */
struct source_location {
	/** Filename. */
	const char *file;
	/** Line number. */
	uint32_t line;
	/** Column number. */
	uint32_t column;
};

/**
 * Describes the type of undefined behaviour.
 *
 * Currently largely unused, \todo implement better undefined behaviour bug
 * messages.
 */
struct type_descriptor {
	/** Type kind. */
	uint16_t kind;
	/** Additional type info. */
	uint16_t info;
	/** Type name. */
	char name[];
};

/**
 * Describes overflows.
 */
struct overflow_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information. */
	struct type_descriptor *type;
};

/**
 * Describes out of bounds shifts.
 */
struct shift_out_of_bounds_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information #1 */
	struct type_descriptor *left_type;
	/** Extra information #2 */
	struct type_descriptor *right_type;
};

/**
 * Describes invalid values.
 */
struct invalid_value_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information. */
	struct type_descriptor *type;
};

/**
 * Describes out of bounds array accesses.
 */
struct array_out_of_bounds_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Information about the array. */
	struct type_descriptor *array_type;
	/** Information about the access. */
	struct type_descriptor *index_type;
};

/**
 * Describes mismatched types. Incorrect pointers at least, possibly other stuff
 * as well.
 */
struct type_mismatch_v1_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information. */
	struct type_descriptor *type;
	/** Alignment. */
	unsigned char log_alignment;
	/** Kind (?). */
	unsigned char type_check_kind;
};

/**
 * Describes negative VLAs. Note that this kernel explicitly disallows VLAs,
 * as does the Linux kernel.
 */
struct negative_vla_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information. */
	struct type_descriptor *type;
};

/**
 * Describes nonnull return. A function is marked with nonnull, but still
 * returns null.
 */
struct nonnull_return_data {
	/** Corresponding source location. */
	struct source_location location;
};

/**
 * Describes nonnull arguments.  Situations where an argument is marked with
 * __attribute__("null"), but is given a value.
 */
struct nonnull_arg_data {
	/** Corresponding source location. */
	struct source_location location;
};

/**
 * Describes unreachable conditions. Mainly for __builtin_unreachable().
 */
struct unreachable_data {
	/** Corresponding source location. */
	struct source_location location;
};

/**
 * Describes invalid builtin data.  Incorrect usage of __builtin_*().
 */
struct invalid_builtin_data {
	/** Corresponding source location. */
	struct source_location location;
	/** Extra information (?) */
	unsigned char kind;
};

/**
 * Wrapper around \ref bug(). Prepends location information to the message.
 *
 * @param message Message to be printed.
 * @param loc Location information.
 */
inline static void print_location(const char *message,
                                  const struct source_location loc)
{
	bug("ubsan: %s at file %s, line %ju, column %ju\n", message, loc.file,
	    (uintmax_t)loc.line, (uintmax_t)loc.column);
}

/**
 * Handle overflow during addition.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_add_overflow(const struct overflow_data *data)
{
	print_location("addition overflow", data->location);
}

/**
 * Handle overflow during subtraction.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_sub_overflow(const struct overflow_data *data)
{
	print_location("subtraction overflow", data->location);
}

/**
 * Handle overflow during multiplication.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_mul_overflow(const struct overflow_data *data)
{
	print_location("multiplication overflow", data->location);
}

/**
 * Handle overflow during division or taking a remainder.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_divrem_overflow(const struct overflow_data *data)
{
	print_location("division overflow", data->location);
}

/**
 * Handle overflow during negation.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_negate_overflow(const struct overflow_data *data)
{
	print_location("negation overflow", data->location);
}

/**
 * Handle pointer overflow.
 *
 * @param data Overflow information, generated by the compiler.
 */
void __ubsan_handle_pointer_overflow(const struct overflow_data *data)
{
	print_location("pointer overflow", data->location);
}

/**
 * Handle out of bounds shifts.
 *
 * @param data Out of bounds information, generated by the compiler.
 */
void __ubsan_handle_shift_out_of_bounds(
	const struct shift_out_of_bounds_data *data)
{
	print_location("shift out of bounds", data->location);
}

/**
 * Handle invalid load values.
 *
 * @param data Load information, generated by the compiler.
 */
void __ubsan_handle_load_invalid_value(const struct invalid_value_data *data)
{
	print_location("invalid load value", data->location);
}

/**
 * Handle array out of bounds accesses.
 *
 * @param data Out of bounds information, generated by the compiler.
 */
void __ubsan_handle_out_of_bounds(const struct array_out_of_bounds_data *data)
{
	print_location("array out of bounds", data->location);
}

/**
 * Handle type mismatch, one format.
 *
 * @param data Type mismatch information, generated by the compiler.
 * @param ptr Pointer that caused a mismatch.
 */
void __ubsan_handle_type_mismatch_v1(const struct type_mismatch_v1_data *data,
                                     uintptr_t ptr)
{
	if (!ptr) {
		print_location("use of NULL pointer", data->location);
	}

	else if (ptr & ((1 << data->log_alignment) - 1)) {
		print_location("use of misaligned pointer", data->location);
	} else {
		print_location("no space for object", data->location);
	}
}

/**
 * Handle negative VLA.
 *
 * @param data VLA information, generated by the compiler.
 */
void __ubsan_handle_vla_bound_not_positive(const struct negative_vla_data *data)
{
	print_location("variable-length argument is negative",
	               data->location);
}

/**
 * Handle nonnull return.
 *
 * @param data Return information, generated by compiler.
 */
void __ubsan_handle_nonnull_return(const struct nonnull_return_data *data)
{
	print_location("non-null return is null", data->location);
}

/**
 * Handle nonnull argument.
 *
 * @param data Argument information, generated by compiler.
 */
void __ubsan_handle_nonnull_arg(const struct nonnull_arg_data *data)
{
	print_location("non-null argument is null", data->location);
}

/**
 * Handle unreachable code.
 *
 * @param data Unreachable code information, generated by compiler.
 */
void __ubsan_handle_builtin_unreachable(const struct unreachable_data *data)
{
	print_location("unreachable code reached", data->location);
}

/**
 * Handle invalid builtin.
 *
 * @param data Builtin information, generated by compiler.
 */
void __ubsan_handle_invalid_builtin(const struct invalid_builtin_data *data)
{
	print_location("invalid builtin", data->location);
}

#endif
