Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/mms/reporting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,13 @@ MmsReportBitField decode_bit_field(
result.raw = value.raw_value();
const auto unused = result.raw.front();
if (unused > 7U) throw MmsReportingFormatError("MMS BIT STRING unused-bit count is invalid.");
const auto bit_count = (result.raw.size() - 1U) * 8U - unused;
const auto payload_bits = (result.raw.size() - 1U) * 8U;
// With no payload byte, a nonzero unused count underflows unsigned
// bit_count and would iterate beyond the allocated BIT STRING buffer.
if (unused > payload_bits) {
throw MmsReportingFormatError("MMS BIT STRING unused bits exceed its payload.");
}
const auto bit_count = payload_bits - unused;
for (std::size_t bit = 0U; bit < bit_count; ++bit) {
const auto byte = result.raw[1U + bit / 8U];
if ((byte & static_cast<std::uint8_t>(0x80U >> (bit % 8U))) == 0U) continue;
Expand Down
15 changes: 15 additions & 0 deletions tests/test_reporting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,20 @@ void offline_monitor_enforces_frame_and_stream_limits() {
}), "Monitor frame limit was not enforced.");
}

void empty_bit_string_unused_bits_fail_closed() {
auto options = realistic_report();
options.items[1U].value = MmsDataValue::bit_string(1U, {});
require_throws([&options] {
static_cast<void>(MmsReportFrameMapper::map(options, {}));
}, "Empty OptFlds payload with unused bits was accepted.");

auto inclusion = realistic_report();
inclusion.items[8U].value = MmsDataValue::bit_string(7U, {});
require_throws([&inclusion] {
static_cast<void>(MmsReportFrameMapper::map(inclusion, {}));
}, "Empty inclusion payload with unused bits was accepted.");
}

void malformed_and_bound_paths_are_rejected() {
require_throws([] {
static_cast<void>(MmsInformationReportCodec::decode(std::array<std::uint8_t, 2>{0xA3U, 0x00U}));
Expand Down Expand Up @@ -453,6 +467,7 @@ int main() {
{"sequence continuity", sequence_tracker_detects_gap_duplicate_and_changes},
{"segmentation continuity", segmentation_continuity_is_tracked},
{"bounded monitor", offline_monitor_enforces_frame_and_stream_limits},
{"empty BIT STRING unused bits", empty_bit_string_unused_bits_fail_closed},
{"malformed and bounds", malformed_and_bound_paths_are_rejected},
};
std::size_t passed = 0U;
Expand Down
Loading