1414namespace Envoy {
1515namespace Http {
1616
17+ namespace {
18+ constexpr size_t MinDynamicCapacity{32 };
19+ // This includes the NULL (StringUtil::itoa technically only needs 21).
20+ constexpr size_t MaxIntegerLength{32 };
21+
22+ void validateCapacity (size_t new_capacity) {
23+ // If the resizing will cause buffer overflow due to hitting uint32_t::max, an OOM is likely
24+ // imminent. Fast-fail rather than allow a buffer overflow attack (issue #1421)
25+ RELEASE_ASSERT (new_capacity <= std::numeric_limits<uint32_t >::max (), " " );
26+ ASSERT (new_capacity >= MinDynamicCapacity);
27+ }
28+
29+ } // namespace
30+
1731HeaderString::HeaderString () : type_(Type::Inline) {
1832 buffer_.dynamic_ = inline_buffer_;
1933 clear ();
34+ static_assert (sizeof (inline_buffer_) >= MaxIntegerLength, " " );
35+ static_assert (MinDynamicCapacity >= MaxIntegerLength, " " );
2036}
2137
2238HeaderString::HeaderString (const LowerCaseString& ref_value) : type_(Type::Reference) {
@@ -70,7 +86,9 @@ void HeaderString::append(const char* data, uint32_t size) {
7086 // Rather than be too clever and optimize this uncommon case, we dynamically
7187 // allocate and copy.
7288 type_ = Type::Dynamic;
73- dynamic_capacity_ = (string_length_ + size) * 2 ;
89+ dynamic_capacity_ =
90+ std::max (MinDynamicCapacity, static_cast <size_t >((string_length_ + size) * 2 ));
91+ validateCapacity (dynamic_capacity_);
7492 char * buf = static_cast <char *>(malloc (dynamic_capacity_));
7593 RELEASE_ASSERT (buf != nullptr , " " );
7694 memcpy (buf, buffer_.ref_ , string_length_);
@@ -90,19 +108,18 @@ void HeaderString::append(const char* data, uint32_t size) {
90108 case Type::Dynamic: {
91109 // We can get here either because we didn't fit in inline or we are already dynamic.
92110 if (type_ == Type::Inline) {
93- const uint64_t new_capacity = (static_cast <uint64_t >(string_length_) + size) * 2 ;
94- // If the resizing will cause buffer overflow due to hitting uint32_t::max, an OOM is likely
95- // imminent. Fast-fail rather than allow a buffer overflow attack (issue #1421)
96- RELEASE_ASSERT (new_capacity <= std::numeric_limits<uint32_t >::max (), " " );
111+ const size_t new_capacity = (string_length_ + size) * 2 ;
112+ validateCapacity (new_capacity);
97113 buffer_.dynamic_ = static_cast <char *>(malloc (new_capacity));
98- memcpy (buffer_.dynamic_ , inline_buffer_, string_length_);
99114 RELEASE_ASSERT (buffer_.dynamic_ != nullptr , " " );
115+ memcpy (buffer_.dynamic_ , inline_buffer_, string_length_);
100116 dynamic_capacity_ = new_capacity;
101117 type_ = Type::Dynamic;
102118 } else {
103119 if (size + 1 + string_length_ > dynamic_capacity_) {
104120 // Need to reallocate.
105121 dynamic_capacity_ = (string_length_ + size) * 2 ;
122+ validateCapacity (dynamic_capacity_);
106123 buffer_.dynamic_ = static_cast <char *>(realloc (buffer_.dynamic_ , dynamic_capacity_));
107124 RELEASE_ASSERT (buffer_.dynamic_ != nullptr , " " );
108125 }
@@ -153,14 +170,18 @@ void HeaderString::setCopy(const char* data, uint32_t size) {
153170 // We can get here either because we didn't fit in inline or we are already dynamic.
154171 if (type_ == Type::Inline) {
155172 dynamic_capacity_ = size * 2 ;
173+ validateCapacity (dynamic_capacity_);
156174 buffer_.dynamic_ = static_cast <char *>(malloc (dynamic_capacity_));
175+ RELEASE_ASSERT (buffer_.dynamic_ != nullptr , " " );
157176 type_ = Type::Dynamic;
158177 } else {
159178 if (size + 1 > dynamic_capacity_) {
160179 // Need to reallocate. Use free/malloc to avoid the copy since we are about to overwrite.
161180 dynamic_capacity_ = size * 2 ;
181+ validateCapacity (dynamic_capacity_);
162182 free (buffer_.dynamic_ );
163183 buffer_.dynamic_ = static_cast <char *>(malloc (dynamic_capacity_));
184+ RELEASE_ASSERT (buffer_.dynamic_ != nullptr , " " );
164185 }
165186 }
166187 }
@@ -182,8 +203,14 @@ void HeaderString::setInteger(uint64_t value) {
182203 }
183204
184205 case Type::Inline:
206+ // buffer_.dynamic_ should always point at inline_buffer_ for Type::Inline.
207+ ASSERT (buffer_.dynamic_ == inline_buffer_);
185208 case Type::Dynamic: {
186209 // Whether dynamic or inline the buffer is guaranteed to be large enough.
210+ ASSERT (type_ == Type::Inline || dynamic_capacity_ >= MaxIntegerLength);
211+ // It's safe to use buffer.dynamic_, since buffer.ref_ is union aliased.
212+ // This better not change without verifying assumptions across this file.
213+ static_assert (offsetof (Buffer, dynamic_) == offsetof (Buffer, ref_), " " );
187214 string_length_ = StringUtil::itoa (buffer_.dynamic_ , 32 , value);
188215 }
189216 }
0 commit comments