Skip to content

Commit 23e1842

Browse files
authored
Merge pull request ceph#60429 from VVoidV/lbr-fix-formatter
<common> fix formatter buffer out-of-bounds Reviewed-by: Casey Bodley <[email protected]>
2 parents ce87d8f + 38601d0 commit 23e1842

File tree

7 files changed

+160
-16
lines changed

7 files changed

+160
-16
lines changed

src/common/Formatter.cc

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <algorithm>
2424
#include <set>
2525
#include <limits>
26+
#include <utility>
27+
#include <boost/container/small_vector.hpp>
2628

2729
// -----------------------
2830
namespace ceph {
@@ -365,10 +367,21 @@ std::ostream& JSONFormatter::dump_stream(std::string_view name)
365367

366368
void JSONFormatter::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap)
367369
{
368-
char buf[LARGE_SIZE];
369-
vsnprintf(buf, LARGE_SIZE, fmt, ap);
370+
auto buf = boost::container::small_vector<char, LARGE_SIZE>{
371+
LARGE_SIZE, boost::container::default_init};
370372

371-
add_value(name, buf, quoted);
373+
va_list ap_copy;
374+
va_copy(ap_copy, ap);
375+
int len = vsnprintf(buf.data(), buf.size(), fmt, ap_copy);
376+
va_end(ap_copy);
377+
378+
if (std::cmp_greater_equal(len, buf.size())) {
379+
// output was truncated, allocate a buffer large enough
380+
buf.resize(len + 1, boost::container::default_init);
381+
vsnprintf(buf.data(), buf.size(), fmt, ap);
382+
}
383+
384+
add_value(name, buf.data(), quoted);
372385
}
373386

374387
int JSONFormatter::get_len() const
@@ -550,15 +563,27 @@ std::ostream& XMLFormatter::dump_stream(std::string_view name)
550563

