Repository: capnproto/capnproto-rust (language: rust)

Issue:
Dynamic parsing of fields may panic
When I use the has_named interface `some_struct.has_named("some_field_name")`, sometimes it's panic.

```
thread 'main' panicked at XXXX\capnp-0.19.2\src\schema.rs:76:31:
index out of bounds: the len is 4 but the index is 4
```

Then I check the code:
https://github.com/capnproto/capnproto-rust/blob/master/capnp/src/schema.rs
```rust
pub fn find_field_by_name(&self, name: &str) -> Result<Option<Field>> {
        let fields = self.get_fields()?;
        let mut lower: usize = 0;
        let mut upper: usize = self.raw.generic.members_by_name.len();
        let mut mid: usize = (lower + upper) / 2;
        let mut candidate_index = self.raw.generic.members_by_name[mid];
        let mut candidate_name = fields.get(candidate_index).get_proto().get_name()?;

        while lower < upper {
            use core::cmp::Ordering;
            match (&name).partial_cmp(&candidate_name) {
                Some(Ordering::Equal) => return Ok(Some(fields.get(candidate_index))),
                Some(Ordering::Greater) => lower = mid + 1,
                Some(Ordering::Less) => upper = mid,
                None => unreachable!(),
            }
            mid = (lower + upper) / 2;
            candidate_index = self.raw.generic.members_by_name[mid];
            candidate_name = fields.get(candidate_index).get_proto().get_name()?;
        }
        Ok(None)
    }
```

I'm guessing that `mid` might be equal to `upper`, which would cause the array to go out of bounds. I don't know if my findings are correct. 



Discussion / hints:

Fix patch:
diff --git a/capnp/src/schema.rs b/capnp/src/schema.rs
index b3f79dd1d..3e6dd40c3 100644
--- a/capnp/src/schema.rs
+++ b/capnp/src/schema.rs
@@ -60,11 +60,12 @@ impl StructSchema {
         let fields = self.get_fields()?;
         let mut lower: usize = 0;
         let mut upper: usize = self.raw.generic.members_by_name.len();
-        let mut mid: usize = (lower + upper) / 2;
-        let mut candidate_index = self.raw.generic.members_by_name[mid];
-        let mut candidate_name = fields.get(candidate_index).get_proto().get_name()?;
 
         while lower < upper {
+            let mid: usize = (lower + upper) / 2;
+            let candidate_index = self.raw.generic.members_by_name[mid];
+            let candidate_name = fields.get(candidate_index).get_proto().get_name()?;
+
             use core::cmp::Ordering;
             match (&name).partial_cmp(&candidate_name) {
                 Some(Ordering::Equal) => return Ok(Some(fields.get(candidate_index))),
@@ -72,9 +73,6 @@ impl StructSchema {
                 Some(Ordering::Less) => upper = mid,
                 None => unreachable!(),
             }
-            mid = (lower + upper) / 2;
-            candidate_index = self.raw.generic.members_by_name[mid];
-            candidate_name = fields.get(candidate_index).get_proto().get_name()?;
         }
         Ok(None)
     }


Test patch:
diff --git a/capnpc/test/dynamic.rs b/capnpc/test/dynamic.rs
index 937088bff..8ee4babf1 100644
--- a/capnpc/test/dynamic.rs
+++ b/capnpc/test/dynamic.rs
@@ -391,3 +391,20 @@ fn test_stringify_list_list() {
     let stringified = format!("{:?}", root.into_reader());
     assert_eq!(stringified, "[[1111, 2222, 3333], [123456]]");
 }
+
+#[test]
+fn test_get_named_missing() {
+    let mut builder = message::Builder::new_default();
+    let root: test_all_types::Builder<'_> = builder.init_root();
+    let root: dynamic_value::Builder<'_> = root.into();
+    let mut root: dynamic_struct::Builder<'_> = root.downcast();
+    test_util::dynamic_init_test_message(root.reborrow());
+    let root = root.into_reader();
+    // try a bunch of fields that don't exist
+    assert!(root.get_named("AAAAAAA").is_err());
+    assert!(root.has_named("AAAAAAA").is_err());
+    assert!(root.get_named("abcdef").is_err());
+    assert!(root.has_named("abcdef").is_err());
+    assert!(root.get_named("zzzzzzz").is_err());
+    assert!(root.has_named("zzzzzzz").is_err());
+}