|
1 | 1 | use std::borrow::Cow;
|
| 2 | +use std::cell::Cell; |
2 | 3 | use std::collections::TryReserveError::*;
|
| 4 | +use std::ops::Bound; |
3 | 5 | use std::ops::Bound::*;
|
| 6 | +use std::ops::RangeBounds; |
4 | 7 | use std::panic;
|
| 8 | +use std::str; |
5 | 9 |
|
6 | 10 | pub trait IntoCow<'a, B: ?Sized>
|
7 | 11 | where
|
@@ -561,6 +565,52 @@ fn test_replace_range_unbounded() {
|
561 | 565 | assert_eq!(s, "");
|
562 | 566 | }
|
563 | 567 |
|
| 568 | +#[test] |
| 569 | +fn test_replace_range_evil_start_bound() { |
| 570 | + struct EvilRange(Cell<bool>); |
| 571 | + |
| 572 | + impl RangeBounds<usize> for EvilRange { |
| 573 | + fn start_bound(&self) -> Bound<&usize> { |
| 574 | + Bound::Included(if self.0.get() { |
| 575 | + &1 |
| 576 | + } else { |
| 577 | + self.0.set(true); |
| 578 | + &0 |
| 579 | + }) |
| 580 | + } |
| 581 | + fn end_bound(&self) -> Bound<&usize> { |
| 582 | + Bound::Unbounded |
| 583 | + } |
| 584 | + } |
| 585 | + |
| 586 | + let mut s = String::from("🦀"); |
| 587 | + s.replace_range(EvilRange(Cell::new(false)), ""); |
| 588 | + assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); |
| 589 | +} |
| 590 | + |
| 591 | +#[test] |
| 592 | +fn test_replace_range_evil_end_bound() { |
| 593 | + struct EvilRange(Cell<bool>); |
| 594 | + |
| 595 | + impl RangeBounds<usize> for EvilRange { |
| 596 | + fn start_bound(&self) -> Bound<&usize> { |
| 597 | + Bound::Included(&0) |
| 598 | + } |
| 599 | + fn end_bound(&self) -> Bound<&usize> { |
| 600 | + Bound::Excluded(if self.0.get() { |
| 601 | + &3 |
| 602 | + } else { |
| 603 | + self.0.set(true); |
| 604 | + &4 |
| 605 | + }) |
| 606 | + } |
| 607 | + } |
| 608 | + |
| 609 | + let mut s = String::from("🦀"); |
| 610 | + s.replace_range(EvilRange(Cell::new(false)), ""); |
| 611 | + assert_eq!(Ok(""), str::from_utf8(s.as_bytes())); |
| 612 | +} |
| 613 | + |
564 | 614 | #[test]
|
565 | 615 | fn test_extend_ref() {
|
566 | 616 | let mut a = "foo".to_string();
|
|
0 commit comments