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

syscall: refactor address APIs for deeper errno latching #3897

Merged
merged 7 commits into from
Jul 27, 2018

Conversation

venilnoronha
Copy link
Member

@venilnoronha venilnoronha commented Jul 19, 2018

Description:
The errno set by a syscall can be overwritten by code (e.g. logging) as it propagates up through the call stack. This PR refactors the bind, connect and socket methods in the address API to allow for returning the errno from deeper down the call stack i.e. as soon as a syscall is performed. See #3819 for more information.

Signed-off-by: Venil Noronha veniln@vmware.com

Risk Level: Low
Testing: Existing tests suffice
Docs Changes: N/A
Release Notes: N/A

/cc @alyssawilk @mattklein123

The errno set by a syscall can be overwritten by code (e.g. logging) as
it propagates up through the call stack. This commit refactors the
bind and connect methods in the address API to allow for returning the
errno from deeper down the call stack i.e. as soon as a syscall is
performed.

Signed-off-by: Venil Noronha <veniln@vmware.com>
@venilnoronha venilnoronha changed the title Refactor address APIs for deeper errno latching syscall: refactor address APIs for deeper errno latching Jul 19, 2018
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Cool - safer and safer :-)

if (fd < 0) {
err = errno;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it'd be nice to have socket return a tuple for consistency.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 6c65b13.

@alyssawilk alyssawilk self-assigned this Jul 19, 2018
This commit ensures that address implementations return the errno along
with the file descriptor in socket method calls.

Signed-off-by: Venil Noronha <veniln@vmware.com>
*/
virtual int bind(int fd) const PURE;
virtual std::tuple<int, int> bind(int fd) const PURE;
Copy link
Member

Choose a reason for hiding this comment

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

would absl::optional be a better return type? (ditto for connect)

Copy link
Member Author

Choose a reason for hiding this comment

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

@alyssawilk thoughts?

Copy link
Member Author

@venilnoronha venilnoronha Jul 21, 2018

Choose a reason for hiding this comment

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

Would it be better to have a custom type with rc (or fd) and the errno instead? Or, maybe, a generic for the first value?

Copy link
Member

Choose a reason for hiding this comment

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

I would prefer a struct for socket rather than a tuple, it is clearer which field is fd or errno.

Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for a struct instead of tuple, for all of these.

Copy link
Member Author

Choose a reason for hiding this comment

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

Single struct or multiple? As, the returned values could be rc or fd depending on the call.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think a single struct is fine, with members rc and errno. It's always up to the caller to know what the function is returning and how to interpret it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 36c81e5.

This commit adds a struct named Result which holds the rc and errno
values resulting from a syscall. Also, the address API is refactored to
use the new Result struct instead of std::tuple<int, int>.

Signed-off-by: Venil Noronha <veniln@vmware.com>
/**
* The return code from the system call.
*/
int rc;
Copy link
Member

Choose a reason for hiding this comment

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

Struct/Class member variables have a _ postfix (e.g., int foo_;).
https://github.com/envoyproxy/envoy/blob/master/STYLE.md

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in bb2841e.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thanks for switching this over to a struct. I prefer this also.

/**
* Result holds the rc and errno values resulting from a system call.
*/
struct Result {
Copy link
Member

Choose a reason for hiding this comment

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

Q: Is this really address specific? Should we potentially put this in https://github.com/envoyproxy/envoy/blob/master/include/envoy/api/os_sys_calls.h as SyscalResult or something like that? I don't feel that strongly but throwing it out there.

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes perfect sense. I wanted to use this struct in other places (buffer, etc.) anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in bb2841e.

Result Ipv4Instance::bind(int fd) const {
return {::bind(fd, reinterpret_cast<const sockaddr*>(&ip_.ipv4_.address_),
sizeof(ip_.ipv4_.address_)),
errno};
Copy link
Member

Choose a reason for hiding this comment

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

It's quite possible that C++ guarantees that the ::bind call happens first, and then errno is captured, but TBH it's not very obvious to me that is the case. I would prefer that we do the call into a local variable and then return the result and errno. Same elsewhere. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right. The c++14 documentation states:

The order of evaluation of function arguments is unspecified.

I'll fix this.

Copy link
Member

Choose a reason for hiding this comment

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

@venilnoronha You're referring to function arguments, but this is list-initialization, and C++14 guarantees the order:
https://timsong-cpp.github.io/cppwp/n4140/dcl.init.list#4

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions ([temp.variadic]), are evaluated in the order in which they appear.

Copy link
Member Author

Choose a reason for hiding this comment

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

@lizan my bad, I just realized that we switched over to a struct. Thanks for sharing!

I think I'd still have it explicit, at least for readability.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 7e05d55.

* Fixes struct styling
* Moves address/Result to os_sys_calls/SysCallResult

Signed-off-by: Venil Noronha <veniln@vmware.com>
@venilnoronha venilnoronha force-pushed the gh-3819-fix-address branch from 737eabf to bb2841e Compare July 24, 2018 17:08
Signed-off-by: Venil Noronha <veniln@vmware.com>
@@ -168,7 +168,7 @@ int InstanceBase::socketFromSocketType(SocketType socketType) const {
RELEASE_ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) != -1, "");
#endif

return fd;
return {fd, errno};
Copy link
Contributor

Choose a reason for hiding this comment

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

In the __APPLE__ case, I think that fcntl could be resetting errno, so you need to latch it earlier.

On the other hand, there's already a RELEASE_ASSERT that an error did not occur (fd != -1). So should this function even return errno?

Copy link
Member Author

@venilnoronha venilnoronha Jul 24, 2018

Choose a reason for hiding this comment

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

@ggreenway good catch! What do you suggest? Should we still keep the function return type as Api::SysCallResult for consistency, and to provide a way for future error handling?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say leave this function as returning an int, and make sure the docs mention that this will never fail.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in dc0c613.

RELEASE_ASSERT statements in the socket implementation ensure that the
routine returns only on success. This commit does the following:

* Revert function return type to int
* Update socket documentation
* Remove redundant error handling

Signed-off-by: Venil Noronha <veniln@vmware.com>
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

LGTM, nice. Will defer to @ggreenway for final review.

@@ -551,10 +551,10 @@ ClientConnectionImpl::ClientConnectionImpl(
}

if (source_address != nullptr) {
const int rc = source_address->bind(fd());
if (rc < 0) {
Api::SysCallResult result = source_address->bind(fd());
Copy link
Member

Choose a reason for hiding this comment

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

nit: result here and below can be const.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 42d0771.

Copy link
Contributor

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

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

This is looking good. After fixing the one small comment from me, and the one from Matt, I think this is ready.

@@ -23,7 +23,7 @@ Writer::Writer(Network::Address::InstanceConstSharedPtr address) {
fd_ = address->socket(Network::Address::SocketType::Datagram);
ASSERT(fd_ != -1);

int rc = address->connect(fd_);
const int rc = address->connect(fd_).rc_;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to keep the SysCallResult instead of an int, so that if the assert fires in a test it's easier in a debugger to see the value of errno.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in 42d0771.

* Use const Api::SysCallResult in connection_impl.cc
* Capture result value in place of rc_ for better debugging

Signed-off-by: Venil Noronha <veniln@vmware.com>
@ggreenway ggreenway merged commit 324e628 into envoyproxy:master Jul 27, 2018
@venilnoronha
Copy link
Member Author

@mattklein123 @ggreenway @alyssawilk @lizan thanks for reviewing/merging!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants