Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Require 'unsafe' keyword for custom implementations of nom traits
`nom_locate` progressively advances through a fragment by slicing it, but expects to be able to go backward by as much as it advanced. This is normally fine, but custom implementations of `nom` types could cause UB by implementing slicing incorrectly. After this commit, they will need to implement the unsafe trait `RewindableFragment`, putting the burden of soundness on these implementations. stephaneyfx provides an example of a maliciously constructed fragment type exercising this behavior: > This function is called from public and safe functions like get_line_beginning. It assumes that the current fragment is part of a larger fragment and attempts to read before the beginning of the current fragment. This assumption may be incorrect as demonstrated by the following program that exhibits UB without unsafe and outputs garbage (which can change on every run). ```rust use nom::{AsBytes, InputTake, Offset, Slice}; use nom_locate::LocatedSpan; use std::{ cell::Cell, ops::{RangeFrom, RangeTo}, rc::Rc, }; struct EvilInput<'a>(Rc<Cell<&'a [u8]>>); impl<'a> AsBytes for EvilInput<'a> { fn as_bytes(&self) -> &[u8] { self.0.get() } } impl Offset for EvilInput<'_> { fn offset(&self, second: &Self) -> usize { self.as_bytes().offset(second.as_bytes()) } } impl Slice<RangeFrom<usize>> for EvilInput<'_> { fn slice(&self, range: RangeFrom<usize>) -> Self { Self(Rc::new(Cell::new(self.0.get().slice(range)))) } } impl Slice<RangeTo<usize>> for EvilInput<'_> { fn slice(&self, range: RangeTo<usize>) -> Self { Self(Rc::new(Cell::new(self.0.get().slice(range)))) } } fn main() { let new_input = [32u8]; let original_input = [33u8; 3]; let evil_input = EvilInput(Rc::new(Cell::new(&original_input))); let span = LocatedSpan::new(evil_input).take_split(2).0; span.fragment().0.set(&new_input); let beginning = span.get_line_beginning(); dbg!(beginning); dbg!(new_input.as_ptr() as usize - beginning.as_ptr() as usize); } ``` Example output: ``` [src/main.rs:43] beginning = [ 201, 127, 32, ] [src/main.rs:44] new_input.as_ptr() as usize - beginning.as_ptr() as usize = 2 ```
- Loading branch information