//
// hardened-malloc-sys: Fatal error handler
// src/util.c: Build-time replacement for vendor/hardened-malloc/util.c
//
// Copyright (c) 2026 Ali Polatel <alip@chesswob.org>
// Based upon hardened_malloc's util.c which is:
//   Copyright (c) 2018 GrapheneOS
//   SPDX-License-Identifier: MIT
//
// SPDX-License-Identifier: MIT

#include <errno.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#ifdef __ANDROID__
#include <async_safe/log.h>
int mallopt(int param, int value);
#define M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER (-1003)
#endif

#include "util.h"

//
// Functions to set a fatal error hook.
//
typedef void (*h_fatal_error_hook)(const char *msg) noreturn;
static _Atomic(h_fatal_error_hook) h_err_hook;

void h_set_fatal_error_hook(h_fatal_error_hook hook)
{
	atomic_store_explicit(&h_err_hook, hook, memory_order_release);
}
//////

#ifndef __ANDROID__
static int write_full(int fd, const char *buf, size_t length)
{
	do {
		ssize_t bytes_written = write(fd, buf, length);
		if (bytes_written == -1) {
			if (errno == EINTR) {
				continue;
			}
			return -1;
		}
		buf += bytes_written;
		length -= bytes_written;
	} while (length);

	return 0;
}
#endif

COLD noreturn void fatal_error(const char *s)
{
	// Use fatal error hook if available.
	h_fatal_error_hook hook = atomic_load_explicit(&h_err_hook,
	                          memory_order_acquire);
	if (hook) {
		hook(s); // noreturn
	}

#ifdef __ANDROID__
	mallopt(M_BIONIC_RESTORE_DEFAULT_SIGABRT_HANDLER, 0);
	async_safe_fatal("hardened_malloc: fatal allocator error: %s", s);
#else
	const char *prefix = "fatal allocator error: ";
	(void)(write_full(STDERR_FILENO, prefix, strlen(prefix)) != -1 &&
	       write_full(STDERR_FILENO, s, strlen(s)) != -1 &&
	       write_full(STDERR_FILENO, "\n", 1));
	abort();
#endif
}
