1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/base/data_type_terminated.h"
32 
33 #include "mongo/base/data_range.h"
34 #include "mongo/base/data_range_cursor.h"
35 #include "mongo/unittest/unittest.h"
36 #include "mongo/util/stringutils.h"
37 #include <string>
38 
39 namespace mongo {
40 namespace {
41 
42 // For testing purposes, a type that has a fixed load and store size, and some
43 // arbitrary serialization format of 'd' repeated N times.
44 template <size_t N>
45 struct Dummy {
46     static constexpr size_t extent = N;
47 };
48 }  // namespace
49 // Pop the anonymous namespace to specialize mongo::DataType::Handler.
50 // Template specialization is a drag.
51 
52 template <size_t N>
53 struct DataType::Handler<Dummy<N>> {
54     using handledType = Dummy<N>;
55     static constexpr size_t extent = handledType::extent;
56 
loadmongo::DataType::Handler57     static Status load(handledType* sdata,
58                        const char* ptr,
59                        size_t length,
60                        size_t* advanced,
61                        std::ptrdiff_t debug_offset) {
62         if (length < extent) {
63             return Status(ErrorCodes::Overflow, "too short for Dummy");
64         }
65         for (size_t i = 0; i < extent; ++i) {
66             if (*ptr++ != 'd') {
67                 return Status(ErrorCodes::Overflow, "load of invalid Dummy object");
68             }
69         }
70         *advanced = extent;
71         return Status::OK();
72     }
73 
storemongo::DataType::Handler74     static Status store(const handledType& sdata,
75                         char* ptr,
76                         size_t length,
77                         size_t* advanced,
78                         std::ptrdiff_t debug_offset) {
79         if (length < extent) {
80             return Status(ErrorCodes::Overflow, "insufficient space for Dummy");
81         }
82         for (size_t i = 0; i < extent; ++i) {
83             *ptr++ = 'd';
84         }
85         *advanced = extent;
86         return Status::OK();
87     }
88 
defaultConstructmongo::DataType::Handler89     static handledType defaultConstruct() {
90         return {};
91     }
92 };
93 
94 // Re-push the anonymous namespace.
95 namespace {
96 
97 /**
98  * Tests specifically for Terminated, unrelated to the DataRange
99  * or DataRangeCursor classes that call it.
100  */
101 
TEST(DataTypeTerminated,StringDataNormalStore)102 TEST(DataTypeTerminated, StringDataNormalStore) {
103     const StringData writes[] = {StringData("a"), StringData("bb"), StringData("ccc")};
104     std::string buf(100, '\xff');
105     char* const bufBegin = &*buf.begin();
106     char* ptr = bufBegin;
107     size_t avail = buf.size();
108     std::string expected;
109     for (const auto& w : writes) {
110         size_t adv;
111         ASSERT_OK(
112             DataType::store(Terminated<'\0', StringData>(w), ptr, avail, &adv, ptr - bufBegin));
113         ASSERT_EQ(adv, w.size() + 1);
114         ptr += adv;
115         avail -= adv;
116         expected += w.toString();
117         expected += '\0';
118     }
119     ASSERT_EQUALS(expected, buf.substr(0, buf.size() - avail));
120 }
121 
TEST(DataTypeTerminated,StringDataNormalLoad)122 TEST(DataTypeTerminated, StringDataNormalLoad) {
123     const StringData writes[] = {StringData("a"), StringData("bb"), StringData("ccc")};
124     std::string buf;
125     for (const auto& w : writes) {
126         buf += w.toString();
127         buf += '\0';
128     }
129     const char* const bufBegin = &*buf.begin();
130     const char* ptr = bufBegin;
131     size_t avail = buf.size();
132 
133     for (const auto& w : writes) {
134         size_t adv;
135         auto term = Terminated<'\0', StringData>{};
136         ASSERT_OK(DataType::load(&term, ptr, avail, &adv, ptr - bufBegin));
137         ASSERT_EQ(adv, term.value.size() + 1);
138         ptr += adv;
139         avail -= adv;
140         ASSERT_EQUALS(term.value, w);
141     }
142 }
143 
TEST(DataTypeTerminated,LoadStatusOkPropagation)144 TEST(DataTypeTerminated, LoadStatusOkPropagation) {
145     // Test that the nested type's .load complaints are surfaced.
146     const char buf[] = {'d', 'd', 'd', '\0'};
147     size_t advanced = 123;
148     auto x = Terminated<'\0', Dummy<3>>();
149     Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
150     ASSERT_OK(s);
151     ASSERT_EQUALS(advanced, 4u);  // OK must overwrite advanced
152 }
153 
TEST(DataTypeTerminated,StoreStatusOkAdvanced)154 TEST(DataTypeTerminated, StoreStatusOkAdvanced) {
155     // Test that an OK .store sets proper 'advanced'.
156     char buf[4] = {};
157     size_t advanced = 123;  // should be overwritten
158     Status s = DataType::store(Terminated<'\0', Dummy<3>>(), buf, sizeof(buf), &advanced, 0);
159     ASSERT_OK(s);
160     ASSERT_EQ(StringData(buf, 4), StringData(std::string{'d', 'd', 'd', '\0'}));
161     ASSERT_EQUALS(advanced, 4u);  // OK must overwrite advanced
162 }
163 
TEST(DataTypeTerminated,ErrorUnterminatedRead)164 TEST(DataTypeTerminated, ErrorUnterminatedRead) {
165     const char buf[] = {'h', 'e', 'l', 'l', 'o'};
166     size_t advanced = 123;
167     auto x = Terminated<'\0', StringData>();
168     Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
169     ASSERT_EQ(s.codeString(), "Overflow");
170     ASSERT_STRING_CONTAINS(s.reason(), "couldn't locate");
171     ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
172     ASSERT_EQUALS(advanced, 123u);  // fails must not overwrite advanced
173 }
174 
TEST(DataTypeTerminated,LoadStatusPropagation)175 TEST(DataTypeTerminated, LoadStatusPropagation) {
176     // Test that the nested type's .load complaints are surfaced.
177     const char buf[] = {'d', 'd', '\0'};
178     size_t advanced = 123;
179     auto x = Terminated<'\0', Dummy<3>>();
180     Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
181     ASSERT_EQ(s.codeString(), "Overflow");
182     ASSERT_STRING_CONTAINS(s.reason(), "too short for Dummy");
183     // ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
184     ASSERT_EQUALS(advanced, 123u);  // fails must not overwrite advanced
185 }
186 
TEST(DataTypeTerminated,StoreStatusPropagation)187 TEST(DataTypeTerminated, StoreStatusPropagation) {
188     // Test that the nested type's .store complaints are surfaced.
189     char in[2];  // Not big enough to hold a Dummy<3>.
190     size_t advanced = 123;
191     Status s = DataType::store(Terminated<'\0', Dummy<3>>(), in, sizeof(in), &advanced, 0);
192     ASSERT_EQ(s.codeString(), "Overflow");
193     ASSERT_STRING_CONTAINS(s.reason(), "insufficient space for Dummy");
194     ASSERT_EQUALS(advanced, 123u);  // fails must not overwrite advanced
195 }
196 
TEST(DataTypeTerminated,ErrorShortRead)197 TEST(DataTypeTerminated, ErrorShortRead) {
198     // The span before the '\0' is passed to Dummy<3>'s load.
199     // This consumes only the first 3 bytes, so Terminated complains
200     // about the unconsumed 'X'.
201     const char buf[] = {'d', 'd', 'd', 'X', '\0'};
202     size_t advanced = 123;
203     auto x = Terminated<'\0', Dummy<3>>();
204     Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
205     ASSERT_EQ(s.codeString(), "Overflow");
206     ASSERT_STRING_CONTAINS(s.reason(), "only read");
207     ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
208     ASSERT_EQUALS(advanced, 123u);  // fails must not overwrite advanced
209 }
210 
TEST(DataTypeTerminated,ErrorShortWrite)211 TEST(DataTypeTerminated, ErrorShortWrite) {
212     char in[3] = {};
213     auto x = Terminated<'\0', Dummy<3>>();
214     size_t advanced = 123;
215     Status s = DataType::store(x, in, sizeof(in), &advanced, 0);
216     ASSERT_EQ(s.codeString(), "Overflow");
217     ASSERT_STRING_CONTAINS(s.reason(), "couldn't write");
218     ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
219     ASSERT_EQUALS(advanced, 123u);  // fails must not overwrite advanced
220 }
221 
TEST(DataTypeTerminated,ThroughDataRangeCursor)222 TEST(DataTypeTerminated, ThroughDataRangeCursor) {
223     char buf[100];
224     const std::string parts[] = {"a", "bb", "ccc"};
225     std::string serialized;
226     for (const std::string& s : parts) {
227         serialized += s + '\0';
228     }
229     {
230         auto buf_writer = DataRangeCursor(buf, buf + sizeof(buf));
231         for (const std::string& s : parts) {
232             Terminated<'\0', ConstDataRange> tcdr(ConstDataRange(s.data(), s.data() + s.size()));
233             ASSERT_OK(buf_writer.writeAndAdvance(tcdr));
234         }
235         const auto written = std::string(static_cast<const char*>(buf), buf_writer.data());
236         ASSERT_EQUALS(written, serialized);
237     }
238     {
239         auto buf_source = ConstDataRangeCursor(buf, buf + sizeof(buf));
240         for (const std::string& s : parts) {
241             Terminated<'\0', ConstDataRange> tcdr;
242             ASSERT_OK(buf_source.readAndAdvance(&tcdr));
243             std::string read(tcdr.value.data(), tcdr.value.data() + tcdr.value.length());
244             ASSERT_EQUALS(s, read);
245         }
246     }
247 }
248 
249 }  // namespace
250 }  // namespace mongo
251