Merge commit '802798ce3c8baa4697120580f87bc1ee377306d3' as 'external/capstone'

This commit is contained in:
2026-05-11 11:55:07 +02:00
3968 changed files with 2967598 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.15)
enable_testing()
set(UNIT_TEST_SOURCES sstream.c utils.c)
if(CAPSTONE_RISCV_SUPPORT)
list(APPEND UNIT_TEST_SOURCES riscv_op_count_iter.c riscv_sysreg.c riscv_reg_access.c)
endif()
include_directories(include)
foreach(TSRC ${UNIT_TEST_SOURCES})
string(REGEX REPLACE ".c$" "" TBIN ${TSRC})
add_executable(${TBIN} "${TESTS_UNIT_DIR}/${TSRC}")
target_link_libraries(${TBIN} PRIVATE capstone)
add_test(NAME "unit_${TBIN}" COMMAND ${TBIN})
endforeach()
+2
View File
@@ -0,0 +1,2 @@
Nothing in here yet :(
+62
View File
@@ -0,0 +1,62 @@
// Copyright © 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#define CHECK_OS_EQUAL_RET_FALSE(OS, str) \
do { \
if (strcmp(OS.buffer, str) != 0) { \
printf("OS.buffer != str\n"); \
printf("OS.buffer: %s\n", OS.buffer); \
printf("str : %s\n", str); \
return false; \
} \
} while (0);
#define CHECK_STR_EQUAL_RET_FALSE(a, b) \
do { \
if (strcmp(a, b) != 0) { \
printf("%s != %s\n", a, b); \
return false; \
} \
} while (0);
#define CHECK_NULL_RET_FALSE(ptr) \
do { \
if (ptr != NULL) { \
printf(#ptr " is not NULL\n"); \
return false; \
} \
} while (0);
#define CHECK_PTR_EQUAL_RET_FALSE(a, b) \
do { \
if (a != b) { \
printf("%p != %p\n", a, b); \
return false; \
} \
} while (0);
#define CHECK_INT_EQUAL_RET_FALSE(a, b) \
do { \
if (a != b) { \
printf("%zu != %" PRId32 "\n", a, b); \
return false; \
} \
} while (0);
#define CHECK_OS_EQUALS_ANY_RET_FALSE(OS, ...) \
do { \
const char *const candidates__[] = { __VA_ARGS__ }; \
bool matched__ = false; \
for (size_t i__ = 0; \
i__ < (sizeof(candidates__) / sizeof(candidates__[0])); \
i__++) { \
if (strcmp((OS).buffer, candidates__[i__]) == 0) { \
matched__ = true; \
break; \
} \
} \
if (!matched__) { \
printf("Failed: got '%s'\n", (OS).buffer); \
return false; \
} \
} while (0);
+50
View File
@@ -0,0 +1,50 @@
#include "unit_test.h"
#include <capstone/capstone.h>
#include <stdio.h>
#include <inttypes.h>
// c.addi sp, -16 (2 ops) -> addi a0, a1, 1 (3 ops) -> c.addi sp, -16 (2 ops)
// Without RISCV_init_cs_detail(), op_count accumulates: 2, 5, 7
static bool test_riscv_op_count_no_stale()
{
printf("Test test_riscv_op_count_no_stale\n");
static const uint8_t code[] = {
0x41, 0x11, // c.addi sp, -16
0x13, 0x85, 0x15, 0x00, // addi a0, a1, 1
0x41, 0x11, // c.addi sp, -16
};
static const int32_t expected_op_counts[] = { 2, 3, 2 };
csh handle;
if (cs_open(CS_ARCH_RISCV, CS_MODE_RISCV64 | CS_MODE_RISCV_C,
&handle) != CS_ERR_OK) {
return false;
}
cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON | CS_OPT_DETAIL_REAL);
cs_option(handle, CS_OPT_SYNTAX,
CS_OPT_SYNTAX_NO_ALIAS_TEXT |
CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED);
cs_insn *insn = cs_malloc(handle);
const uint8_t *start = code;
size_t size = sizeof(code);
uint64_t address = 0;
size_t i = 0;
while (cs_disasm_iter(handle, &start, &size, &address, insn)) {
CHECK_INT_EQUAL_RET_FALSE((size_t)insn->detail->riscv.op_count,
expected_op_counts[i]);
++i;
}
cs_free(insn, 1);
cs_close(&handle);
return true;
}
int main(void)
{
bool ret = true;
ret &= test_riscv_op_count_no_stale();
return ret ? 0 : -1;
}
+146
View File
@@ -0,0 +1,146 @@
#include "unit_test.h"
#include <capstone/capstone.h>
#include <stdio.h>
#include <string.h>
static bool test_reg_access(csh handle, const uint8_t *code, size_t code_size,
const uint16_t *expected_read,
size_t expected_read_count,
const uint16_t *expected_write,
size_t expected_write_count)
{
cs_insn *insn;
size_t count = cs_disasm(handle, code, code_size, 0, 1, &insn);
if (count == 0) {
printf("Failed to disassemble instruction\n");
return false;
}
// debugging print, useful but noisy
//printf("\n\n======================= TEST GOT INSTRUCTION TEXT: %s %s \n\n======================= (num operands: %d)\n",
// insn->mnemonic, insn->op_str, insn->detail->riscv.op_count);
cs_regs regs_read, regs_write;
uint8_t regs_read_count, regs_write_count;
cs_err err = cs_regs_access(handle, insn, regs_read, &regs_read_count,
regs_write, &regs_write_count);
if (err != CS_ERR_OK) {
printf("cs_regs_access failed with error: %d\n", err);
cs_free(insn, count);
return false;
}
bool success = true;
if (regs_read_count != expected_read_count) {
printf("Read count mismatch: expected %zu, got %u\n",
expected_read_count, regs_read_count);
success = false;
} else {
for (size_t i = 0; i < expected_read_count; i++) {
bool found = false;
for (size_t j = 0; j < regs_read_count; j++) {
if (regs_read[j] == expected_read[i]) {
found = true;
break;
}
}
if (!found) {
printf("Expected read register %d not found\n",
expected_read[i]);
success = false;
}
}
}
if (regs_write_count != expected_write_count) {
printf("Write count mismatch: expected %zu, got %u\n",
expected_write_count, regs_write_count);
success = false;
} else {
for (size_t i = 0; i < expected_write_count; i++) {
bool found = false;
for (size_t j = 0; j < regs_write_count; j++) {
if (regs_write[j] == expected_write[i]) {
found = true;
break;
}
}
if (!found) {
printf("Expected write register %d not found\n",
expected_write[i]);
success = false;
}
}
}
cs_free(insn, count);
return success;
}
int main(void)
{
csh handle;
if (cs_open(CS_ARCH_RISCV, CS_MODE_RISCV64, &handle) != CS_ERR_OK) {
return 1;
}
cs_option(handle, CS_OPT_DETAIL, CS_OPT_DETAIL_REAL | CS_OPT_ON);
bool success[10];
memset(success, true, sizeof(success));
// addi a0, a1, 10 -> 0x00a58513
printf("Test 0: Testing addi a0, a1, 10\n");
uint8_t addi_code[] = { 0x13, 0x85, 0xa5, 0x00 };
uint16_t addi_read[] = { RISCV_REG_X11 }; // a1
uint16_t addi_write[] = { RISCV_REG_X10 }; // a0
success[0] = test_reg_access(handle, addi_code, sizeof(addi_code),
addi_read, 1, addi_write, 1);
// jalr ra, a1, 0 -> 0x000580e7 (rd=x1=ra, rs1=x11=a1, imm=0)
printf("Test 1: Testing jalr ra, a1, 0\n");
uint8_t jalr_code[] = { 0xe7, 0x80, 0x05, 0x00 };
uint16_t jalr_read[] = { RISCV_REG_X11 };
uint16_t jalr_write[] = { RISCV_REG_X1 }; // ra
success[1] = test_reg_access(handle, jalr_code, sizeof(jalr_code),
jalr_read, 1, jalr_write, 1);
// lb a0, 0(sp) -> 0x00010503
printf("Test 2: Testing lb a0, 0(sp)\n");
uint8_t lb_code[] = { 0x03, 0x05, 0x01, 0x00 };
uint16_t lb_read[] = { RISCV_REG_X2 }; // sp
uint16_t lb_write[] = { RISCV_REG_X10 };
success[2] = test_reg_access(handle, lb_code, sizeof(lb_code), lb_read,
1, lb_write, 1);
// c.addi a0, 10 -> 0x0529
printf("Test 3: Testing c.addi a0, 10\n");
uint8_t caddi_code[] = { 0x29, 0x05 };
uint16_t caddi_read[] = { RISCV_REG_X10 }; // x10 is both read and write
uint16_t caddi_write[] = { RISCV_REG_X10 };
success[3] = test_reg_access(handle, caddi_code, sizeof(caddi_code),
caddi_read, 1, caddi_write, 1);
// ecall -> 0x00000073
printf("Test 4: Testing ecall\n");
uint8_t ecall_code[] = { 0x73, 0x00, 0x00, 0x00 };
success[4] = test_reg_access(handle, ecall_code, sizeof(ecall_code),
NULL, 0, NULL, 0);
// csrrw a0, sstatus, a1 -> 0x10059533 (Wait, CSRRW is 0x10059573?)
// 0x10059573: csrrw x10, sstatus, x11
printf("Test 5: Testing csrrw a0, sstatus, a1\n");
uint8_t csrrw_code[] = { 0x73, 0x95, 0x05, 0x10 };
uint16_t csrrw_read[] = {
RISCV_REG_X11
}; // sstatus (CSR) should NOT be here
uint16_t csrrw_write[] = { RISCV_REG_X10 };
success[5] = test_reg_access(handle, csrrw_code, sizeof(csrrw_code),
csrrw_read, 1, csrrw_write, 1);
cs_close(&handle);
bool all_success = true;
for (int i = 0; i < sizeof(success) / sizeof(success[0]); i++) {
if (!success[i]) {
printf("Test %d failed\n", i);
all_success = false;
}
}
return all_success ? 0 : 1;
}
+83
View File
@@ -0,0 +1,83 @@
#include "unit_test.h"
#include <capstone/capstone.h>
#include <stdio.h>
#include <inttypes.h>
static bool test_riscv_sysreg()
{
printf("Test test_riscv_sysreg\n");
// csrr a0, sstatus (0x10002573)
// csrr a0, mtvec (0x30502573)
// csrr a0, mcause (0x34202573)
static const uint8_t code[] = {
0x73, 0x25, 0x00, 0x10, 0x73, 0x25,
0x50, 0x30, 0x73, 0x25, 0x20, 0x34,
};
static const uint16_t expected_sysregs[] = {
RISCV_SYSREG_SSTATUS,
RISCV_SYSREG_MTVEC,
RISCV_SYSREG_MCAUSE,
};
csh handle;
if (cs_open(CS_ARCH_RISCV, CS_MODE_RISCV64, &handle) != CS_ERR_OK) {
return false;
}
cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
cs_insn *insn;
size_t count = cs_disasm(handle, code, sizeof(code), 0, 0, &insn);
if (count != 3) {
cs_close(&handle);
return false;
}
for (size_t i = 0; i < count; i++) {
cs_riscv *riscv = &insn[i].detail->riscv;
bool found_sysreg = false;
for (size_t j = 0; j < riscv->op_count; j++) {
if (riscv->operands[j].type == RISCV_OP_CSR) {
CHECK_INT_EQUAL_RET_FALSE(
(size_t)riscv->operands[j].csr,
(int32_t)expected_sysregs[i]);
found_sysreg = true;
switch (riscv->operands[j].csr) {
case RISCV_SYSREG_SSTATUS:
printf(" Found SSTATUS\n");
break;
case RISCV_SYSREG_MTVEC:
printf(" Found MTVEC\n");
break;
case RISCV_SYSREG_MCAUSE:
printf(" Found MCAUSE\n");
break;
default:
printf(" Found other sysreg: 0x%x\n",
riscv->operands[j].csr);
break;
}
}
}
if (!found_sysreg) {
printf(" CSR operand not found in instruction %zu\n",
i);
cs_free(insn, count);
cs_close(&handle);
return false;
}
}
cs_free(insn, count);
cs_close(&handle);
return true;
}
int main(void)
{
if (test_riscv_sysreg()) {
return 0;
} else {
return 1;
}
}
+696
View File
@@ -0,0 +1,696 @@
// Copyright © 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#include "unit_test.h"
#include "../SStream.h"
#include "../utils.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
static void overflow_SStream_concat0(SStream *OS, bool *returned_in_time)
{
char buf[SSTREAM_BUF_LEN + 1] = { 0 };
memset(&buf, 'A', SSTREAM_BUF_LEN);
SStream_concat0(OS, buf);
*returned_in_time = OS->buffer[SSTREAM_BUF_LEN - 1] == '\0';
}
static void overflow_SStream_concat(SStream *OS, bool *returned_in_time)
{
char buf[SSTREAM_BUF_LEN + 1] = { 0 };
memset(&buf, 'A', SSTREAM_BUF_LEN);
SStream_concat(OS, "%s", buf);
*returned_in_time = OS->buffer[SSTREAM_BUF_LEN - 1] == '\0';
}
static void overflow_SStream_concat1(SStream *OS, bool *returned_in_time)
{
char buf[SSTREAM_BUF_LEN] = { 0 };
memset(&buf, 'A', SSTREAM_BUF_LEN - 1);
SStream_concat0(OS, buf);
// Should return here because null byte is overflown.
SStream_concat1(OS, 'A');
*returned_in_time = OS->buffer[SSTREAM_BUF_LEN - 1] == '\0';
}
static bool test_overflow_check()
{
printf("Test test_overflow_check\n");
SStream OS = { 0 };
SStream_Init(&OS);
bool returned_in_time = true;
overflow_SStream_concat0(&OS, &returned_in_time);
if (!returned_in_time) {
printf("Failed overflow_SStream_concat0\n");
return false;
}
overflow_SStream_concat(&OS, &returned_in_time);
if (!returned_in_time) {
printf("Failed overflow_SStream_concat\n");
return false;
}
overflow_SStream_concat1(&OS, &returned_in_time);
if (!returned_in_time) {
printf("Failed overflow_SStream_concat1\n");
return false;
}
return true;
}
static bool test_markup_os()
{
printf("Test test_markup_os\n");
SStream OS = { 0 };
SStream_Init(&OS);
SStream_concat0(&OS, "0");
CHECK_OS_EQUAL_RET_FALSE(OS, "0");
OS.markup_stream = true;
printUInt64(&OS, 0);
CHECK_OS_EQUAL_RET_FALSE(OS, "00");
markup_OS(&OS, Markup_Immediate);
printUInt64(&OS, 0);
CHECK_OS_EQUAL_RET_FALSE(OS, "00<imm:0>");
markup_OS(&OS, Markup_Memory);
printUInt32(&OS, 0);
CHECK_OS_EQUAL_RET_FALSE(OS, "00<imm:0><mem:0>");
markup_OS(&OS, Markup_Target);
printUInt32(&OS, 0);
CHECK_OS_EQUAL_RET_FALSE(OS, "00<imm:0><mem:0><tar:0>");
markup_OS(&OS, Markup_Register);
SStream_concat0(&OS, "r19");
CHECK_OS_EQUAL_RET_FALSE(OS, "00<imm:0><mem:0><tar:0><reg:r19>");
return true;
}
bool test_printint8()
{
printf("Test test_printint8\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt8(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printInt8(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printInt8(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "-0xa");
SStream_Flush(&OS, NULL);
printInt8(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "-9");
SStream_Flush(&OS, NULL);
printInt8(&OS, INT8_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0x7f");
SStream_Flush(&OS, NULL);
printInt8(&OS, INT8_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "-0x80");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printint16()
{
printf("Test test_printint16\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt16(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printInt16(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printInt16(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "-0xa");
SStream_Flush(&OS, NULL);
printInt16(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "-9");
SStream_Flush(&OS, NULL);
printInt16(&OS, INT16_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0x7fff");
SStream_Flush(&OS, NULL);
printInt16(&OS, INT16_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "-0x8000");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printint32()
{
printf("Test test_printint32\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt32(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printInt32(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printInt32(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "-0xa");
SStream_Flush(&OS, NULL);
printInt32(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "-9");
SStream_Flush(&OS, NULL);
printInt32(&OS, INT32_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0x7fffffff");
SStream_Flush(&OS, NULL);
printInt32(&OS, INT32_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "-0x80000000");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printint64()
{
printf("Test test_printint64\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt64(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printInt64(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printInt64(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "-0xa");
SStream_Flush(&OS, NULL);
printInt64(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "-9");
SStream_Flush(&OS, NULL);
printInt64(&OS, INT64_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0x7fffffffffffffff");
SStream_Flush(&OS, NULL);
printInt64(&OS, INT64_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "-0x8000000000000000");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printint32_bang()
{
printf("Test test_printint32Bang\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt32Bang(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xa");
SStream_Flush(&OS, NULL);
printInt32Bang(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#9");
SStream_Flush(&OS, NULL);
printInt32Bang(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "#-0xa");
SStream_Flush(&OS, NULL);
printInt32Bang(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#-9");
SStream_Flush(&OS, NULL);
printInt32Bang(&OS, INT32_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0x7fffffff");
SStream_Flush(&OS, NULL);
printInt32Bang(&OS, INT32_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "#-0x80000000");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printint64_bang()
{
printf("Test test_printint64Bang\n");
SStream OS = { 0 };
SStream_Init(&OS);
printInt64Bang(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xa");
SStream_Flush(&OS, NULL);
printInt64Bang(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#9");
SStream_Flush(&OS, NULL);
printInt64Bang(&OS, -(HEX_THRESHOLD + 1));
CHECK_OS_EQUAL_RET_FALSE(OS, "#-0xa");
SStream_Flush(&OS, NULL);
printInt64Bang(&OS, -HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#-9");
SStream_Flush(&OS, NULL);
printInt64Bang(&OS, INT64_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0x7fffffffffffffff");
SStream_Flush(&OS, NULL);
printInt64Bang(&OS, INT64_MIN);
CHECK_OS_EQUAL_RET_FALSE(OS, "#-0x8000000000000000");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printuint16()
{
printf("Test test_printuint16\n");
SStream OS = { 0 };
SStream_Init(&OS);
printUInt16(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printUInt16(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printUInt16(&OS, UINT16_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xffff");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printuint8()
{
printf("Test test_printuint8\n");
SStream OS = { 0 };
SStream_Init(&OS);
printUInt8(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xa");
SStream_Flush(&OS, NULL);
printUInt8(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "9");
SStream_Flush(&OS, NULL);
printUInt8(&OS, UINT8_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xff");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printuint32_bang()
{
printf("Test test_printuint32Bang\n");
SStream OS = { 0 };
SStream_Init(&OS);
printUInt32Bang(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xa");
SStream_Flush(&OS, NULL);
printUInt32Bang(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#9");
SStream_Flush(&OS, NULL);
printUInt32Bang(&OS, UINT32_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xffffffff");
SStream_Flush(&OS, NULL);
return true;
}
bool test_printuint64_bang()
{
printf("Test test_printuint64Bang\n");
SStream OS = { 0 };
SStream_Init(&OS);
printUInt64Bang(&OS, HEX_THRESHOLD + 1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xa");
SStream_Flush(&OS, NULL);
printUInt64Bang(&OS, HEX_THRESHOLD);
CHECK_OS_EQUAL_RET_FALSE(OS, "#9");
SStream_Flush(&OS, NULL);
printUInt64Bang(&OS, UINT64_MAX);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xffffffffffffffff");
SStream_Flush(&OS, NULL);
return true;
}
bool test_trimls()
{
printf("Test test_replc\n");
SStream OS = { 0 };
SStream_Init(&OS);
SStream_concat0(&OS, "AAA");
SStream_trimls(&OS);
CHECK_OS_EQUAL_RET_FALSE(OS, "AAA");
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, "\t AAA");
SStream_trimls(&OS);
CHECK_OS_EQUAL_RET_FALSE(OS, "AAA");
// Don't remove middle tabs and spaces
SStream_concat0(&OS, "\t AAA");
SStream_trimls(&OS);
CHECK_OS_EQUAL_RET_FALSE(OS, "AAA\t AAA");
SStream_Flush(&OS, NULL);
// Test do nothing
SStream_trimls(&OS);
CHECK_OS_EQUAL_RET_FALSE(OS, "");
// Everywhere tabs
char cmp_buf[SSTREAM_BUF_LEN] = { 0 };
memset(cmp_buf, '\t', sizeof(cmp_buf) - 1);
SStream_trimls(&OS);
CHECK_OS_EQUAL_RET_FALSE(OS, "");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 0);
return true;
}
bool test_stream_unsigned_imm()
{
printf("Test test_stream_unsigned_imm\n");
SStream OS = { 0 };
SStream_Init(&OS);
OS.unsigned_num = true;
printInt8(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xff");
SStream_Flush(&OS, NULL);
OS.unsigned_num = true;
printInt16(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xffff");
SStream_Flush(&OS, NULL);
OS.unsigned_num = true;
printInt32(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xffffffff");
SStream_Flush(&OS, NULL);
OS.unsigned_num = true;
printInt32Bang(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xffffffff");
SStream_Flush(&OS, NULL);
OS.unsigned_num = true;
printInt64(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "0xffffffffffffffff");
SStream_Flush(&OS, NULL);
OS.unsigned_num = true;
printInt64Bang(&OS, -1);
CHECK_OS_EQUAL_RET_FALSE(OS, "#0xffffffffffffffff");
SStream_Flush(&OS, NULL);
return true;
}
bool test_copy_mnem_opstr()
{
printf("Test test_copy_mnem_opstr\n");
SStream OS = { 0 };
SStream_Init(&OS);
SStream_concat0(&OS, "AAA\tBBBB");
char mnem_1[1] = { 0 };
char opstr_1[1] = { 0 };
SStream_extract_mnem_opstr(&OS, mnem_1, sizeof(mnem_1), opstr_1,
sizeof(opstr_1));
CHECK_STR_EQUAL_RET_FALSE(mnem_1, "");
CHECK_STR_EQUAL_RET_FALSE(opstr_1, "");
char mnem_3[3] = { 0 };
char opstr_3[3] = { 0 };
SStream_extract_mnem_opstr(&OS, mnem_3, sizeof(mnem_3), opstr_3,
sizeof(opstr_3));
CHECK_STR_EQUAL_RET_FALSE(mnem_3, "AA");
CHECK_STR_EQUAL_RET_FALSE(opstr_3, "BB");
char mnem_4[4] = { 0 };
char opstr_4[4] = { 0 };
SStream_extract_mnem_opstr(&OS, mnem_4, sizeof(mnem_4), opstr_4,
sizeof(opstr_4));
CHECK_STR_EQUAL_RET_FALSE(mnem_4, "AAA");
CHECK_STR_EQUAL_RET_FALSE(opstr_4, "BBB");
char mnem_5[5] = { 0 };
char opstr_5[5] = { 0 };
SStream_extract_mnem_opstr(&OS, mnem_5, sizeof(mnem_5), opstr_5,
sizeof(opstr_5));
CHECK_STR_EQUAL_RET_FALSE(mnem_5, "AAA");
CHECK_STR_EQUAL_RET_FALSE(opstr_5, "BBBB");
// No mnemonic
char mnem_9[9] = { 0 };
char opstr_9[9] = { 0 };
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, " AAA\tBBBB");
SStream_extract_mnem_opstr(&OS, mnem_9, sizeof(mnem_9), opstr_9,
sizeof(opstr_9));
CHECK_STR_EQUAL_RET_FALSE(mnem_9, "");
CHECK_STR_EQUAL_RET_FALSE(opstr_9, "AAA\tBBBB");
// No opstr
char mnem_6[6] = { 0 };
char opstr_6[6] = { 0 };
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, "AAA \t");
SStream_extract_mnem_opstr(&OS, mnem_6, sizeof(mnem_6), opstr_6,
sizeof(opstr_6));
CHECK_STR_EQUAL_RET_FALSE(mnem_6, "AAA");
CHECK_STR_EQUAL_RET_FALSE(opstr_6, "");
return true;
}
bool test_replc()
{
printf("Test test_replc\n");
SStream OS = { 0 };
SStream_Init(&OS);
char cmp_buf[SSTREAM_BUF_LEN] = { 0 };
memset(cmp_buf, 'A', sizeof(cmp_buf) - 1);
cmp_buf[100] = 'C';
SStream_concat0(&OS, cmp_buf);
cmp_buf[0] = 'B';
const char *next = SStream_replc(&OS, 'A', 'B');
CHECK_PTR_EQUAL_RET_FALSE(SStream_rbuf(&OS) + 1, next);
CHECK_OS_EQUAL_RET_FALSE(OS, cmp_buf);
cmp_buf[1] = 'B';
next = SStream_replc(&OS, 'A', 'B');
CHECK_PTR_EQUAL_RET_FALSE(SStream_rbuf(&OS) + 2, next);
CHECK_OS_EQUAL_RET_FALSE(OS, cmp_buf);
cmp_buf[100] = 'A'; // Replace the C from before
next = SStream_replc(&OS, 'C', 'A');
CHECK_PTR_EQUAL_RET_FALSE(SStream_rbuf(&OS) + 101, next);
CHECK_OS_EQUAL_RET_FALSE(OS, cmp_buf);
// X doesn't exist
next = SStream_replc(&OS, 'X', 'A');
CHECK_NULL_RET_FALSE(next);
// Replacing \0 byte is forbidden.
next = SStream_replc(&OS, '\0', 'A');
CHECK_NULL_RET_FALSE(next);
// But replacing any \0 byte is allowed.
SStream_Flush(&OS, NULL);
next = SStream_replc(&OS, '\0', 'A');
CHECK_PTR_EQUAL_RET_FALSE(SStream_rbuf(&OS) + 1, next);
CHECK_OS_EQUAL_RET_FALSE(OS, "A");
return true;
}
bool test_replc_str()
{
printf("Test test_replc_str\n");
SStream OS = { 0 };
SStream_Init(&OS);
SStream_replc_str(&OS, 'A', "REPLACED");
CHECK_OS_EQUAL_RET_FALSE(OS, "");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 0);
SStream_replc_str(&OS, '\0', "REPLACED");
CHECK_OS_EQUAL_RET_FALSE(OS, "REPLACED");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 8);
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, "\tA--X");
SStream_replc_str(&OS, 'A', "REPLACED");
CHECK_OS_EQUAL_RET_FALSE(OS, "\tREPLACED--X");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 12);
SStream_replc_str(&OS, 'X', "REPLACED");
CHECK_OS_EQUAL_RET_FALSE(OS, "\tREPLACED--REPLACED");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 19);
/// Too big strings are ignored.
char repl[SSTREAM_BUF_LEN] = { 0 };
memset(repl, 'A', sizeof(repl) - 1);
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, "\tA--");
SStream_replc_str(&OS, 'A', repl);
CHECK_OS_EQUAL_RET_FALSE(OS, "\tA--");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 4);
/// Last null byte is not replaced.
memset(repl, 'A', sizeof(repl) - 1);
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, repl);
SStream_replc_str(&OS, '\0', repl);
CHECK_OS_EQUAL_RET_FALSE(OS, repl);
CHECK_INT_EQUAL_RET_FALSE(OS.index, 511);
/// Last char is replaced.
memset(repl, 'A', sizeof(repl) - 1);
repl[sizeof(repl) - 2] = 'X';
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, repl);
SStream_replc_str(&OS, 'X', "Y");
repl[sizeof(repl) - 2] = 'Y';
CHECK_OS_EQUAL_RET_FALSE(OS, repl);
CHECK_INT_EQUAL_RET_FALSE(OS.index, 511);
// Possible overflow
char too_long[SSTREAM_BUF_LEN + 10] = { 0 };
memset(too_long, 'A', sizeof(too_long) - 1);
SStream_Flush(&OS, NULL);
SStream_concat0(&OS, "\tA--");
SStream_replc_str(&OS, 'A', too_long);
CHECK_OS_EQUAL_RET_FALSE(OS, "\tA--");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 4);
return true;
}
bool test_printfFloat()
{
printf("Test test_printfFloat\n");
SStream OS = { 0 };
SStream_Init(&OS);
printfFloat(&OS, "%f", 1.5f);
CHECK_OS_EQUAL_RET_FALSE(OS, "1.500000");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%.2f", 1.5f);
CHECK_OS_EQUAL_RET_FALSE(OS, "1.50");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%+.2f", -2.25f);
CHECK_OS_EQUAL_RET_FALSE(OS, "-2.25");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%e", 0.0f);
CHECK_OS_EQUAL_RET_FALSE(OS, "0.000000e+00");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%f", NAN);
CHECK_OS_EQUALS_ANY_RET_FALSE(OS, "nan", "NaN", "NAN", "nan(ind)",
"NaN(ind)", "NAN(ind)");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%f", INFINITY);
CHECK_OS_EQUALS_ANY_RET_FALSE(OS, "inf", "Inf", "INF", "infinity",
"Infinity", "INFINITY");
SStream_Flush(&OS, NULL);
printfFloat(&OS, "%f", -INFINITY);
CHECK_OS_EQUALS_ANY_RET_FALSE(OS, "-inf", "-Inf", "-INF", "-infinity",
"-Infinity", "-INFINITY");
SStream_Flush(&OS, NULL);
return true;
}
static int evil_vsnprintf(char *str, size_t size, const char *fmt, va_list ap)
{
(void)str;
(void)size;
(void)fmt;
(void)ap;
return -1; // forces index underflow
}
/// Possible underflow of SStream.index and subsequent OOB reads/writes.
/// Reported by Finder16.
bool test_underflow_in_sstream(void)
{
cs_opt_mem mem = { .malloc = malloc,
.calloc = calloc,
.realloc = realloc,
.free = free,
.vsnprintf = evil_vsnprintf };
cs_option(0, CS_OPT_MEM, (size_t)&mem);
SStream OS;
SStream_Init(&OS);
SStream_concat(&OS, "%s", "AAAA"); // index += -1
SStream_concat1(&OS, 'B'); // writes before buffer => crash/ASan hit
CHECK_OS_EQUAL_RET_FALSE(OS, "B");
CHECK_INT_EQUAL_RET_FALSE(OS.index, 1);
return true;
}
int main()
{
bool result = true;
result &= test_markup_os();
result &= test_overflow_check();
result &= test_printint8();
result &= test_printint16();
result &= test_printint32();
result &= test_printint64();
result &= test_printint32_bang();
result &= test_printint64_bang();
result &= test_printuint8();
result &= test_printuint16();
result &= test_printuint32_bang();
result &= test_printuint64_bang();
result &= test_stream_unsigned_imm();
result &= test_replc();
result &= test_replc_str();
result &= test_printfFloat();
result &= test_copy_mnem_opstr();
result &= test_trimls();
result &= test_underflow_in_sstream();
if (result) {
printf("All tests passed.\n");
} else {
printf("Some tests failed.\n");
}
return result ? 0 : -1;
}
+76
View File
@@ -0,0 +1,76 @@
// Copyright © 2024 Rot127 <unisono@quyllur.org>
// SPDX-License-Identifier: BSD-3
#include "unit_test.h"
#include "../utils.h"
#include <stdio.h>
#include <string.h>
static bool test_str_append_no_realloc()
{
printf("Test test_str_append_no_realloc\n");
char str_a[] = "AAAA\0\0\0\0\0";
char str_b[] = "BBBB";
char str_c[] = "\0\0\0\0\0";
CHECK_NULL_RET_FALSE(str_append(NULL, NULL));
CHECK_NULL_RET_FALSE(str_append(str_a, NULL));
CHECK_NULL_RET_FALSE(str_append(NULL, str_b));
str_append_no_realloc(str_a, sizeof(str_a), str_c);
CHECK_STR_EQUAL_RET_FALSE(str_a, "AAAA");
str_append_no_realloc(str_a, sizeof(str_a), str_b);
CHECK_STR_EQUAL_RET_FALSE(str_a, "AAAABBBB");
str_append_no_realloc(str_c, sizeof(str_c), str_b);
CHECK_STR_EQUAL_RET_FALSE(str_c, "BBBB");
str_append_no_realloc(str_b, sizeof(str_b), str_c);
CHECK_STR_EQUAL_RET_FALSE(str_b, "BBBB");
return true;
}
static bool test_str_append()
{
printf("Test test_str_append\n");
char *str_a = NULL;
char *str_b = NULL;
CHECK_NULL_RET_FALSE(str_append(str_a, str_b));
str_a = calloc(5, sizeof(char));
memcpy(str_a, "AAAA", 5);
CHECK_NULL_RET_FALSE(str_append(str_a, str_b));
str_b = calloc(5, sizeof(char));
str_a = str_append(str_a, str_b);
CHECK_STR_EQUAL_RET_FALSE(str_a, "AAAA");
memcpy(str_b, "BBBB", 5);
str_a = str_append(str_a, str_b);
CHECK_STR_EQUAL_RET_FALSE(str_a, "AAAABBBB");
memset(str_a, 0, strlen(str_a) + 1);
str_a = str_append(str_a, str_b);
CHECK_STR_EQUAL_RET_FALSE(str_a, "BBBB");
free(str_a);
free(str_b);
return true;
}
int main()
{
bool result = true;
result &= test_str_append();
result &= test_str_append_no_realloc();
if (result) {
printf("All tests passed.\n");
} else {
printf("Some tests failed.\n");
}
return result ? 0 : -1;
}