Просмотр исходного кода

zkas: fix out-of-memory panic (#202)

* zkas: fix out-of-memory panic

Fix panic due to unchecked Vec::with_capacity inputs. Also change
VecDeque to use the same validation pattern even though it was not
directly affected

Add panic regression unit test based on fuzzer input that caused the crash

* Add the vec alloc try to async serial as well

---------

Co-authored-by: y <y>
Co-authored-by: parazyd <parazyd@dyne.org>
greptile 2 лет назад
Родитель
Сommit
9d5ca4b904
3 измененных файлов с 21 добавлено и 4 удалено
  1. 4 2
      src/serial/src/async_lib.rs
  2. 4 2
      src/serial/src/lib.rs
  3. 13 0
      src/zkas/decoder.rs

+ 4 - 2
src/serial/src/async_lib.rs

@@ -544,7 +544,8 @@ impl<T: AsyncDecodable + Send> AsyncDecodable for Vec<T> {
     #[inline]
     #[inline]
     async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
     async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
         let len = VarInt::decode_async(d).await?.0;
         let len = VarInt::decode_async(d).await?.0;
-        let mut ret = Vec::with_capacity(len as usize);
+        let mut ret = Vec::new();
+        ret.try_reserve(len as usize).map_err(|_| std::io::ErrorKind::InvalidData)?;
         for _ in 0..len {
         for _ in 0..len {
             ret.push(AsyncDecodable::decode_async(d).await?);
             ret.push(AsyncDecodable::decode_async(d).await?);
         }
         }
@@ -570,7 +571,8 @@ impl<T: AsyncDecodable + Send> AsyncDecodable for VecDeque<T> {
     #[inline]
     #[inline]
     async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
     async fn decode_async<D: AsyncRead + Unpin + Send>(d: &mut D) -> Result<Self> {
         let len = VarInt::decode_async(d).await?.0;
         let len = VarInt::decode_async(d).await?.0;
-        let mut ret = VecDeque::with_capacity(len as usize);
+        let mut ret = Vec::new();
+        ret.try_reserve(len as usize).map_err(|_| std::io::ErrorKind::InvalidData)?;
         for _ in 0..len {
         for _ in 0..len {
             ret.push_back(AsyncDecodable::decode_async(d).await?);
             ret.push_back(AsyncDecodable::decode_async(d).await?);
         }
         }

+ 4 - 2
src/serial/src/lib.rs

@@ -467,7 +467,8 @@ impl<T: Decodable> Decodable for Vec<T> {
     #[inline]
     #[inline]
     fn decode<D: Read>(mut d: D) -> Result<Self, Error> {
     fn decode<D: Read>(mut d: D) -> Result<Self, Error> {
         let len = VarInt::decode(&mut d)?.0;
         let len = VarInt::decode(&mut d)?.0;
-        let mut ret = Vec::with_capacity(len as usize);
+        let mut ret = Vec::new();
+        ret.try_reserve(len as usize).map_err(|_| std::io::ErrorKind::InvalidData)?;
         for _ in 0..len {
         for _ in 0..len {
             ret.push(Decodable::decode(&mut d)?);
             ret.push(Decodable::decode(&mut d)?);
         }
         }
@@ -491,7 +492,8 @@ impl<T: Decodable> Decodable for VecDeque<T> {
     #[inline]
     #[inline]
     fn decode<D: Read>(mut d: D) -> Result<Self, Error> {
     fn decode<D: Read>(mut d: D) -> Result<Self, Error> {
         let len = VarInt::decode(&mut d)?.0;
         let len = VarInt::decode(&mut d)?.0;
-        let mut ret = VecDeque::with_capacity(len as usize);
+        let mut ret = VecDeque::new();
+        ret.try_reserve(len as usize).map_err(|_| std::io::ErrorKind::InvalidData)?;
         for _ in 0..len {
         for _ in 0..len {
             ret.push_back(Decodable::decode(&mut d)?);
             ret.push_back(Decodable::decode(&mut d)?);
         }
         }

+ 13 - 0
src/zkas/decoder.rs

@@ -243,3 +243,16 @@ impl ZkBinary {
         Ok(opcodes)
         Ok(opcodes)
     }
     }
 }
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::zkas::ZkBinary;
+
+    #[test]
+    fn panic_regression_001() {
+        // Out-of-memory panic from string deserialization.
+        // Read `doc/src/zkas/bincode.md` to understand the input.
+        let data = vec![11u8, 1, 177, 53, 1, 0, 0, 0, 0, 255, 0, 204, 200, 72, 72, 72, 72, 1];
+        let _dec = ZkBinary::decode(&data);
+    }
+}