Botan 3.8.1
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 <concepts>
14#include <cstdint>
15#include <exception>
16#include <memory>
17
18namespace Botan_FFI {
19
21 public:
22 FFI_Error(std::string_view what, int err_code) : Exception("FFI error", what), m_err_code(err_code) {}
23
24 int error_code() const noexcept override { return m_err_code; }
25
27
28 private:
29 int m_err_code;
30};
31
32template <typename T, uint32_t MAGIC>
34 public:
35 botan_struct(std::unique_ptr<T> obj) : m_magic(MAGIC), m_obj(std::move(obj)) {}
36
37 virtual ~botan_struct() {
38 m_magic = 0;
39 m_obj.reset();
40 }
41
42 bool magic_ok() const { return (m_magic == MAGIC); }
43
44 T* unsafe_get() const { return m_obj.get(); }
45
46 private:
47 uint32_t m_magic = 0;
48 std::unique_ptr<T> m_obj;
49};
50
51#define BOTAN_FFI_DECLARE_STRUCT(NAME, TYPE, MAGIC) \
52 struct NAME final : public Botan_FFI::botan_struct<TYPE, MAGIC> { \
53 explicit NAME(std::unique_ptr<TYPE> x) : botan_struct(std::move(x)) {} \
54 }
55
56#define BOTAN_FFI_DECLARE_DUMMY_STRUCT(NAME, MAGIC) \
57 struct NAME final : public Botan_FFI::botan_struct<int, MAGIC> {}
58
59// Declared in ffi.cpp
61
62int ffi_error_exception_thrown(const char* func_name, const char* exn, int rc);
63
64int ffi_error_exception_thrown(const char* func_name, const char* exn, Botan::ErrorType err);
65
66template <typename T, uint32_t M>
68 if(!p) {
69 throw FFI_Error("Null pointer argument", BOTAN_FFI_ERROR_NULL_POINTER);
70 }
71 if(p->magic_ok() == false) {
72 throw FFI_Error("Bad magic in ffi object", BOTAN_FFI_ERROR_INVALID_OBJECT);
73 }
74
75 if(T* t = p->unsafe_get()) {
76 return *t;
77 }
78
79 throw FFI_Error("Invalid object pointer", BOTAN_FFI_ERROR_INVALID_OBJECT);
80}
81
82template <std::invocable T>
83int ffi_guard_thunk(const char* func_name, T thunk) {
85
86 try {
87 return thunk();
88 } catch(std::bad_alloc&) {
89 return ffi_error_exception_thrown(func_name, "bad_alloc", BOTAN_FFI_ERROR_OUT_OF_MEMORY);
90 } catch(Botan_FFI::FFI_Error& e) {
91 return ffi_error_exception_thrown(func_name, e.what(), e.error_code());
92 } catch(Botan::Exception& e) {
93 return ffi_error_exception_thrown(func_name, e.what(), e.error_type());
94 } catch(std::exception& e) {
96 } catch(...) {
97 return ffi_error_exception_thrown(func_name, "unknown exception", BOTAN_FFI_ERROR_EXCEPTION_THROWN);
98 }
99}
100
101template <typename T, uint32_t M, typename F>
102int botan_ffi_visit(botan_struct<T, M>* o, F func, const char* func_name) {
103 using RetT = std::invoke_result_t<F, T&>;
104 static_assert(std::is_void_v<RetT> || std::is_same_v<RetT, BOTAN_FFI_ERROR> || std::is_same_v<RetT, int>,
105 "BOTAN_FFI_DO must be used with a block that returns either nothing, int or BOTAN_FFI_ERROR");
106
107 if(!o) {
109 }
110
111 if(o->magic_ok() == false) {
113 }
114
115 T* p = o->unsafe_get();
116 if(p == nullptr) {
118 }
119
120 if constexpr(std::is_void_v<RetT>) {
121 return ffi_guard_thunk(func_name, [&] {
122 func(*p);
123 return BOTAN_FFI_SUCCESS;
124 });
125 } else {
126 return ffi_guard_thunk(func_name, [&] { return func(*p); });
127 }
128}
129
130// TODO: C++20 introduces std::source_location which will allow to eliminate this
131// macro altogether. Instead, using code would just call the C++ function
132// that makes use of std::source_location like so:
133//
134// template<typename T, uint32_t M, typename F>
135// int botan_ffi_visit(botan_struct<T, M>* obj, F func,
136// const std::source_location sl = std::source_location::current())
137// {
138// // [...]
139// if constexpr(...)
140// {
141// return ffi_guard_thunk(sl.function_name(), [&] { return func(*p); })
142// }
143// // [...]
144// }
145#define BOTAN_FFI_VISIT(obj, lambda) botan_ffi_visit(obj, lambda, __func__)
146
147template <typename T, uint32_t M>
148int ffi_delete_object(botan_struct<T, M>* obj, const char* func_name) {
149 return ffi_guard_thunk(func_name, [=]() -> int {
150 // ignore delete of null objects
151 if(obj == nullptr) {
152 return BOTAN_FFI_SUCCESS;
153 }
154
155 if(obj->magic_ok() == false) {
157 }
158
159 delete obj;
160 return BOTAN_FFI_SUCCESS;
161 });
162}
163
164#define BOTAN_FFI_CHECKED_DELETE(o) ffi_delete_object(o, __func__)
165
166inline int invoke_view_callback(botan_view_bin_fn view, botan_view_ctx ctx, std::span<const uint8_t> buf) {
167 if(view == nullptr) {
169 }
170 return view(ctx, buf.data(), buf.size());
171}
172
173inline int invoke_view_callback(botan_view_str_fn view, botan_view_ctx ctx, std::string_view str) {
174 if(view == nullptr) {
176 }
177 return view(ctx, str.data(), str.size() + 1);
178}
179
181 uint8_t* out_ptr;
182 size_t* out_len;
183};
184
185int botan_view_bin_bounce_fn(botan_view_ctx ctx, const uint8_t* buf, size_t len);
186int botan_view_str_bounce_fn(botan_view_ctx ctx, const char* str, size_t len);
187
188template <typename Fn, typename... Args>
189int copy_view_bin(uint8_t out[], size_t* out_len, Fn fn, Args... args) {
191 ctx.out_ptr = out;
192 ctx.out_len = out_len;
193 return fn(args..., &ctx, botan_view_bin_bounce_fn);
194}
195
196template <typename Fn, typename... Args>
197int copy_view_str(uint8_t out[], size_t* out_len, Fn fn, Args... args) {
198 if(fn == nullptr) {
200 }
202 ctx.out_ptr = out;
203 ctx.out_len = out_len;
204 return fn(args..., &ctx, botan_view_str_bounce_fn);
205}
206
207inline int write_output(uint8_t out[], size_t* out_len, const uint8_t buf[], size_t buf_len) {
208 if(out_len == nullptr) {
210 }
211
212 const size_t avail = *out_len;
213 *out_len = buf_len;
214
215 if((avail >= buf_len) && (out != nullptr)) {
216 Botan::copy_mem(out, buf, buf_len);
217 return BOTAN_FFI_SUCCESS;
218 } else {
219 if(out != nullptr) {
220 Botan::clear_mem(out, avail);
221 }
223 }
224}
225
226inline int write_vec_output(uint8_t out[], size_t* out_len, std::span<const uint8_t> buf) {
227 return write_output(out, out_len, buf.data(), buf.size());
228}
229
230inline int write_str_output(uint8_t out[], size_t* out_len, std::string_view str) {
231 return write_output(out, out_len, Botan::cast_char_ptr_to_uint8(str.data()), str.size() + 1);
232}
233
234inline int write_str_output(char out[], size_t* out_len, std::string_view str) {
235 return write_str_output(Botan::cast_char_ptr_to_uint8(out), out_len, str);
236}
237
238inline int write_str_output(char out[], size_t* out_len, const std::vector<uint8_t>& str_vec) {
239 return write_output(Botan::cast_char_ptr_to_uint8(out), out_len, str_vec.data(), str_vec.size());
240}
241
242} // namespace Botan_FFI
243
244#endif
#define BOTAN_UNSTABLE_API
Definition api.h:32
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:26
FFI_Error(std::string_view what, int err_code)
Definition ffi_util.h:22
int error_code() const noexcept override
Definition ffi_util.h:24
int(* botan_view_bin_fn)(botan_view_ctx view_ctx, const uint8_t *data, size_t len)
Definition ffi.h:159
void * botan_view_ctx
Definition ffi.h:150
@ BOTAN_FFI_ERROR_EXCEPTION_THROWN
Definition ffi.h:124
@ BOTAN_FFI_ERROR_OUT_OF_MEMORY
Definition ffi.h:125
@ BOTAN_FFI_ERROR_INVALID_OBJECT
Definition ffi.h:137
@ BOTAN_FFI_ERROR_NULL_POINTER
Definition ffi.h:130
@ BOTAN_FFI_SUCCESS
Definition ffi.h:113
@ BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE
Definition ffi.h:121
int(* botan_view_str_fn)(botan_view_ctx view_ctx, const char *str, size_t len)
Definition ffi.h:168
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:166
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:111
int botan_view_str_bounce_fn(botan_view_ctx vctx, const char *str, size_t len)
Definition ffi.cpp:107
int botan_ffi_visit(botan_struct< T, M > *o, F func, const char *func_name)
Definition ffi_util.h:102
int copy_view_bin(uint8_t out[], size_t *out_len, Fn fn, Args... args)
Definition ffi_util.h:189
int write_str_output(uint8_t out[], size_t *out_len, std::string_view str)
Definition ffi_util.h:230
int ffi_delete_object(botan_struct< T, M > *obj, const char *func_name)
Definition ffi_util.h:148
T & safe_get(botan_struct< T, M > *p)
Definition ffi_util.h:67
int copy_view_str(uint8_t out[], size_t *out_len, Fn fn, Args... args)
Definition ffi_util.h:197
int ffi_guard_thunk(const char *func_name, T thunk)
Definition ffi_util.h:83
int write_vec_output(uint8_t out[], size_t *out_len, std::span< const uint8_t > buf)
Definition ffi_util.h:226
int write_output(uint8_t out[], size_t *out_len, const uint8_t buf[], size_t buf_len)
Definition ffi_util.h:207
ErrorType
Definition exceptn.h:20
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:149
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:123
const uint8_t * cast_char_ptr_to_uint8(const char *s)
Definition mem_ops.h:276
T * unsafe_get() const
Definition ffi_util.h:44
virtual ~botan_struct()
Definition ffi_util.h:37
botan_struct(std::unique_ptr< T > obj)
Definition ffi_util.h:35
bool magic_ok() const
Definition ffi_util.h:42