#include "amduat/pel/queue.h" #include "amduat/hash/asl1.h" #include #include #include #include #include #include #include #include #include #include static void store_u32_be(uint8_t *out, uint32_t value) { out[0] = (uint8_t)((value >> 24) & 0xffu); out[1] = (uint8_t)((value >> 16) & 0xffu); out[2] = (uint8_t)((value >> 8) & 0xffu); out[3] = (uint8_t)(value & 0xffu); } static void free_ref(amduat_reference_t *ref) { if (ref == NULL) { return; } free((void *)ref->digest.data); ref->digest.data = NULL; ref->digest.len = 0; } static bool make_ref(amduat_hash_id_t hash_id, uint8_t fill, amduat_reference_t *out_ref) { const amduat_hash_asl1_desc_t *desc; uint8_t *digest; if (out_ref == NULL) { return false; } desc = amduat_hash_asl1_desc_lookup(hash_id); if (desc == NULL || desc->digest_len == 0) { return false; } digest = (uint8_t *)malloc(desc->digest_len); if (digest == NULL) { return false; } memset(digest, fill, desc->digest_len); *out_ref = amduat_reference(hash_id, amduat_octets(digest, desc->digest_len)); return true; } static bool join_path(const char *base, const char *segment, char **out_path) { size_t base_len; size_t seg_len; bool needs_sep; size_t total_len; char *buffer; size_t offset; if (base == NULL || segment == NULL || out_path == NULL) { return false; } if (base[0] == '\0' || segment[0] == '\0') { return false; } base_len = strlen(base); seg_len = strlen(segment); needs_sep = base[base_len - 1u] != '/'; total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u; buffer = (char *)malloc(total_len); if (buffer == NULL) { return false; } offset = 0u; memcpy(buffer + offset, base, base_len); offset += base_len; if (needs_sep) { buffer[offset++] = '/'; } memcpy(buffer + offset, segment, seg_len); offset += seg_len; buffer[offset] = '\0'; *out_path = buffer; return true; } static bool write_all(int fd, const uint8_t *data, size_t len) { size_t total = 0u; while (total < len) { ssize_t rc = write(fd, data + total, len - total); if (rc < 0) { if (errno == EINTR) { continue; } return false; } total += (size_t)rc; } return true; } static char *make_temp_root(void) { char *templ; const char template_prefix[] = "amduat_test_pel_queue_XXXXXX"; templ = (char *)malloc(sizeof(template_prefix)); if (templ == NULL) { return NULL; } memcpy(templ, template_prefix, sizeof(template_prefix)); if (mkdtemp(templ) == NULL) { free(templ); return NULL; } return templ; } static void cleanup_root(const char *root) { char *path = NULL; if (root == NULL) { return; } if (join_path(root, "PEL_QUEUE", &path)) { unlink(path); free(path); } if (join_path(root, "PEL_HEAD", &path)) { unlink(path); free(path); } if (join_path(root, "TGK_HEAD", &path)) { unlink(path); free(path); } rmdir(root); } static int test_append_and_read(void) { char *root = NULL; amduat_reference_t ref1; amduat_reference_t ref2; amduat_reference_t out_ref; uint64_t offset = 0; uint64_t next_offset = 0; int exit_code = 1; root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return exit_code; } if (!make_ref(AMDUAT_HASH_ASL1_ID_SHA256, 0x11u, &ref1) || !make_ref(AMDUAT_HASH_ASL1_ID_SHA256, 0x22u, &ref2)) { fprintf(stderr, "ref alloc failed\n"); goto cleanup; } if (!amduat_pel_queue_append_trace_ref(root, ref1) || !amduat_pel_queue_append_trace_ref(root, ref2)) { fprintf(stderr, "append failed\n"); goto cleanup; } if (!amduat_pel_queue_read_next(root, offset, &out_ref, &next_offset)) { fprintf(stderr, "read first failed\n"); goto cleanup; } if (!amduat_reference_eq(out_ref, ref1)) { fprintf(stderr, "first ref mismatch\n"); free_ref(&out_ref); goto cleanup; } free_ref(&out_ref); offset = next_offset; if (!amduat_pel_queue_read_next(root, offset, &out_ref, &next_offset)) { fprintf(stderr, "read second failed\n"); goto cleanup; } if (!amduat_reference_eq(out_ref, ref2)) { fprintf(stderr, "second ref mismatch\n"); free_ref(&out_ref); goto cleanup; } free_ref(&out_ref); offset = next_offset; if (amduat_pel_queue_read_next(root, offset, &out_ref, &next_offset)) { fprintf(stderr, "expected end of queue\n"); free_ref(&out_ref); goto cleanup; } if (next_offset != offset) { fprintf(stderr, "unexpected offset on empty read\n"); goto cleanup; } exit_code = 0; cleanup: free_ref(&ref1); free_ref(&ref2); cleanup_root(root); free(root); return exit_code; } static int test_truncated_tail(void) { char *root = NULL; char *queue_path = NULL; amduat_reference_t ref; amduat_reference_t out_ref; uint64_t offset = 0; uint64_t next_offset = 0; uint8_t len_bytes[4]; int fd; int exit_code = 1; root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return exit_code; } if (!make_ref(AMDUAT_HASH_ASL1_ID_SHA256, 0x33u, &ref)) { fprintf(stderr, "ref alloc failed\n"); goto cleanup; } if (!amduat_pel_queue_append_trace_ref(root, ref)) { fprintf(stderr, "append failed\n"); goto cleanup; } if (!join_path(root, "PEL_QUEUE", &queue_path)) { fprintf(stderr, "queue path failed\n"); goto cleanup; } fd = open(queue_path, O_WRONLY | O_APPEND); free(queue_path); queue_path = NULL; if (fd < 0) { fprintf(stderr, "open queue failed\n"); goto cleanup; } store_u32_be(len_bytes, 7u); if (!write_all(fd, len_bytes, sizeof(len_bytes))) { close(fd); fprintf(stderr, "write truncated failed\n"); goto cleanup; } if (close(fd) != 0) { fprintf(stderr, "close queue failed\n"); goto cleanup; } if (!amduat_pel_queue_read_next(root, offset, &out_ref, &next_offset)) { fprintf(stderr, "read first failed\n"); goto cleanup; } free_ref(&out_ref); offset = next_offset; if (amduat_pel_queue_read_next(root, offset, &out_ref, &next_offset)) { fprintf(stderr, "expected truncated tail to stop\n"); free_ref(&out_ref); goto cleanup; } if (next_offset != offset) { fprintf(stderr, "unexpected offset on truncated read\n"); goto cleanup; } exit_code = 0; cleanup: free_ref(&ref); cleanup_root(root); free(root); return exit_code; } static int test_heads_round_trip(void) { char *root = NULL; uint64_t pel_offset = 12345u; uint64_t tgk_offset = 67890u; uint64_t loaded = 0u; int exit_code = 1; root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return exit_code; } if (!amduat_pel_queue_store_pel_head(root, pel_offset) || !amduat_pel_queue_load_pel_head(root, &loaded)) { fprintf(stderr, "pel head round-trip failed\n"); goto cleanup; } if (loaded != pel_offset) { fprintf(stderr, "pel head mismatch\n"); goto cleanup; } if (!amduat_pel_queue_store_tgk_head(root, tgk_offset) || !amduat_pel_queue_load_tgk_head(root, &loaded)) { fprintf(stderr, "tgk head round-trip failed\n"); goto cleanup; } if (loaded != tgk_offset) { fprintf(stderr, "tgk head mismatch\n"); goto cleanup; } exit_code = 0; cleanup: cleanup_root(root); free(root); return exit_code; } int main(void) { if (test_append_and_read() != 0) { return 1; } if (test_truncated_tail() != 0) { return 1; } if (test_heads_round_trip() != 0) { return 1; } return 0; }