nghttpx: Fix heap-use-after-free in ClientHandler object

This bug was found by scan-build
This commit is contained in:
Tatsuhiro Tsujikawa 2014-10-30 21:47:38 +09:00
parent 279fc2ad37
commit 20900b133e
2 changed files with 101 additions and 81 deletions

View File

@ -143,35 +143,9 @@ namespace {
void upstream_http2_connhd_readcb(bufferevent *bev, void *arg) void upstream_http2_connhd_readcb(bufferevent *bev, void *arg)
{ {
// This callback assumes upstream is Http2Upstream. // This callback assumes upstream is Http2Upstream.
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
auto handler = static_cast<ClientHandler*>(arg); auto handler = static_cast<ClientHandler*>(arg);
auto leftlen = handler->get_left_connhd_len(); if(handler->on_http2_connhd_read() != 0) {
auto input = bufferevent_get_input(bev);
auto readlen = evbuffer_remove(input, data, leftlen);
if(readlen == -1) {
delete handler; delete handler;
return;
}
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - leftlen,
data, readlen) != 0) {
// There is no downgrade path here. Just drop the connection.
if(LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "invalid client connection header";
}
delete handler;
return;
}
leftlen -= readlen;
handler->set_left_connhd_len(leftlen);
if(leftlen == 0) {
handler->set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
// Run on_read to process data left in buffer since they are not
// notified further
if(handler->on_read() != 0) {
delete handler;
return;
}
} }
} }
} // namespace } // namespace
@ -180,49 +154,9 @@ namespace {
void upstream_http1_connhd_readcb(bufferevent *bev, void *arg) void upstream_http1_connhd_readcb(bufferevent *bev, void *arg)
{ {
// This callback assumes upstream is HttpsUpstream. // This callback assumes upstream is HttpsUpstream.
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
auto handler = static_cast<ClientHandler*>(arg); auto handler = static_cast<ClientHandler*>(arg);
auto leftlen = handler->get_left_connhd_len(); if(handler->on_http1_connhd_read() != 0) {
auto input = bufferevent_get_input(bev);
auto readlen = evbuffer_copyout(input, data, leftlen);
if(readlen == -1) {
delete handler; delete handler;
return;
}
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - leftlen,
data, readlen) != 0) {
if(LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "This is HTTP/1.1 connection, "
<< "but may be upgraded to HTTP/2 later.";
}
// Reset header length for later HTTP/2 upgrade
handler->set_left_connhd_len(NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
handler->set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
if(handler->on_read() != 0) {
delete handler;
return;
}
return;
}
if(evbuffer_drain(input, readlen) == -1) {
delete handler;
return;
}
leftlen -= readlen;
handler->set_left_connhd_len(leftlen);
if(leftlen == 0) {
if(LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "direct HTTP/2 connection";
}
handler->direct_http2_upgrade();
handler->set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
// Run on_read to process data left in buffer since they are not
// notified further
if(handler->on_read() != 0) {
delete handler;
return;
}
} }
} }
} // namespace } // namespace
@ -385,7 +319,9 @@ int ClientHandler::validate_next_proto()
// At this point, input buffer is already filled with some // At this point, input buffer is already filled with some
// bytes. The read callback is not called until new data // bytes. The read callback is not called until new data
// come. So consume input buffer here. // come. So consume input buffer here.
upstream_http2_connhd_readcb(bev_, this); if(on_http2_connhd_read() != 0) {
return -1;
}
return 0; return 0;
} else { } else {
@ -456,6 +392,100 @@ int ClientHandler::on_event()
return upstream_->on_event(); return upstream_->on_event();
} }
int ClientHandler::on_http2_connhd_read()
{
// This callback assumes upstream is Http2Upstream.
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
auto input = bufferevent_get_input(bev_);
auto readlen = evbuffer_remove(input, data, left_connhd_len_);
if(readlen == -1) {
return -1;
}
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
data, readlen) != 0) {
// There is no downgrade path here. Just drop the connection.
if(LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "invalid client connection header";
}
return -1;
}
left_connhd_len_ -= readlen;
if(left_connhd_len_ > 0) {
return 0;
}
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
// Run on_read to process data left in buffer since they are not
// notified further
if(on_read() != 0) {
return -1;
}
return 0;
}
int ClientHandler::on_http1_connhd_read()
{
uint8_t data[NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN];
auto input = bufferevent_get_input(bev_);
auto readlen = evbuffer_copyout(input, data, left_connhd_len_);
if(readlen == -1) {
return -1;
}
if(memcmp(NGHTTP2_CLIENT_CONNECTION_PREFACE +
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN - left_connhd_len_,
data, readlen) != 0) {
if(LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "This is HTTP/1.1 connection, "
<< "but may be upgraded to HTTP/2 later.";
}
// Reset header length for later HTTP/2 upgrade
left_connhd_len_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
if(on_read() != 0) {
return -1;
}
return 0;
}
if(evbuffer_drain(input, readlen) == -1) {
return -1;
}
left_connhd_len_ -= readlen;
if(left_connhd_len_ > 0) {
return 0;
}
if(LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "direct HTTP/2 connection";
}
direct_http2_upgrade();
set_bev_cb(upstream_readcb, upstream_writecb, upstream_eventcb);
// Run on_read to process data left in buffer since they are not
// notified further
if(on_read() != 0) {
return -1;
}
return 0;
}
const std::string& ClientHandler::get_ipaddr() const const std::string& ClientHandler::get_ipaddr() const
{ {
return ipaddr_; return ipaddr_;
@ -552,16 +582,6 @@ ConnectBlocker* ClientHandler::get_http1_connect_blocker() const
return http1_connect_blocker_; return http1_connect_blocker_;
} }
size_t ClientHandler::get_left_connhd_len() const
{
return left_connhd_len_;
}
void ClientHandler::set_left_connhd_len(size_t left)
{
left_connhd_len_ = left;
}
void ClientHandler::direct_http2_upgrade() void ClientHandler::direct_http2_upgrade()
{ {
upstream_= util::make_unique<Http2Upstream>(this); upstream_= util::make_unique<Http2Upstream>(this);

View File

@ -75,8 +75,6 @@ public:
Http2Session* get_http2_session() const; Http2Session* get_http2_session() const;
void set_http1_connect_blocker(ConnectBlocker *http1_connect_blocker); void set_http1_connect_blocker(ConnectBlocker *http1_connect_blocker);
ConnectBlocker* get_http1_connect_blocker() const; ConnectBlocker* get_http1_connect_blocker() const;
size_t get_left_connhd_len() const;
void set_left_connhd_len(size_t left);
// Call this function when HTTP/2 connection header is received at // Call this function when HTTP/2 connection header is received at
// the start of the connection. // the start of the connection.
void direct_http2_upgrade(); void direct_http2_upgrade();
@ -91,6 +89,8 @@ public:
bool get_tls_handshake() const; bool get_tls_handshake() const;
void set_tls_renegotiation(bool f); void set_tls_renegotiation(bool f);
bool get_tls_renegotiation() const; bool get_tls_renegotiation() const;
int on_http2_connhd_read();
int on_http1_connhd_read();
private: private:
std::unique_ptr<Upstream> upstream_; std::unique_ptr<Upstream> upstream_;
std::string ipaddr_; std::string ipaddr_;