From c3f8b3ae193aa583fa9cc7fcefd3d1a9df639308 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Fri, 3 Jan 2025 16:30:35 -0800 Subject: [PATCH] svm: include elf binary tests --- data/test-elfs/reloc_64_64_sbpfv1.so | Bin 0 -> 1440 bytes .../reloc_64_relative_data_sbpfv1.so | Bin 0 -> 1920 bytes data/test-elfs/reloc_64_relative_sbpfv1.so | Bin 0 -> 1616 bytes data/test-elfs/rodata_section_sbpfv1.so | Bin 0 -> 1616 bytes data/test-elfs/static_internal_call_sbpfv1.so | Bin 0 -> 1256 bytes data/test-elfs/syscall_reloc_64_32.so | Bin 0 -> 1632 bytes src/sig.zig | 1 + src/svm/Executable.zig | 11 +- src/svm/memory.zig | 2 +- src/svm/syscalls.zig | 29 +++-- src/svm/tests.zig | 100 ++++++++++++++++++ 11 files changed, 125 insertions(+), 18 deletions(-) create mode 100755 data/test-elfs/reloc_64_64_sbpfv1.so create mode 100755 data/test-elfs/reloc_64_relative_data_sbpfv1.so create mode 100755 data/test-elfs/reloc_64_relative_sbpfv1.so create mode 100755 data/test-elfs/rodata_section_sbpfv1.so create mode 100755 data/test-elfs/static_internal_call_sbpfv1.so create mode 100755 data/test-elfs/syscall_reloc_64_32.so diff --git a/data/test-elfs/reloc_64_64_sbpfv1.so b/data/test-elfs/reloc_64_64_sbpfv1.so new file mode 100755 index 0000000000000000000000000000000000000000..a68fc3b742e8541bc79d04dea4f5c1872fd1df2d GIT binary patch literal 1440 zcmbtT%}&BV5FUOML=TFI;b2T}>cI^d5W)!~nwV7M)e}N(lqkiNn#jqg@XllT5MF!& zo$1ckkVX#bq}y+IX1<-7-PzYu_bi*q5F|&1c;%?7qXs7j526mWD98U26-nHdc)&l6 z6{P`b-718a=kcy_(s-pjAb45UFY{C<=^MDdoei@ywa z7(Do4+i~5lQ)`&2smE>$f5OSr=U3WcR}|T7a(}RI^W?|8Oys{rWNxQHe>C*s$z(dR z7=>g)|51!&yzywnMUY{0)E_Y3PomJyEE4;Uz1~rsKT~Vgs)I(odEnhWo9cY|z3%np z@x}QGQu`O;{BYkmS737f8YSJY1=b4}&u>w`qYuH?XBPLL^p>_!(r@Uys>T(*_C%gMjb%?a#JhqX@qveIKy3I3 qc2s$zO36`jKz!8SR=v6#Qu8Z7H2efgS_(cy5i`&C-pN@)#Y%f`=FQB`?(FQnTC6OUMn+7dY(EX!R}%9w zj7l(@w;NXW?{OO+_#SYR{6mZwCbWs1Kn^T9#?&F6&fQF?xRUb+eO_Q(h;PXY$GFZk zd8*dqKHiW>>l>X~_1e{{3rW^|QLnBiSrXUef0lyEFiCwI8}c!ggGI!XM5eLd{eB0O zwt+JY2IG%XohG8D7|XOD$yHO{Q`80Dck}hv=&yb5P?z5@s^-I-WbAXwcdPr1Xc%{# z^qc#|gQ&h%Z)a)mX{XuFY-y#kNcX{c;fi_2XUQhEBI;Y8cJIA=|N5n?f32syh{F-e zoRGnVqta|Ysi!xiN@XFsJQvTz*F%rJI@TDqZ~eWj*+mega?xM1WAFRb_+c=feV{#?dF9rus7TRbxf#AOCzkI18Vz4J@X zA-BTx-i3Pa?lKrUxAZv|e)zvd@oN->=jf>aiSjG{mJ5e{Dt?=SFrs4^ulnJ9uql3r zf-sENe@VFNu{hI=&*AZb>*zl!o6ax1?*`?T5AX}gO_IkPQD>cB^{Yx15S@HLDMg|b32{OZryjt{hNNtJDo{|77Q_JwaZ0_r7DSL;v^A(X@fW!8 zKlnNP4Q_}_m|4$@BC8-FMslA0Ja6a2c0NDOo^(4Mg7s*lT@92v)QvAacn}>@N`3y_ zqD>OF3q0T-<4u(TX*&?2>+?u8PK=jT2aI(;RTtw6)d8XFGJlcBvFc*{raIsyT}$4` zO{lNRyUI6<5B03_fw-l-)FgH-0HqR`Z6^TIq3hB5*K%14rRlY~${O`;$$|}il%9+2 z>i5y7D}kmPivJ1{e}}WcIU*iK1!Et5%4XF&*BlJ8>}Zfo%+QSg9$Dh_>Blnmy3Is~ zx~ra}u28ao*{ZILbGFR$$-TY%t}I9U z$tX{Xq$u5FKONb8T(aj{JEuplpFeo^^btz>FOL+@M4L#}_FekC0$NK={ zeXvVwasIo^Zt{R0?-z(X@y-z+e8O7!Ci3KIYsdOQynFD74?N%v#D!XoGKb!)YYX31|8kw=IpTtr_jI2Aj*=&ERE+h%0SJz09RL6T literal 0 HcmV?d00001 diff --git a/data/test-elfs/rodata_section_sbpfv1.so b/data/test-elfs/rodata_section_sbpfv1.so new file mode 100755 index 0000000000000000000000000000000000000000..d61014d5a8594ec1143f8619a60d3c8d4f1f3a7e GIT binary patch literal 1616 zcmbtUO>fgc5S@HLDMg|bsp<)QtPohSXj-eMw6s-`+=>H2z$G}gX(Nb@oJDF*{R>?9 zAN(Bt1~j?#?*{Wx zqfln6j`&sij}#v$%AUmTIiOe)v#mKmIdtC}|C-M$32Aw@RvBY|D>7i)aA40|+)Z8o zw#M%$t}Fg4Nd7ghBG5ttEKroDo_(surLhsrCJi9B5IyIdiQ4T8QmV^ZlSZVP|2zp(!p5g>{vuCZ#Z+mFZ-hS!X(%I5agAijOi$ zVOC8hyh#dq+N2}K^Qozw=aAfD!)cM3aaD$C+|RT3+uKjFv(I6VhHp=!t=}pAS9{5D zKiWBen(Y+vD9%QCkw--l9n!V)93H$m-8*^y414+?lyj9w?z$MhQ-dztym7t=EU_bc zy;jY47)X%K=lkQk%#qyqUHq-jFvnuz`k&>WCUj!VJ+|Z*x zfanjVSkrA`NSo{}^8-El7l=C1=g5zE#9CPsb+T*iSQkX!BaZyQU7kR0a0K6J@Ir&q m?@|NuWB>ac!`UmS5S}Dz{W}PP=tY7zFQGkI^b)ZOLiFyb*4ko_BqfbtPadR?;5&Hq;6vzJ z_!eHB$LcV4el75b#7p>#smhyq5I%#8ppBVi)I8q&OQT@Ndl^9&tgb4L#-usO>PrnCpiemRQql+xNQ?*KNcspa<@97`11q nA4<5?p!6+gaQkm@4Bt+vHN=|Ky=2q{_uw1-qw04+RCM)U>fApJ literal 0 HcmV?d00001 diff --git a/data/test-elfs/syscall_reloc_64_32.so b/data/test-elfs/syscall_reloc_64_32.so new file mode 100755 index 0000000000000000000000000000000000000000..c765a7cc9a1ae3b01841e4ae5926621b8f14d983 GIT binary patch literal 1632 zcmbVMJx?1!5FKnMd_{r~2~kB?P|##CF^LeO074>=4n&b6ROWm>6ARlHoev3DNt2&| zM9EJ`$M2%0apv94bK(PvWTd?}-!r?jd*46$pGp%GLMqE-CV8gXHp5Ms9wOVaAr<}3 z%Cx}knJ)Slv1(yNna>r7tLQqmIz@yHL@dTlm2!I#FLSjJ83*_0y1rPwBL1>C;*`lt z>d)Swy46JJ>8I{vsB~+7SB9-Po&o#XWcL5kQTl^^7<4*yjfunh z`}KP5t*Zr%R@i7oZ^E++SCzuC)%Q-0b`B3da2x-PI4>;ReGunJXJ#$C5-J-YvV2m1(8LOg0Sp6|j_9cnY4 z?}NxX>XH%rr@W%fv@XV<8bkEVk^5zR=3P@eau(0|hvnhBw=|hAc=oZWJZsYjcx_7l q=kT7ePuA!DcTKM_M)Nk1&tuv@b*c81;q0gbHch$pw^DibP4#cb)?U^C literal 0 HcmV?d00001 diff --git a/src/sig.zig b/src/sig.zig index 0feb66be2..f5b6a80ee 100644 --- a/src/sig.zig +++ b/src/sig.zig @@ -32,3 +32,4 @@ pub const TEST_STATE_DIR = "data/test-state/"; pub const FUZZ_DATA_DIR = "data/fuzz-data/"; pub const BENCHMARK_RESULTS_DIR = "results/"; pub const GENESIS_DIR = "data/genesis-files/"; +pub const ELF_DATA_DIR = "data/test-elfs/"; diff --git a/src/svm/Executable.zig b/src/svm/Executable.zig index 1681e59f0..3fe8e64d1 100644 --- a/src/svm/Executable.zig +++ b/src/svm/Executable.zig @@ -2,6 +2,7 @@ const std = @import("std"); const ebpf = @import("ebpf.zig"); const Elf = @import("Elf.zig"); const memory = @import("memory.zig"); +const syscalls = @import("syscalls.zig"); const Vm = @import("Vm.zig"); const Executable = @This(); @@ -487,16 +488,8 @@ pub fn Registry(T: type) type { }; } -pub const SyscallError = error{ - InvalidVirtualAddress, - AccessNotMapped, - SyscallAbort, - AccessViolation, - VirtualAccessTooLong, -}; - pub const BuiltinProgram = struct { - functions: Registry(*const fn (*Vm) SyscallError!void) = .{}, + functions: Registry(*const fn (*Vm) syscalls.Error!void) = .{}, pub fn deinit(program: *BuiltinProgram, allocator: std.mem.Allocator) void { program.functions.deinit(allocator); diff --git a/src/svm/memory.zig b/src/svm/memory.zig index 3804bd5a5..3d27ac25c 100644 --- a/src/svm/memory.zig +++ b/src/svm/memory.zig @@ -231,7 +231,7 @@ test "aligned region" { ); try expectError( error.AccessNotMapped, - m.region(INPUT_START + 4), + m.region(INPUT_START + 3), ); } diff --git a/src/svm/syscalls.zig b/src/svm/syscalls.zig index 77ea16dbe..552e8266b 100644 --- a/src/svm/syscalls.zig +++ b/src/svm/syscalls.zig @@ -6,8 +6,21 @@ const Executable = @import("Executable.zig"); const Pubkey = sig.core.Pubkey; +pub const Syscall = struct { + name: []const u8, + builtin_fn: *const fn (*Vm) Error!void, +}; + +pub const Error = error{ + InvalidVirtualAddress, + AccessNotMapped, + SyscallAbort, + AccessViolation, + VirtualAccessTooLong, +}; + // logging -pub fn printString(vm: *Vm) Executable.SyscallError!void { +pub fn printString(vm: *Vm) Error!void { const vm_addr = vm.registers.get(.r1); const len = vm.registers.get(.r2); const host_addr = try vm.memory_map.vmap(.constant, vm_addr, len); @@ -15,14 +28,14 @@ pub fn printString(vm: *Vm) Executable.SyscallError!void { if (!builtin.is_test) std.debug.print("{s}", .{string}); } -pub fn log(vm: *Vm) Executable.SyscallError!void { +pub fn log(vm: *Vm) Error!void { const vm_addr = vm.registers.get(.r1); const len = vm.registers.get(.r2); const host_addr = try vm.memory_map.vmap(.constant, vm_addr, len); std.debug.print("log: {s}\n", .{host_addr}); } -pub fn log64(vm: *Vm) Executable.SyscallError!void { +pub fn log64(vm: *Vm) Error!void { const arg1 = vm.registers.get(.r1); const arg2 = vm.registers.get(.r2); const arg3 = vm.registers.get(.r3); @@ -35,19 +48,19 @@ pub fn log64(vm: *Vm) Executable.SyscallError!void { ); } -pub fn logPubkey(vm: *Vm) Executable.SyscallError!void { +pub fn logPubkey(vm: *Vm) Error!void { const pubkey_addr = vm.registers.get(.r1); const pubkey_bytes = try vm.memory_map.vmap(.constant, pubkey_addr, @sizeOf(Pubkey)); const pubkey: Pubkey = @bitCast(pubkey_bytes[0..@sizeOf(Pubkey)].*); std.debug.print("log: {}\n", .{pubkey}); } -pub fn logComputeUnits(_: *Vm) Executable.SyscallError!void { +pub fn logComputeUnits(_: *Vm) Error!void { std.debug.print("TODO: compute budget calculations\n", .{}); } // memory operators -pub fn memset(vm: *Vm) Executable.SyscallError!void { +pub fn memset(vm: *Vm) Error!void { const dst_addr = vm.registers.get(.r1); const scalar = vm.registers.get(.r2); const len = vm.registers.get(.r3); @@ -56,7 +69,7 @@ pub fn memset(vm: *Vm) Executable.SyscallError!void { @memset(host_addr, @truncate(scalar)); } -pub fn memcpy(vm: *Vm) Executable.SyscallError!void { +pub fn memcpy(vm: *Vm) Error!void { const dst_addr = vm.registers.get(.r1); const src_addr = vm.registers.get(.r2); const len = vm.registers.get(.r3); @@ -67,6 +80,6 @@ pub fn memcpy(vm: *Vm) Executable.SyscallError!void { } // special -pub fn abort(_: *Vm) Executable.SyscallError!void { +pub fn abort(_: *Vm) Error!void { return error.SyscallAbort; } diff --git a/src/svm/tests.zig b/src/svm/tests.zig index 8ade04df2..a4ad6537a 100644 --- a/src/svm/tests.zig +++ b/src/svm/tests.zig @@ -1,7 +1,11 @@ const std = @import("std"); +const sig = @import("../sig.zig"); const Executable = @import("Executable.zig"); const memory = @import("memory.zig"); const Vm = @import("Vm.zig"); +const syscalls = @import("syscalls.zig"); +const ebpf = @import("ebpf.zig"); +const Elf = @import("Elf.zig"); const Region = memory.Region; const MemoryMap = memory.MemoryMap; @@ -1539,3 +1543,99 @@ test "fixed stack out of bounds" { \\ exit , error.AccessNotMapped); } + +fn testElf(path: []const u8, expected: anytype) !void { + return testElfWithSyscalls(path, &.{}, expected); +} + +fn testElfWithSyscalls( + path: []const u8, + extra_syscalls: []const syscalls.Syscall, + expected: anytype, +) !void { + const allocator = std.testing.allocator; + + const input_file = try std.fs.cwd().openFile(path, .{}); + const bytes = try input_file.readToEndAlloc(allocator, ebpf.MAX_FILE_SIZE); + defer allocator.free(bytes); + + var loader: Executable.BuiltinProgram = .{}; + defer loader.deinit(allocator); + + for (extra_syscalls) |syscall| { + _ = try loader.functions.registerFunctionHashed( + allocator, + syscall.name, + syscall.builtin_fn, + ); + } + + const elf = try Elf.parse(bytes, allocator, &loader); + + var executable = try Executable.fromElf(allocator, &elf); + defer executable.deinit(allocator); + + const stack_memory = try allocator.alloc(u8, 4096); + defer allocator.free(stack_memory); + + const m = try MemoryMap.init(&.{ + executable.getRoRegion(), + Region.init(.mutable, stack_memory, memory.STACK_START), + Region.init(.constant, &.{}, memory.HEAP_START), + Region.init(.mutable, &.{}, memory.INPUT_START), + }, .v1); + + var vm = try Vm.init(allocator, &executable, m, &loader); + defer vm.deinit(); + + const result = vm.run(); + try expectEqual(expected, result); +} + +test "BPF_64_64 sbpfv1" { + // [ 1] .text PROGBITS 0000000000000120 000120 000018 00 AX 0 0 8 + // prints the address of the first byte in the .text section + try testElf( + sig.ELF_DATA_DIR ++ "reloc_64_64_sbpfv1.so", + memory.PROGRAM_START + 0x120, + ); +} + +test "BPF_64_RELATIVE data sbpv1" { + // [ 1] .text PROGBITS 00000000000000e8 0000e8 000020 00 AX 0 0 8 + // [ 2] .rodata PROGBITS 0000000000000108 000108 000019 01 AMS 0 0 1 + // prints the address of the first byte in the .rodata sections + try testElf( + sig.ELF_DATA_DIR ++ "reloc_64_relative_data_sbpfv1.so", + memory.PROGRAM_START + 0x108, + ); +} + +test "BPF_64_RELATIVE sbpv1" { + try testElf( + sig.ELF_DATA_DIR ++ "reloc_64_relative_sbpfv1.so", + memory.PROGRAM_START + 0x138, + ); +} + +test "load elf rodata sbpfv1" { + try testElf( + sig.ELF_DATA_DIR ++ "rodata_section_sbpfv1.so", + 42, + ); +} + +test "static internal call sbpv1" { + try testElf( + sig.ELF_DATA_DIR ++ "static_internal_call_sbpfv1.so", + 10, + ); +} + +test "syscall reloc 64_32" { + try testElfWithSyscalls( + sig.ELF_DATA_DIR ++ "syscall_reloc_64_32.so", + &.{.{ .name = "log", .builtin_fn = syscalls.printString }}, + 0, + ); +}