From 68cd2a392ad251fb7d16773867cadad1e41e57b5 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 18 Sep 2025 15:57:59 -0700 Subject: [PATCH 1/2] vm: implement simd-321 --- conformance/src/vm_interp.zig | 15 ++++++----- conformance/src/vm_syscall.zig | 11 ++++---- src/core/features.zon | 1 + src/runtime/program/bpf/execute.zig | 25 ++++++++++------- src/runtime/program/bpf/serialize.zig | 39 ++++++++++++++------------- src/vm/interpreter.zig | 2 ++ src/vm/main.zig | 1 + src/vm/tests.zig | 4 +++ 8 files changed, 58 insertions(+), 40 deletions(-) diff --git a/conformance/src/vm_interp.zig b/conformance/src/vm_interp.zig index 5ba2641c68..2788f51049 100644 --- a/conformance/src/vm_interp.zig +++ b/conformance/src/vm_interp.zig @@ -114,17 +114,17 @@ fn executeVmTest( .mask_out_rent_epoch_in_vm_serialization, slot, ); - var parameter_bytes, var regions, const accounts_metadata = try serialize.serializeParameters( + var serialized = try serialize.serializeParameters( allocator, &ic, !direct_mapping, mask_out_rent_epoch_in_vm_serialization, ); defer { - parameter_bytes.deinit(allocator); - regions.deinit(allocator); + serialized.memory.deinit(allocator); + serialized.regions.deinit(allocator); } - tc.serialized_accounts = accounts_metadata; + tc.serialized_accounts = serialized.account_metas; const rodata = try allocator.dupe(u8, vm_context.rodata.getSlice()); defer allocator.free(rodata); @@ -220,7 +220,7 @@ fn executeVmTest( ), memory.Region.init(.mutable, heap, memory.HEAP_START), }); - try input_memory_regions.appendSlice(allocator, regions.items); + try input_memory_regions.appendSlice(allocator, serialized.regions.items); const map = try memory.MemoryMap.init( allocator, @@ -236,18 +236,19 @@ fn executeVmTest( map, &syscall_registry, STACK_SIZE, + 0, &tc, ); defer vm.deinit(); - // r1, r10, pc are initialized by Vm.init, modifying them will most like break execution. + // r1, r2, r10, pc are initialized by Vm.init, modifying them will most like break execution. // In vm_syscalls we allow override them (especially r1) because that simulates the fact // that a program partially executed before reaching the syscall. // Here we want to test what happens when the program starts from the beginning. // [agave] https://github.com/firedancer-io/solfuzz-agave/blob/0b8a7971055d822df3f602c287c368400a784c15/src/vm_interp.rs#L357-L362 vm.registers.set(.r0, vm_context.r0); // vm.registers.set(.r1, vm_context.r1); - vm.registers.set(.r2, vm_context.r2); + // vm.registers.set(.r2, vm_context.r2); vm.registers.set(.r3, vm_context.r3); vm.registers.set(.r4, vm_context.r4); vm.registers.set(.r5, vm_context.r5); diff --git a/conformance/src/vm_syscall.zig b/conformance/src/vm_syscall.zig index 45a28c1496..e6abeaae8c 100644 --- a/conformance/src/vm_syscall.zig +++ b/conformance/src/vm_syscall.zig @@ -223,17 +223,17 @@ fn executeSyscall( .mask_out_rent_epoch_in_vm_serialization, tc.slot, ); - var parameter_bytes, var regions, const accounts_metadata = try serialize.serializeParameters( + var serialized = try serialize.serializeParameters( allocator, ic, !direct_mapping, mask_out_rent_epoch_in_vm_serialization, ); defer { - parameter_bytes.deinit(allocator); - regions.deinit(allocator); + serialized.memory.deinit(allocator); + serialized.regions.deinit(allocator); } - tc.serialized_accounts = accounts_metadata; + tc.serialized_accounts = serialized.account_metas; if (pb_vm.heap_max > HEAP_MAX) return error.InvalidHeapSize; @@ -260,7 +260,7 @@ fn executeSyscall( ), memory.Region.init(.mutable, heap, memory.HEAP_START), }); - try input_memory_regions.appendSlice(allocator, regions.items); + try input_memory_regions.appendSlice(allocator, serialized.regions.items); const memory_map = try memory.MemoryMap.init( allocator, @@ -275,6 +275,7 @@ fn executeSyscall( memory_map, syscall_registry, stack.len, + 0, &tc, ); defer vm.deinit(); diff --git a/src/core/features.zon b/src/core/features.zon index 123d584090..b374b9b97d 100644 --- a/src/core/features.zon +++ b/src/core/features.zon @@ -241,4 +241,5 @@ .{ .name = "enable_vote_address_leader_schedule", .pubkey = "5JsG4NWH8Jbrqdd8uL6BNwnyZK3dQSoieRXG5vmofj9y" }, .{ .name = "enshrine_slashing_program", .pubkey = "sProgVaNWkYdP2eTRAy1CPrgb3b9p8yXCASrPEqo6VJ" }, .{ .name = "enable_partitioned_epoch_reward", .pubkey = "9bn2vTJUsUcnpiZWbu2woSKtTGW3ErZC9ERv88SDqQjK" }, + .{ .name = "provide_instruction_data_offset_in_vm_r2", .pubkey = "5xXZc66h4UdB6Yq7FzdBxBiRAFMMScMLwHxk2QZDaNZL" }, } diff --git a/src/runtime/program/bpf/execute.zig b/src/runtime/program/bpf/execute.zig index cdec601fee..614e11f6e7 100644 --- a/src/runtime/program/bpf/execute.zig +++ b/src/runtime/program/bpf/execute.zig @@ -64,24 +64,26 @@ pub fn execute( .bpf_account_data_direct_mapping, ic.tc.slot, ); + const provide_instruction_data_offset = ic.tc.feature_set.active( + .provide_instruction_data_offset_in_vm_r2, + ic.tc.slot, + ); // [agave] https://github.com/anza-xyz/agave/blob/32ac530151de63329f9ceb97dd23abfcee28f1d4/programs/bpf_loader/src/lib.rs#L1588 - var parameter_bytes, // - var regions, // - const accounts_metadata = try serialize.serializeParameters( + var serialized = try serialize.serializeParameters( allocator, ic, !direct_mapping, mask_out_rent_epoch_in_vm_serialization, ); defer { - parameter_bytes.deinit(allocator); - regions.deinit(allocator); + serialized.memory.deinit(allocator); + serialized.regions.deinit(allocator); } // [agave] https://github.com/anza-xyz/agave/blob/a11b42a73288ab5985009e21ffd48e79f8ad6c58/programs/bpf_loader/src/lib.rs#L278-L282 const old_accounts = ic.tc.serialized_accounts; - ic.tc.serialized_accounts = accounts_metadata; + ic.tc.serialized_accounts = serialized.account_metas; defer ic.tc.serialized_accounts = old_accounts; // [agave] https://github.com/anza-xyz/agave/blob/a2af4430d278fcf694af7a2ea5ff64e8a1f5b05b/programs/bpf_loader/src/lib.rs#L1604-L1617 @@ -94,8 +96,9 @@ pub fn execute( allocator, ic.tc, &executable, - regions.items, + serialized.regions.items, &ic.tc.vm_environment.loader, + if (provide_instruction_data_offset) serialized.instruction_data_offset else 0, ) catch |err| { try ic.tc.log("Failed to create SBPF VM: {s}", .{@errorName(err)}); return InstructionError.ProgramEnvironmentSetupFailure; @@ -143,8 +146,8 @@ pub fn execute( allocator, ic, !direct_mapping, - parameter_bytes.items, - accounts_metadata.constSlice(), + serialized.memory.items, + serialized.account_metas.constSlice(), ) catch |err| { maybe_execute_error = err; }; @@ -160,8 +163,9 @@ pub fn initVm( allocator: std.mem.Allocator, tc: *TransactionContext, executable: *const vm.Executable, - regions: []vm.memory.Region, + regions: []const vm.memory.Region, syscalls: *const Registry(Syscall), + instruction_data_offset: u64, ) !struct { vm.Vm, []u8, @@ -217,6 +221,7 @@ pub fn initVm( memory_map, syscalls, stack.len, + instruction_data_offset, tc, ); diff --git a/src/runtime/program/bpf/serialize.zig b/src/runtime/program/bpf/serialize.zig index d770d83179..9adfcea993 100644 --- a/src/runtime/program/bpf/serialize.zig +++ b/src/runtime/program/bpf/serialize.zig @@ -188,9 +188,10 @@ pub const Serializer = struct { }; const SerializeReturn = struct { - std.ArrayListUnmanaged(u8), - std.ArrayListUnmanaged(Region), - std.BoundedArray(SerializedAccountMeta, InstructionInfo.MAX_ACCOUNT_METAS), + memory: std.ArrayListUnmanaged(u8), + regions: std.ArrayListUnmanaged(Region), + account_metas: std.BoundedArray(SerializedAccountMeta, InstructionInfo.MAX_ACCOUNT_METAS), + instruction_data_offset: u64, }; /// [agave] https://github.com/anza-xyz/agave/blob/108fcb4ff0f3cb2e7739ca163e6ead04e377e567/program-runtime/src/serialization.rs#L188 @@ -345,7 +346,7 @@ fn serializeParametersUnaligned( } } _ = serializer.write(u64, std.mem.nativeToLittle(u64, instruction_data.len)); - _ = serializer.writeBytes(instruction_data); + const instruction_data_offset = serializer.writeBytes(instruction_data); _ = serializer.writeBytes(&program_id.data); var memory, var regions = try serializer.finish(); @@ -355,9 +356,10 @@ fn serializeParametersUnaligned( } return .{ - memory, - regions, - account_metas, + .memory = memory, + .regions = regions, + .account_metas = account_metas, + .instruction_data_offset = instruction_data_offset, }; } @@ -470,7 +472,7 @@ fn serializeParametersAligned( } _ = serializer.write(u64, std.mem.nativeToLittle(u64, instruction_data.len)); - _ = serializer.writeBytes(instruction_data); + const instruction_data_offset = serializer.writeBytes(instruction_data); _ = serializer.writeBytes(&program_id.data); var memory, var regions = try serializer.finish(); @@ -480,9 +482,10 @@ fn serializeParametersAligned( } return .{ - memory, - regions, - account_metas, + .memory = memory, + .regions = regions, + .account_metas = account_metas, + .instruction_data_offset = instruction_data_offset, }; } @@ -938,21 +941,21 @@ test "serializeParameters" { allocator.free(pre_accounts); } - var memory, var regions, const account_metas = try serializeParameters( + var serialized = try serializeParameters( allocator, &ic, copy_account_data, false, ); defer { - memory.deinit(allocator); - regions.deinit(allocator); + serialized.memory.deinit(allocator); + serialized.regions.deinit(allocator); } - const serialized_regions = try concatRegions(allocator, regions.items); + const serialized_regions = try concatRegions(allocator, serialized.regions.items); defer allocator.free(serialized_regions); if (copy_account_data) { - try std.testing.expectEqualSlices(u8, memory.items, serialized_regions); + try std.testing.expectEqualSlices(u8, serialized.memory.items, serialized_regions); } // TODO: compare against entrypoint deserialize method once implemented @@ -963,8 +966,8 @@ test "serializeParameters" { allocator, &ic, copy_account_data, - memory.items, - account_metas.constSlice(), + serialized.memory.items, + serialized.account_metas.constSlice(), ); for (pre_accounts, 0..) |pre_account, index_in_transaction| { const post_account = tc.accounts[index_in_transaction]; diff --git a/src/vm/interpreter.zig b/src/vm/interpreter.zig index ad25dac52d..b274b07c84 100644 --- a/src/vm/interpreter.zig +++ b/src/vm/interpreter.zig @@ -41,6 +41,7 @@ pub const Vm = struct { memory_map: MemoryMap, loader: *const Registry(Syscall), stack_len: u64, + instruction_data_offset: u64, ctx: *TransactionContext, ) error{OutOfMemory}!Vm { const offset = if (executable.version.enableDynamicStackFrames()) @@ -64,6 +65,7 @@ pub const Vm = struct { self.registers.set(.r10, stack_pointer); self.registers.set(.r1, memory.INPUT_START); + self.registers.set(.r2, instruction_data_offset); self.registers.set(.pc, executable.entry_pc); return self; diff --git a/src/vm/main.zig b/src/vm/main.zig index c6c2ef6e19..eb0146fa18 100644 --- a/src/vm/main.zig +++ b/src/vm/main.zig @@ -121,6 +121,7 @@ pub fn main() !void { m, &loader, stack_memory.len, + 0, &tc, ); defer ebpf_vm.deinit(); diff --git a/src/vm/tests.zig b/src/vm/tests.zig index a8c518d441..42a3a51750 100644 --- a/src/vm/tests.zig +++ b/src/vm/tests.zig @@ -86,6 +86,7 @@ fn testAsmWithMemory( m, &loader, stack_memory.len, + 0, &tc, ); defer vm.deinit(); @@ -2007,6 +2008,7 @@ test "pqr" { map, &loader, 0, + 0, &tc, ); defer vm.deinit(); @@ -2067,6 +2069,7 @@ test "pqr divide by zero" { map, &loader, 0, + 0, &tc, ); defer vm.deinit(); @@ -2365,6 +2368,7 @@ pub fn testElfWithSyscalls( m, &loader, stack_memory.len, + 0, &tc, ); defer vm.deinit(); From f97b80ff25fe1ae0f1ffa9451fdade161af3d56c Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 25 Sep 2025 20:21:34 -0700 Subject: [PATCH 2/2] wip --- data/test-elfs/bss_section_sbpfv0.so | Bin 1688 -> 1680 bytes data/test-elfs/data_section_sbpfv0.so | Bin 1688 -> 1688 bytes data/test-elfs/direct_mapping.so | Bin 2416 -> 2048 bytes data/test-elfs/elf.ld | 9 +- data/test-elfs/hash_collision_sbpfv0.so | Bin 1488 -> 1480 bytes data/test-elfs/poseidon_test.so | Bin 5800 -> 5408 bytes data/test-elfs/r2_instruction_data_offset.so | Bin 0 -> 1320 bytes data/test-elfs/r2_instruction_data_offset.zig | 10 ++ data/test-elfs/relative_call_sbpfv0.so | Bin 1672 -> 1672 bytes data/test-elfs/reloc_64_64.so | Bin 1624 -> 1288 bytes data/test-elfs/reloc_64_64_sbpfv0.so | Bin 1544 -> 1536 bytes data/test-elfs/reloc_64_relative.so | Bin 1672 -> 1336 bytes data/test-elfs/reloc_64_relative_data.so | Bin 1736 -> 1400 bytes .../reloc_64_relative_data_sbpfv0.so | Bin 1840 -> 1832 bytes data/test-elfs/reloc_64_relative_sbpfv0.so | Bin 1688 -> 1680 bytes data/test-elfs/rodata_section.so | Bin 1736 -> 1384 bytes data/test-elfs/rodata_section_sbpfv0.so | Bin 1728 -> 1720 bytes data/test-elfs/strict_header.so | Bin 1816 -> 1440 bytes data/test-elfs/struct_func_pointer.so | Bin 1864 -> 1456 bytes data/test-elfs/struct_func_pointer_sbpfv0.so | Bin 1832 -> 1824 bytes data/test-elfs/syscall_reloc_64_32_sbpfv0.so | Bin 1776 -> 1768 bytes data/test-elfs/syscall_static.so | Bin 1688 -> 1352 bytes src/vm/elf.zig | 118 +++++++----------- src/vm/executable.zig | 26 ++-- src/vm/interpreter.zig | 4 +- src/vm/memory.zig | 10 +- src/vm/sbpf.zig | 11 +- src/vm/tests.zig | 25 ++-- 28 files changed, 106 insertions(+), 107 deletions(-) create mode 100755 data/test-elfs/r2_instruction_data_offset.so create mode 100644 data/test-elfs/r2_instruction_data_offset.zig diff --git a/data/test-elfs/bss_section_sbpfv0.so b/data/test-elfs/bss_section_sbpfv0.so index 7737293e7465dfceab6f79dc3bd7f3c84dadde6b..2398a1a83c350939bcd1d55d00320a6b4dba6220 100755 GIT binary patch delta 87 zcmV-d0I2_%4Ui3xC;`y1DPsaEATVPyFkxh2VL3HnHe)a_H)b|6F)(B_F*h?cGdD42 tGBsl~V>vNpWGRv18nf5}-~s_>leq;q0l1U#1wR2ulQ9NA0hyC=1}Aw#8m|BV delta 99 zcmbQhJA-$E2IGy5n#oLhdg+-Z3Z@o`29~Lo=0;}8#zw{|iDtD^ni&md-6tBbH)RcU$WXW229puv*%pM%m4-^lRvV^0|4s7 B9YO#A diff --git a/data/test-elfs/data_section_sbpfv0.so b/data/test-elfs/data_section_sbpfv0.so index 22b51cddd5907610cce4e010017bb74bddff6ea9..7648c12259d34fee8694034ffbb8d6d29835cf53 100755 GIT binary patch delta 64 zcmbQiJA-$_1STy7gJffa#FWHDOVdQNWCH{9G&3VZgA@}(b7NCubHg+v(`1umOT*L@ U&4~}?Hs4@+z{Hq7c_-_A0A8pRKL7v# delta 68 zcmbQiJA-$_1SUPb^vn_kQ;S3c%T!BqBeP^m&4~}CHs4@+z{Hq6c_-_A0LbYU6#xJL diff --git a/data/test-elfs/direct_mapping.so b/data/test-elfs/direct_mapping.so index a306d4144ef0572c5851a9e84f0648f62bd2f32f..4da3cc439561c4a6198083a0d7eabc70b04c9ca6 100755 GIT binary patch delta 310 zcmew$)F3cHL!5z)0SuUdqyvKmkn6y}!N5MzUQuxY6GRF|b3nKZj6j+Nhz%w->T`lP zAR{~`?o<~P2JX4}aQERvIzS=%RXVC`UBFxir= zXYy6HY9K3+-JH>2awoezqsQcvV3LzVd-4_z5l#<~i-912@%}`6zvqC0MQaI`z@aq;(V&=4 zcLzera3uT%L2Pq+&|Zk{yw|JTq8vQTjIzRD4ufBXy)x%&^a;FW?&z3d(_V++L)Wox z@|kaR!Yl3ZL~M#Vd=%p{A#Ld6ur($n{Pf}WQ%ng09v%i zjGe};@bSsE6LEF!FvHw=2rJT0r6}kDS>yp|Q&ieC2GURFl}e{2m2EV@roDn&_9^Ct zLj%r47kB0PNVRcTYaGBlbNv|KWRrx7QxGM*bc&*kFKSJ>x&1+E^>-d$-`L5vFxY4I z+t`=nAYqn1K8>m8e6BaPF5|T`Ar^7n^`n!LM8c7Ztzz5tvlluR(U0nq&a?i{*-#y> JSR!n5hhNE6XVw4! diff --git a/data/test-elfs/elf.ld b/data/test-elfs/elf.ld index 6d38a41612..fe01d0a2d7 100644 --- a/data/test-elfs/elf.ld +++ b/data/test-elfs/elf.ld @@ -21,11 +21,6 @@ SECTIONS _heap_end = .; . = ALIGN(8); } :heap - .dynsym 0xFFFFFFFF00000000 : { - *(.dynsym) - . = ALIGN(8); - } :dynsym - .dynstr : { *(.dynstr) } :other .strtab : { *(.strtab) } :other /DISCARD/ : { *(.comment*) @@ -35,6 +30,8 @@ SECTIONS *(.data*) *(.rel.dyn*) *(.dynamic) + *(.dynsym) + *(.dynstr) } } @@ -42,7 +39,7 @@ PHDRS { text PT_LOAD FLAGS(1); rodata PT_LOAD FLAGS(4); - stack PT_GNU_STACK FLAGS(6); + stack PT_LOAD FLAGS(6); heap PT_LOAD FLAGS(6); dynsym PT_NULL FLAGS(0); other PT_NULL FLAGS(0); diff --git a/data/test-elfs/hash_collision_sbpfv0.so b/data/test-elfs/hash_collision_sbpfv0.so index 1bbfbc796e1c8938e5b1834094c5dce55af57e7e..0cd37bc933b946e486e94c8e6f5d46b84a871ac2 100755 GIT binary patch delta 99 zcmcb>eS&*}24lxYO<^W21%qT`gT$1?L`%~|vt$DU^E5LfLxU6(Lvv$OV{^kaBhzG) zWJ|-;6wQebr6wO>WZB%ql)}WAKKUbyIb+6TO;&qGfytSy_ME}Y3}9e1c_WKF0D*uS AUH||9 delta 95 zcmX@XeSv#|2IGW{n!-$adg+-Z3Z@o`29~Lo=0;}8#zw{|iDt6~=oS7&#_Bbf4@XqA}TkS75RR zBhO@QHXf-s0=i1!G=*G+x&NBHHi#nsz(zNM~ZynEX+w9!M4n_iW}B ziDzVFo~+0w%w@m}QFV!NGMA_vs}7LI1d}X>j0_Aw1jL&sF}g8x?Qw#LhcivS#iBl$ zlhu$>WU@7@@8lJ%0UE3fAm=eK%t@4h(2Fd?Kwb_vOHREIj{tQmI6E;uePiJCOnEX?)9!ORS_3))s<`q}w zGU$QmlA_Jb!ikKr+(2`XT>uJ91EfF(S;GdzAP>PJ0}@&c3_3tDkSk&OVUYrhq>#ys zqVkL;lM_Y6nRNIjSBa=G+Du+3BCZMY1qj@L`jH*TRsmvk3tT{2Id2Gq)G{#mfs6wH Dx42qk diff --git a/data/test-elfs/r2_instruction_data_offset.so b/data/test-elfs/r2_instruction_data_offset.so new file mode 100755 index 0000000000000000000000000000000000000000..ed88d18c58b02a5521a6a9b9c8a670524591341f GIT binary patch literal 1320 zcmbtTy-EW?5T5+#1sg*u5eo&86mkTy&_+_IRSIjH3a0WEZ0xl0B`i&8 z?@I_)c6K_mJ9C*FK|~jJznO1l_Gf1H=A?64E|ow=1w5qzwAs+CIDw!I2e6E54LqPo znzyqpoFlKT0@he3_bSe*iW8Ag{EA4nWt=bKjB*(#4MD5a?V9hiD2|^5f{WtZE`MM3 zJ@qcJpCp{-2p&x7t=g%5j2?YUtUdDYB9&uZ9? zah&wWE~<5|gs$VE@K{gHA6uR}2C^O)9v&^??a3Z}HkVs9~ zMVwqZ95Q2%`PV|3ph>-}hx{AKgV?0+MfXskc%1{%<^L4@$)?9DD&(W%NzVHd^QA{I zejSa)c*^f@tmg2>@kcr1 IgyQ?Z0sA;m=>Px# literal 0 HcmV?d00001 diff --git a/data/test-elfs/r2_instruction_data_offset.zig b/data/test-elfs/r2_instruction_data_offset.zig new file mode 100644 index 0000000000..7dc9ada75e --- /dev/null +++ b/data/test-elfs/r2_instruction_data_offset.zig @@ -0,0 +1,10 @@ +const set_return_data: *align(1) const fn ([*]const u8, usize) void = @ptrFromInt(0xa226d3eb); + +export fn entrypoint(_: [*]u8, instruction_data_addr: [*]const u8) u64 { + const instruction_data_len = @as([*]align(1) const u64, @ptrCast(instruction_data_addr - 8))[0]; + const instruction_data = instruction_data_addr[0..instruction_data_len]; + + set_return_data(instruction_data.ptr, instruction_data.len); + + return 0; +} diff --git a/data/test-elfs/relative_call_sbpfv0.so b/data/test-elfs/relative_call_sbpfv0.so index 1e99d958d0bbadeff3d21b910fad5ba2b71acfc7..37df64c22842976d9195cc4622bbb008b17ca351 100755 GIT binary patch delta 68 zcmeC+?cm+e!K9^NkZf#_n39-iX_{!3Y+zuXW@cn)kYZwJZft67ZkT3dnrxD6X_%U# YIq{*~<_kAP1A7m1qyodD+0HypDU;qFB delta 72 zcmeC+?cm+e!KAB~o>`(`YLRGQnQCcnWR`4fWSo*{mYiZ{mXwxgX_l6fW@u(+Y?PX2 cY-w&hc_Wkb<_k9DsA7m1qyodD+01_e=;Q#;t diff --git a/data/test-elfs/reloc_64_64.so b/data/test-elfs/reloc_64_64.so index c3f0dd24581bd3b2d44ed85b3f96eb18cb16c930..4a849a4f3cc5b4488ed5d009bcf8157325358747 100755 GIT binary patch delta 266 zcmcb?)4?@CL!5(|0SrKt1A_$v8<6B+V4rBOsHgxG0s#du$p93F07f9i0>m38HtKVN zIBYvGqQmuQ1A0X*%b0x4}UGZM~)HdaUKMJ7k?}MCkKA5_zIEgKFN4h zPUz}0t2m$6#TPA(e}?*WSy7zp`}9iMCe6+}$8AH6u8Eo_nA2jBv}0#O{upuVy|fB%%-?!XDUl<3WB>=sPfg{uCQsc|?+$9cvF>A0l0r{kK%zw5ZI zxUXYik_$sY+KD3*QE=4l23F2IglZJTS-YE^<=&*k1*wUS*k@GP zedl_=H0R&@3MasK?-thj-u-&yT$|4kmcG{qb&< z-{Tw%`k=R-M|D|d4E>akdhkO5HhJxuzyDQE_PsMHz7+nxOXk?;Z!xC5-O^_Pp9IAJ Wgg*ZMcLbNR!rjQG4hkd>{QjR$QG&++ diff --git a/data/test-elfs/reloc_64_64_sbpfv0.so b/data/test-elfs/reloc_64_64_sbpfv0.so index 3d6bdafee3825ac2b5430e0a734dbb8f2288ebea..8636df2beeec5a136fb1d9369516e2efe8c21852 100755 GIT binary patch delta 91 zcmeC+Y2cZl!Pu}-)0#0EZM-oJk89=&>+Rc(A?P6*xWG9$TZm` u+0rmIW%5KO>CGFMDwr74CqHBqo@~Ra!Z=}aBC9EM~5!Pv1;)0#<7FFmtF!PFwrz%td++{i51*vL2~(JVQ|%q%G_(b6m}CC$*x z%-AS3&Dhf1Saae-xyc_GSvId>Dq&*Go~+1f&NyRoB&$86z~q^%_M9=y3}B!(c_E8D E00J``m38HtKVN zIBYVE)LQ&p>4pb;d5Ei{GOl=3!Vq6bc zHYu)KVgGA5sL-J-p4VdGBwQ`|-OftS68THP8m>75iX6u(SwE2OM8$WxK3E!F2{ROc z_o9VOk?ioji$wVF2Iq*V0dD}{ZZ)`pPcmt&60U(=(`rl;ws9&`{1jb$5trDJMjKv#>od6#iVM1ia`KIgA4!xcCN`?jOLTOnWj#b zV=iJ;m^_iWXR~3D(4|8MR>Y9hLgFX% zA0W1jF!B=^7?J3f4eaD@!NWcK`T6`&_-~DLD>_k7nnUjq^q@ws4_-Eiv*$IJlk@EU zz)QZy!x{y7_PQC0vI2myA{X5_w=9vrWUOIK*&$2u(}}jvq=^V@mhq*c;hTJR1>j6{ zaU^`~hz4FtwYOUKt907P1HvZo>;UHH(r@EJM(qvqn@}xSY04yYFm^NsvXAfLiT=J3 z8b9RY%5XBen_R;_OABWFUulj-= zdzWG!$d1fvNpWRtN18?(>?=mG&}lK}z~lgtGq0kD(t1wR2mlQ9NA0hE(*1}Dl88s-21 delta 104 zcmbQhJA-$E2IGy5n$b*pdg+-Z3Z@o`29~Lo=0;}8#zw{|iDtpnOIk%>u+XCN}DGf;enI z{9@uxbwOkm5W$b;lM`4pCVyZQV0ytg`5~hiPcl$32!QCxEsQ?mbHQ8&1}!KJr6%uW zs-JAe9L4A{c_MSqWL=hOAZsOyIpdAV4_WLPUrbhH1(AuY+KkLVgN3|gm@xSwv$#qF6O;#}V6q^3E<~8& zGLSvFh0%wVoeSu)$!8g5CL1t{8-mOhVnEmcb0iN?Tz~=O3kC+L$zZnR!4$z5F!>-;4_``UUU6kEgC2-3DVprfTnm&q z$!yN}U@{|%J>!STj$pEpMLUig%|ehLU?BkWF(_z2PUe8v${+!yb$~R;IWPx<)PwZH z!p4eWvKNcoWDQmcu3b1``9zMy)C)J-zhI5(QI>L<7rIOLHT$WMd=alti=S6f?7=v_wm@w3IYM zGc#kO)HGvDb7RfPj7)Nq4VYLqzhQdA#F#x=`pA2Lj1GlP9vN F0|2XK9OM81 diff --git a/data/test-elfs/strict_header.so b/data/test-elfs/strict_header.so index 60692bf776fd5727f6ae612381008b03e7197941..e9e8d244dd0f495ba50b2891d9b4cd1257bc5fd9 100755 GIT binary patch delta 322 zcmbQiw}5+shWG+z1~33o4h$9yY(SEOfqkOAqM`#t1}Fdl98j7ONV5R(g^7*&oFEPx z5I>l>Q(X{Q1w`y}J(Fu$su>+7|70;| zyfE32)t>Rei4~!xqJIu0|}Gt&b)8FZ|3cr-Tm~k|Ek?;iCIVHf|W>56JKYl zpaa*F4VlILw%igbd3~9dsEB&+Mt)w`q_gBW&{;^opgi-YdvoEkS>(=*jJT~_^M0v9 zqtVAYJ`2sfU#QOdc?`F(-yaxp`coyCk98-yDdvq&|5J5mh#A^GKche6@0$OiiQn)H zzv=!g|3rCLxg`)5CtarJ%bIQAT2_{|&_nnIJ(=)aR}6C~ zn&*3u|2xkE!{d1-Ep@ZvWgn*FD`A&A({&)_)()Gr#K>oGwX9=~R$H_tgU4!;q`lmAFmK+8^QK4{SM z`?;?E5x947h5h;8G5slWG@cK8+rIA;gstLYKhu*>z5UPnScewxe+|jn_pan$TJSg& mBaZc(z^r#j?P<|@6>v^`hIs!kfHn2ss$OY%9Tr6#_x}q6_n!y= diff --git a/data/test-elfs/struct_func_pointer.so b/data/test-elfs/struct_func_pointer.so index 207ac496884f82c6b857cfafed75c27fb59c0b5e..423ad192b02b49e30ff7cdbe3bdc33517acdb0f2 100755 GIT binary patch delta 325 zcmYjKyAAGZu+>B#1(6V@F7IR)x}U==^|+Mkgwp`vnq}L_+Bge1eicSf$P# zGb>JV?zxYXb8m%nepe4QvzRzN3)>i(upv`aeU^#GK`?ceRDCxCsYkrW>weY@ikQUL zu=MY+%U6UCPZNtg0enKWu^_WSswTi7lfx~vImrZ_`*o~oWceYyLp+v$i=PlnzaE3mEdydRVv z#I|h70P9V;AbjN4k=hLBmgiBPsg(CL&ZOnN`{AYo_%19MdRJpjdUHL1 z7Dk!o@mokHy`gzl&tq8PdcQ!#DVJ10OTUVwd1rbk`YoRFyT*ToPSwu&P3yBa#NLA$ z@EJ_{0v-(qYnPDyUbka7eI)vb2W$1pfbO5FB4Z6@zc+x#HeC%uI zW|Y-b@`JLN8SHhUZQHhCES2p(8HeS%Me&WRh&}DgPk$HUn7c6A=JePHG3#&I^ybz4 z%*%PS&6V?Lo9k=f>pF>ee!Pz!Pv)9?{NK4A8lKKIbLEMXtUjI=u8?f6X)-^-pmI=> zO&3+OI119$f`Hl;dytExAP*Elk!un=N#+|7lf7&9j+rn0&xdd@Kfb%P?R~R%Y*qek zIJjq=&%R%96B7Ctlfj1Fhmz)Vum8<|h}c`;c-DNUEPn|fiTz=1`+oZ_-bdE6{3Uee zpDFMGc1-E{pTZe`yTx8={@ehSY5sHIRyz)z>82x1;GEXL>G@v(YpZ_}uC%<3H-(28k(&iI%2`X2}Ky=4oa|h6X7nhUUhm#^#1;MyAOo u$(DwxDU&}kNpBWlwqRyVpRC9vJUNCn-S8(RPX diff --git a/data/test-elfs/syscall_reloc_64_32_sbpfv0.so b/data/test-elfs/syscall_reloc_64_32_sbpfv0.so index 1a764fc21f7569c5171705d2ec2f08749800f68c..81461135c6f113e503b355a6a92e9c08522bf2ab 100755 GIT binary patch delta 100 zcmeys`+|3Z2BXGC&B;t!3I@r>28k(&iI%2`X2}Ky=4oa|h6X7nhUUhm#^#1;MyAOo z$(DwxDVmc%GD=NWU}D+K!_30Wm_AvM&7ASYWKTAG#tD-<+3Y!=Ff)LG?c|B9@&Jo? B8aV&} delta 95 zcmaFC`+;|Y2BX17&B;u9dg+-Z3Z@o`29~Lo=0;}8#zw{|iDtc!L8Iv1<;4 zlX;j!7%e6zGWSgW%Ulg)RkD~fo|wFm#h&rP-laJE6^b+AHx7@ltT!LED2ImfwY*neYP!% z$AjSfXELtQp)B5Zn!;Kbjac=Q(y1i=Qm}_HXF`$VtBkrM&z=Zel@W~$AH*jqL4;JX)fwum>ttZ6*D2q{)xj7u@ZPjQc@-bkc4_ntP! z)0?~LHS9A^Zm}mHsPH2DVu9Z>6D1~!^OfpduKs(F=Xcw?ZTVYxVWeC_+n^^bbaArLhTJn#$3lq_9E^Xn96pj_FROFu!lUxr=yYHE9`~wZWS}gzo diff --git a/src/vm/elf.zig b/src/vm/elf.zig index c6a55ec634..53ab1383e9 100644 --- a/src/vm/elf.zig +++ b/src/vm/elf.zig @@ -155,10 +155,15 @@ pub const Elf = struct { } } + fn getShdr(self: Headers, index: u64) !elf.Elf64_Shdr { + if (index >= self.shdrs.len) return error.OutOfBounds; + return self.shdrs[index]; + } + /// Returns the byte contents of the `index`nth section. pub fn shdrSlice(self: Headers, index: u64) ![]const u8 { - if (index >= self.shdrs.len) return error.OutOfBounds; - return self.shdrBytes(self.shdrs[index]); + const shdr = try self.getShdr(index); + return try self.shdrBytes(shdr); } fn shdrBytes(self: Headers, shdr: elf.Elf64_Shdr) ![]const u8 { @@ -228,7 +233,7 @@ pub const Elf = struct { return .{ .strtab = strtab, - .dynamic_table = dynamic_table orelse .{0} ** elf.DT_NUM, + .dynamic_table = dynamic_table orelse @splat(0), .relocations_table = relocations_table, .symbol_table = symbol_table, }; @@ -236,7 +241,7 @@ pub const Elf = struct { /// Parses and returns the dynamic entry table, if the ELF has one. /// - /// [agave] https://github.com/anza-xyz/sbpf/blob/615f120f70d3ef387aab304c5cdf66ad32dae194/src/elf_parser/mod.rs#L358-L404 + /// [agave] https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L361-L413 fn parseDynamic(headers: Headers) !?DynamicTable { var output_table: DynamicTable = .{0} ** elf.DT_NUM; @@ -276,7 +281,7 @@ pub const Elf = struct { return output_table; } - /// [agave] https://github.com/anza-xyz/sbpf/blob/615f120f70d3ef387aab304c5cdf66ad32dae194/src/elf_parser/mod.rs#L412 + /// [agave] https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L415-L449 fn parseDynamicRelocations( headers: Headers, dynamic_table: DynamicTable, @@ -308,7 +313,7 @@ pub const Elf = struct { return std.mem.bytesAsSlice(elf.Elf64_Rel, bytes); } - /// [agave] https://github.com/anza-xyz/sbpf/blob/615f120f70d3ef387aab304c5cdf66ad32dae194/src/elf_parser/mod.rs#L448-L462 + /// [agave] https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf_parser/mod.rs#L451-L465 fn parseDynamicSymbolTable( headers: Headers, dynamic_table: DynamicTable, @@ -330,7 +335,7 @@ pub const Elf = struct { } else return error.InvalidDynamicSectionTable; } - /// [agave] https://github.com/anza-xyz/sbpf/blob/615f120f70d3ef387aab304c5cdf66ad32dae194/src/elf.rs#L827-L1002 + /// [agave] https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L812 fn parseRoSections( self: Data, allocator: std.mem.Allocator, @@ -484,7 +489,7 @@ pub const Elf = struct { return ro_section; } - /// [agave] https://github.com/anza-xyz/sbpf/blob/615f120f70d3ef387aab304c5cdf66ad32dae194/src/elf.rs#L1194-L1346 + /// [agave] https://github.com/anza-xyz/sbpf/blob/v0.12.2/src/elf.rs#L990 fn relocate( self: Data, allocator: std.mem.Allocator, @@ -789,11 +794,8 @@ pub const Elf = struct { const headers = try Headers.parse(bytes); const data = try Data.parse(headers); - // The behaviour for parsing the version changed in v1. Before it was - // either equal `EM_SBPF_V1` or it wasn't. This method didn't allow - // for specifying more than 2 possible versions, so now we have - // well-defined values for the versions. We will determine which method - // to use based on the configuration of the VM. + // Keeps backwards compat by just allowing any other value for v0 when we're in a + // v0 execution environment, later that was refined to specify exact values/ const sbpf_version: sbpf.Version = if (config.maximum_version == .v0) if (headers.header.e_flags == sbpf.EM_SBPF_V1) .v1 @@ -804,6 +806,7 @@ pub const Elf = struct { 1 => .v1, 2 => .v2, 3 => .v3, + 4 => .v4, else => |v| @enumFromInt(v), }; @@ -811,7 +814,6 @@ pub const Elf = struct { if (@intFromEnum(sbpf_version) < @intFromEnum(config.minimum_version) or @intFromEnum(sbpf_version) > @intFromEnum(config.maximum_version)) { - // TODO: Conformance issue vs. agave-v2.1.0 return error.VersionUnsupported; } @@ -857,14 +859,13 @@ pub const Elf = struct { ) !Elf { const header = headers.header; - // A list of the first 5 expected program headers. + // A list of the first 4 expected program headers. // Since this is a stricter parsing scheme, we need them to match exactly. - const expected_phdrs = .{ - .{ elf.PT_LOAD, elf.PF_X, memory.BYTECODE_START }, - .{ elf.PT_LOAD, elf.PF_R, memory.RODATA_START }, - .{ elf.PT_GNU_STACK, elf.PF_R | elf.PF_W, memory.STACK_START }, - .{ elf.PT_LOAD, elf.PF_R | elf.PF_W, memory.HEAP_START }, - .{ elf.PT_NULL, 0, 0xFFFFFFFF00000000 }, + const expected_phdrs: [4]struct { u32, u64 } = .{ + .{ elf.PF_X, memory.BYTECODE_START }, // byte code + .{ elf.PF_R, memory.RODATA_START }, // read only data + .{ elf.PF_R | elf.PF_W, memory.STACK_START }, // stack + .{ elf.PF_R | elf.PF_W, memory.HEAP_START }, // heap }; const ident: ElfIdent = @bitCast(header.e_ident); @@ -891,16 +892,15 @@ pub const Elf = struct { return error.InvalidFileHeader; } - inline for ( + for ( expected_phdrs, headers.phdrs[0..expected_phdrs.len], ) |entry, phdr| { - const p_type, const p_flags, const p_vaddr = entry; - // For writable sections, (those with the PF_W bit set), we expect their - // value for p_filesz to be zero. + const p_flags, const p_vaddr = entry; + // For writable sections, (those with the PF_W bit set), we expect their value for p_filesz to be zero. const p_filesz = if (p_flags & elf.PF_W != 0) 0 else phdr.p_memsz; - if (phdr.p_type != p_type or + if (phdr.p_type != elf.PT_LOAD or phdr.p_flags != p_flags or phdr.p_offset < phdr_table_end or phdr.p_offset >= bytes.len or @@ -915,17 +915,6 @@ pub const Elf = struct { } } - const maybe_symbols_shdr = if (config.enable_symbol_and_section_labels) blk: { - const strtab = data.strtab orelse return error.NoSectionNamesStringTable; - for (headers.shdrs) |shdr| { - const name = try safeString(strtab, shdr.sh_name, 64); - if (std.mem.eql(u8, name, ".dynstr")) { - break :blk shdr; - } - } - break :blk null; - } else null; - const bytecode_hdr = headers.phdrs[0]; const rodata_hdr = headers.phdrs[1]; const ro_section: Section = .{ .borrowed = .{ @@ -933,8 +922,20 @@ pub const Elf = struct { .start = rodata_hdr.p_offset, .end = rodata_hdr.p_offset + rodata_hdr.p_filesz, } }; + if (!inRangeOfPhdrVm(bytecode_hdr, (header.e_entry +| 8) - 1) or + header.e_entry % 8 != 0) + { + return error.InvalidFileHeader; + } + // const text_section_vaddr = bytecode_hdr.p_vaddr; const entry_pc = (header.e_entry -| bytecode_hdr.p_vaddr) / 8; + // above loop checks that the header doesn't index out of bounds + const entry_opcode: sbpf.Instruction = @bitCast(bytes[bytecode_hdr.p_offset..][0..8].*); + if (!entry_opcode.isFunctionStartMarker()) { + return error.InvalidFileHeader; + } + var self: Elf = .{ .bytes = bytes, .headers = headers, @@ -947,37 +948,13 @@ pub const Elf = struct { }; errdefer self.deinit(allocator); - const dynsym_table = std.mem.bytesAsSlice(elf.Elf64_Sym, try headers.phdrSlice(4)); - var expected_symbol_address = bytecode_hdr.p_vaddr; - for (dynsym_table) |symbol| { - if (symbol.st_info & elf.STT_FUNC == 0) continue; - if (symbol.st_value != expected_symbol_address) return error.OutOfBounds; - if (symbol.st_size == 0 or symbol.st_size % 8 != 0) return error.InvalidSize; - if (!inRangeOfPhdrVm(bytecode_hdr, symbol.st_value)) return error.OutOfBounds; - - const name = if (config.enable_symbol_and_section_labels) - try headers.shdrString( - maybe_symbols_shdr orelse return error.NoStringTable, - symbol.st_name, - 0xff, - ) - else - &.{}; - - const target_pc = (symbol.st_value -| bytecode_hdr.p_vaddr) / 8; - try self.function_registry.register(allocator, target_pc, name, target_pc); - expected_symbol_address = symbol.st_value +| symbol.st_size; - } - if (expected_symbol_address != bytecode_hdr.p_vaddr +| bytecode_hdr.p_memsz) { - return error.OutOfBounds; - } - if (!inRangeOfPhdrVm(bytecode_hdr, header.e_entry) or - header.e_entry % 8 != 0) - { - return error.InvalidFileHeader; - } - if (self.function_registry.lookupKey(self.entry_pc) == null) { - return error.InvalidFileHeader; + if (config.enable_symbol_and_section_labels) { + for (data.symbol_table) |symbol| { + if (symbol.st_info & elf.STT_FUNC == 0) continue; + const target_pc = symbol.st_value -| bytecode_hdr.p_vaddr / 8; + const name = try data.getSectionName(symbol.st_name); + try self.function_registry.register(allocator, target_pc, name, target_pc); + } } return self; @@ -1296,11 +1273,8 @@ test "strict header functions" { ); defer parsed.deinit(allocator); - const entrypoint = parsed.function_registry.lookupKey(0).?; + const entrypoint = parsed.function_registry.lookupKey(5).?; try expect(std.mem.eql(u8, entrypoint.name, "entrypoint")); - - const foo = parsed.function_registry.lookupKey(2).?; - try expect(std.mem.eql(u8, foo.name, "strict_header.foo")); } test "strict header corrupt file header" { diff --git a/src/vm/executable.zig b/src/vm/executable.zig index 57d6685d6a..fa42382a5f 100644 --- a/src/vm/executable.zig +++ b/src/vm/executable.zig @@ -127,8 +127,10 @@ pub const Executable = struct { const instructions = self.instructions; if (instructions.len == 0) return error.NoProgram; - const map = self.function_registry.map; - var iter = map.iterator(); + if (version.enableStricterVerification() and !instructions[0].isFunctionStartMarker()) { + return error.InvalidFunction; + } + var function_start: u64 = 0; var function_end: u64 = instructions.len; @@ -137,11 +139,15 @@ pub const Executable = struct { const inst = instructions[pc]; var store: bool = false; - if (version.enableStaticSyscalls() and map.contains(pc)) { - function_start = if (iter.next()) |entry| entry.key_ptr.* else 0; - const before_index = iter.index; - function_end = if (iter.next()) |entry| entry.key_ptr.* else instructions.len; - iter.index = before_index; + if (version.enableStricterVerification() and inst.isFunctionStartMarker()) { + function_start = pc; + function_end = pc +| 1; + + while (function_end < instructions.len and + !instructions[function_end].isFunctionStartMarker()) + { + function_end -|= 1; + } switch (instructions[function_end -| 1].opcode) { .ja, .@"return" => {}, else => return error.InvalidFunction, @@ -341,7 +347,9 @@ pub const Executable = struct { .call_imm => if (version.enableStaticSyscalls()) { const target_pc = version.computeTargetPc(pc, inst); - if (!self.function_registry.map.contains(target_pc)) { + if (target_pc >= instructions.len or + !instructions[target_pc].isFunctionStartMarker()) + { return error.InvalidFunction; } }, @@ -953,7 +961,7 @@ pub const Config = struct { /// Allowed [SBPFVersion]s minimum_version: sbpf.Version = .v0, - maximum_version: sbpf.Version = .v3, + maximum_version: sbpf.Version = .v4, pub fn stackSize(config: Config) u64 { return config.stack_frame_size * config.max_call_depth; diff --git a/src/vm/interpreter.zig b/src/vm/interpreter.zig index b274b07c84..c480cd7deb 100644 --- a/src/vm/interpreter.zig +++ b/src/vm/interpreter.zig @@ -656,8 +656,8 @@ pub const Vm = struct { next_pc = (target_pc -% self.vm_addr) / 8; if (next_pc >= instructions.len) return error.CallOutsideTextSegment; - if (version.enableStaticSyscalls() and - self.executable.function_registry.lookupKey(next_pc) == null) + if (version.enableStricterVerification() and + instructions[next_pc].isFunctionStartMarker()) { return error.UnsupportedInstruction; } diff --git a/src/vm/memory.zig b/src/vm/memory.zig index 35f6388f83..804933d9d6 100644 --- a/src/vm/memory.zig +++ b/src/vm/memory.zig @@ -32,7 +32,7 @@ pub const MemoryMap = union(enum) { version: sbpf.Version, config: exe.Config, ) (error{OutOfMemory} || InitError)!MemoryMap { - return if (config.aligned_memory_mapping) + return if (version == .v4 or config.aligned_memory_mapping) .{ .aligned = try AlignedMemoryMap.init(allocator, regions, version, config) } else .{ .unaligned = try UnalignedMemoryMap.init(allocator, regions, version, config) }; @@ -677,7 +677,7 @@ test "aligned vmap" { Region.init(.mutable, &program_mem, RODATA_START), Region.init(.constant, &stack_mem, STACK_START), }, - .v3, + .v4, .{}, ); defer m.deinit(allocator); @@ -721,7 +721,7 @@ test "aligned region" { Region.init(.mutable, &program_mem, RODATA_START), Region.init(.constant, &stack_mem, STACK_START), }, - .v3, + .v4, .{}, ); defer m.deinit(allocator); @@ -737,8 +737,8 @@ test "aligned region" { } test "invalid memory region" { - var program_mem: [4]u8 = .{0xFF} ** 4; - var stack_mem: [4]u8 = .{0xDD} ** 4; + var program_mem: [4]u8 = @splat(0xFF); + var stack_mem: [4]u8 = @splat(0xDD); try expectError( error.InvalidMemoryRegion, diff --git a/src/vm/sbpf.zig b/src/vm/sbpf.zig index db958920e9..7d27985400 100644 --- a/src/vm/sbpf.zig +++ b/src/vm/sbpf.zig @@ -19,6 +19,8 @@ pub const Version = enum(u32) { v2, /// SIMD-0178, SIMD-0179, SIMD-0189 v3, + /// SIMD-0177 + v4, /// support other versions as well! reserved, _, @@ -62,10 +64,13 @@ pub const Version = enum(u32) { } /// Enable SIMD-0178: SBPF Static Syscalls - /// Enable SIMD-0179: SBPF stricter verification constraints pub fn enableStaticSyscalls(version: Version) bool { return version.gte(.v3); } + /// Enable SIMD-0179: SBPF stricter verification constraints + pub fn enableStricterVerification(version: Version) bool { + return version.gte(.v3); + } /// Enable SIMD-0189: SBPF stricter ELF headers pub fn enableStricterElfHeaders(version: Version) bool { return version.gte(.v3); @@ -721,6 +726,10 @@ pub const Instruction = packed struct(u64) { return error.InvalidDestinationRegister; } + pub fn isFunctionStartMarker(inst: Instruction) bool { + return inst.opcode == .add64_imm and inst.dst == .r10; + } + pub fn format( inst: Instruction, comptime fmt: []const u8, diff --git a/src/vm/tests.zig b/src/vm/tests.zig index 42a3a51750..68e46a556e 100644 --- a/src/vm/tests.zig +++ b/src/vm/tests.zig @@ -2023,7 +2023,6 @@ test "pqr divide by zero" { program[0] = @intFromEnum(OpCode.mov32_imm); program[16] = @intFromEnum(OpCode.exit_or_syscall); - // TODO: Why does this cause a transitive error when using inline? for ([_]OpCode{ OpCode.udiv32_reg, OpCode.udiv64_reg, @@ -2036,7 +2035,7 @@ test "pqr divide by zero" { }) |opcode| { program[8] = @intFromEnum(opcode); - const config: Config = .{ .maximum_version = .v2 }; + const config: Config = .{ .maximum_version = .v4 }; var registry: sig.vm.Registry(u64) = .{}; var loader: Registry(Syscall) = .{}; @@ -2050,7 +2049,7 @@ test "pqr divide by zero" { ); defer executable.deinit(allocator); - const map = try MemoryMap.init(allocator, &.{}, .v3, .{}); + const map = try MemoryMap.init(allocator, &.{}, .v4, .{}); var prng = std.Random.DefaultPrng.init(10); var cache, var tc = try createTransactionContext( @@ -2461,7 +2460,7 @@ test "static syscall" { test "struct func pointer" { try testElfWithSyscalls( - .{}, + .{ .minimum_version = .v3, .maximum_version = .v4 }, sig.ELF_DATA_DIR ++ "struct_func_pointer.so", &.{}, .{ 0x0102030405060708, 3 }, @@ -2647,7 +2646,7 @@ test "lddw cannot be last" { } test "invalid dst reg" { - inline for (.{ .v0, .v3 }) |sbpf_version| { + inline for (.{ .v0, .v4 }) |sbpf_version| { try testVerify(.{ .maximum_version = sbpf_version }, \\entrypoint: \\ mov pc, 1 @@ -2657,7 +2656,7 @@ test "invalid dst reg" { } test "invalid src reg" { - inline for (.{ .v0, .v3 }) |sbpf_version| { + inline for (.{ .v0, .v4 }) |sbpf_version| { try testVerify(.{ .maximum_version = sbpf_version }, \\entrypoint: \\ mov r0, pc @@ -2710,7 +2709,7 @@ test "call lddw" { } test "callx r10" { - inline for (.{ .v0, .v3 }) |sbpf_version| { + inline for (.{ .v0, .v4 }) |sbpf_version| { try testVerify(.{ .maximum_version = sbpf_version }, \\entrypoint: \\ callx r10 @@ -2877,7 +2876,7 @@ test "sdiv disabled" { "sdiv64 r0, 4", "sdiv64 r0, r1", }) |inst| { - inline for (.{ .v0, .v3 }) |sbpf_version| { + inline for (.{ .v0, .v4 }) |sbpf_version| { const assembly = try std.fmt.allocPrint(allocator, \\entrypoint: \\ {s} @@ -2889,7 +2888,7 @@ test "sdiv disabled" { assembly, switch (sbpf_version) { .v0 => error.UnsupportedInstruction, - .v3 => {}, + .v4 => {}, else => unreachable, }, ); @@ -2898,7 +2897,7 @@ test "sdiv disabled" { } test "return instruction" { - inline for (.{ .v0, .v3 }) |sbpf_version| { + inline for (.{ .v0, .v4 }) |sbpf_version| { try testVerifyTextBytes( .{ .maximum_version = sbpf_version }, &.{ @@ -2908,7 +2907,7 @@ test "return instruction" { }, switch (sbpf_version) { .v0 => error.UnsupportedInstruction, - .v3 => error.InvalidSyscall, + .v4 => error.InvalidSyscall, else => unreachable, }, ); @@ -2918,6 +2917,7 @@ test "return instruction" { test "return in v2" { try testVerify(.{}, \\entrypoint: + \\ add64 r10, 0 \\ mov r0, 2 \\ return , {}); @@ -2926,6 +2926,7 @@ test "return in v2" { test "function without return" { try testVerify(.{}, \\entrypoint: + \\ add64 r10, 0 \\ mov r0, 2 \\ add64 r0, 5 , error.InvalidFunction); @@ -2942,7 +2943,7 @@ pub fn testSyscall( ) anyerror!void, config: struct { align_memory_map: bool = false, - version: sbpf.Version = .v3, + version: sbpf.Version = .v4, compute_meter: u64 = 10_000, }, ) !void {