|
24 | 24 | #include <string> |
25 | 25 | #include <exception> |
26 | 26 | #include <stdexcept> |
| 27 | +#include <vector> |
27 | 28 |
|
28 | 29 | #include "hll.hpp" |
29 | 30 | #include "CouponList.hpp" |
@@ -180,4 +181,71 @@ TEST_CASE("coupon list: check corrupt stream data", "[coupon_list]") { |
180 | 181 | REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument); |
181 | 182 | } |
182 | 183 |
|
| 184 | +TEST_CASE("coupon list: rejects malformed coupon count in bytes", "[coupon_list]") { |
| 185 | + // Reject declared LIST counts at or beyond the fixed LIST capacity. |
| 186 | + uint8_t lgK = 8; |
| 187 | + hll_sketch sk1(lgK); |
| 188 | + sk1.update(1); |
| 189 | + sk1.update(2); |
| 190 | + const auto validBytes = sk1.serialize_compact(); |
| 191 | + |
| 192 | + const uint8_t listCapacity = static_cast<uint8_t>(1u << hll_constants::LG_INIT_LIST_SIZE); |
| 193 | + const uint8_t malformedCounts[] = {listCapacity, static_cast<uint8_t>(listCapacity + 2)}; |
| 194 | + for (const uint8_t malformedCount: malformedCounts) { |
| 195 | + auto sketchBytes = validBytes; |
| 196 | + // Keep the input long enough to validate the declared LIST count. |
| 197 | + const size_t requiredLen = hll_constants::LIST_INT_ARR_START |
| 198 | + + malformedCount * sizeof(uint32_t); |
| 199 | + if (sketchBytes.size() < requiredLen) { |
| 200 | + sketchBytes.resize(requiredLen, 0); |
| 201 | + } |
| 202 | + sketchBytes[hll_constants::LIST_COUNT_BYTE] = malformedCount; |
| 203 | + |
| 204 | + REQUIRE_THROWS_AS( |
| 205 | + hll_sketch::deserialize(sketchBytes.data(), sketchBytes.size()), |
| 206 | + std::invalid_argument); |
| 207 | + REQUIRE_THROWS_AS( |
| 208 | + CouponList<std::allocator<uint8_t>>::newList( |
| 209 | + sketchBytes.data(), sketchBytes.size(), std::allocator<uint8_t>()), |
| 210 | + std::invalid_argument); |
| 211 | + } |
| 212 | +} |
| 213 | + |
| 214 | +TEST_CASE("coupon list: rejects malformed coupon count in stream", "[coupon_list]") { |
| 215 | + uint8_t lgK = 8; |
| 216 | + hll_sketch sk1(lgK); |
| 217 | + sk1.update(1); |
| 218 | + sk1.update(2); |
| 219 | + |
| 220 | + const uint8_t listCapacity = static_cast<uint8_t>(1u << hll_constants::LG_INIT_LIST_SIZE); |
| 221 | + const uint8_t malformedCounts[] = {listCapacity, static_cast<uint8_t>(listCapacity + 2)}; |
| 222 | + for (const uint8_t malformedCount: malformedCounts) { |
| 223 | + std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary); |
| 224 | + sk1.serialize_compact(ss); |
| 225 | + |
| 226 | + const size_t requiredLen = hll_constants::LIST_INT_ARR_START |
| 227 | + + malformedCount * sizeof(uint32_t); |
| 228 | + // Keep the stream long enough to validate the declared LIST count. |
| 229 | + ss.seekp(0, std::ios::end); |
| 230 | + const auto endPos = static_cast<size_t>(ss.tellp()); |
| 231 | + if (endPos < requiredLen) { |
| 232 | + std::vector<char> pad(requiredLen - endPos, 0); |
| 233 | + ss.write(pad.data(), pad.size()); |
| 234 | + } |
| 235 | + |
| 236 | + ss.seekp(hll_constants::LIST_COUNT_BYTE); |
| 237 | + ss.put(static_cast<char>(malformedCount)); |
| 238 | + |
| 239 | + ss.clear(); |
| 240 | + ss.seekg(0); |
| 241 | + REQUIRE_THROWS_AS(hll_sketch::deserialize(ss), std::invalid_argument); |
| 242 | + |
| 243 | + ss.clear(); |
| 244 | + ss.seekg(0); |
| 245 | + REQUIRE_THROWS_AS( |
| 246 | + CouponList<std::allocator<uint8_t>>::newList(ss, std::allocator<uint8_t>()), |
| 247 | + std::invalid_argument); |
| 248 | + } |
| 249 | +} |
| 250 | + |
183 | 251 | } /* namespace datasketches */ |
0 commit comments