Skip to content

Commit

Permalink
exception: make Ipv6Instance and Ipv4Instance not throw and remove so…
Browse files Browse the repository at this point in the history
…me try catch pattern (envoyproxy#16122)

Commit Message:This is part of the effort to remove C++ exception from data plane by adding assertion that the code is executed in main thread when an exception is caught.(envoyproxy#14320) By making the constructor of instances(PipeInstance, Ipv6Instance, Ipv4Instance) no throw, remove some try catch code from envoy.

Signed-off-by: chaoqin-li1123 <chaoqinli@google.com>
Signed-off-by: chris.xin <xinchuantao@qq.com>
  • Loading branch information
chaoqin-li1123 authored and chrisxrepo committed Jul 8, 2021
1 parent 98eadea commit 8597c64
Show file tree
Hide file tree
Showing 13 changed files with 308 additions and 133 deletions.
10 changes: 10 additions & 0 deletions source/common/common/thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ bool MainThread::isMainThread() {
return main_thread_singleton->inMainThread() || main_thread_singleton->inTestThread();
}

bool MainThread::isWorkerThread() {
auto main_thread_singleton = MainThreadSingleton::getExisting();
// Allow worker thread code to be executed in test thread.
if (main_thread_singleton == nullptr) {
return true;
}
// When threading is on, compare thread id with main thread id.
return !main_thread_singleton->inMainThread();
}

void MainThread::clear() {
delete MainThreadSingleton::getExisting();
MainThreadSingleton::clear();
Expand Down
1 change: 1 addition & 0 deletions source/common/common/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ struct MainThread {
*/
static void clear();
static bool isMainThread();
static bool isWorkerThread();

private:
std::thread::id main_thread_id_;
Expand Down
2 changes: 2 additions & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ envoy_cc_library(
"//source/common/api:os_sys_calls_lib",
"//source/common/common:assert_lib",
"//source/common/common:safe_memcpy_lib",
"//source/common/common:statusor_lib",
"//source/common/common:thread_lib",
"//source/common/common:utility_lib",
],
)
Expand Down
205 changes: 147 additions & 58 deletions source/common/network/address_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "source/common/common/assert.h"
#include "source/common/common/fmt.h"
#include "source/common/common/safe_memcpy.h"
#include "source/common/common/thread.h"
#include "source/common/common/utility.h"
#include "source/common/network/socket_interface.h"

Expand All @@ -19,26 +20,6 @@ namespace Address {

namespace {

// Validate that IPv4 is supported on this platform, raise an exception for the
// given address if not.
void validateIpv4Supported(const std::string& address) {
static const bool supported = SocketInterfaceSingleton::get().ipFamilySupported(AF_INET);
if (!supported) {
throw EnvoyException(
fmt::format("IPv4 addresses are not supported on this machine: {}", address));
}
}

// Validate that IPv6 is supported on this platform, raise an exception for the
// given address if not.
void validateIpv6Supported(const std::string& address) {
static const bool supported = SocketInterfaceSingleton::get().ipFamilySupported(AF_INET6);
if (!supported) {
throw EnvoyException(
fmt::format("IPv6 addresses are not supported on this machine: {}", address));
}
}

// Constructs a readable string with the embedded nulls in the abstract path replaced with '@'.
std::string friendlyNameFromAbstractPath(absl::string_view path) {
std::string friendly_name(path.data(), path.size());
Expand All @@ -50,17 +31,30 @@ const SocketInterface* sockInterfaceOrDefault(const SocketInterface* sock_interf
return sock_interface == nullptr ? &SocketInterfaceSingleton::get() : sock_interface;
}

void throwOnError(absl::Status status) {
if (!status.ok()) {
throw EnvoyException(status.ToString());
}
}

InstanceConstSharedPtr throwOnError(StatusOr<InstanceConstSharedPtr> address) {
if (!address.ok()) {
throwOnError(address.status());
}
return *address;
}

} // namespace

Address::InstanceConstSharedPtr addressFromSockAddr(const sockaddr_storage& ss, socklen_t ss_len,
bool v6only) {
StatusOr<Address::InstanceConstSharedPtr> addressFromSockAddr(const sockaddr_storage& ss,
socklen_t ss_len, bool v6only) {
RELEASE_ASSERT(ss_len == 0 || static_cast<unsigned int>(ss_len) >= sizeof(sa_family_t), "");
switch (ss.ss_family) {
case AF_INET: {
RELEASE_ASSERT(ss_len == 0 || static_cast<unsigned int>(ss_len) == sizeof(sockaddr_in), "");
const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(&ss);
ASSERT(AF_INET == sin->sin_family);
return std::make_shared<Address::Ipv4Instance>(sin);
return Address::InstanceFactory::createInstancePtr<Address::Ipv4Instance>(sin);
}
case AF_INET6: {
RELEASE_ASSERT(ss_len == 0 || static_cast<unsigned int>(ss_len) == sizeof(sockaddr_in6), "");
Expand All @@ -77,9 +71,9 @@ Address::InstanceConstSharedPtr addressFromSockAddr(const sockaddr_storage& ss,
#else
struct sockaddr_in sin = {AF_INET, sin6->sin6_port, {sin6->sin6_addr.s6_addr32[3]}, {}};
#endif
return std::make_shared<Address::Ipv4Instance>(&sin);
return Address::InstanceFactory::createInstancePtr<Address::Ipv4Instance>(&sin);
} else {
return std::make_shared<Address::Ipv6Instance>(*sin6, v6only);
return Address::InstanceFactory::createInstancePtr<Address::Ipv6Instance>(*sin6, v6only);
}
}
case AF_UNIX: {
Expand All @@ -88,27 +82,47 @@ Address::InstanceConstSharedPtr addressFromSockAddr(const sockaddr_storage& ss,
RELEASE_ASSERT(ss_len == 0 || static_cast<unsigned int>(ss_len) >=
offsetof(struct sockaddr_un, sun_path) + 1,
"");
return std::make_shared<Address::PipeInstance>(sun, ss_len);
return Address::InstanceFactory::createInstancePtr<Address::PipeInstance>(sun, ss_len);
}
default:
throw EnvoyException(fmt::format("Unexpected sockaddr family: {}", ss.ss_family));
return absl::InvalidArgumentError(fmt::format("Unexpected sockaddr family: {}", ss.ss_family));
}
NOT_REACHED_GCOVR_EXCL_LINE;
}

Address::InstanceConstSharedPtr addressFromSockAddrOrThrow(const sockaddr_storage& ss,
socklen_t ss_len, bool v6only) {
// Though we don't have any test coverage where address validation in addressFromSockAddr() fails,
// this code is called in worker thread and can throw in theory. In that case, the program will
// crash due to uncaught exception. In practice, we don't expect any address validation in
// addressFromSockAddr() to fail in worker thread.
StatusOr<InstanceConstSharedPtr> address = addressFromSockAddr(ss, ss_len, v6only);
return throwOnError(address);
}

Address::InstanceConstSharedPtr
addressFromSockAddrOrDie(const sockaddr_storage& ss, socklen_t ss_len, os_fd_t fd, bool v6only) {
// Set v6only to false so that mapped-v6 address can be normalize to v4
// address. Though dual stack may be disabled, it's still okay to assume the
// address is from a dual stack socket. This is because mapped-v6 address
// must come from a dual stack socket. An actual v6 address can come from
// both dual stack socket and v6 only socket. If |peer_addr| is an actual v6
// address and the socket is actually v6 only, the returned address will be
// regarded as a v6 address from dual stack socket. However, this address is not going to be
// used to create socket. Wrong knowledge of dual stack support won't hurt.
ASSERT(Thread::MainThread::isWorkerThread());
StatusOr<Address::InstanceConstSharedPtr> address =
Address::addressFromSockAddr(ss, ss_len, v6only);
if (!address.ok()) {
PANIC(fmt::format("Invalid address for fd: {}, error: {}", fd, address.status().ToString()));
}
return *address;
}

Ipv4Instance::Ipv4Instance(const sockaddr_in* address, const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
memset(&ip_.ipv4_.address_, 0, sizeof(ip_.ipv4_.address_));
ip_.ipv4_.address_ = *address;
ip_.friendly_address_ = sockaddrToString(*address);

// Based on benchmark testing, this reserve+append implementation runs faster than absl::StrCat.
fmt::format_int port(ntohs(address->sin_port));
friendly_name_.reserve(ip_.friendly_address_.size() + 1 + port.size());
friendly_name_.append(ip_.friendly_address_);
friendly_name_.push_back(':');
friendly_name_.append(port.data(), port.size());
validateIpv4Supported(friendly_name_);
throwOnError(validateProtocolSupported());
initHelper(address);
}

Ipv4Instance::Ipv4Instance(const std::string& address, const SocketInterface* sock_interface)
Expand All @@ -117,6 +131,7 @@ Ipv4Instance::Ipv4Instance(const std::string& address, const SocketInterface* so
Ipv4Instance::Ipv4Instance(const std::string& address, uint32_t port,
const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
throwOnError(validateProtocolSupported());
memset(&ip_.ipv4_.address_, 0, sizeof(ip_.ipv4_.address_));
ip_.ipv4_.address_.sin_family = AF_INET;
ip_.ipv4_.address_.sin_port = htons(port);
Expand All @@ -126,21 +141,30 @@ Ipv4Instance::Ipv4Instance(const std::string& address, uint32_t port,
}

friendly_name_ = absl::StrCat(address, ":", port);
validateIpv4Supported(friendly_name_);
ip_.friendly_address_ = address;
}

Ipv4Instance::Ipv4Instance(uint32_t port, const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
throwOnError(validateProtocolSupported());
memset(&ip_.ipv4_.address_, 0, sizeof(ip_.ipv4_.address_));
ip_.ipv4_.address_.sin_family = AF_INET;
ip_.ipv4_.address_.sin_port = htons(port);
ip_.ipv4_.address_.sin_addr.s_addr = INADDR_ANY;
friendly_name_ = absl::StrCat("0.0.0.0:", port);
validateIpv4Supported(friendly_name_);
ip_.friendly_address_ = "0.0.0.0";
}

Ipv4Instance::Ipv4Instance(absl::Status& status, const sockaddr_in* address,
const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
status = validateProtocolSupported();
if (!status.ok()) {
return;
}
initHelper(address);
}

bool Ipv4Instance::operator==(const Instance& rhs) const {
const Ipv4Instance* rhs_casted = dynamic_cast<const Ipv4Instance*>(&rhs);
return (rhs_casted && (ip_.ipv4_.address() == rhs_casted->ip_.ipv4_.address()) &&
Expand Down Expand Up @@ -173,6 +197,27 @@ std::string Ipv4Instance::sockaddrToString(const sockaddr_in& addr) {
return std::string(start, str + BufferSize - start);
}

absl::Status Ipv4Instance::validateProtocolSupported() {
static const bool supported = SocketInterfaceSingleton::get().ipFamilySupported(AF_INET);
if (supported) {
return absl::OkStatus();
}
return absl::FailedPreconditionError("IPv4 addresses are not supported on this machine");
}

void Ipv4Instance::initHelper(const sockaddr_in* address) {
memset(&ip_.ipv4_.address_, 0, sizeof(ip_.ipv4_.address_));
ip_.ipv4_.address_ = *address;
ip_.friendly_address_ = sockaddrToString(*address);

// Based on benchmark testing, this reserve+append implementation runs faster than absl::StrCat.
fmt::format_int port(ntohs(address->sin_port));
friendly_name_.reserve(ip_.friendly_address_.size() + 1 + port.size());
friendly_name_.append(ip_.friendly_address_);
friendly_name_.push_back(':');
friendly_name_.append(port.data(), port.size());
}

absl::uint128 Ipv6Instance::Ipv6Helper::address() const {
absl::uint128 result{0};
static_assert(sizeof(absl::uint128) == 16, "The size of asbl::uint128 is not 16.");
Expand All @@ -194,11 +239,8 @@ std::string Ipv6Instance::Ipv6Helper::makeFriendlyAddress() const {
Ipv6Instance::Ipv6Instance(const sockaddr_in6& address, bool v6only,
const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
ip_.ipv6_.address_ = address;
ip_.friendly_address_ = ip_.ipv6_.makeFriendlyAddress();
ip_.ipv6_.v6only_ = v6only;
friendly_name_ = fmt::format("[{}]:{}", ip_.friendly_address_, ip_.port());
validateIpv6Supported(friendly_name_);
throwOnError(validateProtocolSupported());
initHelper(address, v6only);
}

Ipv6Instance::Ipv6Instance(const std::string& address, const SocketInterface* sock_interface)
Expand All @@ -207,6 +249,7 @@ Ipv6Instance::Ipv6Instance(const std::string& address, const SocketInterface* so
Ipv6Instance::Ipv6Instance(const std::string& address, uint32_t port,
const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
throwOnError(validateProtocolSupported());
ip_.ipv6_.address_.sin6_family = AF_INET6;
ip_.ipv6_.address_.sin6_port = htons(port);
if (!address.empty()) {
Expand All @@ -219,7 +262,6 @@ Ipv6Instance::Ipv6Instance(const std::string& address, uint32_t port,
// Just in case address is in a non-canonical format, format from network address.
ip_.friendly_address_ = ip_.ipv6_.makeFriendlyAddress();
friendly_name_ = fmt::format("[{}]:{}", ip_.friendly_address_, ip_.port());
validateIpv6Supported(friendly_name_);
}

Ipv6Instance::Ipv6Instance(uint32_t port, const SocketInterface* sock_interface)
Expand All @@ -231,6 +273,31 @@ bool Ipv6Instance::operator==(const Instance& rhs) const {
(ip_.port() == rhs_casted->ip_.port()));
}

Ipv6Instance::Ipv6Instance(absl::Status& status, const sockaddr_in6& address, bool v6only,
const SocketInterface* sock_interface)
: InstanceBase(Type::Ip, sockInterfaceOrDefault(sock_interface)) {
status = validateProtocolSupported();
if (!status.ok()) {
return;
}
initHelper(address, v6only);
}

absl::Status Ipv6Instance::validateProtocolSupported() {
static const bool supported = SocketInterfaceSingleton::get().ipFamilySupported(AF_INET6);
if (supported) {
return absl::OkStatus();
}
return absl::FailedPreconditionError("IPv6 addresses are not supported on this machine");
}

void Ipv6Instance::initHelper(const sockaddr_in6& address, bool v6only) {
ip_.ipv6_.address_ = address;
ip_.friendly_address_ = ip_.ipv6_.makeFriendlyAddress();
ip_.ipv6_.v6only_ = v6only;
friendly_name_ = fmt::format("[{}]:{}", ip_.friendly_address_, ip_.port());
}

PipeInstance::PipeInstance(const sockaddr_un* address, socklen_t ss_len, mode_t mode,
const SocketInterface* sock_interface)
: InstanceBase(Type::Pipe, sockInterfaceOrDefault(sock_interface)) {
Expand All @@ -243,18 +310,8 @@ PipeInstance::PipeInstance(const sockaddr_un* address, socklen_t ss_len, mode_t
pipe_.abstract_namespace_ = true;
pipe_.address_length_ = ss_len - offsetof(struct sockaddr_un, sun_path);
}
pipe_.address_ = *address;
if (pipe_.abstract_namespace_) {
if (mode != 0) {
throw EnvoyException("Cannot set mode for Abstract AF_UNIX sockets");
}
// Replace all null characters with '@' in friendly_name_.
friendly_name_ = friendlyNameFromAbstractPath(
absl::string_view(pipe_.address_.sun_path, pipe_.address_length_));
} else {
friendly_name_ = address->sun_path;
}
pipe_.mode_ = mode;
absl::Status status = initHelper(address, mode);
throwOnError(status);
}

PipeInstance::PipeInstance(const std::string& pipe_path, mode_t mode,
Expand Down Expand Up @@ -300,8 +357,40 @@ PipeInstance::PipeInstance(const std::string& pipe_path, mode_t mode,
pipe_.mode_ = mode;
}

PipeInstance::PipeInstance(absl::Status& error, const sockaddr_un* address, socklen_t ss_len,
mode_t mode, const SocketInterface* sock_interface)
: InstanceBase(Type::Pipe, sockInterfaceOrDefault(sock_interface)) {
if (address->sun_path[0] == '\0') {
#if !defined(__linux__)
error = absl::FailedPreconditionError("Abstract AF_UNIX sockets are only supported on linux.");
return;
#endif
RELEASE_ASSERT(static_cast<unsigned int>(ss_len) >= offsetof(struct sockaddr_un, sun_path) + 1,
"");
pipe_.abstract_namespace_ = true;
pipe_.address_length_ = ss_len - offsetof(struct sockaddr_un, sun_path);
}
error = initHelper(address, mode);
}

bool PipeInstance::operator==(const Instance& rhs) const { return asString() == rhs.asString(); }

absl::Status PipeInstance::initHelper(const sockaddr_un* address, mode_t mode) {
pipe_.address_ = *address;
if (pipe_.abstract_namespace_) {
if (mode != 0) {
return absl::FailedPreconditionError("Cannot set mode for Abstract AF_UNIX sockets");
}
// Replace all null characters with '@' in friendly_name_.
friendly_name_ = friendlyNameFromAbstractPath(
absl::string_view(pipe_.address_.sun_path, pipe_.address_length_));
} else {
friendly_name_ = address->sun_path;
}
pipe_.mode_ = mode;
return absl::OkStatus();
}

EnvoyInternalInstance::EnvoyInternalInstance(const std::string& address_id,
const SocketInterface* sock_interface)
: InstanceBase(Type::EnvoyInternal, sockInterfaceOrDefault(sock_interface)),
Expand Down
Loading

0 comments on commit 8597c64

Please sign in to comment.