Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for out of bound read in std::strtol while parsing HTTP requests #1208

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/common/http.cc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <charconv>

namespace Pistache::Http
{
Expand Down Expand Up @@ -287,9 +288,18 @@ namespace Pistache::Http
if (!match_until(' ', cursor))
return State::Again;

char* end;
auto code = strtol(codeToken.rawText(), &end, 10);
if (*end != ' ')
const char* beg = codeToken.rawText();
const char* beg_trim = beg;
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view(beg_trim, codeToken.size());
const char* end = view.data() + view.size();
Comment on lines +298 to +299
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a string_view being used here?

Copy link
Member

@Tachi107 Tachi107 Apr 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Citing cppreference:

constexpr basic_string_view( const CharT* s, size_type count );

After construction, data() is equal to s, and size() is equal to count.

So isn't your code exactly the same as:

Suggested change
std::string_view view(beg_trim, codeToken.size());
const char* end = view.data() + view.size();
const char* end = beg_trim + codeToken.size();

long code = 0;
auto res = std::from_chars(beg_trim, end, code);
if (*res.ptr != ' ')
raise("Failed to parse return code");
response->code_ = static_cast<Http::Code>(code);

Expand Down Expand Up @@ -455,10 +465,18 @@ namespace Pistache::Http
if (!cursor.advance(1))
return Incomplete;

char* end;
const char* raw = chunkSize.rawText();
auto sz = std::strtol(raw, &end, 16);
if (*end != '\r')
const char* beg = chunkSize.rawText();
const char* beg_trim = beg;
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
Comment on lines +475 to +476
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally a fan of string_view, but here I personally find it unnecessary. I'd suggest using strlen() explicitly, like:

Suggested change
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
const char* end = beg_trim + std::strlen(beg_trim);

long sz = 0;
auto res = std::from_chars(beg_trim, end, sz, 16);
if (*res.ptr != '\r')
throw std::runtime_error("Invalid chunk size");

// CRLF
Expand Down
15 changes: 12 additions & 3 deletions src/common/http_header.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <string>
#include <string_view>
#include <sys/socket.h>
#include <charconv>

namespace Pistache::Http::Header
{
Expand Down Expand Up @@ -221,13 +222,21 @@ namespace Pistache::Http::Header
"Invalid caching directive, missing delta-seconds");
}

char* end;
const char* beg = cursor.offset();
const char* beg_trim = beg;
// @Security: if str is not \0 terminated, there might be a situation
// where strtol can overflow. Double-check that it's harmless and fix
// if not
auto secs = strtol(beg, &end, 10);
cursor.advance(end - beg);
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
long secs = 0;
auto res = std::from_chars(beg_trim, end, secs);
cursor.advance(res.ptr - beg);
if (!cursor.eof() && cursor.current() != ',')
{
throw std::runtime_error(
Expand Down
44 changes: 37 additions & 7 deletions src/common/net.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <charconv>

namespace Pistache
{
Expand All @@ -38,9 +39,18 @@ namespace Pistache
{
if (data.empty())
throw std::invalid_argument("Invalid port: empty port");
char* end = nullptr;
long port_num = strtol(data.c_str(), &end, 10);
if (*end != 0 || port_num < Port::min() || port_num > Port::max())
const char* beg = data.c_str();
const char* beg_trim = beg;
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
long port_num = 0;
auto res = std::from_chars(beg_trim, end, port_num);
if (*res.ptr != 0 || port_num < Port::min() || port_num > Port::max())
throw std::invalid_argument("Invalid port: " + data);
port = static_cast<uint16_t>(port_num);
}
Expand Down Expand Up @@ -312,9 +322,18 @@ namespace Pistache
throw std::invalid_argument("Invalid port");

// Check if port_ is a valid number
char* tmp;
std::strtol(port_.c_str(), &tmp, 10);
hasNumericPort_ = *tmp == '\0';
const char* beg = port_.c_str();
const char* beg_trim = beg;
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
long port_num = 0;
auto res = std::from_chars(beg_trim, end, port_num);
hasNumericPort_ = !(res.ec != std::errc{} || res.ptr != end);
}
}

Expand Down Expand Up @@ -446,7 +465,18 @@ namespace Pistache
port_ = Port(ip_.getPort());

// Check that the port has not overflowed while calling getaddrinfo()
if (parser.hasNumericPort() && port_ != std::strtol(addrinfo_port, nullptr, 10))
const char* beg = addrinfo_port;
const char* beg_trim = beg;
while (*beg_trim == '+' || *beg_trim == ' ')
{
if (*(beg_trim + 1))
++beg_trim;
}
std::string_view view = beg_trim;
const char* end = view.data() + view.size();
long addrinfo_port_num = 0;
std::from_chars(beg_trim, end, addrinfo_port_num);
if (parser.hasNumericPort() && port_ != addrinfo_port_num)
{
throw std::invalid_argument("Invalid numeric port");
}
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.9.20240428
0.2.10.20240428