about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-01-28 13:31:51 +0000
committerbors <bors@rust-lang.org>2022-01-28 13:31:51 +0000
commit8d5d9e01037b841ca9ae9add09cdf4ada12a0fae (patch)
tree369d700246fcfdb4494835a5fb050d5d1c2bf3de
parentfb94992c39e64c280ccb21299509aee767ebd6d9 (diff)
parent23fd95a5e99acf35b471ca9b01230d7578bfac5f (diff)
downloadrust-8d5d9e01037b841ca9ae9add09cdf4ada12a0fae.tar.gz
rust-8d5d9e01037b841ca9ae9add09cdf4ada12a0fae.zip
Auto merge of #8250 - pr2502:fix_repeat_underflow, r=giraffate
Fix underflow in `manual_split_once` lint

Hi, a friend found clippy started crashing on a suspiciously large allocation of `u64::MAX` memory on their code.

The mostly minimized repro is:
```rust
fn _f01(title: &str) -> Option<()> {
    let _ = title[1..].splitn(2, '[').next()?;
    Some(())
}
```

The underflow happens in this case on line 57 of the patch but I've changed the other substraction to saturating as well since it could potentially cause the same issue.

I'm not sure where to put a regression test, or if it's even worth for such a thing.

Aside, has it been considered before to build clippy with overflow checks enabled?

changelog: fix ICE of underflow in `manual_split_once` lint
-rw-r--r--clippy_lints/src/methods/str_splitn.rs6
-rw-r--r--tests/ui/crashes/ice-8250.rs6
-rw-r--r--tests/ui/crashes/ice-8250.stderr18
3 files changed, 27 insertions, 3 deletions
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index b2f624ed480..926c25b4b40 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -45,16 +45,16 @@ pub(super) fn check_manual_split_once(
         IterUsageKind::Next | IterUsageKind::Second => {
             let self_deref = {
                 let adjust = cx.typeck_results().expr_adjustments(self_arg);
-                if adjust.is_empty() {
+                if adjust.len() < 2 {
                     String::new()
                 } else if cx.typeck_results().expr_ty(self_arg).is_box()
                     || adjust
                         .iter()
                         .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
                 {
-                    format!("&{}", "*".repeat(adjust.len() - 1))
+                    format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
                 } else {
-                    "*".repeat(adjust.len() - 2)
+                    "*".repeat(adjust.len().saturating_sub(2))
                 }
             };
             if matches!(usage.kind, IterUsageKind::Next) {
diff --git a/tests/ui/crashes/ice-8250.rs b/tests/ui/crashes/ice-8250.rs
new file mode 100644
index 00000000000..d9a5ee1162a
--- /dev/null
+++ b/tests/ui/crashes/ice-8250.rs
@@ -0,0 +1,6 @@
+fn _f(s: &str) -> Option<()> {
+    let _ = s[1..].splitn(2, '.').next()?;
+    Some(())
+}
+
+fn main() {}
diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr
new file mode 100644
index 00000000000..04ea4456656
--- /dev/null
+++ b/tests/ui/crashes/ice-8250.stderr
@@ -0,0 +1,18 @@
+error: manual implementation of `split_once`
+  --> $DIR/ice-8250.rs:2:13
+   |
+LL |     let _ = s[1..].splitn(2, '.').next()?;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
+   |
+   = note: `-D clippy::manual-split-once` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+  --> $DIR/ice-8250.rs:2:13
+   |
+LL |     let _ = s[1..].splitn(2, '.').next()?;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')`
+   |
+   = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
+