Repository: sharksforarms/deku (Rust)

Issue:
Offset in from_bytes() causes panic in reader.rs if the bytes are greater than 128
At some point after 0.16 my code started crashing when I was using an offset in from_bytes() where the input bits total more than 128 bytes. So if I have something like this, for example:

```rust
 match AccuracyDescriptionRecord::from_bytes((&data, 1032)) {
    Ok((_remaining_data, acc_desc_record)) => acc_desc_record.print(),
    Err(error) => panic!("Error getting Accuracy Description Record from data: {error}"),
}
``` 

The error looks like:

> thread 'main' panicked at /Users/justin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/deku-0.18.1/src/reader.rs:258:63:
> range end index 434 out of range for slice of length 128

This seems to be happening because from_bytes() is limiting the input offset to the MAX_BITS_AMT in the reader.


Discussion:
Oh, I wonder if the addition of `seek_bits` would solve this, I'll take a look at the source sometime to fix this for `from_bytes`



Patch:
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index a5d2ee47..53788aad 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -42,7 +42,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::Current(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -52,7 +53,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::End(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -62,7 +64,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
+                let seek_amt = u64::try_from(#num).expect("could not convert into u64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::Start(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 2d19c214..cd917a36 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -33,7 +33,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::Current(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -43,7 +44,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::End(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -53,7 +55,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
+                let seek_amt = u64::try_from(#num).expect("could not convert into u64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::Start(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
diff --git a/src/reader.rs b/src/reader.rs
index 82528230..80d7e829 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -174,8 +174,24 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
         {
             #[cfg(feature = "logging")]
             log::trace!("skip_bits: {amt}");
+
+            let bytes_amt = amt / 8;
+            let bits_amt = amt % 8;
+
+            // first, seek with bytes
+            if bytes_amt != 0 {
+                self.seek(SeekFrom::Current(
+                    i64::try_from(bytes_amt).expect("could not convert seek usize into i64"),
+                ))
+                .map_err(|e| DekuError::Io(e.kind()))?;
+            }
+
+            // Unlike normal seek not couting as bits_read, this one does
+            // to keep from_bytes returns
+            self.bits_read += bytes_amt * 8;
+
             // Save, and keep the leftover bits since the read will most likely be less than a byte
-            self.read_bits(amt)?;
+            self.read_bits(bits_amt)?;
         }
 
         #[cfg(not(feature = "bits"))]


Test patch:
diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs
index aac1f4d7..cb43076b 100644
--- a/tests/test_from_bytes.rs
+++ b/tests/test_from_bytes.rs
@@ -54,3 +54,30 @@ fn test_from_bytes_enum() {
     assert_eq!(6, i);
     assert_eq!(0b0101_1010u8, rest[0]);
 }
+
+#[cfg(feature = "bits")]
+#[test]
+fn test_from_bytes_long() {
+    #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+    #[deku(id_type = "u8", bits = 4)]
+    enum TestDeku {
+        #[deku(id = "0b0110")]
+        VariantA(#[deku(bits = 4)] u8),
+        #[deku(id = "0b0101")]
+        VariantB(#[deku(bits = 2)] u8),
+    }
+
+    let mut test_data = vec![0x00; 200];
+    test_data.extend([0b0110_0110u8, 0b0101_1010u8].to_vec());
+
+    let ((rest, i), ret_read) = TestDeku::from_bytes((&test_data, 200 * 8)).unwrap();
+    assert_eq!(TestDeku::VariantA(0b0110), ret_read);
+    assert_eq!(1, rest.len());
+    assert_eq!(0, i);
+
+    let ((rest, i), ret_read) = TestDeku::from_bytes((rest, i)).unwrap();
+    assert_eq!(TestDeku::VariantB(0b10), ret_read);
+    assert_eq!(1, rest.len());
+    assert_eq!(6, i);
+    assert_eq!(0b0101_1010u8, rest[0]);
+}