Files
Interpreter/tests/src/vm_test.cpp
T
Admin b13585711a 阶段 C 步骤 8:vm 读侧型号匹配 + SHA-256 校验。
- vm/Image.cpp:执行器侧 SHA-256 实现(与 compiler 各一份)、fill_model_id、
  model_id()/model_matches()/sha_ok();data_len 不含 SHA 尾
- Machine::create:sha256 不匹配 → 'image sha256 mismatch';型号不匹配 →
  'image model mismatch'(内建 kModelName=STATOR/kModelVersion=1),均拒绝
- vm_test:手拼映像填型号(修越界读 bug)+ compiler::sha256 填尾(95 断言);
  新增篡改拒绝(sha256 mismatch)与型号不匹配(重算 SHA 后 model mismatch)用例
- 验证:line1 正常执行 Q=1;篡改一字节 → 执行器 'image sha256 mismatch' 拒绝;ctest 12/12
2026-08-21 22:29:33 +08:00

514 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file vm_test.cpp
* @brief VM 测试(12.9):手工映像 + 编译器产物 + 定时器 + 确定性
* @author
* @date 2026-08-21
*/
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include "compiler/Codegen.h"
#include "compiler/Linker.h"
#include "compiler/Project.h"
#include "compiler/Stb.h"
#include "compiler/Typecheck.h"
#include "isa/Encode.h"
#include "isa/Instr.h"
#include "isa/Op.h"
#include "vm/Machine.h"
#ifndef REPO_ROOT
#define REPO_ROOT "."
#endif
static int g_checks = 0;
#define CHECK(cond) \
do { \
if (!(cond)) { \
std::printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); \
return false; \
} \
++g_checks; \
} while (0)
// ---- 数据区槽访问 ----
static int64_t slot(const vm::Machine& m, int s) {
int64_t v = 0;
for (int i = 0; i < 8; ++i) {
v |= static_cast<int64_t>(m.data()[s * 8 + i]) << (8 * i);
}
return v;
}
static void wslot(vm::Machine& m, int s, int64_t v) {
for (int i = 0; i < 8; ++i) {
m.data()[s * 8 + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFF);
}
}
// ---- 编译器产物全链路(编译 → 建机 → 跑周期)----
static bool make_machine(const char* dir, vm::Machine* m, std::string* err) {
using namespace compiler;
MachineConfig cfg;
if (!cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", err)) {
return false;
}
const std::string toml = std::string(REPO_ROOT) + "/" + dir + "/project.toml";
Project p;
if (!parse_project(toml, &p, err)) {
return false;
}
std::vector<SourceUnit> units;
for (const std::string& f : compile_files(p)) {
SourceUnit u;
if (!load_unit(p.base_dir + "/" + f, &u, err)) {
return false;
}
units.push_back(std::move(u));
}
LinkResult link;
if (!link_project(p, units, &link, err)) {
return false;
}
if (!check_project(p, units, link, err)) {
return false;
}
std::vector<uint8_t> img;
if (!codegen_project(p, units, link, cfg, &img, err)) {
return false;
}
return vm::Machine::create(img, m, err);
}
// ---- 手工拼映像 ----
struct HFunc {
std::vector<isa::Instr> code;
uint32_t nregs = 8;
};
static std::vector<uint8_t> hand_image(const std::vector<vm::ConstEntry>& consts,
const std::vector<HFunc>& funcs,
uint32_t entry = 0,
uint32_t cycle_limit = 100000,
const std::vector<uint8_t>& data = {}) {
size_t code_total = 0;
for (const HFunc& f : funcs) {
code_total += f.code.size() * 4;
}
const uint32_t off_const = 104;
const uint32_t off_funcs = off_const + static_cast<uint32_t>(consts.size()) * 12;
const uint32_t off_code = off_funcs + static_cast<uint32_t>(funcs.size()) * 12;
const uint32_t off_data = off_code + static_cast<uint32_t>(code_total);
const uint32_t off_end = off_data + static_cast<uint32_t>(data.size()) + 32; // SHA 尾占位
std::vector<uint8_t> b(off_end, 0);
auto put32 = [&](size_t o, uint32_t v) {
b[o + 0] = static_cast<uint8_t>(v & 0xFFu);
b[o + 1] = static_cast<uint8_t>((v >> 8) & 0xFFu);
b[o + 2] = static_cast<uint8_t>((v >> 16) & 0xFFu);
b[o + 3] = static_cast<uint8_t>((v >> 24) & 0xFFu);
};
auto put64 = [&](size_t o, uint64_t v) {
for (int i = 0; i < 8; ++i) {
b[o + i] = static_cast<uint8_t>((v >> (8 * i)) & 0xFFu);
}
};
put32(0, 0x43545353u);
put32(4, 1u);
put32(8, cycle_limit);
put32(12, 10); // dt_ms
put32(24, entry);
put32(44, static_cast<uint32_t>(consts.size()));
put32(48, static_cast<uint32_t>(funcs.size()));
put32(52, off_const);
put32(56, off_funcs);
put32(60, off_code);
put32(64, off_data);
put32(68, off_data);
for (size_t i = 0; i < consts.size(); ++i) {
const size_t o = off_const + i * 12;
put32(o, static_cast<uint32_t>(consts[i].tag));
put64(o + 4, consts[i].value);
}
size_t acc = 0;
for (size_t i = 0; i < funcs.size(); ++i) {
const size_t o = off_funcs + i * 12;
put32(o, funcs[i].nregs);
put32(o + 4, static_cast<uint32_t>(acc));
put32(o + 8, static_cast<uint32_t>(funcs[i].code.size()));
acc += funcs[i].code.size() * 4;
}
size_t c = off_code;
for (const HFunc& f : funcs) {
for (const isa::Instr in : f.code) {
put32(c, in);
c += 4;
}
}
for (size_t i = 0; i < data.size(); ++i) {
b[off_data + i] = data[i];
}
// 型号标识(STATOR1+ 真实 SHA-256 尾(用 compiler 实现填)
const char* mid = "STATOR1";
for (size_t i = 0; i < 32; ++i) {
b[72 + i] = i < 7 ? static_cast<uint8_t>(mid[i]) : 0;
}
uint8_t digest[32];
compiler::sha256(b.data(), b.size() - 32, digest);
for (int i = 0; i < 32; ++i) {
b[b.size() - 32 + i] = digest[i];
}
return b;
}
// ---- 1. 手工映像:标量 + 跳转 + 数据区 ----
static bool test_hand_scalar() {
// r8 := 5r9 := r8 + 3;槽 0 ← r9;槽 1 → r10;JT 跳过一条
const std::vector<vm::ConstEntry> consts = {
{1, 5},
{1, 3},
{1, 1},
};
const std::vector<HFunc> funcs = {{
{
isa::enc_imm(isa::Op::LOADK, 8, 0),
isa::enc_rr(isa::Op::MOVE, 9, 8),
isa::enc_imm(isa::Op::LOADK, 11, 1),
isa::enc_rrr(isa::Op::ADD, 9, 9, 11),
isa::enc_slot(isa::Op::STORE_GLOBAL, 9, 0),
isa::enc_slot(isa::Op::LOAD_GLOBAL, 10, 0),
isa::enc_jc(isa::Op::JT, 9, 1), // r9≠0 → 跳过下一条
isa::enc_imm(isa::Op::LOADK, 10, 2),
isa::enc_ret(),
},
12,
}};
const std::vector<uint8_t> img = hand_image(consts, funcs, 0, 100000,
std::vector<uint8_t>(16, 0)); // 2 槽
vm::Machine m;
std::string err;
CHECK(vm::Machine::create(img, &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 5);
CHECK(m.reg(9) == 8); // 5 + 3
CHECK(slot(m, 0) == 8); // 槽 0 收到 8
CHECK(m.reg(10) == 8); // 槽 1 读回(JT 跳过覆盖)
CHECK(m.cycle_count() == 8);
return true;
}
// ---- 2. 手工映像:CALL/RET 帧复制 ----
static bool test_hand_call() {
// fn0(入口):r8=5、r2=7、实参 r1←r8CALL 1;结果 r0 → r8
// fn1r0 = r1 + r2r1/r2 来自调用约定区复制)
const std::vector<vm::ConstEntry> consts = {
{1, 5},
{1, 7},
};
const std::vector<HFunc> funcs = {
{{
isa::enc_imm(isa::Op::LOADK, 8, 0),
isa::enc_imm(isa::Op::LOADK, 2, 1),
isa::enc_rr(isa::Op::MOVE, 1, 8),
isa::enc_call(1),
isa::enc_rr(isa::Op::MOVE, 8, 0),
isa::enc_ret(),
},
12},
{{
isa::enc_rrr(isa::Op::ADD, 0, 1, 2),
isa::enc_ret(),
},
10},
};
const std::vector<uint8_t> img = hand_image(consts, funcs);
vm::Machine m;
std::string err;
CHECK(vm::Machine::create(img, &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 12); // 5 + 7
CHECK(m.call_depth() == 1);
return true;
}
// ---- 3. 手工映像:自调用 → StackOverflow ----
static bool test_stack_overflow() {
const std::vector<HFunc> funcs = {{
{
isa::enc_call(0),
isa::enc_ret(),
},
8,
}};
const std::vector<uint8_t> img = hand_image({}, funcs);
vm::Machine m;
std::string err;
CHECK(vm::Machine::create(img, &m, &err));
CHECK(m.run_cycle() == vm::Fault::StackOverflow);
CHECK(m.call_depth() == 1); // 周期结束已清栈(留 MAIN
return true;
}
// ---- 4. 手工映像:越界槽 → BadSlot ----
static bool test_bad_slot() {
const std::vector<HFunc> funcs = {{
{
isa::enc_slot(isa::Op::STORE_GLOBAL, 8, 60000), // 数据区 0 字节
isa::enc_ret(),
},
12,
}};
const std::vector<uint8_t> img = hand_image({}, funcs);
vm::Machine m;
std::string err;
CHECK(vm::Machine::create(img, &m, &err));
CHECK(m.run_cycle() == vm::Fault::BadSlot);
return true;
}
// ---- 5. 编译器产物正例 ----
static bool test_cases_positive() {
std::string err;
vm::Machine m;
// 01 空 MAIN
CHECK(make_machine("tests/cases/01_empty_main", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
// 02 BOOL 赋值:a=TRUE(r8) b=FALSE(r9)
CHECK(make_machine("tests/cases/02_bool_assign", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 1 && m.reg(9) == 0);
// 03 短路:a=0 b=0 → x=0
CHECK(make_machine("tests/cases/03_short_circuit", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(10) == 0);
// 04 IFsel=0 → out=10
CHECK(make_machine("tests/cases/04_if_elsif_else", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(9) == 10);
// 05 WHILEn=10
CHECK(make_machine("tests/cases/05_while_normal", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 10);
// 07 算术:a=42 b=10 c=32 eq=1
CHECK(make_machine("tests/cases/07_int_arith", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 42 && m.reg(9) == 10 && m.reg(10) == 32 && m.reg(11) == 1);
// 08 TIME 字面量:t1=10 t2=1250
CHECK(make_machine("tests/cases/08_time_literal", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 10 && m.reg(9) == 1250);
// 09 GVL + EXTERNALx = G1 初值 5(槽 0
CHECK(make_machine("tests/cases/09_gvl_external", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 5);
// 13 FUNCTIONx = Add(3,4) = 7
CHECK(make_machine("tests/cases/13_function_call", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 7);
// 15 FB 内联:starter(start=TRUE, stop=FALSE) → q=TRUE
CHECK(make_machine("tests/cases/15_fb_instance", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(m.reg(8) == 1);
// 17 TONdt=10、pt=30in 恒真 → 第 3 周期 q=1(槽 2)
CHECK(make_machine("tests/cases/17_ton", &m, &err));
m.run_cycle();
m.run_cycle();
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(slot(m, 2) == 1); // t.Q
CHECK(slot(m, 3) == 30); // t.ET
// 18 TOF/CTU:跑一个周期无故障
CHECK(make_machine("tests/cases/18_tof_ctu", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
// 20 line1I 组合 → Q0_0 真值表(槽 0=急停 1=I0_0 2=I0_1 3=Q0_0
CHECK(make_machine("tests/cases/20_line1", &m, &err));
struct {
int start, stop, es, expect;
} table[] = {
{1, 0, 0, 1},
{1, 1, 0, 0},
{0, 0, 0, 0},
{1, 0, 1, 0},
};
for (const auto& t : table) {
wslot(m, 0, t.es);
wslot(m, 1, t.start);
wslot(m, 2, t.stop);
CHECK(m.run_cycle() == vm::Fault::None);
CHECK(slot(m, 3) == t.expect);
}
return true;
}
// ---- 6. 用例 06cycle_limit 打满 ----
static bool test_cycle_limit() {
std::string err;
vm::Machine m;
CHECK(make_machine("tests/cases/06_while_cycle_limit", &m, &err));
CHECK(m.run_cycle() == vm::Fault::CycleLimit);
return true;
}
// ---- 7. 确定性:同一 I 序列跑两遍,数据区一致 ----
static bool test_determinism() {
std::string err;
vm::Machine a, b;
CHECK(make_machine("tests/cases/20_line1", &a, &err));
CHECK(make_machine("tests/cases/20_line1", &b, &err));
const int seq[4][3] = {{1, 0, 0}, {1, 1, 0}, {0, 0, 0}, {1, 0, 1}};
for (int rep = 0; rep < 2; ++rep) {
vm::Machine& m = rep ? b : a;
for (const auto& s : seq) {
wslot(m, 0, s[2]);
wslot(m, 1, s[0]);
wslot(m, 2, s[1]);
CHECK(m.run_cycle() == vm::Fault::None);
}
}
// 两遍结束:数据区逐字节一致(含 starter 实例状态)
CHECK(a.data_len() == b.data_len());
for (size_t i = 0; i < a.data_len(); ++i) {
if (a.data()[i] != b.data()[i]) {
std::printf("FAIL determinism byte %zu: %d vs %d\n", i, a.data()[i], b.data()[i]);
return false;
}
}
++g_checks;
return true;
}
// ---- 8. step 单步:停在指令边界 ----
static bool test_step() {
std::string err;
vm::Machine m;
CHECK(make_machine("tests/cases/02_bool_assign", &m, &err));
// 3 条指令:LOADK ×2 + RET
CHECK(m.step());
CHECK(m.cycle_count() == 1 && m.reg(8) == 1);
CHECK(m.step());
CHECK(m.cycle_count() == 2 && m.reg(9) == 0);
CHECK(!m.step()); // RET → 周期结束
CHECK(m.cycle_count() == 3);
CHECK(!m.step()); // 结束后不再执行
return true;
}
// ---- 9. 手工映像:坏常量 id → BadConst ----
static bool test_bad_const() {
const std::vector<HFunc> funcs = {{
{
isa::enc_imm(isa::Op::LOADK, 8, 99), // 常量表为空
isa::enc_ret(),
},
12,
}};
const std::vector<uint8_t> img = hand_image({}, funcs);
vm::Machine m;
std::string err;
CHECK(vm::Machine::create(img, &m, &err));
CHECK(m.run_cycle() == vm::Fault::BadConst);
return true;
}
// ---- 10. 12.13 校验:SHA 篡改拒绝 + 型号不匹配拒绝 ----
static bool test_verify() {
std::string err;
vm::Machine m;
// 正常映像可跑
CHECK(make_machine("tests/cases/01_empty_main", &m, &err));
CHECK(m.run_cycle() == vm::Fault::None);
// SHA 篡改:改一字节 → create 拒绝
{
const std::string toml = std::string(REPO_ROOT) + "/tests/cases/01_empty_main/project.toml";
compiler::Project p;
CHECK(compiler::parse_project(toml, &p, &err));
std::vector<compiler::SourceUnit> units;
for (const std::string& f : compiler::compile_files(p)) {
compiler::SourceUnit u;
CHECK(compiler::load_unit(p.base_dir + "/" + f, &u, &err));
units.push_back(std::move(u));
}
compiler::LinkResult link;
CHECK(compiler::link_project(p, units, &link, &err));
compiler::MachineConfig cfg;
CHECK(cfg.load(std::string(REPO_ROOT) + "/compiler/machine.toml", &err));
std::vector<uint8_t> img;
CHECK(compiler::codegen_project(p, units, link, cfg, &img, &err));
img[100] ^= 0x01; // 篡改代码段一字节
vm::Machine bad;
CHECK(!vm::Machine::create(img, &bad, &err));
CHECK(err.find("sha256 mismatch") != std::string::npos);
}
// 型号不匹配:手拼映像改型号(重算 SHA,仅型号不一致)→ create 拒绝
{
const std::vector<vm::ConstEntry> consts;
const std::vector<HFunc> funcs = {{
{
isa::enc_ret(),
},
8,
}};
std::vector<uint8_t> img = hand_image(consts, funcs);
for (int i = 0; i < 5; ++i) {
img[72 + i] = "OTHER"[i];
}
uint8_t digest[32];
compiler::sha256(img.data(), img.size() - 32, digest);
for (int i = 0; i < 32; ++i) {
img[img.size() - 32 + i] = digest[i];
}
vm::Machine bad;
CHECK(!vm::Machine::create(img, &bad, &err));
CHECK(err.find("model mismatch") != std::string::npos);
}
return true;
}
int main() {
if (!test_hand_scalar()) return 1;
if (!test_hand_call()) return 1;
if (!test_stack_overflow()) return 1;
if (!test_bad_slot()) return 1;
if (!test_cases_positive()) return 1;
if (!test_cycle_limit()) return 1;
if (!test_determinism()) return 1;
if (!test_step()) return 1;
if (!test_bad_const()) return 1;
if (!test_verify()) return 1;
std::printf("vm_test: %d checks passed\n", g_checks);
return 0;
}