Botan 3.9.0
Crypto and TLS for C&
ffi_util.h
Go to the documentation of this file.
1/*
2* (C) 2015,2017 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#ifndef BOTAN_FFI_UTILS_H_
8#define BOTAN_FFI_UTILS_H_
9
10#include <botan/exceptn.h>
11#include <botan/ffi.h>
12#include <botan/mem_ops.h>
13#include <botan/internal/mem_utils.h>
14#include <concepts>
15#include <cstdint>
16#include <exception>
17#include <memory>
18
19namespace Botan_FFI {
20
22
24 public:
25 FFI_Error(std::string_view what, int err_code) : Exception("FFI error", what), m_err_code(err_code) {}
26
27 int error_code() const noexcept override { return m_err_code; }
28
30
31 private:
32 int m_err_code;
33};
34
35template <typename T, uint32_t MAGIC>
37 public:
38 explicit botan_struct(std::unique_ptr<T> obj) : m_magic(MAGIC), m_obj(std::move(obj)) {}
39
40 virtual ~botan_struct() {
41 m_magic = 0;
42 m_obj.reset();
43 }
44
45 botan_struct(const botan_struct& other) = delete;
46 botan_struct(botan_struct&& other) = delete;
47 botan_struct& operator=(const botan_struct& other) = delete;
49
50 bool magic_ok() const { return (m_magic == MAGIC); }
51
52 T* unsafe_get() const { return m_obj.get(); }
53
54 private:
55 uint32_t m_magic = 0;
56 std::unique_ptr<T> m_obj;
57};
58
59// NOLINTBEGIN(*-macro-usage)
60
61#define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \
62 struct NAME final : public Botan_FFI::botan_struct<TYPE, MAGIC> { \
63 explicit NAME(std::unique_ptr<TYPE> x) : botan_struct(std::move(x)) {} \
64 }
65
66#define BOTAN_FFI_DECLARE_DUMMY_STRUCT(NAME, MAGIC) \
67 struct NAME final : public Botan_FFI::botan_struct<int, MAGIC> {}
68
69// NOLINTEND(*-macro-usage)
70
71// Declared in ffi.cpp
73
74int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc);
75
76int ffi_error_exception_thrown(const char* func_name, const char* exn, Botan::ErrorType err);
77
78template <typename T, uint32_t M>
80 if(!p) {
81 throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER);
82 }
83 if(!p->magic_ok()) {
84 throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT);
85 }
86
87 if(T* t = p->unsafe_get()) {
88 return *t;
89 }
90
91 throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT);
92}
93
94template <std::invocable T>
95int ffi_guard_thunk(const char* func_name, T thunk) {
97
98 try {
99 return thunk();
100 } catch(std::bad_alloc&) {
101 return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY);
102 } catch(Botan_FFI::FFI_Error& e) {
103 return ffi_error_exception_thrown(func_name, e.what(), e.error_code());
104 } catch(Botan::Exception& e) {
105 return ffi_error_exception_thrown(func_name, e.what(), e.error_type());
106 } catch(std::exception& e) {
108 } catch(...) {
109 return ffi_error_exception_thrown(func_name, "unknown exception", BOTAN_FFI_ERROR_EXCEPTION_THROWN);
110 }
111}
112
113template <typename T, uint32_t M, typename F>
114int botan_ffi_visit(botan_struct<T, M>* o, F func, const char* func_name) {
115 using RetT = std::invoke_result_t<F, T&>;
116 static_assert(std::is_void_v<RetT> || std::is_same_v<RetT, BOTAN_FFI_ERROR> || std::is_same_v<RetT, int>,
117 "BOTAN_FFI_DO must be used with a block that returns either nothing, int or BOTAN_FFI_ERROR");
118
119 if(!o) {
121 }
122
123 if(!o->magic_ok()) {
125 }
126
127 T* p = o->unsafe_get();
128 if(p == nullptr) {
130 }
131
132 if constexpr(std::is_void_v<RetT>) {
133 return ffi_guard_thunk(func_name, [&] {
134 func(*p);
135 return BOTAN_FFI_SUCCESS;
136 });
137 } else {
138 return ffi_guard_thunk(func_name, [&] { return func(*p); });
139 }
140}
141
142// TODO: C++20 introduces std::source_location which will allow to eliminate this
143// macro altogether. Instead, using code would just call the C++ function
144// that makes use of std::source_location like so:
145//
146// template<typename T, uint32_t M, typename F>
147// int botan_ffi_visit(botan_struct<T, M>* obj, F func,
148// const std::source_location sl = std::source_location::current())
149// {
150// // [...]
151// if constexpr(...)
152// {
153// return ffi_guard_thunk(sl.function_name(), [&] { return func(*p); })
154// }
155// // [...]
156// }
157// NOLINTNEXTLINE(*-macro-usage)
158#define BOTAN_FFI_VISIT(obj, lambda) botan_ffi_visit(obj, lambda, __func__)
159
160template <typename T, uint32_t M>
161int ffi_delete_object(botan_struct<T, M>* obj, const char* func_name) {
162 return ffi_guard_thunk(func_name, [=]() -> int {
163 // ignore delete of null objects
164 if(obj == nullptr) {
165 return BOTAN_FFI_SUCCESS;
166 }
167
168 if(!obj->magic_ok()) {
170 }
171
172 delete obj; // NOLINT(*-owning-memory)
173 return BOTAN_FFI_SUCCESS;
174 });
175}
176
177template <typename T, typename... Args>
178BOTAN_FFI_ERROR ffi_new_object(T* obj, Args&&... args) {
179 // NOLINTNEXTLINE(*-owning-memory)
180 *obj = new std::remove_pointer_t<T>(std::forward<Args>(args)...);
181 return BOTAN_FFI_SUCCESS;
182}
183
184// NOLINTNEXTLINE(*-macro-usage)
185#define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__)
186
187inline int invoke_view_callback(botan_view_bin_fn view, botan_view_ctx ctx, std::span<const uint8_t> buf) {
188 if(view == nullptr) {
190 }
191 return view(ctx, buf.data(), buf.size());
192}
193
194// Should not be std::string_view as we rely on being able to NULL terminate
195inline int invoke_view_callback(botan_view_str_fn view, botan_view_ctx ctx, const std::string& str) {
196 if(view == nullptr) {
198 }
199 return view(ctx, str.data(), str.size() + 1);
200}
201
203 uint8_t* out_ptr;
204 size_t* out_len;
205};
206
207int botan_view_bin_bounce_fn(botan_view_ctx ctx, const uint8_t* buf, size_t len);
208int botan_view_str_bounce_fn(botan_view_ctx ctx, const char* str, size_t len);
209
210template <typename Fn, typename... Args>
211int copy_view_bin(uint8_t out[], size_t* out_len, Fn fn, Args... args) {
212 botan_view_bounce_struct ctx{out, out_len};
213 return fn(args..., &ctx, botan_view_bin_bounce_fn);
214}
215
216template <typename Fn, typename... Args>
217int copy_view_str(uint8_t out[], size_t* out_len, Fn fn, Args... args) {
218 if(fn == nullptr) {
220 }
221 botan_view_bounce_struct ctx{out, out_len};
222 return fn(args..., &ctx, botan_view_str_bounce_fn);
223}
224
225template <std::integral T>
226inline int write_output(T out[], size_t* out_len, const T buf[], size_t buf_len) {
227 static_assert(sizeof(T) == 1, "T should be either uint8_t or char");
228
229 if(out_len == nullptr) {
231 }
232
233 const size_t avail = *out_len;
234 *out_len = buf_len;
235
236 if((avail >= buf_len) && (out != nullptr)) {
237 Botan::copy_mem(out, buf, buf_len);
238 return BOTAN_FFI_SUCCESS;
239 } else {
240 if(out != nullptr) {
241 Botan::clear_mem(out, avail);
242 }
244 }
245}
246
247inline int write_vec_output(uint8_t out[], size_t* out_len, std::span<const uint8_t> buf) {
248 return write_output<uint8_t>(out, out_len, buf.data(), buf.size());
249}
250
251inline int write_str_output(char out[], size_t* out_len, const std::string& str) {
252 return write_output<char>(out, out_len, str.data(), str.size() + 1);
253}
254
255} // namespace Botan_FFI
256
257#endif
#define BOTAN_UNSTABLE_API
Definition api.h:34
const char * what() const noexcept override
Definition exceptn.h:93
Exception(std::string_view msg)
Definition exceptn.cpp:71
Botan::ErrorType error_type() const noexcept override
Definition ffi_util.h:29
FFI_Error(std::string_view what, int err_code)
Definition ffi_util.h:25
int error_code() const noexcept override
Definition ffi_util.h:27
int(* botan_view_bin_fn)(botan_view_ctx view_ctx, const uint8_t *data, size_t len)
Definition ffi.h:161
void * botan_view_ctx
Definition ffi.h:152
BOTAN_FFI_ERROR
Definition ffi.h:114
@ BOTAN_FFI_ERROR_EXCEPTION_THROWN
Definition ffi.h:126
@ BOTAN_FFI_ERROR_OUT_OF_MEMORY
Definition ffi.h:127
@ BOTAN_FFI_ERROR_INVALID_OBJECT
Definition ffi.h:139
@ BOTAN_FFI_ERROR_NULL_POINTER
Definition ffi.h:132
@ BOTAN_FFI_SUCCESS
Definition ffi.h:115
@ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE
Definition ffi.h:123
int(* botan_view_str_fn)(botan_view_ctx view_ctx, const char *str, size_t len)
Definition ffi.h:170
void ffi_clear_last_exception()
Definition ffi.cpp:86
int invoke_view_callback(botan_view_bin_fn view, botan_view_ctx ctx, std::span< const uint8_t > buf)
Definition ffi_util.h:187
int ffi_error_exception_thrown(const char *func_name, const char *exn, int rc)
Definition ffi.cpp:90
int botan_view_bin_bounce_fn(botan_view_ctx vctx, const uint8_t *buf, size_t len)
Definition ffi.cpp:112
int botan_view_str_bounce_fn(botan_view_ctx vctx, const char *str, size_t len)
Definition ffi.cpp:108
int botan_ffi_visit(botan_struct< T, M > *o, F func, const char *func_name)
Definition ffi_util.h:114
int copy_view_bin(uint8_t out[], size_t *out_len, Fn fn, Args... args)
Definition ffi_util.h:211
int ffi_delete_object(botan_struct< T, M > *obj, const char *func_name)
Definition ffi_util.h:161
T & safe_get(botan_struct< T, M > *p)
Definition ffi_util.h:79
BOTAN_FFI_ERROR ffi_new_object(T *obj, Args &&... args)
Definition ffi_util.h:178
int copy_view_str(uint8_t out[], size_t *out_len, Fn fn, Args... args)
Definition ffi_util.h:217
int write_output(T out[], size_t *out_len, const T buf[], size_t buf_len)
Definition ffi_util.h:226
int ffi_guard_thunk(const char *func_name, T thunk)
Definition ffi_util.h:95
int write_vec_output(uint8_t out[], size_t *out_len, std::span< const uint8_t > buf)
Definition ffi_util.h:247
int write_str_output(char out[], size_t *out_len, const std::string &str)
Definition ffi_util.h:251
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:145
ErrorType
Definition exceptn.h:20
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:119
bool any_null_pointers(Ptrs... ptr)
Definition mem_utils.h:23
botan_struct(const botan_struct &other)=delete
T * unsafe_get() const
Definition ffi_util.h:52
virtual ~botan_struct()
Definition ffi_util.h:40
botan_struct(std::unique_ptr< T > obj)
Definition ffi_util.h:38
botan_struct & operator=(const botan_struct &other)=delete
botan_struct(botan_struct &&other)=delete
botan_struct & operator=(botan_struct &&other)=delete
bool magic_ok() const
Definition ffi_util.h:50