Skip to content

Commit 702fa3a

Browse files
committed
Fix MCTP serial parsing and framing with escape sequences
1 parent 114dee1 commit 702fa3a

File tree

1 file changed

+180
-4
lines changed

1 file changed

+180
-4
lines changed

mctp-estack/src/serial.rs

Lines changed: 180 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ enum Pos {
4242
SerialRevision,
4343
ByteCount,
4444
Data,
45-
// Byte following a 0x7d
45+
// Byte following a 0x7d in Data state
4646
DataEscaped,
4747
Check,
48+
// Byte following a 0x7d in Check state (FCS bytes can also be escaped)
49+
CheckEscaped,
4850
FrameEnd,
4951
}
5052

@@ -192,7 +194,31 @@ impl MctpSerialHandler {
192194
}
193195
}
194196
Pos::Check => {
195-
self.rxbuf.push(b).unwrap();
197+
match b {
198+
// Unexpected framing, restart
199+
FRAMING_FLAG => self.rxpos = Pos::SerialRevision,
200+
FRAMING_ESCAPE => self.rxpos = Pos::CheckEscaped,
201+
_ => {
202+
self.rxbuf.push(b).unwrap();
203+
if self.rxbuf.len() == self.rxcount + RXBUF_FRAMING {
204+
self.rxpos = Pos::FrameEnd;
205+
}
206+
}
207+
}
208+
}
209+
Pos::CheckEscaped => {
210+
match b {
211+
FLAG_ESCAPED => {
212+
self.rxbuf.push(FRAMING_FLAG).unwrap();
213+
self.rxpos = Pos::Check;
214+
}
215+
ESCAPE_ESCAPED => {
216+
self.rxbuf.push(FRAMING_ESCAPE).unwrap();
217+
self.rxpos = Pos::Check;
218+
}
219+
// Unexpected escape, restart
220+
_ => self.rxpos = Pos::FrameSearch,
221+
}
196222
if self.rxbuf.len() == self.rxcount + RXBUF_FRAMING {
197223
self.rxpos = Pos::FrameEnd;
198224
}
@@ -261,7 +287,7 @@ impl MctpSerialHandler {
261287

262288
output.write_all(&start).await?;
263289
Self::write_escaped_async(p, output).await?;
264-
output.write_all(&cs.to_be_bytes()).await?;
290+
Self::write_escaped_async(&cs.to_be_bytes(), output).await?;
265291
output.write_all(&[FRAMING_FLAG]).await?;
266292
Ok(())
267293
}
@@ -285,7 +311,7 @@ impl MctpSerialHandler {
285311

286312
output.write_all(&start)?;
287313
Self::write_escaped_sync(p, output)?;
288-
output.write_all(&cs.to_be_bytes())?;
314+
Self::write_escaped_sync(&cs.to_be_bytes(), output)?;
289315
output.write_all(&[FRAMING_FLAG])?;
290316
Ok(())
291317
}
@@ -432,4 +458,154 @@ mod tests {
432458
do_roundtrip_sync(&payload)
433459
}
434460
}
461+
462+
/// Helper to parse a manually-constructed frame
463+
fn parse_frame(frame: &[u8]) -> std::vec::Vec<u8> {
464+
let mut h = MctpSerialHandler::new();
465+
let mut s = frame;
466+
h.recv_sync(&mut s).unwrap().to_vec()
467+
}
468+
469+
// Test vectors with known FCS values:
470+
// payload=[01, 00, 00, 00, 00, 49] -> FCS=[7e, 10] (FLAG in first byte)
471+
// payload=[01, 00, 00, 00, 00, 65] -> FCS=[95, 7e] (FLAG in second byte)
472+
// payload=[01, 00, 00, 00, 00, 7a] -> FCS=[7d, 08] (ESCAPE in first byte)
473+
// payload=[01, 00, 00, 00, 01, 7f] -> FCS=[33, 7d] (ESCAPE in second byte)
474+
// payload=[01, 00, 00, 00, 16, cd] -> FCS=[7d, 7d] (both bytes need escaping)
475+
476+
/// Test FCS with 0x7e (FLAG) in first byte
477+
#[test]
478+
fn fcs_escape_flag_first_byte() {
479+
start_log();
480+
// payload produces FCS = [0x7e, 0x10]
481+
let payload = [0x01, 0x00, 0x00, 0x00, 0x00, 0x49];
482+
do_roundtrip_sync(&payload);
483+
484+
// Also test RX directly with manually escaped frame
485+
let frame = [
486+
FRAMING_FLAG,
487+
0x01,
488+
0x06, // revision, length
489+
0x01,
490+
0x00,
491+
0x00,
492+
0x00,
493+
0x00,
494+
0x49, // payload
495+
FRAMING_ESCAPE,
496+
FLAG_ESCAPED,
497+
0x10, // FCS with escaped first byte
498+
FRAMING_FLAG,
499+
];
500+
assert_eq!(parse_frame(&frame), payload);
501+
}
502+
503+
/// Test FCS with 0x7e (FLAG) in second byte
504+
#[test]
505+
fn fcs_escape_flag_second_byte() {
506+
start_log();
507+
// payload produces FCS = [0x95, 0x7e]
508+
let payload = [0x01, 0x00, 0x00, 0x00, 0x00, 0x65];
509+
do_roundtrip_sync(&payload);
510+
511+
// Also test RX directly with manually escaped frame
512+
let frame = [
513+
FRAMING_FLAG,
514+
0x01,
515+
0x06, // revision, length
516+
0x01,
517+
0x00,
518+
0x00,
519+
0x00,
520+
0x00,
521+
0x65, // payload
522+
0x95,
523+
FRAMING_ESCAPE,
524+
FLAG_ESCAPED, // FCS with escaped second byte
525+
FRAMING_FLAG,
526+
];
527+
assert_eq!(parse_frame(&frame), payload);
528+
}
529+
530+
/// Test FCS with 0x7d (ESCAPE) in first byte
531+
#[test]
532+
fn fcs_escape_escape_first_byte() {
533+
start_log();
534+
// payload produces FCS = [0x7d, 0x08]
535+
let payload = [0x01, 0x00, 0x00, 0x00, 0x00, 0x7a];
536+
do_roundtrip_sync(&payload);
537+
538+
// Also test RX directly with manually escaped frame
539+
let frame = [
540+
FRAMING_FLAG,
541+
0x01,
542+
0x06, // revision, length
543+
0x01,
544+
0x00,
545+
0x00,
546+
0x00,
547+
0x00,
548+
0x7a, // payload
549+
FRAMING_ESCAPE,
550+
ESCAPE_ESCAPED,
551+
0x08, // FCS with escaped first byte
552+
FRAMING_FLAG,
553+
];
554+
assert_eq!(parse_frame(&frame), payload);
555+
}
556+
557+
/// Test FCS with 0x7d (ESCAPE) in second byte
558+
#[test]
559+
fn fcs_escape_escape_second_byte() {
560+
start_log();
561+
// payload produces FCS = [0x33, 0x7d]
562+
let payload = [0x01, 0x00, 0x00, 0x00, 0x01, 0x7f];
563+
do_roundtrip_sync(&payload);
564+
565+
// Also test RX directly with manually escaped frame
566+
let frame = [
567+
FRAMING_FLAG,
568+
0x01,
569+
0x06, // revision, length
570+
0x01,
571+
0x00,
572+
0x00,
573+
0x00,
574+
0x01,
575+
0x7f, // payload
576+
0x33,
577+
FRAMING_ESCAPE,
578+
ESCAPE_ESCAPED, // FCS with escaped second byte
579+
FRAMING_FLAG,
580+
];
581+
assert_eq!(parse_frame(&frame), payload);
582+
}
583+
584+
/// Test FCS where both bytes need escaping
585+
#[test]
586+
fn fcs_escape_both_bytes() {
587+
start_log();
588+
// payload produces FCS = [0x7d, 0x7d]
589+
let payload = [0x01, 0x00, 0x00, 0x00, 0x16, 0xcd];
590+
do_roundtrip_sync(&payload);
591+
592+
// Also test RX directly with manually escaped frame
593+
let frame = [
594+
FRAMING_FLAG,
595+
0x01,
596+
0x06, // revision, length
597+
0x01,
598+
0x00,
599+
0x00,
600+
0x00,
601+
0x16,
602+
0xcd, // payload
603+
FRAMING_ESCAPE,
604+
ESCAPE_ESCAPED, // first FCS byte escaped
605+
FRAMING_ESCAPE,
606+
ESCAPE_ESCAPED, // second FCS byte escaped
607+
FRAMING_FLAG,
608+
];
609+
assert_eq!(parse_frame(&frame), payload);
610+
}
435611
}

0 commit comments

Comments
 (0)