Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simd-0178: static syscalls #3711

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/flamenco/runtime/tests/fd_vm_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ do{
ulong mask = (1UL << (max_pc % 64)) - 1UL;
calldests[ max_pc / 64 ] &= mask;
}
ulong entry_pc = fd_ulong_min( input->vm_ctx.entry_pc, rodata_sz / 8 - 1 );
if( input->vm_ctx.sbpf_version >= FD_SBPF_V3 ) {
/* in v3 we have to enable the entrypoint */
calldests[ entry_pc / 64 ] |= ( 1 << ( entry_pc % 64 ) );
}

/* Setup syscalls. Have them all be no-ops */
fd_sbpf_syscalls_t * syscalls = fd_sbpf_syscalls_new( fd_valloc_malloc( valloc, fd_sbpf_syscalls_align(), fd_sbpf_syscalls_footprint() ) );
Expand Down Expand Up @@ -154,7 +159,7 @@ do{
rodata_sz / 8, /* text_cnt */
0, /* text_off */
rodata_sz, /* text_sz */
input->vm_ctx.entry_pc,
entry_pc,
calldests,
input->vm_ctx.sbpf_version,
syscalls,
Expand Down
98 changes: 72 additions & 26 deletions src/flamenco/vm/fd_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,22 +156,29 @@ fd_vm_strerror( int err ) {
int
fd_vm_validate( fd_vm_t const * vm ) {

ulong sbpf_version = vm->sbpf_version;

/* A mapping of all the possible 1-byte sBPF opcodes to their
validation criteria. */

# define FD_VALID ((uchar)0) /* Valid opcode */
# define FD_CHECK_JMP ((uchar)1) /* Validation should check that the instruction is a valid jump */
# define FD_CHECK_END ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
# define FD_CHECK_ST ((uchar)3) /* Validation should check that the instruction is a valid store */
# define FD_CHECK_LDQ ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
# define FD_CHECK_DIV ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
# define FD_CHECK_SH32 ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
# define FD_CHECK_SH64 ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
# define FD_INVALID ((uchar)8) /* The opcode is invalid */
# define FD_CHECK_CALLX ((uchar)9) /* Validation should check that callx has valid register number */
# define FD_CHECK_CALLX_DEPR ((uchar)10) /* Older / deprecated FD_CHECK_CALLX */

static uchar validation_map[ 256 ] = {
# define FD_VALID ((uchar)0) /* Valid opcode */
# define FD_CHECK_JMP_V3 ((uchar)1) /* Validation should check that the instruction is a valid jump (v3+) */
# define FD_CHECK_END ((uchar)2) /* Validation should check that the instruction is a valid endianness conversion */
# define FD_CHECK_ST ((uchar)3) /* Validation should check that the instruction is a valid store */
# define FD_CHECK_LDQ ((uchar)4) /* Validation should check that the instruction is a valid load-quad */
# define FD_CHECK_DIV ((uchar)5) /* Validation should check that the instruction is a valid division by immediate */
# define FD_CHECK_SH32 ((uchar)6) /* Validation should check that the immediate is a valid 32-bit shift exponent */
# define FD_CHECK_SH64 ((uchar)7) /* Validation should check that the immediate is a valid 64-bit shift exponent */
# define FD_INVALID ((uchar)8) /* The opcode is invalid */
# define FD_CHECK_CALL_REG ((uchar)9) /* Validation should check that callx has valid register number */
# define FD_CHECK_CALL_REG_DEPR ((uchar)10) /* Older / deprecated FD_CHECK_CALLX */
# define FD_CHECK_CALL_IMM ((uchar)11) /* Check call against functions registry */
# define FD_CHECK_SYSCALL ((uchar)12) /* Check call against syscalls registry */
# define FD_CHECK_JMP_V0 ((uchar)13) /* Validation should check that the instruction is a valid jump (v0..v2) */

uchar FD_CHECK_JMP = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_JMP_V3 : FD_CHECK_JMP_V0;

uchar validation_map[ 256 ] = {
/* 0x00 */ FD_INVALID, /* 0x01 */ FD_INVALID, /* 0x02 */ FD_INVALID, /* 0x03 */ FD_INVALID,
/* 0x04 */ FD_VALID, /* 0x05 */ FD_CHECK_JMP, /* 0x06 */ FD_INVALID, /* 0x07 */ FD_VALID,
/* 0x08 */ FD_INVALID, /* 0x09 */ FD_INVALID, /* 0x0a */ FD_INVALID, /* 0x0b */ FD_INVALID,
Expand Down Expand Up @@ -205,13 +212,13 @@ fd_vm_validate( fd_vm_t const * vm ) {
/* 0x78 */ FD_INVALID, /* 0x79 */ FD_INVALID, /* 0x7a */ FD_INVALID, /* 0x7b */ FD_INVALID,
/* 0x7c */ FD_VALID, /* 0x7d */ FD_CHECK_JMP, /* 0x7e */ FD_VALID, /* 0x7f */ FD_VALID,
/* 0x80 */ FD_INVALID, /* 0x81 */ FD_INVALID, /* 0x82 */ FD_INVALID, /* 0x83 */ FD_INVALID,
/* 0x84 */ FD_INVALID, /* 0x85 */ FD_VALID, /* 0x86 */ FD_VALID, /* 0x87 */ FD_CHECK_ST,
/* 0x84 */ FD_INVALID, /* 0x85 */ FD_CHECK_CALL_IMM,/*0x86*/FD_VALID, /* 0x87 */ FD_CHECK_ST,
/* 0x88 */ FD_INVALID, /* 0x89 */ FD_INVALID, /* 0x8a */ FD_INVALID, /* 0x8b */ FD_INVALID,
/* 0x8c */ FD_VALID, /* 0x8d */ FD_CHECK_CALLX,/* 0x8e */ FD_VALID, /* 0x8f */ FD_CHECK_ST,
/* 0x8c */ FD_VALID, /* 0x8d */ FD_CHECK_CALL_REG,/*0x8e*/FD_VALID, /* 0x8f */ FD_CHECK_ST,
/* 0x90 */ FD_INVALID, /* 0x91 */ FD_INVALID, /* 0x92 */ FD_INVALID, /* 0x93 */ FD_INVALID,
/* 0x94 */ FD_INVALID, /* 0x95 */ FD_VALID, /* 0x96 */ FD_VALID, /* 0x97 */ FD_CHECK_ST,
/* 0x94 */ FD_INVALID, /* 0x95 */ FD_CHECK_SYSCALL,/*0x96*/ FD_VALID, /* 0x97 */ FD_CHECK_ST,
/* 0x98 */ FD_INVALID, /* 0x99 */ FD_INVALID, /* 0x9a */ FD_INVALID, /* 0x9b */ FD_INVALID,
/* 0x9c */ FD_VALID, /* 0x9d */ FD_INVALID, /* 0x9e */ FD_VALID, /* 0x9f */ FD_CHECK_ST,
/* 0x9c */ FD_VALID, /* 0x9d */ FD_VALID, /* 0x9e */ FD_VALID, /* 0x9f */ FD_CHECK_ST,
/* 0xa0 */ FD_INVALID, /* 0xa1 */ FD_INVALID, /* 0xa2 */ FD_INVALID, /* 0xa3 */ FD_INVALID,
/* 0xa4 */ FD_VALID, /* 0xa5 */ FD_CHECK_JMP, /* 0xa6 */ FD_INVALID, /* 0xa7 */ FD_VALID,
/* 0xa8 */ FD_INVALID, /* 0xa9 */ FD_INVALID, /* 0xaa */ FD_INVALID, /* 0xab */ FD_INVALID,
Expand All @@ -238,8 +245,6 @@ fd_vm_validate( fd_vm_t const * vm ) {
/* 0xfc */ FD_INVALID, /* 0xfd */ FD_INVALID, /* 0xfe */ FD_VALID, /* 0xff */ FD_INVALID,
};

ulong sbpf_version = vm->sbpf_version;

/* SIMD-0173: LDDW */
validation_map[ 0x18 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? FD_CHECK_LDQ : FD_INVALID;
validation_map[ 0xf7 ] = FD_VM_SBPF_ENABLE_LDDW(sbpf_version) ? FD_INVALID : FD_VALID; /* HOR64 */
Expand Down Expand Up @@ -280,7 +285,7 @@ fd_vm_validate( fd_vm_t const * vm ) {
validation_map[ 0x9f ] = FD_VM_SBPF_MOVE_MEMORY_IX_CLASSES(sbpf_version) ? FD_CHECK_ST : FD_VALID;

/* SIMD-0173: CALLX */
validation_map[ 0x8d ] = FD_VM_SBPF_CALLX_USES_SRC_REG(sbpf_version) ? FD_CHECK_CALLX : FD_CHECK_CALLX_DEPR;
validation_map[ 0x8d ] = FD_VM_SBPF_CALLX_USES_SRC_REG(sbpf_version) ? FD_CHECK_CALL_REG : FD_CHECK_CALL_REG_DEPR;

/* SIMD-0174: MUL, DIV, MOD */
validation_map[ 0x24 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_INVALID : FD_VALID;
Expand Down Expand Up @@ -318,6 +323,11 @@ fd_vm_validate( fd_vm_t const * vm ) {
validation_map[ 0xf6 ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_CHECK_DIV : FD_INVALID; /* SREM64 */
validation_map[ 0xfe ] = FD_VM_SBPF_ENABLE_PQR (sbpf_version) ? FD_VALID : FD_INVALID;

/* SIMD-0178: static syscalls */
validation_map[ 0x85 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_CALL_IMM : FD_VALID;
validation_map[ 0x95 ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_CHECK_SYSCALL : FD_VALID;
validation_map[ 0x9d ] = FD_VM_SBPF_STATIC_SYSCALLS (sbpf_version) ? FD_VALID : FD_INVALID;

/* FIXME: These checks are not necessary assuming fd_vm_t is populated by metadata
generated in fd_sbpf_elf_peek (which performs these checks). But there is no guarantee, and
this non-guarantee is (rightfully) exploited by the fuzz harnesses.
Expand Down Expand Up @@ -345,20 +355,31 @@ fd_vm_validate( fd_vm_t const * vm ) {

case FD_VALID: break;

case FD_CHECK_JMP: {
/* Store ops are special because they allow dreg==r10.
We use a special validation_code, used later in the
"Check registers" section.
But there's nothing to do at this time. */
case FD_CHECK_ST: break;

case FD_CHECK_JMP_V0: {
long jmp_dst = (long)i + (long)instr.offset + 1L;
if( FD_UNLIKELY( (jmp_dst<0) | (jmp_dst>=(long)text_cnt) ) ) return FD_VM_ERR_JMP_OUT_OF_BOUNDS;
//FIXME: this shouldn't be here?
if( FD_UNLIKELY( fd_sbpf_instr( text[ jmp_dst ] ).opcode.raw==FD_SBPF_OP_ADDL_IMM ) ) return FD_VM_ERR_JMP_TO_ADDL_IMM;
break;
}

case FD_CHECK_JMP_V3: {
long jmp_dst = (long)i + (long)instr.offset + 1L;
if( FD_UNLIKELY( (jmp_dst<0) | (jmp_dst>=(long)text_cnt) ) ) return FD_VM_ERR_JMP_OUT_OF_BOUNDS;
break;
}

case FD_CHECK_END: {
if( FD_UNLIKELY( !((instr.imm==16) | (instr.imm==32) | (instr.imm==64)) ) ) return FD_VM_ERR_INVALID_END_IMM;
break;
}

case FD_CHECK_ST: break; /* FIXME: HMMM ... */

/* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L244 */
case FD_CHECK_LDQ: {
/* https://github.com/solana-labs/rbpf/blob/b503a1867a9cfa13f93b4d99679a17fe219831de/src/verifier.rs#L131 */
Expand All @@ -376,7 +397,7 @@ fd_vm_validate( fd_vm_t const * vm ) {
}

case FD_CHECK_DIV: {
if( FD_UNLIKELY( instr.imm==0 ) ) return FD_VM_ERR_SIGFPE; /* FIXME: SIGILL? */
if( FD_UNLIKELY( instr.imm==0 ) ) return FD_VM_ERR_SIGFPE;
break;
}

Expand All @@ -391,19 +412,44 @@ fd_vm_validate( fd_vm_t const * vm ) {
}

/* https://github.com/solana-labs/rbpf/blob/v0.8.5/src/verifier.rs#L207 */
case FD_CHECK_CALLX: {
case FD_CHECK_CALL_REG: {
if( FD_UNLIKELY( instr.src_reg > 9 ) ) {
return FD_VM_ERR_INVALID_REG;
}
break;
}
case FD_CHECK_CALLX_DEPR: {
case FD_CHECK_CALL_REG_DEPR: {
if( FD_UNLIKELY( instr.imm > 9 ) ) {
return FD_VM_ERR_INVALID_REG;
}
break;
}

/* https://github.com/solana-labs/rbpf/blob/4ad935be/src/verifier.rs#L411-L418 */
case FD_CHECK_CALL_IMM: {
ulong target_pc = (ulong)( fd_long_sat_add( (long)i, fd_long_sat_add( (long)(int)instr.imm, 1 ) ) );
if( FD_UNLIKELY( target_pc>text_cnt || !fd_sbpf_calldests_test( vm->calldests, target_pc ) ) ) {
return FD_VM_INVALID_FUNCTION;
}
break;
}

/* https://github.com/solana-labs/rbpf/blob/4ad935be/src/verifier.rs#L423-L428 */
case FD_CHECK_SYSCALL: {
uint imm = instr.imm;
/* check out of bound */
if( FD_UNLIKELY( imm==0 || imm >= FD_VM_SBPF_STATIC_SYSCALLS_LIST_SZ ) ) {
return FD_VM_INVALID_SYSCALL;
}
uint syscall_key = FD_VM_SBPF_STATIC_SYSCALLS_LIST[ imm ];
/* check active syscall */
fd_sbpf_syscalls_t const * syscall = fd_sbpf_syscalls_query_const( vm->syscalls, syscall_key, NULL );
if( FD_UNLIKELY( !syscall ) ) {
return FD_VM_INVALID_SYSCALL;
}
break;
}

case FD_INVALID: default: return FD_VM_ERR_INVALID_OPCODE;
}

Expand Down
2 changes: 2 additions & 0 deletions src/flamenco/vm/fd_vm_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
#define FD_VM_ERR_BAD_TEXT (-36) /* detected a bad text section (overflow, outside rodata boundary, etc.,)*/
#define FD_VM_SH_OVERFLOW (-37) /* detected a shift overflow, equivalent to VeriferError::ShiftWithOverflow */
#define FD_VM_TEXT_SZ_UNALIGNED (-38) /* detected a text section that is not a multiple of 8 */
#define FD_VM_INVALID_FUNCTION (-39) /* detected an invalid function */
#define FD_VM_INVALID_SYSCALL (-40) /* detected an invalid syscall */

/* Syscall Errors
https://github.com/anza-xyz/agave/blob/v2.0.7/programs/bpf_loader/src/syscalls/mod.rs#L81 */
Expand Down
Loading
Loading