diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/ffi/ffi_util.h')
-rw-r--r-- | comm/third_party/botan/src/lib/ffi/ffi_util.h | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/ffi/ffi_util.h b/comm/third_party/botan/src/lib/ffi/ffi_util.h new file mode 100644 index 0000000000..4269aa3e8c --- /dev/null +++ b/comm/third_party/botan/src/lib/ffi/ffi_util.h @@ -0,0 +1,182 @@ +/* +* (C) 2015,2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_FFI_UTILS_H_ +#define BOTAN_FFI_UTILS_H_ + +#include <cstdint> +#include <memory> +#include <stdexcept> +#include <functional> +#include <botan/exceptn.h> +#include <botan/mem_ops.h> + +namespace Botan_FFI { + +class BOTAN_UNSTABLE_API FFI_Error final : public Botan::Exception + { + public: + FFI_Error(const std::string& what, int err_code) : + Exception("FFI error", what), + m_err_code(err_code) + {} + + int error_code() const noexcept override { return m_err_code; } + + Botan::ErrorType error_type() const noexcept override { return Botan::ErrorType::InvalidArgument; } + + private: + int m_err_code; + }; + +template<typename T, uint32_t MAGIC> +struct botan_struct + { + public: + botan_struct(T* obj) : m_magic(MAGIC), m_obj(obj) {} + virtual ~botan_struct() { m_magic = 0; m_obj.reset(); } + + bool magic_ok() const { return (m_magic == MAGIC); } + + T* unsafe_get() const + { + return m_obj.get(); + } + private: + uint32_t m_magic = 0; + std::unique_ptr<T> m_obj; + }; + +#define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \ + struct NAME final : public Botan_FFI::botan_struct<TYPE, MAGIC> { explicit NAME(TYPE* x) : botan_struct(x) {} } + +// Declared in ffi.cpp +int ffi_error_exception_thrown(const char* func_name, const char* exn, + int rc = BOTAN_FFI_ERROR_EXCEPTION_THROWN); + +template<typename T, uint32_t M> +T& safe_get(botan_struct<T,M>* p) + { + if(!p) + throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER); + if(p->magic_ok() == false) + throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT); + + if(T* t = p->unsafe_get()) + return *t; + + throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT); + } + +int ffi_guard_thunk(const char* func_name, std::function<int ()>); + +template<typename T, uint32_t M, typename F> +int apply_fn(botan_struct<T, M>* o, const char* func_name, F func) + { + if(!o) + return BOTAN_FFI_ERROR_NULL_POINTER; + + if(o->magic_ok() == false) + return BOTAN_FFI_ERROR_INVALID_OBJECT; + + T* p = o->unsafe_get(); + if(p == nullptr) + return BOTAN_FFI_ERROR_INVALID_OBJECT; + + return ffi_guard_thunk(func_name, [&]() { return func(*p); }); + } + +#define BOTAN_FFI_DO(T, obj, param, block) \ + apply_fn(obj, __func__, \ + [=](T& param) -> int { do { block } while(0); return BOTAN_FFI_SUCCESS; }) + +/* +* Like BOTAN_FFI_DO but with no trailing return with the expectation +* that the block always returns a value. This exists because otherwise +* MSVC warns about the dead return after the block in FFI_DO. +*/ +#define BOTAN_FFI_RETURNING(T, obj, param, block) \ + apply_fn(obj, __func__, \ + [=](T& param) -> int { do { block } while(0); }) + +template<typename T, uint32_t M> +int ffi_delete_object(botan_struct<T, M>* obj, const char* func_name) + { + try + { + if(obj == nullptr) + return BOTAN_FFI_SUCCESS; // ignore delete of null objects + + if(obj->magic_ok() == false) + return BOTAN_FFI_ERROR_INVALID_OBJECT; + + delete obj; + return BOTAN_FFI_SUCCESS; + } + catch(std::exception& e) + { + return ffi_error_exception_thrown(func_name, e.what()); + } + catch(...) + { + return ffi_error_exception_thrown(func_name, "unknown exception"); + } + } + +#define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__) + +inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len) + { + if(out_len == nullptr) + return BOTAN_FFI_ERROR_NULL_POINTER; + + const size_t avail = *out_len; + *out_len = buf_len; + + if((avail >= buf_len) && (out != nullptr)) + { + Botan::copy_mem(out, buf, buf_len); + return BOTAN_FFI_SUCCESS; + } + else + { + if(out != nullptr) + { + Botan::clear_mem(out, avail); + } + return BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE; + } + } + +template<typename Alloc> +int write_vec_output(uint8_t out[], size_t* out_len, const std::vector<uint8_t, Alloc>& buf) + { + return write_output(out, out_len, buf.data(), buf.size()); + } + +inline int write_str_output(uint8_t out[], size_t* out_len, const std::string& str) + { + return write_output(out, out_len, + Botan::cast_char_ptr_to_uint8(str.data()), + str.size() + 1); + } + +inline int write_str_output(char out[], size_t* out_len, const std::string& str) + { + return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str); + } + +inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec) + { + return write_output(Botan::cast_char_ptr_to_uint8(out), + out_len, + str_vec.data(), + str_vec.size()); + } + +} + +#endif |