Skip to content

Commit ee1c3b3

Browse files
committed
Auto merge of #102529 - colinba:master, r=joshtriplett
Detect and reject out-of-range integers in format string literals Until now out-of-range integers in format string literals were silently ignored. They wrapped around to zero at usize::MAX, producing unexpected results. When using debug builds of rustc, such integers in format string literals even cause an 'attempt to add with overflow' panic in rustc. Fix this by producing an error diagnostic for integers in format string literals which do not fit into usize. Fixes #102528
2 parents 9b0a099 + b9e85bf commit ee1c3b3

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

compiler/rustc_parse_format/src/lib.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -740,20 +740,40 @@ impl<'a> Parser<'a> {
740740
word
741741
}
742742

743-
/// Optionally parses an integer at the current position. This doesn't deal
744-
/// with overflow at all, it's just accumulating digits.
745743
fn integer(&mut self) -> Option<usize> {
746-
let mut cur = 0;
744+
let mut cur: usize = 0;
747745
let mut found = false;
746+
let mut overflow = false;
747+
let start = self.current_pos();
748748
while let Some(&(_, c)) = self.cur.peek() {
749749
if let Some(i) = c.to_digit(10) {
750-
cur = cur * 10 + i as usize;
750+
let (tmp, mul_overflow) = cur.overflowing_mul(10);
751+
let (tmp, add_overflow) = tmp.overflowing_add(i as usize);
752+
if mul_overflow || add_overflow {
753+
overflow = true;
754+
}
755+
cur = tmp;
751756
found = true;
752757
self.cur.next();
753758
} else {
754759
break;
755760
}
756761
}
762+
763+
if overflow {
764+
let end = self.current_pos();
765+
let overflowed_int = &self.input[start..end];
766+
self.err(
767+
format!(
768+
"integer `{}` does not fit into the type `usize` whose range is `0..={}`",
769+
overflowed_int,
770+
usize::MAX
771+
),
772+
"integer out of range for `usize`",
773+
self.span(start, end),
774+
);
775+
}
776+
757777
if found { Some(cur) } else { None }
758778
}
759779

compiler/rustc_parse_format/src/tests.rs

+15
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ fn invalid06() {
5757
musterr("{:>>>}")
5858
}
5959

60+
#[test]
61+
fn invalid_position() {
62+
musterr("{18446744073709551616}");
63+
}
64+
65+
#[test]
66+
fn invalid_width() {
67+
musterr("{:18446744073709551616}");
68+
}
69+
70+
#[test]
71+
fn invalid_precision() {
72+
musterr("{:.18446744073709551616}");
73+
}
74+
6075
#[test]
6176
fn format_nothing() {
6277
same(

library/alloc/src/fmt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@
327327
//! - `text` must not contain any `'{'` or `'}'` characters,
328328
//! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic
329329
//! meaning and is completely optional,
330-
//! - `integer` is a decimal integer that may contain leading zeroes and
330+
//! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and
331331
//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html).
332332
//!
333333
//! # Formatting traits

0 commit comments

Comments
 (0)