Skip to content
Open
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
25 changes: 13 additions & 12 deletions src/gdb/exec_result/recv/result_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,18 +176,19 @@ fn update_stack(data: HashMap<String, String>, state: &mut State, begin: String)
}
}

// all string? Request the next
if val > 0xff {
let bytes = val.to_le_bytes();
if bytes
.iter()
.all(|a| a.is_ascii_alphabetic() || a.is_ascii_graphic() || a.is_ascii_whitespace())
{
let addr = data["begin"].strip_prefix("0x").unwrap().to_string();
let addr = u64::from_str_radix(&addr, 16).unwrap();
state.next_write.push(data_read_memory_bytes(addr + len, 0, len));
state.written.push_back(Written::Stack(Some(begin)));
return;
if state.config.deref_show_string {
//all string? Request the next
if val > 0xff {
let bytes = val.to_le_bytes();
if bytes.iter().all(|a| {
a.is_ascii_alphabetic() || a.is_ascii_graphic() || a.is_ascii_whitespace()
}) {
let addr = data["begin"].strip_prefix("0x").unwrap().to_string();
let addr = u64::from_str_radix(&addr, 16).unwrap();
state.next_write.push(data_read_memory_bytes(addr + len, 0, len));
state.written.push_back(Written::Stack(Some(begin)));
return;
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/gdb/exec_result/recv/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ pub fn recv_exec_result_value(state: &mut State, value: &String) {
} else {
// program is stopped, get the current pc
let pc: Vec<&str> = value.split_whitespace().collect();
let pc = pc[0].strip_prefix("0x").unwrap();
state.current_pc = u64::from_str_radix(pc, 16).unwrap();
if let Some(pc) = pc[0].strip_prefix("0x") {
state.current_pc = u64::from_str_radix(pc, 16).unwrap();
}
}
}
70 changes: 63 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ impl Scroll {
}
}

#[derive(Clone, Debug)]
struct Config {
// Deref and show string values
deref_show_string: bool,
}

impl Default for Config {
fn default() -> Self {
Self { deref_show_string: true }
}
}

#[derive(Clone, Debug)]
struct State {
/// Messages to write to gdb mi
Expand Down Expand Up @@ -253,10 +265,11 @@ struct State {
status: String,
bt: Vec<Bt>,
completions: Vec<String>,
config: Config,
}

impl State {
pub fn new(args: Args) -> State {
pub fn new(args: Args, config: Config) -> State {
State {
next_write: vec![],
written: VecDeque::new(),
Expand Down Expand Up @@ -286,6 +299,7 @@ impl State {
status: String::new(),
bt: vec![],
completions: vec![],
config,
}
}
}
Expand Down Expand Up @@ -425,7 +439,8 @@ fn main() -> anyhow::Result<()> {
}
// Start rx thread
let (gdb_stdout, mut app) = App::new_stream(args.clone());
let state = State::new(args.clone());
let config = Config::default();
let state = State::new(args.clone(), config);
let mut state_share = StateShare { state: Arc::new(Mutex::new(state)) };

// Setup terminal
Expand Down Expand Up @@ -1067,9 +1082,15 @@ mod tests {
use ratatui::{Terminal, backend::TestBackend};
use test_assets_ureq::{TestAssetDef, dl_test_files_backoff};

fn run_a_bit(args: Args) -> (App, StateShare, Terminal<TestBackend>) {
fn run_a_bit(
args: Args,
mode: Mode,
size: Option<(u16, u16)>,
) -> (App, StateShare, Terminal<TestBackend>) {
let (gdb_stdout, mut app) = App::new_stream(args.clone());
let state = State::new(args.clone());
let config = Config { deref_show_string: false, ..Config::default() };
let mut state = State::new(args.clone(), config);
state.mode = mode;
let state_share = StateShare { state: Arc::new(Mutex::new(state)) };
spawn_gdb_interact(&state_share, gdb_stdout);

Expand All @@ -1083,7 +1104,11 @@ mod tests {
}
}
}
let mut terminal = Terminal::new(TestBackend::new(160, 50)).unwrap();
let mut terminal = if let Some((x, y)) = size {
Terminal::new(TestBackend::new(x, y)).unwrap()
} else {
Terminal::new(TestBackend::new(160, 50)).unwrap()
};
let start_time = Instant::now();
let duration = Duration::from_secs(10);

Expand Down Expand Up @@ -1147,7 +1172,7 @@ mod tests {
let mut args = Args::default();
args.cmds = Some(PathBuf::from("test-sources/repeated_ptr.source"));

let (_, state, terminal) = run_a_bit(args);
let (_, state, terminal) = run_a_bit(args, Mode::All, None);
let _output = terminal.backend();
let registers = state.state.lock().unwrap().registers.clone();
let stack = state.state.lock().unwrap().stack.clone();
Expand All @@ -1164,6 +1189,37 @@ mod tests {
assert!(stack[5].1.repeated_pattern);
}

#[test]
fn test_segfault() {
// ```
// int main() {
// int *p = (int*)0xdeadbeef;
// *p = 42;
// return 0;
// }
// ```
const FILE_NAME: &str = "segfault";
const TEST_PATH: &str = "test-assets/segfault/";
let file_path = format!("{TEST_PATH}/{FILE_NAME}");
let asset_defs = [TestAssetDef {
filename: FILE_NAME.to_string(),
hash: "61611926b49c78c45d3988dfca1a612c0eb78f68b2cc4ae07ac8d7080f4c2e77".to_string(),
url: "https://wcampbell.dev/heretek/segfault/segfault".to_string(),
}];

dl_test_files_backoff(&asset_defs, TEST_PATH, true, Duration::from_secs(1)).unwrap();
let c_path = CString::new(file_path.to_string()).expect("CString::new failed");
let mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
unsafe { chmod(c_path.as_ptr(), mode) };

let mut args = Args::default();
args.cmds = Some(PathBuf::from("test-sources/segfault.source"));

let (_, state, terminal) = run_a_bit(args, Mode::All, Some((160, 40)));
let output = terminal.backend();
assert_snapshot!(output);
}

#[test]
fn test_render_app() {
// gcc test.c -g -fno-stack-protector -static
Expand Down Expand Up @@ -1209,7 +1265,7 @@ mod tests {
let mut args = Args::default();
args.cmds = Some(PathBuf::from("test-sources/test.source"));

let (_, state, terminal) = run_a_bit(args);
let (_, state, terminal) = run_a_bit(args, Mode::All, None);
let output = terminal.backend();

// Now, we need to rewrite all the addresses that change for the registers and stack
Expand Down
4 changes: 2 additions & 2 deletions src/snapshots/heretek__tests__render_app.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ snapshot_kind: text
" rax → 0x401825 → main+0 (push rbp) █"
" rbx → 0x1 ║"
" rcx → <rcx_0> → 0x04 ║"
" rdx → <rdx_0> → <rdx_1> → <rdx_2> ║"
" rsi → <rsi_0> → <rsi_1> → <rsi_2> ║"
" rdx → <rdx_0> → <rdx_1> → <string> ║"
" rsi → <rsi_0> → <rsi_1> → <string> ║"
" rdi → 0x1 ║"
" rbp → <stack_8> → <rbp_1> → <rbp_2> → 0x00 ║"
" rsp → <stack_0> → <stack_6> → <stack_6_0> → <stack_6_1> → <rbp_1> → <rbp_2> → 0x00 ║"
Expand Down
45 changes: 45 additions & 0 deletions src/snapshots/heretek__tests__segfault.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
source: src/main.rs
expression: output
snapshot_kind: text
---
"────────────────────────────────────────────────────────────────────────|heretek-v0.5.1|──────────────────────────────── | Heap | Stack | Code | String | Asm | "
" F1 Main | F2 Registers | F3 Stack | F4 Instructions | F5 Output | F6 Mapping | F7 Hexdump "
"Registers──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────▲"
" rax → 0xdeadbeef █"
" rbx → 0x1 ║"
" rcx → 0x111 ║"
" rdx → 0x7fffffffb238 → 0x7fffffffb5dd → <string> ║"
" rsi → 0x7fffffffb228 → 0x7fffffffb594 → <string> ║"
" rdi → 0x1 ║"
" rbp → 0x7fffffffb100 → 0x7fffffffb110 → 0x7fffffffb1b0 → 0x7fffffffb200 → 0x00 ║"
" rsp → 0x7fffffffb100 → 0x7fffffffb110 → 0x7fffffffb1b0 → 0x7fffffffb200 → 0x00 ║"
" r8 → 0x4a71c0 → main_arena+0 (add BYTE PTR [rax],al) ▼"
"Stack───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
" 0x7fffffffb100 → 0x7fffffffb110 → 0x7fffffffb1b0 → 0x7fffffffb200 → 0x00 "
" 0x7fffffffb108 → 0x402e6c → main+9 (mov eax,0x0) "
" 0x7fffffffb110 → 0x7fffffffb1b0 → 0x7fffffffb200 → 0x00 "
" 0x7fffffffb118 → 0x4033f5 → __libc_start_call_main+101 (mov edi,eax) "
" 0x7fffffffb120 → 0x4a8ae0 → (bad) "
"Instructions (child)────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
" 0x402e46 child+01 mov rbp,rsp "
" 0x402e49 child+04 mov eax,0xdeadbeef "
" 0x402e4e child+09 mov QWORD PTR [rbp-0x8],rax "
" 0x402e52 child+0d mov rax,QWORD PTR [rbp-0x8] "
">>0x402e56 child+11 mov DWORD PTR [rax],0x2a "
"Backtrace───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
" 00402e56 → child "
" 00402e6c → main "
"┌Output ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐"
"│h> -gdb-set mi-async on │"
"│run │"
"│file test-assets/segfault/segfault │"
"│Reading symbols from test-assets/segfault/segfault... │"
"│(No debugging symbols found in test-assets/segfault/segfault) │"
"│Program │"
"│ received signal SIGSEGV, Segmentation fault. │"
"│0x0000000000402e56 in child () │"
"└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘"
"┌|Press q to exit, i to enter input|─|Status: signal-name=SIGSEGV, signal-meaning=Segmentation fault, reason=signal-received, stopped-threads=all, thread-id=1|┐"
"│(gdb) │"
"└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘"
47 changes: 30 additions & 17 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,25 +199,38 @@ pub fn add_deref_to_span(
width: usize,
) {
for (i, v) in deref.map.iter().enumerate() {
// check if ascii
if *v > 0xff {
let bytes = (*v).to_le_bytes();
if bytes
.iter()
.all(|a| a.is_ascii_alphabetic() || a.is_ascii_graphic() || a.is_ascii_whitespace())
{
// if we detect it's ascii, the rest is ascii
let mut full_s = String::new();
for r in deref.map.iter().skip(i) {
let bytes = (*r).to_le_bytes();
if let Ok(s) = std::str::from_utf8(&bytes) {
full_s.push_str(s);
if state.config.deref_show_string {
// check if ascii
if *v > 0xff {
let bytes = (*v).to_le_bytes();
if bytes.iter().all(|a| {
a.is_ascii_alphabetic() || a.is_ascii_graphic() || a.is_ascii_whitespace()
}) {
// if we detect it's ascii, the rest is ascii
let mut full_s = String::new();
for r in deref.map.iter().skip(i) {
let bytes = (*r).to_le_bytes();
if let Ok(s) = std::str::from_utf8(&bytes) {
full_s.push_str(s);
}
}
let cell =
Span::from(format!("→ \"{full_s}\"")).style(Style::new().fg(STRING_COLOR));
spans.push(cell);
return;
}
}
} else {
if *v > 0xff {
let bytes = (*v).to_le_bytes();
if bytes.iter().all(|a| {
a.is_ascii_alphabetic() || a.is_ascii_graphic() || a.is_ascii_whitespace()
}) {
let cell =
Span::from(format!("→ <string>")).style(Style::new().fg(STRING_COLOR));
spans.push(cell);
return;
}
let cell =
Span::from(format!("→ \"{full_s}\"")).style(Style::new().fg(STRING_COLOR));
spans.push(cell);
return;
}
}

Expand Down
2 changes: 2 additions & 0 deletions test-sources/segfault.source
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
file test-assets/segfault/segfault
run
Loading