1// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22#define CAPNP_PRIVATE
23#include "message.h"
24#include <kj/debug.h>
25#include "arena.h"
26#include "orphan.h"
27#include <stdlib.h>
28#include <errno.h>
29
30namespace capnp {
31
32namespace {
33
34class DummyCapTableReader: public _::CapTableReader {
35public:
36#if !CAPNP_LITE
37 kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override {
38 return nullptr;
39 }
40#endif
41};
42static KJ_CONSTEXPR(const) DummyCapTableReader dummyCapTableReader = DummyCapTableReader();
43
44} // namespace
45
46MessageReader::MessageReader(ReaderOptions options): options(options), allocatedArena(false) {}
47MessageReader::~MessageReader() noexcept(false) {
48 if (allocatedArena) {
49 arena()->~ReaderArena();
50 }
51}
52
53bool MessageReader::isCanonical() {
54 if (!allocatedArena) {
55 static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
56 "arenaSpace is too small to hold a ReaderArena. Please increase it. This will break "
57 "ABI compatibility.");
58 kj::ctor(*arena(), this);
59 allocatedArena = true;
60 }
61
62 _::SegmentReader *segment = arena()->tryGetSegment(_::SegmentId(0));
63
64 if (segment == NULL) {
65 // The message has no segments
66 return false;
67 }
68
69 if (arena()->tryGetSegment(_::SegmentId(1))) {
70 // The message has more than one segment
71 return false;
72 }
73
74 const word* readHead = segment->getStartPtr() + 1;
75 bool rootIsCanonical = _::PointerReader::getRoot(segment, nullptr,
76 segment->getStartPtr(),
77 this->getOptions().nestingLimit)
78 .isCanonical(&readHead);
79 bool allWordsConsumed = segment->getOffsetTo(readHead) == segment->getSize();
80 return rootIsCanonical && allWordsConsumed;
81}
82
83
84AnyPointer::Reader MessageReader::getRootInternal() {
85 if (!allocatedArena) {
86 static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
87 "arenaSpace is too small to hold a ReaderArena. Please increase it. This will break "
88 "ABI compatibility.");
89 kj::ctor(*arena(), this);
90 allocatedArena = true;
91 }
92
93 _::SegmentReader* segment = arena()->tryGetSegment(_::SegmentId(0));
94 KJ_REQUIRE(segment != nullptr &&
95 segment->checkObject(segment->getStartPtr(), ONE * WORDS),
96 "Message did not contain a root pointer.") {
97 return AnyPointer::Reader();
98 }
99
100 // const_cast here is safe because dummyCapTableReader has no state.
101 return AnyPointer::Reader(_::PointerReader::getRoot(
102 segment, const_cast<DummyCapTableReader*>(&dummyCapTableReader),
103 segment->getStartPtr(), options.nestingLimit));
104}
105
106// -------------------------------------------------------------------
107
108MessageBuilder::MessageBuilder(): allocatedArena(false) {}
109
110MessageBuilder::~MessageBuilder() noexcept(false) {
111 if (allocatedArena) {
112 kj::dtor(*arena());
113 }
114}
115
116MessageBuilder::MessageBuilder(kj::ArrayPtr<SegmentInit> segments)
117 : allocatedArena(false) {
118 kj::ctor(*arena(), this, segments);
119 allocatedArena = true;
120}
121
122_::SegmentBuilder* MessageBuilder::getRootSegment() {
123 if (allocatedArena) {
124 return arena()->getSegment(_::SegmentId(0));
125 } else {
126 static_assert(sizeof(_::BuilderArena) <= sizeof(arenaSpace),
127 "arenaSpace is too small to hold a BuilderArena. Please increase it.");
128 kj::ctor(*arena(), this);
129 allocatedArena = true;
130
131 auto allocation = arena()->allocate(POINTER_SIZE_IN_WORDS);
132
133 KJ_ASSERT(allocation.segment->getSegmentId() == _::SegmentId(0),
134 "First allocated word of new arena was not in segment ID 0.");
135 KJ_ASSERT(allocation.words == allocation.segment->getPtrUnchecked(ZERO * WORDS),
136 "First allocated word of new arena was not the first word in its segment.");
137 return allocation.segment;
138 }
139}
140
141AnyPointer::Builder MessageBuilder::getRootInternal() {
142 _::SegmentBuilder* rootSegment = getRootSegment();
143 return AnyPointer::Builder(_::PointerBuilder::getRoot(
144 rootSegment, arena()->getLocalCapTable(), rootSegment->getPtrUnchecked(ZERO * WORDS)));
145}
146
147kj::ArrayPtr<const kj::ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
148 if (allocatedArena) {
149 return arena()->getSegmentsForOutput();
150 } else {
151 return nullptr;
152 }
153}
154
155Orphanage MessageBuilder::getOrphanage() {
156 // We must ensure that the arena and root pointer have been allocated before the Orphanage
157 // can be used.
158 if (!allocatedArena) getRootSegment();
159
160 return Orphanage(arena(), arena()->getLocalCapTable());
161}
162
163bool MessageBuilder::isCanonical() {
164 _::SegmentReader *segment = getRootSegment();
165
166 if (segment == NULL) {
167 // The message has no segments
168 return false;
169 }
170
171 if (arena()->tryGetSegment(_::SegmentId(1))) {
172 // The message has more than one segment
173 return false;
174 }
175
176 const word* readHead = segment->getStartPtr() + 1;
177 return _::PointerReader::getRoot(segment, nullptr, segment->getStartPtr(), kj::maxValue)
178 .isCanonical(&readHead);
179}
180
181// =======================================================================================
182
183SegmentArrayMessageReader::SegmentArrayMessageReader(
184 kj::ArrayPtr<const kj::ArrayPtr<const word>> segments, ReaderOptions options)
185 : MessageReader(options), segments(segments) {}
186
187SegmentArrayMessageReader::~SegmentArrayMessageReader() noexcept(false) {}
188
189kj::ArrayPtr<const word> SegmentArrayMessageReader::getSegment(uint id) {
190 if (id < segments.size()) {
191 return segments[id];
192 } else {
193 return nullptr;
194 }
195}
196
197// -------------------------------------------------------------------
198
199MallocMessageBuilder::MallocMessageBuilder(
200 uint firstSegmentWords, AllocationStrategy allocationStrategy)
201 : nextSize(firstSegmentWords), allocationStrategy(allocationStrategy),
202 ownFirstSegment(true), returnedFirstSegment(false), firstSegment(nullptr) {}
203
204MallocMessageBuilder::MallocMessageBuilder(
205 kj::ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
206 : nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
207 ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) {
208 KJ_REQUIRE(firstSegment.size() > 0, "First segment size must be non-zero.");
209
210 // Checking just the first word should catch most cases of failing to zero the segment.
211 KJ_REQUIRE(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
212 "First segment must be zeroed.");
213}
214
215MallocMessageBuilder::~MallocMessageBuilder() noexcept(false) {
216 if (returnedFirstSegment) {
217 if (ownFirstSegment) {
218 free(firstSegment);
219 } else {
220 // Must zero first segment.
221 kj::ArrayPtr<const kj::ArrayPtr<const word>> segments = getSegmentsForOutput();
222 if (segments.size() > 0) {
223 KJ_ASSERT(segments[0].begin() == firstSegment,
224 "First segment in getSegmentsForOutput() is not the first segment allocated?");
225 memset(firstSegment, 0, segments[0].size() * sizeof(word));
226 }
227 }
228
229 for (void* ptr: moreSegments) {
230 free(ptr);
231 }
232 }
233}
234
235kj::ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
236 KJ_REQUIRE(bounded(minimumSize) * WORDS <= MAX_SEGMENT_WORDS,
237 "MallocMessageBuilder asked to allocate segment above maximum serializable size.");
238 KJ_ASSERT(bounded(nextSize) * WORDS <= MAX_SEGMENT_WORDS,
239 "MallocMessageBuilder nextSize out of bounds.");
240
241 if (!returnedFirstSegment && !ownFirstSegment) {
242 kj::ArrayPtr<word> result = kj::arrayPtr(reinterpret_cast<word*>(firstSegment), nextSize);
243 if (result.size() >= minimumSize) {
244 returnedFirstSegment = true;
245 return result;
246 }
247 // If the provided first segment wasn't big enough, we discard it and proceed to allocate
248 // our own. This never happens in practice since minimumSize is always 1 for the first
249 // segment.
250 ownFirstSegment = true;
251 }
252
253 uint size = kj::max(minimumSize, nextSize);
254
255 void* result = calloc(size, sizeof(word));
256 if (result == nullptr) {
257 KJ_FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
258 }
259
260 if (!returnedFirstSegment) {
261 firstSegment = result;
262 returnedFirstSegment = true;
263
264 // After the first segment, we want nextSize to equal the total size allocated so far.
265 if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) nextSize = size;
266 } else {
267 moreSegments.add(result);
268 if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) {
269 // set nextSize = min(nextSize+size, MAX_SEGMENT_WORDS)
270 // while protecting against possible overflow of (nextSize+size)
271 nextSize = (size <= unbound(MAX_SEGMENT_WORDS / WORDS) - nextSize)
272 ? nextSize + size : unbound(MAX_SEGMENT_WORDS / WORDS);
273 }
274 }
275
276 return kj::arrayPtr(reinterpret_cast<word*>(result), size);
277}
278
279// -------------------------------------------------------------------
280
281FlatMessageBuilder::FlatMessageBuilder(kj::ArrayPtr<word> array): array(array), allocated(false) {}
282FlatMessageBuilder::~FlatMessageBuilder() noexcept(false) {}
283
284void FlatMessageBuilder::requireFilled() {
285 KJ_REQUIRE(getSegmentsForOutput()[0].end() == array.end(),
286 "FlatMessageBuilder's buffer was too large.");
287}
288
289kj::ArrayPtr<word> FlatMessageBuilder::allocateSegment(uint minimumSize) {
290 KJ_REQUIRE(!allocated, "FlatMessageBuilder's buffer was not large enough.");
291 allocated = true;
292 return array;
293}
294
295} // namespace capnp
296