github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/executor/_include/flatbuffers/grpc.h (about) 1 /* 2 * Copyright 2014 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef FLATBUFFERS_GRPC_H_ 18 #define FLATBUFFERS_GRPC_H_ 19 20 // Helper functionality to glue FlatBuffers and GRPC. 21 22 #include "flatbuffers/flatbuffers.h" 23 #include "grpc/byte_buffer_reader.h" 24 #include "grpcpp/support/byte_buffer.h" 25 #include "grpcpp/support/slice.h" 26 27 namespace flatbuffers { 28 namespace grpc { 29 30 // Message is a typed wrapper around a buffer that manages the underlying 31 // `grpc_slice` and also provides flatbuffers-specific helpers such as `Verify` 32 // and `GetRoot`. Since it is backed by a `grpc_slice`, the underlying buffer 33 // is refcounted and ownership is be managed automatically. 34 template<class T> class Message { 35 public: 36 Message() {} 37 38 Message(::grpc::Slice slice) : slice_(slice) {} 39 40 Message &operator=(const Message &other) = delete; 41 42 Message(Message &&other) = default; 43 44 Message(const Message &other) = delete; 45 46 Message &operator=(Message &&other) = default; 47 48 const uint8_t *mutable_data() const { return slice_.begin(); } 49 50 const uint8_t *data() const { return slice_.begin(); } 51 52 size_t size() const { return slice_.size(); } 53 54 bool Verify() const { 55 Verifier verifier(data(), size()); 56 return verifier.VerifyBuffer<T>(nullptr); 57 } 58 59 T *GetMutableRoot() { return flatbuffers::GetMutableRoot<T>(mutable_data()); } 60 61 const T *GetRoot() const { return flatbuffers::GetRoot<T>(data()); } 62 63 // This is only intended for serializer use, or if you know what you're doing 64 const ::grpc::Slice &BorrowSlice() const { return slice_; } 65 66 private: 67 ::grpc::Slice slice_; 68 }; 69 70 class MessageBuilder; 71 72 // SliceAllocator is a gRPC-specific allocator that uses the `grpc_slice` 73 // refcounted slices to manage memory ownership. This makes it easy and 74 // efficient to transfer buffers to gRPC. 75 class SliceAllocator : public Allocator { 76 public: 77 SliceAllocator() {} 78 79 SliceAllocator(const SliceAllocator &other) = delete; 80 SliceAllocator &operator=(const SliceAllocator &other) = delete; 81 82 SliceAllocator(SliceAllocator &&other) { 83 // default-construct and swap idiom 84 swap(other); 85 } 86 87 SliceAllocator &operator=(SliceAllocator &&other) { 88 // move-construct and swap idiom 89 SliceAllocator temp(std::move(other)); 90 swap(temp); 91 return *this; 92 } 93 94 void swap(SliceAllocator &other) { 95 using std::swap; 96 swap(slice_, other.slice_); 97 } 98 99 virtual ~SliceAllocator() {} 100 101 virtual uint8_t *allocate(size_t size) override { 102 FLATBUFFERS_ASSERT(slice_.size() == 0); 103 slice_ = ::grpc::Slice(size); 104 return const_cast<uint8_t *>(slice_.begin()); 105 } 106 107 virtual void deallocate(uint8_t *p, size_t size) override { 108 FLATBUFFERS_ASSERT(p == slice_.begin()); 109 FLATBUFFERS_ASSERT(size == slice_.size()); 110 slice_ = ::grpc::Slice(); 111 } 112 113 virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size, 114 size_t new_size, size_t in_use_back, 115 size_t in_use_front) override { 116 FLATBUFFERS_ASSERT(old_p == slice_.begin()); 117 FLATBUFFERS_ASSERT(old_size == slice_.size()); 118 FLATBUFFERS_ASSERT(new_size > old_size); 119 ::grpc::Slice old_slice = slice_; 120 ::grpc::Slice new_slice = ::grpc::Slice(new_size); 121 uint8_t *new_p = const_cast<uint8_t *>(new_slice.begin()); 122 memcpy_downward(old_p, old_size, new_p, new_size, in_use_back, 123 in_use_front); 124 slice_ = new_slice; 125 return new_p; 126 } 127 128 private: 129 ::grpc::Slice &get_slice(uint8_t *p, size_t size) { 130 FLATBUFFERS_ASSERT(p == slice_.begin()); 131 FLATBUFFERS_ASSERT(size == slice_.size()); 132 return slice_; 133 } 134 135 ::grpc::Slice slice_; 136 137 friend class MessageBuilder; 138 }; 139 140 // SliceAllocatorMember is a hack to ensure that the MessageBuilder's 141 // slice_allocator_ member is constructed before the FlatBufferBuilder, since 142 // the allocator is used in the FlatBufferBuilder ctor. 143 namespace detail { 144 struct SliceAllocatorMember { 145 SliceAllocator slice_allocator_; 146 }; 147 } // namespace detail 148 149 // MessageBuilder is a gRPC-specific FlatBufferBuilder that uses SliceAllocator 150 // to allocate gRPC buffers. 151 class MessageBuilder : private detail::SliceAllocatorMember, 152 public FlatBufferBuilder { 153 public: 154 explicit MessageBuilder(uoffset_t initial_size = 1024) 155 : FlatBufferBuilder(initial_size, &slice_allocator_, false) {} 156 157 MessageBuilder(const MessageBuilder &other) = delete; 158 MessageBuilder &operator=(const MessageBuilder &other) = delete; 159 160 MessageBuilder(MessageBuilder &&other) 161 : FlatBufferBuilder(1024, &slice_allocator_, false) { 162 // Default construct and swap idiom. 163 Swap(other); 164 } 165 166 /// Create a MessageBuilder from a FlatBufferBuilder. 167 explicit MessageBuilder(FlatBufferBuilder &&src, 168 void (*dealloc)(void *, 169 size_t) = &DefaultAllocator::dealloc) 170 : FlatBufferBuilder(1024, &slice_allocator_, false) { 171 src.Swap(*this); 172 src.SwapBufAllocator(*this); 173 if (buf_.capacity()) { 174 uint8_t *buf = buf_.scratch_data(); // pointer to memory 175 size_t capacity = buf_.capacity(); // size of memory 176 slice_allocator_.slice_ = ::grpc::Slice(buf, capacity, dealloc); 177 } else { 178 slice_allocator_.slice_ = ::grpc::Slice(); 179 } 180 } 181 182 /// Move-assign a FlatBufferBuilder to a MessageBuilder. 183 /// Only FlatBufferBuilder with default allocator (basically, nullptr) is 184 /// supported. 185 MessageBuilder &operator=(FlatBufferBuilder &&src) { 186 // Move construct a temporary and swap 187 MessageBuilder temp(std::move(src)); 188 Swap(temp); 189 return *this; 190 } 191 192 MessageBuilder &operator=(MessageBuilder &&other) { 193 // Move construct a temporary and swap 194 MessageBuilder temp(std::move(other)); 195 Swap(temp); 196 return *this; 197 } 198 199 void Swap(MessageBuilder &other) { 200 slice_allocator_.swap(other.slice_allocator_); 201 FlatBufferBuilder::Swap(other); 202 // After swapping the FlatBufferBuilder, we swap back the allocator, which 203 // restores the original allocator back in place. This is necessary because 204 // MessageBuilder's allocator is its own member (SliceAllocatorMember). The 205 // allocator passed to FlatBufferBuilder::vector_downward must point to this 206 // member. 207 buf_.swap_allocator(other.buf_); 208 } 209 210 // Releases the ownership of the buffer pointer. 211 // Returns the size, offset, and the original grpc_slice that 212 // allocated the buffer. Also see grpc_slice_unref(). 213 uint8_t *ReleaseRaw(size_t &size, size_t &offset, ::grpc::Slice &slice) { 214 uint8_t *buf = FlatBufferBuilder::ReleaseRaw(size, offset); 215 slice = slice_allocator_.slice_; 216 slice_allocator_.slice_ = ::grpc::Slice(); 217 return buf; 218 } 219 220 ~MessageBuilder() {} 221 222 // GetMessage extracts the subslice of the buffer corresponding to the 223 // flatbuffers-encoded region and wraps it in a `Message<T>` to handle buffer 224 // ownership. 225 template<class T> Message<T> GetMessage() { 226 auto buf_data = buf_.scratch_data(); // pointer to memory 227 auto buf_size = buf_.capacity(); // size of memory 228 auto msg_data = buf_.data(); // pointer to msg 229 auto msg_size = buf_.size(); // size of msg 230 // Do some sanity checks on data/size 231 FLATBUFFERS_ASSERT(msg_data); 232 FLATBUFFERS_ASSERT(msg_size); 233 FLATBUFFERS_ASSERT(msg_data >= buf_data); 234 FLATBUFFERS_ASSERT(msg_data + msg_size <= buf_data + buf_size); 235 // Calculate offsets from the buffer start 236 auto begin = msg_data - buf_data; 237 auto end = begin + msg_size; 238 // Get the slice we are working with (no refcount change) 239 ::grpc::Slice slice = slice_allocator_.get_slice(buf_data, buf_size); 240 // Extract a subslice of the existing slice (increment refcount) 241 ::grpc::Slice subslice = slice.sub(begin, end); 242 // Wrap the subslice in a `Message<T>`, but don't increment refcount 243 Message<T> msg(subslice); 244 return msg; 245 } 246 247 template<class T> Message<T> ReleaseMessage() { 248 Message<T> msg = GetMessage<T>(); 249 Reset(); 250 return msg; 251 } 252 253 private: 254 // SliceAllocator slice_allocator_; // part of SliceAllocatorMember 255 }; 256 257 } // namespace grpc 258 } // namespace flatbuffers 259 260 namespace grpc { 261 262 template<class T> class SerializationTraits<flatbuffers::grpc::Message<T>> { 263 public: 264 static grpc::Status Serialize(const flatbuffers::grpc::Message<T> &msg, 265 ByteBuffer *buffer, bool *own_buffer) { 266 // Package the single slice into a `ByteBuffer`, 267 // incrementing the refcount in the process. 268 *buffer = ByteBuffer(&msg.BorrowSlice(), 1); 269 *own_buffer = true; 270 return grpc::Status::OK; 271 } 272 273 // Deserialize by pulling the 274 static grpc::Status Deserialize(ByteBuffer *buf, 275 flatbuffers::grpc::Message<T> *msg) { 276 Slice slice; 277 if (!buf->TrySingleSlice(&slice).ok()) { 278 if (!buf->DumpToSingleSlice(&slice).ok()) { 279 buf->Clear(); 280 return ::grpc::Status(::grpc::StatusCode::INTERNAL, "No payload"); 281 } 282 } 283 *msg = flatbuffers::grpc::Message<T>(slice); 284 buf->Clear(); 285 #if FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION 286 return ::grpc::Status::OK; 287 #else 288 if (msg->Verify()) { 289 return ::grpc::Status::OK; 290 } else { 291 return ::grpc::Status(::grpc::StatusCode::INTERNAL, 292 "Message verification failed"); 293 } 294 #endif 295 } 296 }; 297 298 } // namespace grpc 299 300 #endif // FLATBUFFERS_GRPC_H_