Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use std::io::Read;
- use std::io::Write;
- use std::time::Duration;
- use std::net::{TcpStream, TcpListener};
- pub fn main() {
- // with_bug will panic because split_at splits wrong. without_bug .. works
- println!("this works:");
- without_bug();
- println!("time to do a bug!");
- with_bug();
- }
- fn without_bug() {
- let cmd = Command::MemoryRead(0x400000, 0x10);
- assert_eq!(
- cmd.reply_looks_complete("$E01#a6$E01#a6".as_bytes()),
- Err(ReplyCategory::Doubled)
- );
- }
- fn with_bug() {
- std::thread::spawn(|| {
- let listener = TcpListener::bind("127.0.0.1:1234").unwrap();
- let (mut stream, _dest) = listener.accept().unwrap();
- stream.write(b"$E01#a6$E01#a6").unwrap();
- });
- // wait a healthy amount of time for the other thread to start listening
- std::thread::sleep(Duration::from_millis(500));
- let mut data = [0; 0x1000];
- let mut stream = TcpStream::connect("127.0.0.1:1234").unwrap();
- // wait for some data Just In Case
- std::thread::sleep(Duration::from_millis(500));
- stream.read(&mut data).unwrap();
- let cmd = Command::MemoryRead(0x400000, 0x10);
- assert_eq!(
- cmd.reply_looks_complete(&data),
- Err(ReplyCategory::Doubled)
- );
- }
- fn checksum(s: &[u8]) -> u8 {
- s.iter().fold(0, |x, y| x.wrapping_add(*y))
- }
- #[derive(Debug, PartialEq)]
- pub enum Command {
- MemoryRead(usize, usize),
- RegsGet,
- }
- fn check_checksum(buf: &[u8]) -> bool {
- if buf.len() < "$#00".len() {
- // not even a valid checksum string
- return false;
- }
- let checksum_str = &buf[buf.len() - 3..];
- if checksum_str[0] != '#' as u8 {
- // tail is not #XX
- return false;
- }
- let body = &buf[1..][..buf.len() - 4];
- let checksum = checksum(body);
- println!("looking for checksum of {:?}: {:02x}", body, checksum);
- // skip the #
- return &checksum_str[1..] == format!("{:02x}", checksum).as_bytes();
- }
- fn is_digit(b: u8) -> bool {
- b >= '0' as u8 && b <= '9' as u8
- }
- fn is_err(buf: &[u8]) -> bool {
- if buf.len() < b"$EXX#XX".len() {
- // not long enough to be an error
- return false;
- }
- if buf[0] != '$' as u8 || buf[1] != 'E' as u8 || buf[3] != '#' as u8 {
- return false;
- }
- if !check_checksum(&buf[1..=3]) {
- return false;
- }
- is_digit(buf[2]) && is_digit(buf[3])
- }
- #[derive(Debug, Clone, Copy, PartialEq)]
- enum ReplyCategory {
- Doubled,
- Incomplete,
- }
- impl Command {
- fn reply_looks_complete(&self, buf: &[u8]) -> Result<(), ReplyCategory> {
- fn reply_looks_complete_inner(cmd: &Command, buf: &[u8]) -> bool {
- println!("checking to see if {} looks complete...", unsafe { std::str::from_utf8_unchecked(buf) });
- // test checksum...
- match cmd {
- Command::MemoryRead(_size, _len) => {
- is_err(buf) || check_checksum(buf)
- }
- Command::RegsGet => {
- is_err(buf) || check_checksum(buf)
- }
- }
- }
- if reply_looks_complete_inner(self, buf) {
- Ok(())
- } else {
- // gdb sometimes goes ahead and sends replies twice.
- // specifically observed with `get_regs` and `get_mem`
- // requests on `gdbserver localhost:1234 emacs` before the program has been started,
- // where gdbserver replies `+$E01#a6$E01#a6`.
- //
- // i don't know why.
- let (low, high) = buf.split_at(buf.len() / 2);
- println!(
- "half == half? {} == {}",
- std::str::from_utf8(low).unwrap(),
- std::str::from_utf8(high).unwrap(),
- );
- if low == high {
- println!("not as a full message anyway...");
- if reply_looks_complete_inner(self, low) {
- Err(ReplyCategory::Doubled)
- } else {
- Err(ReplyCategory::Incomplete)
- }
- } else {
- Err(ReplyCategory::Incomplete)
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement