From b1dd18918eea27c1a4fc7e1df598709d999121cb Mon Sep 17 00:00:00 2001 From: Katsuyuki Omuro Date: Mon, 24 Aug 2026 15:28:21 +0900 Subject: [PATCH] Fix UB from error out-params not written on success paths The bridge error convention declares the out-param uninitialized on the C++ side and relies on the Rust side to write it. `try_fe!` (and the equivalent `try_kv!` / `try_is!` macros) do set it to null on success, so the pattern is sound for any function that routes every path through a macro. Two KV store functions bypass those macros and wrote `err` only on their error path, leaving the success path reading an indeterminate pointer: - `ListResponse::next()` (src/kv_store.rs) - the `Ok(page)` arm set `out` only. - `m_static_kv_store_kv_store_open()` - the `Ok(..)` arms set `out` only. When the stack slot happened to be non-zero, the C++ wrapper saw `err != nullptr`, reported a bogus error, and called `rust::Box::from_raw()` on garbage, which would later drop a garbage box. Cross-language LTO happened to leave those slots zeroed, so the test suite passed; building with LTO off surfaced it as a failure of `REQUIRE(page.has_value())` at test/kv_store.cpp:92. Both outcomes were luck rather than correctness. Both functions now null `err` on entry, which also covers the paths that return false without an error at all: iteration finishing in `next()`, and `Ok(None)` (store does not exist) in `open()`, which the C++ side must be able to tell apart from a real failure. Also gives all 67 error out-param declarations across include/ and src/cpp/ an explicit `{nullptr}` initializer. That is redundant where a `try_*!` macro already guarantees the write, but it makes the convention robust by default so a future bridge function that forgets cannot reintroduce this class of bug. Verified by building with LTO disabled, where kv_store previously failed and now passes (7/7); the normal LTO build and lint gates are unaffected. Co-Authored-By: Claude Opus 5 (1M context) --- include/fastly/kv_store.h | 2 +- src/cpp/backend.cpp | 4 ++-- src/cpp/config_store.cpp | 6 +++--- src/cpp/device_detection.cpp | 2 +- src/cpp/esi.cpp | 4 ++-- src/cpp/geo.cpp | 2 +- src/cpp/http/body.cpp | 12 ++++++------ src/cpp/http/purge.cpp | 4 ++-- src/cpp/http/request.cpp | 34 +++++++++++++++++----------------- src/cpp/http/response.cpp | 16 ++++++++-------- src/cpp/http/status_code.cpp | 2 +- src/cpp/kv_store.cpp | 34 +++++++++++++++++----------------- src/cpp/log.cpp | 2 +- src/cpp/secret_store.cpp | 8 ++++---- src/cpp/security.cpp | 2 +- src/kv_store.rs | 6 ++++++ 16 files changed, 73 insertions(+), 67 deletions(-) diff --git a/include/fastly/kv_store.h b/include/fastly/kv_store.h index ff3eced..48bd81d 100644 --- a/include/fastly/kv_store.h +++ b/include/fastly/kv_store.h @@ -233,7 +233,7 @@ class ListResponse : public fastly::detail::RustIteratorRange< /// Gets the next page of results. std::optional> next() { fastly::sys::kv_store::ListPage *page; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; if (this->iter_->next(page, err)) { if (err == nullptr) { return ListPage( diff --git a/src/cpp/backend.cpp b/src/cpp/backend.cpp index 7e590d3..d714cda 100644 --- a/src/cpp/backend.cpp +++ b/src/cpp/backend.cpp @@ -6,7 +6,7 @@ namespace fastly::backend { fastly::expected Backend::from_name(std::string_view name) { fastly::sys::backend::Backend *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::backend::m_static_backend_backend_from_name( static_cast(name), out, err); if (err != nullptr) { @@ -214,7 +214,7 @@ BackendBuilder BackendBuilder::tcp_keepalive_time_secs(uint32_t secs) && { fastly::expected BackendBuilder::finish() && { fastly::sys::backend::Backend *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::backend::m_backend_backend_builder_finish( std::move(this->builder), out, err); if (err != nullptr) { diff --git a/src/cpp/config_store.cpp b/src/cpp/config_store.cpp index d42d79f..36b2bd7 100644 --- a/src/cpp/config_store.cpp +++ b/src/cpp/config_store.cpp @@ -6,7 +6,7 @@ namespace fastly::config_store { fastly::expected ConfigStore::open(std::string_view name) { fastly::sys::config_store::ConfigStore *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::config_store::m_static_config_store_config_store_open( static_cast(name), out, err); if (err != nullptr) { @@ -19,7 +19,7 @@ fastly::expected ConfigStore::open(std::string_view name) { fastly::expected> ConfigStore::get(std::string_view key) { std::string out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; auto some{this->cs->get(static_cast(key), out, err)}; if (err != nullptr) { return fastly::unexpected(err); @@ -31,7 +31,7 @@ ConfigStore::get(std::string_view key) { } fastly::expected ConfigStore::contains(std::string_view key) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool out{this->cs->contains(static_cast(key), err)}; if (err != nullptr) { return fastly::unexpected(err); diff --git a/src/cpp/device_detection.cpp b/src/cpp/device_detection.cpp index 21a1454..3bc2702 100644 --- a/src/cpp/device_detection.cpp +++ b/src/cpp/device_detection.cpp @@ -6,7 +6,7 @@ namespace fastly::device_detection { fastly::expected> lookup(std::string_view user_agent) { fastly::sys::device_detection::Device *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::device_detection::f_device_detection_lookup( static_cast(user_agent), out, err); if (err != nullptr) { diff --git a/src/cpp/esi.cpp b/src/cpp/esi.cpp index cfcead6..7f88a05 100644 --- a/src/cpp/esi.cpp +++ b/src/cpp/esi.cpp @@ -78,7 +78,7 @@ tl::expected Processor::process_response( Response &src_document, std::optional client_response_metadata, std::optional dispatch_fragment_request, std::optional process_fragment_response) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; // The Rust side will take ownership auto raw_metadata = client_response_metadata.has_value() @@ -112,7 +112,7 @@ tl::expected Processor::process_document( const std::string &src_document, std::optional dispatch_fragment_request, std::optional process_fragment_response) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; // We convert the callbacks to their tag types here, or pass null if not // present. They will be converted back to their real types when the C++ // callback bindings are invoked from Rust. diff --git a/src/cpp/geo.cpp b/src/cpp/geo.cpp index f2fe41c..d08618b 100644 --- a/src/cpp/geo.cpp +++ b/src/cpp/geo.cpp @@ -5,7 +5,7 @@ namespace fastly::geo { fastly::expected> geo_lookup(std::string_view ip) { fastly::sys::geo::Geo *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::geo::f_geo_geo_lookup(static_cast(ip), out, err); if (err != nullptr) { return fastly::unexpected(err); diff --git a/src/cpp/http/body.cpp b/src/cpp/http/body.cpp index ff29913..f9c903a 100644 --- a/src/cpp/http/body.cpp +++ b/src/cpp/http/body.cpp @@ -62,7 +62,7 @@ void Body::append(Body other) { fastly::expected Body::read(uint8_t *buf, std::size_t bufsize) { rust::Slice slice{buf, bufsize}; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; auto ret{this->bod->read(slice, err)}; if (err != nullptr) { return fastly::unexpected(err); @@ -73,7 +73,7 @@ fastly::expected Body::read(uint8_t *buf, std::size_t bufsize) { fastly::expected Body::write(uint8_t *buf, std::size_t bufsize) { rust::Slice slice{buf, bufsize}; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; auto ret{this->bod->write(slice, err)}; if (err != nullptr) { return fastly::unexpected(err); @@ -88,7 +88,7 @@ fastly::expected Body::write(uint8_t *buf, std::size_t bufsize) { fastly::expected Body::append_trailer(std::string_view header_name, std::string_view header_value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->bod->append_trailer(static_cast(header_name), static_cast(header_value), err); if (err != nullptr) { @@ -136,7 +136,7 @@ int StreamingBody::sync() { fastly::expected StreamingBody::finish() { this->flush(); - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::m_http_streaming_body_finish(std::move(this->bod), err); if (err != nullptr) { return fastly::unexpected(err); @@ -152,7 +152,7 @@ void StreamingBody::append(Body other) { fastly::expected StreamingBody::write(uint8_t *buf, std::size_t bufsize) { rust::Slice slice{buf, bufsize}; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; auto ret{this->bod->write(slice, err)}; if (err != nullptr) { return fastly::unexpected(err); @@ -164,7 +164,7 @@ fastly::expected StreamingBody::write(uint8_t *buf, fastly::expected StreamingBody::append_trailer(std::string_view header_name, std::string_view header_value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->bod->append_trailer(static_cast(header_name), static_cast(header_value), err); if (err != nullptr) { diff --git a/src/cpp/http/purge.cpp b/src/cpp/http/purge.cpp index 424af19..48bb334 100644 --- a/src/cpp/http/purge.cpp +++ b/src/cpp/http/purge.cpp @@ -4,7 +4,7 @@ namespace fastly::http::purge { fastly::expected purge_surrogate_key(std::string_view surrogate_key) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::purge::f_http_purge_purge_surrogate_key( static_cast(surrogate_key), err); if (err != nullptr) { @@ -16,7 +16,7 @@ fastly::expected purge_surrogate_key(std::string_view surrogate_key) { fastly::expected soft_purge_surrogate_key(std::string_view surrogate_key) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::purge::f_http_purge_soft_purge_surrogate_key( static_cast(surrogate_key), err); if (err != nullptr) { diff --git a/src/cpp/http/request.cpp b/src/cpp/http/request.cpp index 9454165..bf8452f 100644 --- a/src/cpp/http/request.cpp +++ b/src/cpp/http/request.cpp @@ -29,7 +29,7 @@ PendingRequest::poll() { fastly::expected PendingRequest::wait() { fastly::sys::http::Response *ret; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::request::m_http_request_pending_request_wait( std::move(this->req), ret, err); if (err != nullptr) { @@ -52,7 +52,7 @@ select(std::vector &reqs) { vecreqs, std::move(boxed.req)); } fastly::sys::http::Response *resp; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::request::f_http_request_select(std::move(vecreqs), resp, others, err); std::vector ret_others; @@ -154,7 +154,7 @@ fastly::expected Request::send(std::string_view backend_name) { fastly::expected Request::send(fastly::backend::Backend &backend) { fastly::sys::http::Response *resp; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::m_http_request_send(std::move(this->req), *backend.backend, resp, err); if (err != nullptr) { @@ -175,7 +175,7 @@ Request::send_async(std::string_view backend_name) { fastly::expected Request::send_async(fastly::backend::Backend &backend) { fastly::sys::http::request::PendingRequest *req; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::m_http_request_send_async(std::move(this->req), *backend.backend, req, err); if (err != nullptr) { @@ -196,7 +196,7 @@ Request::send_async_streaming(std::string_view backend_name) { fastly::expected> Request::send_async_streaming(fastly::backend::Backend &backend) { fastly::sys::http::request::AsyncStreamRes *res; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::http::m_http_request_send_async_streaming( std::move(this->req), *backend.backend, res, err); if (err != nullptr) { @@ -241,7 +241,7 @@ Body Request::into_body() { } fastly::expected Request::set_body_text_plain(std::string_view body) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_body_text_plain(static_cast(body), err); if (err != nullptr) { return fastly::unexpected(err); @@ -257,7 +257,7 @@ Request::with_body_text_html(std::string_view body) && { } fastly::expected Request::set_body_text_html(std::string_view body) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_body_text_html(static_cast(body), err); if (err != nullptr) { return fastly::unexpected(err); @@ -322,7 +322,7 @@ std::optional Request::get_content_length() { } fastly::expected Request::contains_header(std::string_view name) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool has_header{ this->req->contains_header(static_cast(name), err)}; if (err != nullptr) { @@ -350,7 +350,7 @@ fastly::expected> Request::get_header(std::string_view name) { std::vector value; bool is_sensitive{false}; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool has_header{this->req->get_header(static_cast(name), value, is_sensitive, err)}; if (err != nullptr) { @@ -366,7 +366,7 @@ Request::get_header(std::string_view name) { fastly::expected Request::get_header_all(std::string_view name) { fastly::sys::http::HeaderValuesIter *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->get_header_all(static_cast(name), out, err); if (err != nullptr) { return fastly::unexpected(err); @@ -391,7 +391,7 @@ fastly::expected Request::get_header_names() { fastly::expected Request::set_header(std::string_view name, std::string_view value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_header(static_cast(name), static_cast(value), err); if (err != nullptr) { @@ -403,7 +403,7 @@ fastly::expected Request::set_header(std::string_view name, fastly::expected Request::append_header(std::string_view name, std::string_view value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->append_header(static_cast(name), static_cast(value), err); if (err != nullptr) { @@ -415,7 +415,7 @@ fastly::expected Request::append_header(std::string_view name, fastly::expected> Request::remove_header(std::string_view name) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; std::string out; bool has_header{ this->req->remove_header(static_cast(name), out, err)}; @@ -448,7 +448,7 @@ std::string Request::get_url() { } fastly::expected Request::set_url(std::string_view url) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_url(static_cast(url), err); if (err != nullptr) { return fastly::unexpected(err); @@ -468,7 +468,7 @@ fastly::expected Request::with_path(std::string_view path) && { } fastly::expected Request::set_path(std::string_view path) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_path(static_cast(path), err); if (err != nullptr) { return fastly::unexpected(err); @@ -503,7 +503,7 @@ Request::with_query_string(std::string_view query) && { } fastly::expected Request::set_query_string(std::string_view query) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_query_string(static_cast(query), err); if (err != nullptr) { return fastly::unexpected(err); @@ -558,7 +558,7 @@ fastly::expected Request::with_surrogate_key(std::string_view sk) && { } fastly::expected Request::set_surrogate_key(std::string_view sk) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->req->set_surrogate_key(static_cast(sk), err); if (err != nullptr) { return fastly::unexpected(err); diff --git a/src/cpp/http/response.cpp b/src/cpp/http/response.cpp index e6a8456..d48fe85 100644 --- a/src/cpp/http/response.cpp +++ b/src/cpp/http/response.cpp @@ -70,7 +70,7 @@ Body Response::into_body() { } fastly::expected Response::set_body_text_plain(std::string_view body) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->res->set_body_text_plain(static_cast(body), err); if (err != nullptr) { return fastly::unexpected(err); @@ -86,7 +86,7 @@ Response::with_body_text_html(std::string_view body) && { } fastly::expected Response::set_body_text_html(std::string_view body) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->res->set_body_text_html(static_cast(body), err); if (err != nullptr) { return fastly::expected(); @@ -151,7 +151,7 @@ std::optional Response::get_content_length() { } fastly::expected Response::contains_header(std::string_view name) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool has_header{ this->res->contains_header(static_cast(name), err)}; if (err != nullptr) { @@ -179,7 +179,7 @@ fastly::expected> Response::get_header(std::string_view name) { std::vector value; bool is_sensitive{false}; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool has_header{this->res->get_header(static_cast(name), value, is_sensitive, err)}; if (err != nullptr) { @@ -195,7 +195,7 @@ Response::get_header(std::string_view name) { fastly::expected Response::get_header_all(std::string_view name) { fastly::sys::http::HeaderValuesIter *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->res->get_header_all(static_cast(name), out, err); if (err != nullptr) { return fastly::unexpected(err); @@ -220,7 +220,7 @@ fastly::expected Response::get_header_names() { fastly::expected Response::set_header(std::string_view name, std::string_view value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->res->set_header(static_cast(name), static_cast(value), err); if (err != nullptr) { @@ -232,7 +232,7 @@ fastly::expected Response::set_header(std::string_view name, fastly::expected Response::append_header(std::string_view name, std::string_view value) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->res->append_header(static_cast(name), static_cast(value), err); if (err != nullptr) { @@ -244,7 +244,7 @@ fastly::expected Response::append_header(std::string_view name, fastly::expected> Response::remove_header(std::string_view name) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; std::string out; bool has_header{ this->res->remove_header(static_cast(name), out, err)}; diff --git a/src/cpp/http/status_code.cpp b/src/cpp/http/status_code.cpp index 8cf70b1..8a500ae 100644 --- a/src/cpp/http/status_code.cpp +++ b/src/cpp/http/status_code.cpp @@ -138,7 +138,7 @@ uint16_t StatusCode::as_code() { return this->value; } tl::expected, fastly::FastlyError> StatusCode::canonical_reason() { std::string reason; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool has_reason{fastly::sys::http::f_http_status_code_canonical_reason( this->value, reason, err)}; if (err != nullptr) { diff --git a/src/cpp/kv_store.cpp b/src/cpp/kv_store.cpp index 3c5811c..2cd02d5 100644 --- a/src/cpp/kv_store.cpp +++ b/src/cpp/kv_store.cpp @@ -41,7 +41,7 @@ InsertBuilder InsertBuilder::time_to_live(std::chrono::milliseconds ttl) && { } expected<> InsertBuilder::execute(const std::string &key, Body body) && { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; fastly::sys::kv_store::m_kv_store_insert_builder_execute( std::move(builder_), key, std::move(body.bod), err); if (err != nullptr) { @@ -53,7 +53,7 @@ expected<> InsertBuilder::execute(const std::string &key, Body body) && { expected InsertBuilder::execute_async(const std::string &key, Body body) && { std::uint32_t handle; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; fastly::sys::kv_store::m_kv_store_insert_builder_execute_async( std::move(builder_), key, std::move(body.bod), handle, err); if (err != nullptr) { @@ -150,7 +150,7 @@ std::uint64_t LookupResponse::current_generation() const { expected LookupBuilder::execute(std::string_view key) const { fastly::sys::kv_store::LookupResponse *response; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; builder_->execute({key.data(), key.size()}, response, err); if (err != nullptr) { return unexpected(err); @@ -162,7 +162,7 @@ expected LookupBuilder::execute(std::string_view key) const { expected LookupBuilder::execute_async(std::string_view key) const { std::uint32_t handle; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; builder_->execute_async({key.data(), key.size()}, handle, err); if (err != nullptr) { return unexpected(err); @@ -171,7 +171,7 @@ LookupBuilder::execute_async(std::string_view key) const { } expected<> EraseBuilder::execute(std::string_view key) const { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; builder_->execute({key.data(), key.size()}, err); if (err != nullptr) { return unexpected(err); @@ -182,7 +182,7 @@ expected<> EraseBuilder::execute(std::string_view key) const { expected EraseBuilder::execute_async(std::string_view key) const { std::uint32_t handle; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; builder_->execute_async({key.data(), key.size()}, handle, err); if (err != nullptr) { return unexpected(err); @@ -216,7 +216,7 @@ ListBuilder ListBuilder::prefix(const std::string &prefix) && { } expected ListBuilder::execute() && { fastly::sys::kv_store::ListPage *page; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; fastly::sys::kv_store::m_kv_store_list_builder_execute(std::move(builder_), page, err); if (err != nullptr) { @@ -232,7 +232,7 @@ ListResponse ListBuilder::iter() && { expected ListBuilder::execute_async() const { std::uint32_t handle; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; builder_->execute_async(handle, err); if (err != nullptr) { return unexpected(err); @@ -242,7 +242,7 @@ expected ListBuilder::execute_async() const { expected> KVStore::open(std::string_view name) { fastly::sys::kv_store::KVStore *store; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; if (fastly::sys::kv_store::m_static_kv_store_kv_store_open( {name.data(), name.size()}, store, err)) { return KVStore{rust::Box::from_raw(store)}; @@ -258,7 +258,7 @@ expected> KVStore::open(std::string_view name) { expected KVStore::lookup(std::string_view key) const { fastly::sys::kv_store::LookupResponse *response; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->lookup({key.data(), key.size()}, response, err); if (err != nullptr) { return unexpected(err); @@ -272,7 +272,7 @@ LookupBuilder KVStore::build_lookup() const { return {store_->build_lookup()}; } expected KVStore::pending_lookup_wait(PendingLookupHandle pending_request_handle) const { fastly::sys::kv_store::LookupResponse *response; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->pending_lookup_wait(pending_request_handle.as_u32(), response, err); if (err != nullptr) { return unexpected(err); @@ -282,7 +282,7 @@ KVStore::pending_lookup_wait(PendingLookupHandle pending_request_handle) const { } expected<> KVStore::insert(std::string_view key, Body value) const { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->insert({key.data(), key.size()}, std::move(value.bod), err); if (err != nullptr) { return unexpected(err); @@ -294,7 +294,7 @@ InsertBuilder KVStore::build_insert() const { return {store_->build_insert()}; } expected<> KVStore::pending_insert_wait(PendingInsertHandle pending_insert_handle) const { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->pending_insert_wait(pending_insert_handle.as_u32(), err); if (err != nullptr) { return unexpected(err); @@ -303,7 +303,7 @@ KVStore::pending_insert_wait(PendingInsertHandle pending_insert_handle) const { } expected<> KVStore::erase(std::string_view key) const { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->erase({key.data(), key.size()}, err); if (err != nullptr) { return unexpected(err); @@ -315,7 +315,7 @@ EraseBuilder KVStore::build_erase() const { return {store_->build_erase()}; } expected<> KVStore::pending_erase_wait(PendingEraseHandle pending_erase_handle) const { - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->pending_erase_wait(pending_erase_handle.as_u32(), err); if (err != nullptr) { return unexpected(err); @@ -325,7 +325,7 @@ KVStore::pending_erase_wait(PendingEraseHandle pending_erase_handle) const { expected KVStore::list() const { fastly::sys::kv_store::ListPage *page; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->list(page, err); if (err != nullptr) { return unexpected(err); @@ -338,7 +338,7 @@ ListBuilder KVStore::build_list() const { return {store_->build_list()}; } expected KVStore::pending_list_wait(PendingListHandle pending_request_handle) const { fastly::sys::kv_store::ListPage *page; - fastly::sys::kv_store::KVStoreError *err; + fastly::sys::kv_store::KVStoreError *err{nullptr}; store_->pending_list_wait(pending_request_handle.as_u32(), page, err); if (err != nullptr) { return unexpected(err); diff --git a/src/cpp/log.cpp b/src/cpp/log.cpp index 4032e52..c5e6893 100644 --- a/src/cpp/log.cpp +++ b/src/cpp/log.cpp @@ -18,7 +18,7 @@ std::string Endpoint::name() { fastly::expected Endpoint::from_name(std::string_view name) { fastly::sys::log::Endpoint *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::log::m_static_log_endpoint_try_from_name( static_cast(name), out, err); if (err == nullptr) { diff --git a/src/cpp/secret_store.cpp b/src/cpp/secret_store.cpp index 023c924..50b3d72 100644 --- a/src/cpp/secret_store.cpp +++ b/src/cpp/secret_store.cpp @@ -7,7 +7,7 @@ namespace fastly::secret_store { fastly::expected Secret::from_bytes(std::vector data) { fastly::sys::secret_store::Secret *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::secret_store::m_static_secret_store_secret_from_bytes(data, out, err); if (err != nullptr) { @@ -25,7 +25,7 @@ std::string Secret::plaintext() { fastly::expected SecretStore::open(std::string_view name) { fastly::sys::secret_store::SecretStore *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; fastly::sys::secret_store::m_static_secret_store_secret_store_open( static_cast(name), out, err); if (err != nullptr) { @@ -37,7 +37,7 @@ fastly::expected SecretStore::open(std::string_view name) { fastly::expected> SecretStore::get(std::string_view key) { fastly::sys::secret_store::Secret *out; - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; this->ss->get(static_cast(key), out, err); if (err != nullptr) { return fastly::unexpected(err); @@ -49,7 +49,7 @@ fastly::expected> SecretStore::get(std::string_view key) { } fastly::expected SecretStore::contains(std::string_view key) { - fastly::sys::error::FastlyError *err; + fastly::sys::error::FastlyError *err{nullptr}; bool out{this->ss->contains(static_cast(key), err)}; if (err != nullptr) { return fastly::unexpected(err); diff --git a/src/cpp/security.cpp b/src/cpp/security.cpp index 9b54090..39a48ea 100644 --- a/src/cpp/security.cpp +++ b/src/cpp/security.cpp @@ -63,7 +63,7 @@ std::optional InspectResponse::into_redirect() { tl::expected inspect(fastly::http::Request &request, InspectConfig config) { fastly::sys::security::InspectResponse *out; - fastly::sys::security::InspectError *err; + fastly::sys::security::InspectError *err{nullptr}; auto client_ip = config.client_ip() ? &*config.client_ip() : nullptr; auto corp = config.corp() ? &*config.corp() : nullptr; auto workspace = config.workspace() ? &*config.workspace() : nullptr; diff --git a/src/kv_store.rs b/src/kv_store.rs index 8500ebe..a0f320e 100644 --- a/src/kv_store.rs +++ b/src/kv_store.rs @@ -291,6 +291,9 @@ impl ListResponse<'_> { mut out: Pin<&mut *mut ListPage>, mut err: Pin<&mut *mut KVStoreError>, ) -> bool { + // The C++ side branches on `err == nullptr`, so it must be written on + // every path, not just the error path. + err.set(std::ptr::null_mut()); self.0 .next() .map(|page| match page { @@ -438,6 +441,9 @@ pub fn m_static_kv_store_kv_store_open( mut out: Pin<&mut *mut KVStore>, mut err: Pin<&mut *mut KVStoreError>, ) -> bool { + // Written on every path, including `Ok(None)` (the store does not exist), + // which the C++ side must be able to tell apart from a real error. + err.set(std::ptr::null_mut()); match fastly::kv_store::KVStore::open(name) { Ok(store) => store .map(|s| {