Skip to content

Commit a39d4fc

Browse files
committed
Auto merge of #25013 - pnkfelix:span_to_lines-oflo, r=huonw
Guard against overflow in `codemap::span_to_lines`. (Revised/expanded version of PR #24976) Make `span_to_lines` to return a `Result`. In `diagnostic`, catch `Err` from `span_to_lines` and print `"(unprintable span)"` instead. ---- There a number of recent issues that report the bug here. See e.g. #24761 and #24954. This change *might* fix them. However, that is *not* its main goal. The main goals are: 1. Make it possible for callers to recover from an error here, and 2. Insert a more conservative check, in that we are also checking that the files match up. ---- As a drive-by, fix #24997 , which was causing my attempts to `make check-stage1` on an `--enable-debug` build to fail.
2 parents 05d5fca + 939e4c9 commit a39d4fc

File tree

3 files changed

+69
-14
lines changed

3 files changed

+69
-14
lines changed

src/libcore/slice.rs

+24-6
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,14 @@ fn size_from_ptr<T>(_: *const T) -> usize {
631631
}
632632

633633

634-
// Use macro to be generic over const/mut
635-
macro_rules! slice_offset {
634+
// Use macros to be generic over const/mut
635+
//
636+
// They require non-negative `$by` because otherwise the expression
637+
// `(ptr as usize + $by)` would interpret `-1` as `usize::MAX` (and
638+
// thus trigger a panic when overflow checks are on).
639+
640+
// Use this to do `$ptr + $by`, where `$by` is non-negative.
641+
macro_rules! slice_add_offset {
636642
($ptr:expr, $by:expr) => {{
637643
let ptr = $ptr;
638644
if size_from_ptr(ptr) == 0 {
@@ -643,6 +649,18 @@ macro_rules! slice_offset {
643649
}};
644650
}
645651

652+
// Use this to do `$ptr - $by`, where `$by` is non-negative.
653+
macro_rules! slice_sub_offset {
654+
($ptr:expr, $by:expr) => {{
655+
let ptr = $ptr;
656+
if size_from_ptr(ptr) == 0 {
657+
transmute(ptr as usize - $by)
658+
} else {
659+
ptr.offset(-$by)
660+
}
661+
}};
662+
}
663+
646664
macro_rules! slice_ref {
647665
($ptr:expr) => {{
648666
let ptr = $ptr;
@@ -672,7 +690,7 @@ macro_rules! iterator {
672690
None
673691
} else {
674692
let old = self.ptr;
675-
self.ptr = slice_offset!(self.ptr, 1);
693+
self.ptr = slice_add_offset!(self.ptr, 1);
676694
Some(slice_ref!(old))
677695
}
678696
}
@@ -714,7 +732,7 @@ macro_rules! iterator {
714732
if self.end == self.ptr {
715733
None
716734
} else {
717-
self.end = slice_offset!(self.end, -1);
735+
self.end = slice_sub_offset!(self.end, 1);
718736
Some(slice_ref!(self.end))
719737
}
720738
}
@@ -776,7 +794,7 @@ impl<'a, T> Iter<'a, T> {
776794
fn iter_nth(&mut self, n: usize) -> Option<&'a T> {
777795
match self.as_slice().get(n) {
778796
Some(elem_ref) => unsafe {
779-
self.ptr = slice_offset!(elem_ref as *const _, 1);
797+
self.ptr = slice_add_offset!(elem_ref as *const _, 1);
780798
Some(slice_ref!(elem_ref))
781799
},
782800
None => {
@@ -849,7 +867,7 @@ impl<'a, T> IterMut<'a, T> {
849867
fn iter_nth(&mut self, n: usize) -> Option<&'a mut T> {
850868
match make_mut_slice!(T => &'a mut [T]: self.ptr, self.end).get_mut(n) {
851869
Some(elem_ref) => unsafe {
852-
self.ptr = slice_offset!(elem_ref as *mut _, 1);
870+
self.ptr = slice_add_offset!(elem_ref as *mut _, 1);
853871
Some(slice_ref!(elem_ref))
854872
},
855873
None => {

src/libsyntax/codemap.rs

+26-5
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,22 @@ impl CodeMap {
663663
self.lookup_char_pos(sp.lo).file.name.to_string()
664664
}
665665

666-
pub fn span_to_lines(&self, sp: Span) -> FileLines {
666+
pub fn span_to_lines(&self, sp: Span) -> FileLinesResult {
667+
if sp.lo > sp.hi {
668+
return Err(SpanLinesError::IllFormedSpan(sp));
669+
}
670+
667671
let lo = self.lookup_char_pos(sp.lo);
668672
let hi = self.lookup_char_pos(sp.hi);
673+
674+
if lo.file.start_pos != hi.file.start_pos {
675+
return Err(SpanLinesError::DistinctSources(DistinctSources {
676+
begin: (lo.file.name.clone(), lo.file.start_pos),
677+
end: (hi.file.name.clone(), hi.file.start_pos),
678+
}));
679+
}
680+
assert!(hi.line >= lo.line);
681+
669682
let mut lines = Vec::with_capacity(hi.line - lo.line + 1);
670683

671684
// The span starts partway through the first line,
@@ -689,7 +702,7 @@ impl CodeMap {
689702
start_col: start_col,
690703
end_col: hi.col });
691704

692-
FileLines {file: lo.file, lines: lines}
705+
Ok(FileLines {file: lo.file, lines: lines})
693706
}
694707

695708
pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
@@ -914,9 +927,17 @@ impl CodeMap {
914927
}
915928

916929
// _____________________________________________________________________________
917-
// SpanSnippetError, DistinctSources, MalformedCodemapPositions
930+
// SpanLinesError, SpanSnippetError, DistinctSources, MalformedCodemapPositions
918931
//
919932

933+
pub type FileLinesResult = Result<FileLines, SpanLinesError>;
934+
935+
#[derive(Clone, PartialEq, Eq, Debug)]
936+
pub enum SpanLinesError {
937+
IllFormedSpan(Span),
938+
DistinctSources(DistinctSources),
939+
}
940+
920941
#[derive(Clone, PartialEq, Eq, Debug)]
921942
pub enum SpanSnippetError {
922943
IllFormedSpan(Span),
@@ -1082,7 +1103,7 @@ mod tests {
10821103
// Test span_to_lines for a span ending at the end of filemap
10831104
let cm = init_code_map();
10841105
let span = Span {lo: BytePos(12), hi: BytePos(23), expn_id: NO_EXPANSION};
1085-
let file_lines = cm.span_to_lines(span);
1106+
let file_lines = cm.span_to_lines(span).unwrap();
10861107

10871108
assert_eq!(file_lines.file.name, "blork.rs");
10881109
assert_eq!(file_lines.lines.len(), 1);
@@ -1127,7 +1148,7 @@ mod tests {
11271148
assert_eq!(&cm.span_to_snippet(span).unwrap(), "BB\nCCC\nDDDDD");
11281149

11291150
// check that span_to_lines gives us the complete result with the lines/cols we expected
1130-
let lines = cm.span_to_lines(span);
1151+
let lines = cm.span_to_lines(span).unwrap();
11311152
let expected = vec![
11321153
LineInfo { line_index: 1, start_col: CharPos(4), end_col: CharPos(6) },
11331154
LineInfo { line_index: 2, start_col: CharPos(0), end_col: CharPos(3) },

src/libsyntax/diagnostic.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ fn highlight_suggestion(err: &mut EmitterWriter,
522522
suggestion: &str)
523523
-> io::Result<()>
524524
{
525-
let lines = cm.span_to_lines(sp);
525+
let lines = cm.span_to_lines(sp).unwrap();
526526
assert!(!lines.lines.is_empty());
527527

528528
// To build up the result, we want to take the snippet from the first
@@ -567,9 +567,17 @@ fn highlight_lines(err: &mut EmitterWriter,
567567
cm: &codemap::CodeMap,
568568
sp: Span,
569569
lvl: Level,
570-
lines: codemap::FileLines)
570+
lines: codemap::FileLinesResult)
571571
-> io::Result<()>
572572
{
573+
let lines = match lines {
574+
Ok(lines) => lines,
575+
Err(_) => {
576+
try!(write!(&mut err.dst, "(internal compiler error: unprintable span)\n"));
577+
return Ok(());
578+
}
579+
};
580+
573581
let fm = &*lines.file;
574582

575583
let line_strings: Option<Vec<&str>> =
@@ -690,8 +698,16 @@ fn end_highlight_lines(w: &mut EmitterWriter,
690698
cm: &codemap::CodeMap,
691699
sp: Span,
692700
lvl: Level,
693-
lines: codemap::FileLines)
701+
lines: codemap::FileLinesResult)
694702
-> io::Result<()> {
703+
let lines = match lines {
704+
Ok(lines) => lines,
705+
Err(_) => {
706+
try!(write!(&mut w.dst, "(internal compiler error: unprintable span)\n"));
707+
return Ok(());
708+
}
709+
};
710+
695711
let fm = &*lines.file;
696712

697713
let lines = &lines.lines[..];

0 commit comments

Comments
 (0)