551564
void XMLFormatter::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap)
552565
{
553-
char buf[LARGE_SIZE];
554-
size_t len = vsnprintf(buf, LARGE_SIZE, fmt, ap);
566+
auto buf = boost::container::small_vector<char, LARGE_SIZE>{
567+
LARGE_SIZE, boost::container::default_init};
568+
569+
va_list ap_copy;
570+
va_copy(ap_copy, ap);
571+
int len = vsnprintf(buf.data(), buf.size(), fmt, ap_copy);
572+
va_end(ap_copy);
573+
574+
if (std::cmp_greater_equal(len, buf.size())) {
575+
// output was truncated, allocate a buffer large enough
576+
buf.resize(len + 1, boost::container::default_init);
577+
vsnprintf(buf.data(), buf.size(), fmt, ap);
578+
}
579+
555580
auto e = get_xml_name(name);
556581

557582
print_spaces();
558583
if (ns) {
559-
m_ss << "<" << e << " xmlns=\"" << ns << "\">" << xml_stream_escaper(std::string_view(buf, len)) << "</" << e << ">";
584+
m_ss << "<" << e << " xmlns=\"" << ns << "\">" << xml_stream_escaper(std::string_view(buf.data(), len)) << "</" << e << ">";
560585
} else {
561-
m_ss << "<" << e << ">" << xml_stream_escaper(std::string_view(buf, len)) << "</" << e << ">";
586+
m_ss << "<" << e << ">" << xml_stream_escaper(std::string_view(buf.data(), len)) << "</" << e << ">";
562587
}
563588

564589
if (m_pretty)
@@ -927,14 +952,26 @@ void TableFormatter::dump_format_va(std::string_view name,
927952
const char *fmt, va_list ap)
928953
{
929954
finish_pending_string();
930-
char buf[LARGE_SIZE];
931-
vsnprintf(buf, LARGE_SIZE, fmt, ap);
955+
auto buf = boost::container::small_vector<char, LARGE_SIZE>{
956+
LARGE_SIZE, boost::container::default_init};
957+
958+
va_list ap_copy;
959+
va_copy(ap_copy, ap);
960+
int len = vsnprintf(buf.data(), buf.size(), fmt, ap_copy);
961+
va_end(ap_copy);
962+
963+
if (std::cmp_greater_equal(len, buf.size())) {
964+
// output was truncated, allocate a buffer large enough
965+
buf.resize(len + 1, boost::container::default_init);
966+
vsnprintf(buf.data(), buf.size(), fmt, ap);
967+
}
932968

933969
size_t i = m_vec_index(name);
934970
if (ns) {
935-
m_ss << ns << "." << buf;
936-
} else
937-
m_ss << buf;
971+
m_ss << ns << "." << buf.data();
972+
} else {
973+
m_ss << buf.data();
974+
}
938975

939976
m_vec[i].push_back(std::make_pair(get_section_name(name), m_ss.str()));
940977
m_ss.clear();

src/common/HTMLFormatter.cc

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <stdlib.h>
2424
#include <string>
2525
#include <string.h> // for strdup
26+
#include <boost/container/small_vector.hpp>
27+
#include <utility> // for std::cmp_greater_equal
2628

2729
#include "common/escape.h"
2830

@@ -138,17 +140,27 @@ std::ostream& HTMLFormatter::dump_stream(std::string_view name)
138140

139141
void HTMLFormatter::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap)
140142
{
141-
char buf[LARGE_SIZE];
142-
size_t len = vsnprintf(buf, LARGE_SIZE, fmt, ap);
143+
auto buf = boost::container::small_vector<char, LARGE_SIZE>{
144+
LARGE_SIZE, boost::container::default_init};
145+
146+
va_list ap_copy;
147+
va_copy(ap_copy, ap);
148+
size_t len = vsnprintf(buf.data(), buf.size(), fmt, ap);
149+
va_end(ap_copy);
150+
151+
if(std::cmp_greater_equal(len, buf.size())){
152+
buf.resize(len + 1, boost::container::default_init);
153+
vsnprintf(buf.data(), buf.size(), fmt, ap_copy);
154+
}
143155

144156
std::string e(name);
145157
print_spaces();
146158
if (ns) {
147159
m_ss << "<li xmlns=\"" << ns << "\">" << e << ": "
148-
<< xml_stream_escaper(std::string_view(buf, len)) << "</li>";
160+
<< xml_stream_escaper(std::string_view(buf.data(), len)) << "</li>";
149161
} else {
150162
m_ss << "<li>" << e << ": "
151-
<< xml_stream_escaper(std::string_view(buf, len)) << "</li>";
163+
<< xml_stream_escaper(std::string_view(buf.data(), len)) << "</li>";
152164
}
153165

154166
if (m_pretty)

src/test/common/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ add_executable(unittest_xmlformatter
229229
add_ceph_unittest(unittest_xmlformatter)
230230
target_link_libraries(unittest_xmlformatter ceph-common)
231231

232+
add_executable(unittest_htmlformatter
233+
test_htmlformatter.cc
234+
)
235+
add_ceph_unittest(unittest_htmlformatter)
236+
target_link_libraries(unittest_htmlformatter ceph-common)
237+
232238
# unittest_bit_vector
233239
add_executable(unittest_bit_vector
234240
test_bit_vector.cc
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "gtest/gtest.h"
2+
3+
#include "common/HTMLFormatter.h"
4+
#include <sstream>
5+
#include <string>
6+
7+
using namespace ceph;
8+
9+
TEST(htmlformatter, dump_format_large_item)
10+
{
11+
std::stringstream sout;
12+
HTMLFormatter formatter(false);
13+
14+
std::string base_url("http://example.com");
15+
std::string bucket_name("bucket");
16+
std::string object_key(1024, 'a');
17+
18+
formatter.dump_format("Location", "%s/%s/%s", base_url.c_str(), bucket_name.c_str(), object_key.c_str());
19+
20+
formatter.flush(sout);
21+
22+
std::string uri = base_url + "/" + bucket_name + "/" + object_key;
23+
std::string expected_output = "<li>Location: " + uri + "</li>";
24+
25+
EXPECT_EQ(expected_output, sout.str());
26+
}

src/test/common/test_json_formatter.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,27 @@ TEST(formatter, dump_inf_or_nan)
102102
EXPECT_EQ(parser.find_obj("nan_val")->get_data(), "null");
103103
EXPECT_EQ(parser.find_obj("nan_val_alt")->get_data(), "null");
104104
}
105+
106+
TEST(formatter, dump_large_item) {
107+
JSONFormatter formatter;
108+
formatter.open_object_section("large_item");
109+
110+
std::string base_url("http://example.com");
111+
std::string bucket_name("bucket");
112+
std::string object_key(1024, 'a');
113+
114+
std::string full_url = base_url + "/" + bucket_name + "/" + object_key;
115+
formatter.dump_format("Location", "%s/%s/%s", base_url.c_str(), bucket_name.c_str(), object_key.c_str());
116+
117+
formatter.close_section();
118+
bufferlist bl;
119+
formatter.flush(bl);
120+
121+
// std::cout << std::string(bl.c_str(), bl.length()) << std::endl;
122+
123+
JSONParser parser;
124+
parser.parse(bl.c_str(), bl.length());
125+
126+
EXPECT_TRUE(parser.parse(bl.c_str(), bl.length()));
127+
EXPECT_EQ(parser.find_obj("Location")->get_data(), full_url);
128+
}

src/test/common/test_tableformatter.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,23 @@ TEST(tableformatter, multiline_keyval)
250250
EXPECT_EQ(cmp, sout.str());
251251
}
252252

253+
TEST(tableformatter, dump_large_item) {
254+
std::stringstream sout;
255+
TableFormatter* formatter = (TableFormatter*) Formatter::create("table-kv");
256+
257+
std::string base_url("http://example.com");
258+
std::string bucket_name("bucket");
259+
std::string object_key(1024, 'a');
260+
261+
std::string full_url = base_url + "/" + bucket_name + "/" + object_key;
262+
formatter->dump_format("Location", "%s/%s/%s", base_url.c_str(), bucket_name.c_str(), object_key.c_str());
263+
formatter->flush(sout);
264+
delete formatter;
265+
266+
std::string cmp = "key::Location=\"" + full_url + "\" \n";
267+
EXPECT_EQ(cmp, sout.str());
268+
}
269+
253270
/*
254271
* Local Variables:
255272
* compile-command: "cd ../.. ; make -j4 &&

src/test/common/test_xmlformatter.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,25 @@ TEST(xmlformatter, pretty_lowercased_underscored)
163163
"<string_item>String</string_item>\n\n";
164164
EXPECT_EQ(cmp, sout.str());
165165
}
166+
167+
TEST(xmlformatter, dump_format_large_item)
168+
{
169+
std::stringstream sout;
170+
XMLFormatter formatter(
171+
true, // pretty
172+
false, // lowercased
173+
false); // underscored
174+
175+
std::string base_url("http://example.com");
176+
std::string bucket_name("bucket");
177+
std::string object_key(1024, 'a');
178+
179+
formatter.dump_format("Location", "%s/%s/%s", base_url.c_str(), bucket_name.c_str(), object_key.c_str());
180+
181+
formatter.flush(sout);
182+
183+
std::string uri = base_url + "/" + bucket_name + "/" + object_key;
184+
std::string expected_output = "<Location>" + uri + "</Location>\n\n";
185+
186+
EXPECT_EQ(expected_output, sout.str());
187+
}

0 commit comments

Comments
 (0)