Compare commits

...

1 Commits

Author SHA1 Message Date
Peng Wang
518e08a537 wip 2025-11-21 17:51:34 -05:00
16 changed files with 486 additions and 1 deletions

View File

@@ -671,11 +671,126 @@ struct Wasm_test : public beast::unit_test::suite
}
}
void
testWasmMemory()
{
testcase("Wasm additional memory limit tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(memoryPointerAtLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryPointerOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(memoryOffsetOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(memoryEndOfWordOverLimitHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(memoryGrow0To1PageHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryLastByteOf8MBHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(memoryGrow1MoreThan8MBHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == -1);
}
}
}
void
testWasmTable()
{
testcase("Wasm table limit tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(table64ElementsHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
if (BEAST_EXPECT(re.has_value()))
{
BEAST_EXPECT(re->result == 1);
}
}
{
auto const ws = boost::algorithm::unhex(table65ElementsHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
{
auto const ws = boost::algorithm::unhex(table2TablesHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
testWasmProposal()
{
testcase("Wasm disabled proposal tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(proposalMutableGlobalHex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
testWasmTrap()
{
testcase("Wasm trap tests");
auto& engine = WasmEngine::instance();
{
auto const ws = boost::algorithm::unhex(divideBy0Hex);
Bytes const wasm(ws.begin(), ws.end());
auto const re = engine.run(wasm, "finish");
BEAST_EXPECT(!re.has_value());
}
}
void
run() override
{
using namespace test::jtx;
/*
testGetDataHelperFunctions();
testWasmLib();
testBadWasm();
@@ -697,6 +812,11 @@ struct Wasm_test : public beast::unit_test::suite
testCodecovWasm();
testDisabledFloat();
*/
testWasmMemory();
testWasmTable();
testWasmProposal();
testWasmTrap();
// perfTest();
}

View File

@@ -9578,3 +9578,56 @@ extern std::string const updateDataWasmHex =
"3861373930636664623432626432343732302900490f7461726765745f6665617475726573"
"042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e"
"63652d74797065732b0a6d756c746976616c7565";
extern std::string const memoryPointerAtLimitHex =
"0061736d010000000105016000017f030201000503010001070a010666696e69736800000a"
"0e010c0041ffff032d00001a41010b";
extern std::string const memoryPointerOverLimitHex =
"0061736d010000000105016000017f030201000503010001070a010666696e69736800000a"
"0e010c00418080042d00001a41010b";
extern std::string const memoryOffsetOverLimitHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a0e010c00410028028080041a41010b";
extern std::string const memoryEndOfWordOverLimitHex =
"0061736d010000000105016000017f030201000503010001071302066d656d6f7279020006"
"66696e69736800000a0e010c0041feff032802001a41010b";
extern std::string const memoryGrow0To1PageHex =
"0061736d010000000105016000017f030201000503010000071302066d656d6f7279020006"
"66696e69736800000a0b010900410140001a41010b";
extern std::string const memoryLastByteOf8MBHex =
"0061736d010000000105016000017f030201000506010180018001071302066d656d6f7279"
"02000666696e69736800000a0f010d0041ffffff032d00001a41010b";
extern std::string const memoryGrow1MoreThan8MBHex =
"0061736d010000000105016000017f03020100050401008001071302066d656d6f72790200"
"0666696e69736800000a1301110041014000417f460440417f0f0b41010b";
extern std::string const table64ElementsHex =
"0061736d010000000108026000006000017f0303020001040401700040070a010666696e69"
"736800010946010041000b4000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000"
"00000a090202000b040041010b";
extern std::string const table65ElementsHex =
"0061736d010000000108026000006000017f0303020001040401700041070a010666696e69"
"736800010947010041000b4100000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000000000000000000000000000000000000"
"0000000a090202000b040041010b";
extern std::string const table2TablesHex =
"0061736d010000000108026000006000017f03030200010409027001010170010101070a01"
"0666696e6973680001090f020041000b0100020141000b0001000a090202000b040041010"
"b";
extern std::string const proposalMutableGlobalHex =
"0061736d010000000105016000017f030201000606017f0141000b07140207636f756e7465"
"7203000666696e69736800000a0d010b00230041016a240041010b";
extern std::string const divideBy0Hex =
"0061736d010000000105016000017f03020100070a010666696e69736800000a0c010a0041"
"2a41006d1a41010b";

View File

@@ -33,3 +33,19 @@ extern std::string const float0Hex;
extern std::string const disabledFloatHex;
extern std::string const updateDataWasmHex;
extern std::string const memoryPointerAtLimitHex;
extern std::string const memoryPointerOverLimitHex;
extern std::string const memoryOffsetOverLimitHex;
extern std::string const memoryEndOfWordOverLimitHex;
extern std::string const memoryGrow0To1PageHex;
extern std::string const memoryLastByteOf8MBHex;
extern std::string const memoryGrow1MoreThan8MBHex;
extern std::string const table64ElementsHex;
extern std::string const table65ElementsHex;
extern std::string const table2TablesHex;
extern std::string const proposalMutableGlobalHex;
extern std::string const divideBy0Hex;

View File

@@ -0,0 +1,15 @@
(module
(func $finish (export "finish") (result i32)
;; Setup for Requirement 2: Divide an i32 by 0
i32.const 42 ;; Push numerator
i32.const 0 ;; Push denominator (0)
i32.div_s ;; Perform signed division (42 / 0)
;; --- NOTE: Execution usually traps (crashes) at the line above ---
;; Logic to satisfy Requirement 1: Return i32 = 1
;; If execution continued, we would drop the division result and return 1
drop ;; Clear the stack
i32.const 1 ;; Push the return value
)
)

View File

@@ -0,0 +1,28 @@
(module
;; 1. Define Memory: 1 Page = 64KB = 65,536 bytes
(memory 1)
;; Export memory so the host can inspect it if needed
(export "memory" (memory 0))
(func $test_straddle (result i32)
;; Push the address onto the stack.
;; 65534 is valid, but it is only 2 bytes away from the end.
i32.const 65534
;; Attempt to load an i32 (4 bytes) from that address.
;; This requires bytes 65534, 65535, 65536, and 65537.
;; Since 65536 is the first invalid byte, this MUST trap.
i32.load
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export the function so you can call it from your host (JS, Python, etc.)
(export "finish" (func $test_straddle))
)

View File

@@ -0,0 +1,26 @@
(module
;; 1. Define Memory: Start with 0 pages
(memory 0)
;; Export memory to host
(export "memory" (memory 0))
(func $grow_from_zero (result i32)
;; We have 0 pages. We want to add 1 page.
;; Push delta (1) onto stack.
i32.const 1
;; Grow the memory.
;; If successful: memory becomes 64KB, returns old size (0).
;; If failed: memory stays 0, returns -1.
memory.grow
;; Drop the return value of memory.grow
drop
;; Return 1 (as requested)
i32.const 1
)
(export "finish" (func $grow_from_zero))
)

View File

@@ -0,0 +1,26 @@
(module
;; Define memory: 128 pages (8MB) min, 128 pages max
(memory 128 128)
;; Export memory so host can verify size
(export "memory" (memory 0))
(func $access_last_byte (result i32)
;; Math: 128 pages * 64,536 bytes/page = 8,388,608 bytes
;; Valid indices: 0 to 8,388,607
;; Push the address of the LAST valid byte
i32.const 8388607
;; Load byte from that address
i32.load8_u
;; Drop the value (we don't care what it is, just that we could read it)
drop
;; Return 1 to indicate success
i32.const 1
)
(export "finish" (func $access_last_byte))
)

View File

@@ -0,0 +1,27 @@
(module
;; 1. Define Memory: 1 Page = 64KB
(memory 1)
(export "memory" (memory 0))
(func $test_offset_overflow (result i32)
;; 1. Push the base address onto the stack.
;; We use '0', which is the safest, most valid address possible.
i32.const 0
;; 2. Attempt to load using a static offset.
;; syntax: i32.load offset=N align=N
;; We set the offset to 65536 (the size of the memory).
;; The effective address becomes 0 + 65536 = 65536.
i32.load offset=65536
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
(export "finish" (func $test_offset_overflow))
)

View File

@@ -0,0 +1,29 @@
(module
;; Start at your limit: 128 pages (8MB)
(memory 128)
(export "memory" (memory 0))
(func $try_grow_beyond_limit (result i32)
;; Attempt to grow by 1 page
i32.const 1
memory.grow
;; memory.grow returns:
;; -1 if the growth failed (Correct behavior for your limit)
;; 128 (old size) if growth succeeded (Means limit was bypassed)
;; Check if result == -1
i32.const -1
i32.eq
if
;; Growth FAILED (Host blocked it). Return -1.
i32.const -1
return
end
;; Growth SUCCEEDED (Host allowed it). Return 1.
i32.const 1
)
(export "finish" (func $try_grow_beyond_limit))
)

View File

@@ -0,0 +1,22 @@
(module
;; Define 1 page of memory (64KB = 65,536 bytes)
(memory 1)
(func $read_edge (result i32)
;; Push the index of the LAST valid byte
i32.const 65535
;; Load 1 byte (unsigned)
i32.load8_u
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export as "finish" as requested
(export "finish" (func $read_edge))
)

View File

@@ -0,0 +1,23 @@
(module
;; Define 1 page of memory (64KB = 65,536 bytes)
(memory 1)
(func $read_overflow (result i32)
;; Push the index of the FIRST invalid byte
;; Memory is 0..65535, so 65536 is out of bounds.
i32.const 65536
;; Load 1 byte (unsigned)
i32.load8_u
;; Clean up the stack.
;; The load pushed a value, but we don't care what it is.
drop
;; Return 1 to signal "I survived the memory access"
i32.const 1
)
;; Export as "finish" as requested
(export "finish" (func $read_overflow))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a mutable global initialized to 0
(global $counter (mut i32) (i32.const 0))
;; EXPORTING a mutable global is the key feature of this proposal.
;; In strict MVP, exported globals had to be immutable (const).
(export "counter" (global $counter))
(func $finish (result i32)
;; 1. Get current value
global.get $counter
;; 2. Add 1
i32.const 1
i32.add
;; 3. Set new value (Mutation)
global.set $counter
;; 4. Return 1 for success
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1 @@
;;hard to generate

View File

@@ -0,0 +1,24 @@
(module
;; Define a dummy function to put in the tables
(func $dummy)
;; TABLE 0: The default table (allowed in MVP)
;; Size: 1 initial, 1 max
(table $t0 1 1 funcref)
;; Initialize Table 0 at index 0
(elem (table $t0) (i32.const 0) $dummy)
;; TABLE 1: The second table (Requires Reference Types proposal)
;; If strict MVP is enforced, the parser should error here.
(table $t1 1 1 funcref)
;; Initialize Table 1 at index 0
(elem (table $t1) (i32.const 0) $dummy)
(func $finish (result i32)
;; If we successfully loaded a module with 2 tables, return 1.
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a table with exactly 64 entries
(table 64 funcref)
;; A dummy function to reference
(func $dummy)
;; Initialize the table at offset 0 with 64 references to $dummy
(elem (i32.const 0)
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
)
;; Standard finish function
(func $finish (result i32)
i32.const 1
)
(export "finish" (func $finish))
)

View File

@@ -0,0 +1,25 @@
(module
;; Define a table with exactly 65 entries
(table 65 funcref)
;; A dummy function to reference
(func $dummy)
;; Initialize the table at offset 0 with 65 references to $dummy
(elem (i32.const 0)
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 8
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 16
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 24
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 32
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 40
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 48
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 56
$dummy $dummy $dummy $dummy $dummy $dummy $dummy $dummy ;; 64
$dummy ;; 65 (The one that breaks the camel's back)
)
(func $finish (result i32)
i32.const 1
)
(export "finish" (func $finish))
)