activity_incident_parser.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /** Validates incident framing and rejects targets unsafe for the Client's unbounded table read. */
  2. #include <algorithm>
  3. #include "../../encoding/bit_reader.h"
  4. #include "../../encoding/byte_order.h"
  5. #include "incident.h"
  6. namespace sunrise::middleware::bap::activity_message::incident {
  7. namespace {
  8. /** Retains byte fields even when their wire start is not byte-aligned. */
  9. [[nodiscard]] bool read_bytes(encoding::bits::Reader& reader,
  10. std::span<std::byte> output) noexcept {
  11. for (std::byte& value : output) {
  12. std::uint64_t field = 0;
  13. if (!reader.read(encoding::kBitsPerByte, field)) {
  14. return false;
  15. }
  16. value = static_cast<std::byte>(field);
  17. }
  18. return true;
  19. }
  20. /** @return True when one target index is safe to hand to the Client's table lookup. */
  21. [[nodiscard]] bool target_allowed(std::uint32_t target, Verdict& verdict) noexcept {
  22. if (target > kTargetMaximum) {
  23. verdict = Verdict::targetOutOfRange;
  24. return false;
  25. }
  26. if (std::find(kPoisonTargets.begin(), kPoisonTargets.end(), target) != kPoisonTargets.end()) {
  27. verdict = Verdict::targetPoisoned;
  28. return false;
  29. }
  30. return true;
  31. }
  32. } // namespace
  33. /** @return A short stable name for one verdict, for the log line. */
  34. const char* verdict_name(Verdict verdict) noexcept {
  35. switch (verdict) {
  36. case Verdict::accepted:
  37. return "accepted";
  38. case Verdict::truncated:
  39. return "truncated";
  40. case Verdict::targetOutOfRange:
  41. return "target_out_of_range";
  42. case Verdict::targetPoisoned:
  43. return "target_poisoned";
  44. case Verdict::tooManyTargets:
  45. return "too_many_targets";
  46. case Verdict::payloadTooLong:
  47. return "payload_too_long";
  48. case Verdict::selectorTooLong:
  49. return "selector_too_long";
  50. }
  51. return "unknown";
  52. }
  53. /** Validates one incident body from its first target to the end of its payload. */
  54. Verdict validate(std::span<const std::byte> payload, Incident& parsed) noexcept {
  55. parsed = {};
  56. encoding::bits::Reader reader(payload);
  57. std::uint64_t field = 0;
  58. if (!reader.read(kTargetWidth, field)) {
  59. return Verdict::truncated;
  60. }
  61. parsed.primaryTarget = static_cast<std::uint32_t>(field);
  62. Verdict verdict = Verdict::accepted;
  63. if (!target_allowed(parsed.primaryTarget, verdict)) {
  64. return verdict;
  65. }
  66. if (!reader.read(kExtraCountWidth, field)) {
  67. return Verdict::truncated;
  68. }
  69. parsed.extraTargetCount = static_cast<std::uint32_t>(field);
  70. if (parsed.extraTargetCount > kExtraTargetMaximum) {
  71. return Verdict::tooManyTargets;
  72. }
  73. for (std::uint32_t index = 0; index < parsed.extraTargetCount; ++index) {
  74. if (!reader.read(kTargetWidth, field)) {
  75. return Verdict::truncated;
  76. }
  77. parsed.extraTargets[index] = static_cast<std::uint32_t>(field);
  78. if (!target_allowed(parsed.extraTargets[index], verdict)) {
  79. return verdict;
  80. }
  81. }
  82. if (!reader.read(kSelectorPresenceWidth, field)) {
  83. return Verdict::truncated;
  84. }
  85. parsed.hasCompressedSelector = field != 0;
  86. if (parsed.hasCompressedSelector) {
  87. if (!reader.read(kSelectorLengthWidth, field)) {
  88. return Verdict::truncated;
  89. }
  90. parsed.selectorLength = static_cast<std::uint32_t>(field);
  91. if (parsed.selectorLength > kSelectorMaximum) {
  92. return Verdict::selectorTooLong;
  93. }
  94. if (!read_bytes(reader, std::span(parsed.selector).first(parsed.selectorLength))) {
  95. return Verdict::truncated;
  96. }
  97. }
  98. if (!reader.read(kOptionalPresenceWidth, field)) {
  99. return Verdict::truncated;
  100. }
  101. parsed.hasOptionalBlock = field != 0;
  102. if (parsed.hasOptionalBlock) {
  103. if (!reader.read(kOptionalWordWidth, field)) {
  104. return Verdict::truncated;
  105. }
  106. parsed.optionalWordA = static_cast<std::uint32_t>(field);
  107. if (!reader.read(kOptionalWordWidth, field)) {
  108. return Verdict::truncated;
  109. }
  110. parsed.optionalWordB = static_cast<std::uint32_t>(field);
  111. }
  112. if (!reader.read(kPayloadLengthWidth, field)) {
  113. return Verdict::truncated;
  114. }
  115. parsed.payloadLength = static_cast<std::uint32_t>(field);
  116. if (parsed.payloadLength > kPayloadMaximum) {
  117. return Verdict::payloadTooLong;
  118. }
  119. if (!read_bytes(reader, std::span(parsed.payload).first(parsed.payloadLength))) {
  120. return Verdict::truncated;
  121. }
  122. parsed.consumedBits = static_cast<std::uint32_t>(payload.size() * encoding::kBitsPerByte
  123. - reader.remaining_bits());
  124. return Verdict::accepted;
  125. }
  126. } // namespace sunrise::middleware::bap::activity_message::incident