/*
 * atom_harness_fixed.c — the SAME cases as atom_harness_guest.c but with
 * the FIXED logic from findings/poc/DF-433{7,8,9}/fix.diff applied to the
 * mirrored functions.  All cases must exit 0 (no guard-page traps) and
 * reject/terminate cleanly: that is the logic-level fix validation.
 *
 * Build: cc -O1 -g -o atom_harness_fixed atom_harness_fixed.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/mman.h>

typedef uint16_t u16;

static inline uint8_t get_u8(void *bios, int ptr)
{
    return ((unsigned char *)bios)[ptr];
}
static inline uint16_t get_u16(void *bios, int ptr)
{
    return get_u8(bios, ptr) | (((uint16_t)get_u8(bios, ptr + 1)) << 8);
}
#define CU8(ptr) get_u8(ctx->bios, (ptr))
#define CU16(ptr) get_u16(ctx->bios, (ptr))
#define CSTR(ptr) (((char *)(ctx->bios)) + (ptr))

struct atom_context {
	void *bios;
	int bios_size;
	uint32_t cmd_table, data_table;
	uint16_t *iio;
	uint16_t data_block;
};

/* DF-4337 fix: central bounds helper */
static bool atom_in_bios(const struct atom_context *ctx, int off, int len)
{
	return off >= 0 && len >= 0 && off <= ctx->bios_size - len;
}

static void *guarded_alloc(size_t sz)
{
	long ps = 4096;
	size_t guard = (size_t)16 * 1024 * 1024;
	size_t len = (size_t)ps + guard;
	char *base = mmap(NULL, len, PROT_READ | PROT_WRITE,
			  MAP_PRIVATE | MAP_ANON, -1, 0);
	if (base == MAP_FAILED) { perror("mmap"); exit(3); }
	if (mprotect(base + ps, guard, PROT_NONE) != 0) { perror("mprotect"); exit(3); }
	return base + ps - sz;
}

/* FIXED atom_parse_data_header (DF-4337 fix.diff) */
bool atom_parse_data_header(struct atom_context *ctx, int index,
			    uint16_t * size, uint8_t * frev, uint8_t * crev,
			    uint16_t * data_start)
{
	int offset = index * 2 + 4;
	u16 *mdt = (u16 *)((char *)ctx->bios + ctx->data_table + 4);
	int idx;

	if (index < 0 || !atom_in_bios(ctx, ctx->data_table + offset, 2))
		return false;
	idx = CU16(ctx->data_table + offset);
	if (!mdt[index])
		return false;
	if (!atom_in_bios(ctx, idx, 4) || !atom_in_bios(ctx, idx, CU16(idx)))
		return false;
	if (size)
		*size = CU16(idx);
	if (frev)
		*frev = CU8(idx + 2);
	if (crev)
		*crev = CU8(idx + 3);
	*data_start = idx;
	return true;
}

#define ATOM_IIO_START	1
#define ATOM_IIO_END	9
static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };

/* FIXED atom_index_iio (DF-4337 + DF-4338 fix.diffs) */
static void atom_index_iio(struct atom_context *ctx, int base)
{
	ctx->iio = calloc(256, 2);
	if (!ctx->iio)
		return;
	while (atom_in_bios(ctx, base, 2) && CU8(base) == ATOM_IIO_START) {
		ctx->iio[CU8(base + 1)] = base + 2;
		base += 2;
		while (atom_in_bios(ctx, base, 1) && CU8(base) != ATOM_IIO_END) {
			int op = CU8(base);

			if (op > ATOM_IIO_END)
				return;
			base += atom_iio_len[op];
		}
		base += 3;
	}
}

/* FIXED atom_parse name loop (DF-4337 bound + DF-4339 termination) */
static void atom_parse_name(struct atom_context *ctx, int msg_ptr)
{
	char *str;
	char name[512] = { 0 };
	int i;

	if (!atom_in_bios(ctx, msg_ptr, 1)) {
		printf("  msg ptr beyond image: rejected\n");
		return;
	}
	str = CSTR(msg_ptr);
	while (*str && ((*str == '\n') || (*str == '\r')))
		str++;
	for (i = 0; i < 511; i++) {
		if (!atom_in_bios(ctx, msg_ptr + (int)(str - (char *)ctx->bios) + i, 1))
			break;
		name[i] = str[i];
		if (name[i] < '.' || name[i] > 'z') {
			name[i] = 0;
			break;
		}
	}
	printf("  strlen(name) after loop = %zu (<= 511: always terminated)\n",
	       strlen(name));
}

int main(int argc, char **argv)
{
	const char *cs = argc > 1 ? argv[1] : "data_header";
	printf("case: %s (FIXED logic)\n", cs);

	if (!strcmp(cs, "data_header")) {
		struct atom_context sctx, *ctx = &sctx;
		memset(&sctx, 0, sizeof(sctx));
		ctx->bios = guarded_alloc(512);
		ctx->bios_size = 512;			/* DF-4337 plumbing */
		ctx->data_table = 0x20;
		((uint8_t *)ctx->bios)[0x24] = 0x00;	/* mdt[0] = 0x8000 */
		((uint8_t *)ctx->bios)[0x25] = 0x80;
		uint16_t size = 0, data_start = 0;
		bool ok = atom_parse_data_header(ctx, 0, &size, NULL, NULL, &data_start);
		printf("  result: ok=%d (expected 0: entry 0x8000 rejected vs bios_size=512)\n", ok);
		return ok ? 1 : 0;
	}
	if (!strcmp(cs, "iio")) {
		struct atom_context sctx, *ctx = &sctx;
		memset(&sctx, 0, sizeof(sctx));
		ctx->bios = guarded_alloc(512);
		ctx->bios_size = 512;
		uint8_t *b = ctx->bios;
		b[0x40] = ATOM_IIO_START;
		b[0x41] = 0x05;
		b[0x42] = 200;				/* opcode 200: rejected by fix */
		atom_index_iio(ctx, 0x40);
		printf("  result: walk stopped cleanly on opcode 200 (no trap)\n");
		return 0;
	}
	if (!strcmp(cs, "name")) {
		struct atom_context sctx, *ctx = &sctx;
		memset(&sctx, 0, sizeof(sctx));
		size_t sz = 512;
		ctx->bios = guarded_alloc(sz);
		ctx->bios_size = sz;
		memset((uint8_t *)ctx->bios + sz - 300, 'B', 300);
		atom_parse_name(ctx, (int)(sz - 300));
		printf("  result: copy bounded at image end (no trap)\n");
		return 0;
	}
	if (!strcmp(cs, "name_nul")) {
		static uint8_t backing[1024];
		memset(backing, 'C', sizeof(backing));
		struct atom_context sctx, *ctx = &sctx;
		memset(&sctx, 0, sizeof(sctx));
		ctx->bios = backing;
		ctx->bios_size = 1024;
		atom_parse_name(ctx, 0);
		return 0;
	}
	fprintf(stderr, "unknown case %s\n", cs);
	return 2;
}
