Skip to content

Commit 9bfd30a

Browse files
committed
fix: prevent boreal-cli prints to be interleaved when using threads
1 parent 279455d commit 9bfd30a

File tree

2 files changed

+28
-29
lines changed

2 files changed

+28
-29
lines changed

boreal-cli/src/main.rs

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::fs::File;
2-
use std::io::{BufRead, BufReader};
2+
use std::io::{BufRead, BufReader, StdoutLock, Write};
33
use std::path::{Path, PathBuf};
44
use std::process::ExitCode;
55
use std::thread::JoinHandle;
@@ -577,6 +577,9 @@ fn display_scan_results(res: ScanResult, what: &str, options: &ScanOptions) {
577577
}
578578
}
579579

580+
// Lock stdout to avoid having multiple threads interlap their writes
581+
let mut stdout = std::io::stdout().lock();
582+
580583
// Then, print matching rules.
581584
for rule in res.matched_rules {
582585
if let Some(id) = options.identifier.as_ref() {
@@ -592,70 +595,70 @@ fn display_scan_results(res: ScanResult, what: &str, options: &ScanOptions) {
592595

593596
// <rule_namespace>:<rule_name> [<ruletags>] <matched object>
594597
if options.print_namespace {
595-
print!("{}:", rule.namespace.unwrap_or("default"));
598+
write!(stdout, "{}:", rule.namespace.unwrap_or("default")).unwrap();
596599
}
597-
print!("{}", &rule.name);
600+
write!(stdout, "{}", &rule.name).unwrap();
598601
if options.print_tags {
599-
print!(" [{}]", rule.tags.join(","));
602+
write!(stdout, " [{}]", rule.tags.join(",")).unwrap();
600603
}
601604
if options.print_metadata {
602-
print_metadata(rule.metadatas);
605+
print_metadata(&mut stdout, rule.metadatas);
603606
}
604-
println!(" {}", what);
607+
writeln!(stdout, " {}", what).unwrap();
605608

606609
if options.print_strings_matches() {
607610
for string in &rule.matches {
608611
for m in &string.matches {
609612
// <offset>:<length>:<name>: <match>
610-
print!("0x{:x}:", m.base + m.offset);
613+
write!(stdout, "0x{:x}:", m.base + m.offset).unwrap();
611614
if options.print_string_length {
612-
print!("{}:", m.length);
615+
write!(stdout, "{}:", m.length).unwrap();
613616
}
614-
print!("${}", string.name);
617+
write!(stdout, "${}", string.name).unwrap();
615618
if options.print_strings_matches_data {
616-
print!(": ");
617-
print_bytes(&m.data);
619+
write!(stdout, ": ").unwrap();
620+
print_bytes(&mut stdout, &m.data);
618621
}
619-
println!();
622+
writeln!(stdout).unwrap();
620623
}
621624
}
622625
}
623626
}
624627

625628
// Finally, print the statistics
626629
if let Some(stats) = res.statistics {
627-
println!("{}: {:#?}", what, stats);
630+
writeln!(stdout, "{}: {:#?}", what, stats).unwrap();
628631
}
629632
}
630633

631-
fn print_metadata(metadatas: &[Metadata]) {
632-
print!(" [");
634+
fn print_metadata(stdout: &mut StdoutLock, metadatas: &[Metadata]) {
635+
write!(stdout, " [").unwrap();
633636
for (i, meta) in metadatas.iter().enumerate() {
634637
if i != 0 {
635-
print!(",");
638+
write!(stdout, ",").unwrap();
636639
}
637-
print!("{}=", meta.name);
640+
write!(stdout, "{}=", meta.name).unwrap();
638641
match &meta.value {
639642
MetadataValue::Bytes(b) => {
640-
print!("\"");
641-
print_bytes(b);
642-
print!("\"");
643+
write!(stdout, "\"").unwrap();
644+
print_bytes(stdout, b);
645+
write!(stdout, "\"").unwrap();
643646
}
644647
MetadataValue::Integer(i) => {
645-
print!("{}", i);
648+
write!(stdout, "{}", i).unwrap();
646649
}
647650
MetadataValue::Boolean(b) => {
648-
print!("{}", b);
651+
write!(stdout, "{}", b).unwrap();
649652
}
650653
}
651654
}
652-
print!("]");
655+
write!(stdout, "]").unwrap();
653656
}
654657

655-
fn print_bytes(data: &[u8]) {
658+
fn print_bytes(stdout: &mut StdoutLock, data: &[u8]) {
656659
for c in data {
657660
for b in std::ascii::escape_default(*c) {
658-
print!("{}", b as char);
661+
write!(stdout, "{}", b as char).unwrap();
659662
}
660663
}
661664
}

boreal-cli/tests/cli.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,7 +1219,6 @@ fn test_scan_list() {
12191219
// dir1 + c, will match the 3 filse
12201220
let list = test_file(format!("{}\n{}\n", dir1.path().display(), file_c.display()).as_bytes());
12211221
cmd()
1222-
.arg("--threads=1")
12231222
.arg("--scan-list")
12241223
.arg(rule_file.path())
12251224
.arg(list.path())
@@ -1235,8 +1234,6 @@ fn test_scan_list() {
12351234
// a + dir2, but not recursive, will match only a
12361235
let list = test_file(format!("{}\n{}\n", file_a.display(), dir2.path().display()).as_bytes());
12371236
cmd()
1238-
// TODO: avoid messing up the output with multiple threads
1239-
.arg("--threads=1")
12401237
.arg("--scan-list")
12411238
.arg(rule_file.path())
12421239
.arg(list.path())
@@ -1251,7 +1248,6 @@ fn test_scan_list() {
12511248

12521249
// When recursive, will match c
12531250
cmd()
1254-
.arg("--threads=1")
12551251
.arg("--scan-list")
12561252
.arg("-r")
12571253
.arg(rule_file.path())

0 commit comments

Comments
 (0)