Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/network/thrift_proxy/buffer_helper.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/network/thrift_proxy/buffer_helper.h"
2
3
#include "source/common/common/byte_order.h"
4
#include "source/common/common/safe_memcpy.h"
5
6
namespace Envoy {
7
namespace Extensions {
8
namespace NetworkFilters {
9
namespace ThriftProxy {
10
11
0
double BufferHelper::drainBEDouble(Buffer::Instance& buffer) {
12
0
  static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) != sizeof(uint64_t)");
13
0
  static_assert(std::numeric_limits<double>::is_iec559, "non-IEC559 (IEEE 754) double");
14
15
  // Implementation based on:
16
  // https://github.com/CppCon/CppCon2017/raw/master/Presentations/Type%20Punning%20In%20C%2B%2B17%20-%20Avoiding%20Pun-defined%20Behavior/Type%20Punning%20In%20C%2B%2B17%20-%20Avoiding%20Pun-defined%20Behavior%20-%20Scott%20Schurr%20-%20CppCon%202017.pdf
17
  // The short version:
18
  // 1. Reinterpreting uint64_t* to double* falls astray of strict aliasing rules.
19
  // 2. Using union {uint64_t i; double d;} is undefined behavior in C++ (but not C11).
20
  // 3. Using memcpy may be undefined, but probably reliable, and can be optimized to the
21
  //    same instructions as 1 and 2.
22
  // 4. Implementation of last resort is to manually copy from i to d via unsigned char*.
23
0
  uint64_t i = buffer.drainBEInt<uint64_t>();
24
0
  double d;
25
0
  safeMemcpy(&d, &i);
26
0
  return d;
27
0
}
28
29
// Thrift's var int encoding is described in
30
// https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
31
0
uint64_t BufferHelper::peekVarInt(Buffer::Instance& buffer, uint64_t offset, int& size) {
32
  // Need at least 1 byte for a var int.
33
0
  if (buffer.length() <= offset) {
34
0
    throw EnvoyException("buffer underflow");
35
0
  }
36
37
  // Need at most 10 bytes for a 64-bit var int.
38
0
  const uint64_t last = std::min(buffer.length() - offset, static_cast<uint64_t>(10));
39
40
0
  uint8_t shift = 0;
41
0
  uint64_t result = 0;
42
0
  for (uint64_t i = 0; i < last; i++) {
43
0
    uint8_t b = buffer.peekInt<uint8_t>(offset + i);
44
45
    // Note: the compact protocol spec says these variable-length ints are encoded as big-endian,
46
    // but the Apache C++, Java, and Python implementations read and write them little-endian.
47
0
    result |= static_cast<uint64_t>(b & 0x7f) << shift;
48
0
    shift += 7;
49
50
0
    if ((b & 0x80) == 0) {
51
      // End of encoded int.
52
0
      size = i + 1;
53
0
      return result;
54
0
    }
55
0
  }
56
57
  // Ran out of bytes (or it's invalid).
58
0
  size = -last;
59
0
  return 0;
60
0
}
61
62
0
int32_t BufferHelper::peekVarIntI32(Buffer::Instance& buffer, uint64_t offset, int& size) {
63
0
  int underlying_size;
64
0
  uint64_t v64 = peekVarInt(buffer, offset, underlying_size);
65
66
0
  if (underlying_size <= -5 || underlying_size > 5) {
67
0
    throw EnvoyException("invalid compact protocol varint i32");
68
0
  }
69
70
0
  size = underlying_size;
71
0
  if (size < 0) {
72
0
    return 0;
73
0
  }
74
75
0
  return static_cast<int32_t>(v64);
76
0
}
77
78
// Thrift's zig-zag int encoding is described in
79
// https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
80
0
int64_t BufferHelper::peekZigZagI64(Buffer::Instance& buffer, uint64_t offset, int& size) {
81
0
  int underlying_size;
82
0
  uint64_t zz64 = peekVarInt(buffer, offset, underlying_size);
83
84
0
  if (underlying_size <= -10 || underlying_size > 10) {
85
    // Max size is 10, so this must be an invalid encoding.
86
0
    throw EnvoyException("invalid compact protocol zig-zag i64");
87
0
  }
88
89
0
  size = underlying_size;
90
0
  if (size < 0) {
91
    // Still an underflow, but it might become valid with additional data.
92
0
    return 0;
93
0
  }
94
95
0
  return (zz64 >> 1) ^ static_cast<uint64_t>(-static_cast<int64_t>(zz64 & 1));
96
0
}
97
98
// Thrift's zig-zag int encoding is described in
99
// https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
100
0
int32_t BufferHelper::peekZigZagI32(Buffer::Instance& buffer, uint64_t offset, int& size) {
101
0
  int underlying_size;
102
0
  uint64_t zz64 = peekVarInt(buffer, offset, underlying_size);
103
104
0
  if (underlying_size <= -5 || underlying_size > 5) {
105
    // Max size is 5, so this must be an invalid encoding.
106
0
    throw EnvoyException("invalid compact protocol zig-zag i32");
107
0
  }
108
109
0
  size = underlying_size;
110
0
  if (size < 0) {
111
    // Still an underflow, but it might become valid with additional data.
112
0
    return 0;
113
0
  }
114
115
0
  uint32_t zz32 = static_cast<uint32_t>(zz64);
116
0
  return (zz32 >> 1) ^ static_cast<uint32_t>(-static_cast<int32_t>(zz32 & 1));
117
0
}
118
119
0
void BufferHelper::writeBEDouble(Buffer::Instance& buffer, double value) {
120
0
  static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) != sizeof(uint64_t)");
121
0
  static_assert(std::numeric_limits<double>::is_iec559, "non-IEC559 (IEEE 754) double");
122
123
  // See drainDouble for implementation details.
124
0
  uint64_t i;
125
0
  safeMemcpy(&i, &value);
126
0
  buffer.writeBEInt<uint64_t>(i);
127
0
}
128
129
// Thrift's var int encoding is described in
130
// https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
131
0
void BufferHelper::writeVarIntI32(Buffer::Instance& buffer, int32_t value) {
132
0
  uint8_t bytes[5];
133
0
  uint32_t v = static_cast<uint32_t>(value);
134
0
  int pos = 0;
135
0
  while (pos < 5) {
136
0
    if ((v & ~0x7F) == 0) {
137
0
      bytes[pos++] = static_cast<uint8_t>(v);
138
0
      break;
139
0
    }
140
141
0
    bytes[pos++] = static_cast<uint8_t>(v & 0x7F) | 0x80;
142
0
    v >>= 7;
143
0
  }
144
0
  ASSERT(v < 0x80);
145
0
  ASSERT(pos <= 5);
146
147
0
  buffer.add(bytes, pos);
148
0
}
149
150
0
void BufferHelper::writeVarIntI64(Buffer::Instance& buffer, int64_t value) {
151
0
  uint8_t bytes[10];
152
0
  uint64_t v = static_cast<uint64_t>(value);
153
0
  int pos = 0;
154
0
  while (pos < 10) {
155
0
    if ((v & ~0x7F) == 0) {
156
0
      bytes[pos++] = static_cast<uint8_t>(v);
157
0
      break;
158
0
    }
159
160
0
    bytes[pos++] = static_cast<uint8_t>(v & 0x7F) | 0x80;
161
0
    v >>= 7;
162
0
  }
163
164
0
  ASSERT(v < 0x80);
165
0
  ASSERT(pos <= 10);
166
167
0
  buffer.add(bytes, pos);
168
0
}
169
170
0
void BufferHelper::writeZigZagI32(Buffer::Instance& buffer, int32_t value) {
171
0
  uint32_t zz32 = (static_cast<uint32_t>(value) << 1) ^ (value >> 31);
172
0
  writeVarIntI32(buffer, zz32);
173
0
}
174
175
0
void BufferHelper::writeZigZagI64(Buffer::Instance& buffer, int64_t value) {
176
0
  uint64_t zz64 = (static_cast<uint64_t>(value) << 1) ^ (value >> 63);
177
0
  writeVarIntI64(buffer, zz64);
178
0
}
179
180
} // namespace ThriftProxy
181
} // namespace NetworkFilters
182
} // namespace Extensions
183
} // namespace Envoy