1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <object/message_packet.h>
8 
9 #include <err.h>
10 #include <fbl/algorithm.h>
11 #include <new>
12 #include <stdint.h>
13 #include <string.h>
14 
15 // MessagePackets have special allocation requirements because they can contain a variable number of
16 // handles and a variable size payload.
17 //
18 // To reduce heap fragmentation, MessagePackets are stored in a lists of fixed size buffers
19 // (BufferChains) rather than a contiguous blocks of memory.  These lists and buffers are allocated
20 // from the PMM.
21 //
22 // The first buffer in a MessagePacket's BufferChain contains the MessagePacket object, followed by
23 // its handles (if any), and finally its payload data (if any).
24 
25 // The MessagePacket object, its handles and zx_txid_t must all fit in the first buffer.
26 static constexpr size_t kContiguousBytes =
27     sizeof(MessagePacket) + (kMaxMessageHandles * sizeof(Handle*)) + sizeof(zx_txid_t);
28 static_assert(kContiguousBytes <= BufferChain::kContig, "");
29 
30 // Handles are stored just after the MessagePacket.
31 static constexpr uint32_t kHandlesOffset = static_cast<uint32_t>(sizeof(MessagePacket));
32 
33 // PayloadOffset returns the offset of the data payload from the start of the first buffer.
PayloadOffset(uint32_t num_handles)34 static inline uint32_t PayloadOffset(uint32_t num_handles) {
35     // The payload comes after the handles.
36     return kHandlesOffset + num_handles * static_cast<uint32_t>(sizeof(Handle*));
37 }
38 
39 // Creates a MessagePacket in |msg| sufficient to hold |data_size| bytes and |num_handles|.
40 //
41 // Note: This method does not write the payload into the MessagePacket.
42 //
43 // Returns ZX_OK on success.
44 //
45 // static
CreateCommon(uint32_t data_size,uint32_t num_handles,MessagePacketPtr * msg)46 inline zx_status_t MessagePacket::CreateCommon(uint32_t data_size, uint32_t num_handles,
47                                                MessagePacketPtr* msg) {
48     if (unlikely(data_size > kMaxMessageSize || num_handles > kMaxMessageHandles)) {
49         return ZX_ERR_OUT_OF_RANGE;
50     }
51 
52     const uint32_t payload_offset = PayloadOffset(num_handles);
53 
54     // MessagePackets lives *inside* a list of buffers.  The first buffer holds the MessagePacket
55     // object, followed by its handles (if any), and finally the payload data.
56     BufferChain* chain = BufferChain::Alloc(payload_offset + data_size);
57     if (unlikely(!chain)) {
58         return ZX_ERR_NO_MEMORY;
59     }
60     DEBUG_ASSERT(!chain->buffers()->is_empty());
61 
62     char* const data = chain->buffers()->front().data();
63     Handle** const handles = reinterpret_cast<Handle**>(data + kHandlesOffset);
64 
65     // Construct the MessagePacket into the first buffer.
66     MessagePacket* const packet = reinterpret_cast<MessagePacket*>(data);
67     static_assert(kMaxMessageHandles <= UINT16_MAX, "");
68     msg->reset(new (packet) MessagePacket(chain, data_size, payload_offset,
69                                           static_cast<uint16_t>(num_handles), handles));
70     // The MessagePacket now owns the BufferChain and msg owns the MessagePacket.
71 
72     return ZX_OK;
73 }
74 
75 // static
Create(user_in_ptr<const void> data,uint32_t data_size,uint32_t num_handles,MessagePacketPtr * msg)76 zx_status_t MessagePacket::Create(user_in_ptr<const void> data, uint32_t data_size,
77                                   uint32_t num_handles, MessagePacketPtr* msg) {
78     MessagePacketPtr new_msg;
79     zx_status_t status = CreateCommon(data_size, num_handles, &new_msg);
80     if (unlikely(status != ZX_OK)) {
81         return status;
82     }
83     status = new_msg->buffer_chain_->CopyIn(data, PayloadOffset(num_handles), data_size);
84     if (unlikely(status != ZX_OK)) {
85         return status;
86     }
87     *msg = ktl::move(new_msg);
88     return ZX_OK;
89 }
90 
91 // static
Create(const void * data,uint32_t data_size,uint32_t num_handles,MessagePacketPtr * msg)92 zx_status_t MessagePacket::Create(const void* data, uint32_t data_size, uint32_t num_handles,
93                                   MessagePacketPtr* msg) {
94     MessagePacketPtr new_msg;
95     zx_status_t status = CreateCommon(data_size, num_handles, &new_msg);
96     if (unlikely(status != ZX_OK)) {
97         return status;
98     }
99     status = new_msg->buffer_chain_->CopyInKernel(data, PayloadOffset(num_handles), data_size);
100     if (unlikely(status != ZX_OK)) {
101         return status;
102     }
103     *msg = ktl::move(new_msg);
104     return ZX_OK;
105 }
106 
recycle(MessagePacket * packet)107 void MessagePacket::recycle(MessagePacket* packet) {
108     // Grab the buffer chain for this packet
109     BufferChain* chain = packet->buffer_chain_;
110 
111     // Manually destruct the packet.  Do not delete it; its memory did not come
112     // from new, it is contained as part of the buffer chain.
113     packet->~MessagePacket();
114 
115     // Now return the buffer chain to where it came from.
116     BufferChain::Free(chain);
117 }
118