Skip to content

Commit

Permalink
Populate nwritten_out on error in config-store and dictionary get
Browse files Browse the repository at this point in the history
  • Loading branch information
elliottt committed Jun 18, 2024
1 parent 7dca458 commit e67f30f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 17 deletions.
3 changes: 2 additions & 1 deletion lib/compute-at-edge-abi/compute-at-edge.witx
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,8 @@
(param $key string)
(param $value (@witx pointer (@witx char8)))
(param $value_max_len (@witx usize))
(result $err (expected $num_bytes (error $fastly_status)))
(param $nwritten_out (@witx pointer (@witx usize)))
(result $err (expected (error $fastly_status)))
)
)

Expand Down
3 changes: 2 additions & 1 deletion lib/compute-at-edge-abi/config-store.witx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
(param $key string)
(param $value (@witx pointer (@witx char8)))
(param $value_max_len (@witx usize))
(result $err (expected $num_bytes (error $fastly_status)))
(param $nwritten_out (@witx pointer (@witx usize)))
(result $err (expected (error $fastly_status)))
)
)
37 changes: 33 additions & 4 deletions lib/src/wiggle_abi/config_store.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::{
dictionary_impl::DictionaryError,
fastly_config_store::FastlyConfigStore,
fastly_dictionary::FastlyDictionary,
types::{ConfigStoreHandle, DictionaryHandle},
};
use crate::{session::Session, wiggle_abi::fastly_dictionary::FastlyDictionary, Error};
use crate::{session::Session, Error};
use wiggle::GuestPtr;

impl FastlyConfigStore for Session {
Expand All @@ -17,8 +19,35 @@ impl FastlyConfigStore for Session {
key: &GuestPtr<str>,
buf: &GuestPtr<u8>,
buf_len: u32,
) -> Result<u32, Error> {
let dict_handle = DictionaryHandle::from(unsafe { config_store.inner() });
<Self as FastlyDictionary>::get(self, dict_handle, key, buf, buf_len)
nwritten_out: &GuestPtr<u32>,
) -> Result<(), Error> {
let dictionary = DictionaryHandle::from(unsafe { config_store.inner() });
let dict = &self.dictionary(dictionary)?.contents;

let item_bytes = {
let key: &str = &key.as_str()?.ok_or(Error::SharedMemory)?;
dict.get(key)
.ok_or_else(|| DictionaryError::UnknownDictionaryItem(key.to_owned()))?
.as_bytes()
};

if item_bytes.len() > usize::try_from(buf_len).expect("buf_len must fit in usize") {
// Write out the number of bytes necessary to fit this item, or zero on overflow to
// signal an error condition. This is probably unnecessary, as config store entries
// may be at most 8000 utf-8 characters large.
nwritten_out.write(u32::try_from(item_bytes.len()).unwrap_or(0))?;
return Err(Error::BufferLengthError {
buf: "buf",
len: "buf_len",
});
}

// We know the conversion of item_bytes.len() to u32 will succeed, as it's <= buf_len.
let item_len = u32::try_from(item_bytes.len()).unwrap();

nwritten_out.write(item_len)?;
buf.as_array(item_len).copy_from_slice(item_bytes)?;

Ok(())
}
}
18 changes: 13 additions & 5 deletions lib/src/wiggle_abi/dictionary_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ impl FastlyDictionary for Session {
key: &GuestPtr<str>,
buf: &GuestPtr<u8>,
buf_len: u32,
) -> Result<u32, Error> {
nwritten_out: &GuestPtr<u32>,
) -> Result<(), Error> {
let dict = &self.dictionary(dictionary)?.contents;

let item_bytes = {
Expand All @@ -54,16 +55,23 @@ impl FastlyDictionary for Session {
.as_bytes()
};

if item_bytes.len() > buf_len as usize {
if item_bytes.len() > usize::try_from(buf_len).expect("buf_len must fit in usize") {
// Write out the number of bytes necessary to fit this item, or zero on overflow to
// signal an error condition. This is probably unnecessary, as config store entries
// may be at most 8000 utf-8 characters large.
nwritten_out.write(u32::try_from(item_bytes.len()).unwrap_or(0))?;
return Err(Error::BufferLengthError {
buf: "dictionary_item",
len: "dictionary_item_max_len",
});
}
let item_len = u32::try_from(item_bytes.len())
.expect("smaller than dictionary_item_max_len means it must fit");

// We know the conversion of item_bytes.len() to u32 will succeed, as it's <= buf_len.
let item_len = u32::try_from(item_bytes.len()).unwrap();

nwritten_out.write(item_len)?;
buf.as_array(item_len).copy_from_slice(item_bytes)?;
Ok(item_len)

Ok(())
}
}
61 changes: 55 additions & 6 deletions test-fixtures/src/bin/config-store-lookup.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,59 @@
//! A guest program to test that dictionary lookups work properly.
//! A guest program to test that config-store lookups work properly.
use fastly::ConfigStore;
use fastly_shared::FastlyStatus;

fn main() {
let animals = ConfigStore::open("animals");
assert_eq!(animals.get("dog").unwrap(), "woof");
assert_eq!(animals.get("cat").unwrap(), "meow");
assert_eq!(animals.get("lamp"), None);
let animals = unsafe {
let mut dict_handle = fastly_shared::INVALID_CONFIG_STORE_HANDLE;
let res = fastly_sys::fastly_config_store::open(
"animals".as_ptr(),
"animals".len(),
&mut dict_handle as *mut _,
);
assert_eq!(res, FastlyStatus::OK, "Failed to open config-store");
dict_handle
};

#[derive(Debug, PartialEq, Eq)]
enum LookupResult {
Success(String),
Missing,
BufTooSmall(usize),
Err(FastlyStatus),
}

let get = |key: &str, buf_len: usize| unsafe {
let mut value = Vec::with_capacity(buf_len);
let mut nwritten = 0;
let res = fastly_sys::fastly_config_store::get(
animals,
key.as_ptr(),
key.len(),
value.as_mut_ptr(),
buf_len,
&mut nwritten as *mut _,
);

if res != FastlyStatus::OK {
if res == FastlyStatus::NONE {
return LookupResult::Missing;
}

if res == FastlyStatus::BUFLEN {
assert!(nwritten > 0 && buf_len < nwritten);
return LookupResult::BufTooSmall(nwritten);
}

return LookupResult::Err(res);
}

value.set_len(nwritten);
value.shrink_to(nwritten);
LookupResult::Success(String::from_utf8(value).unwrap())
};

assert_eq!(get("dog", 4), LookupResult::Success(String::from("woof")));
assert_eq!(get("cat", 4), LookupResult::Success(String::from("meow")));
assert_eq!(get("cat", 2), LookupResult::BufTooSmall(4));
assert_eq!(get("lamp", 4), LookupResult::Missing);
}

0 comments on commit e67f30f

Please sign in to comment.