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

Fixed Heap-buffer-overflow in parse_unix via clusterfuzz #9833

Merged
merged 5 commits into from Feb 28, 2017

Conversation

Projects
None yet
4 participants
@dgquintas

This comment has been minimized.

Copy link
Author

commented Feb 22, 2017

Do you know if something needs to be done to mark the clusterfuzz entry as fixed?


un->sun_family = AF_UNIX;
strcpy(un->sun_path, uri->path);
strncpy(un->sun_path, uri->path, sizeof(un->sun_path) - 1 /* null term'd */);

This comment has been minimized.

Copy link
@markdroth

markdroth Feb 22, 2017

Member

strncpy() is notoriously difficult to use in a safe way, because if the string is longer than the specified length, it does not copy the trailing NUL byte to the destination string. strlcpy() is a much safer alternative to strncpy(), but it's unfortunately not portable. :(

I assume that this misfeature of strncpy() is why you're using memset() above to initialize the whole struct. However, that seems a bit fragile, because a future developer looking at this code might think "the memset() isn't really necessary, because we're explicitly setting all of the fields below". An alternative would be to do something like this right after this line:

un->sun_path[sizeof(un->sun_path) - 1] = '\0';

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 22, 2017

Author

Yes, that was exactly why I was zeroing the memory. Good point though, setting the terminator explicitly is more robust.

Done.

David Garcia Quintas
@@ -51,7 +51,8 @@ int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr) {
struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr;

un->sun_family = AF_UNIX;
strcpy(un->sun_path, uri->path);
strncpy(un->sun_path, uri->path, sizeof(un->sun_path) - 1 /* null term'd */);

This comment has been minimized.

Copy link
@ctiller

ctiller Feb 23, 2017

Member

Should we just return 0 if uri->path is too long?

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

yes. Done.

@dgquintas dgquintas force-pushed the dgquintas:parse_address_clusterfuzz branch from a40662e to d6d86b0 Feb 23, 2017

@@ -49,12 +49,11 @@

int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr) {
struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr;

const size_t maxlen = sizeof(un->sun_path);
if (strnlen(uri->path, maxlen) == maxlen) return 0;

This comment has been minimized.

Copy link
@markdroth

markdroth Feb 23, 2017

Member

Won't this fail if the size is exactly maxlen?

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

If the size is exactly maxlen, we have a problem: the \0 would be one-past the end

resolved_addr->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;

strcpy(un->sun_path, uri->path);
resolved_addr->len = strlen(un->sun_path) + maxlen + 1;

This comment has been minimized.

Copy link
@markdroth

markdroth Feb 23, 2017

Member

Looks like you put maxlen here instead of sizeof(un->sun_family), where you probably actually meant to replace sizeof(un->sun_path).

Shouldn't this actually just be set to sizeof(struct sockaddr_un)?

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

You are too quick :) I noticed right after pushing, took me ~1 min to fix and re-push but that was already too late!

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

agh! hold on, push didn't go through...

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

ok, NOW. PTAL.

@dgquintas

This comment has been minimized.

Copy link
Author

commented Feb 23, 2017

@dgquintas dgquintas force-pushed the dgquintas:parse_address_clusterfuzz branch from d6d86b0 to e2869fe Feb 23, 2017

resolved_addr->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;

strcpy(un->sun_path, uri->path);
resolved_addr->len = path_len + sizeof(un->sun_family) + 1;

This comment has been minimized.

Copy link
@markdroth

markdroth Feb 23, 2017

Member

I still don't think it's appropriate to use path_len here, since the size of the address is the size of the buffer, not the size of the string in the buffer (which may be smaller than the full size of the buffer).

Why are we not simply saying resolved_addr->len = sizeof(struct sockaddr_un)?

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

This was the previously existing logic, but I think you are right: all other types of addresses populate len with that way. Changed.

@@ -49,12 +49,12 @@

int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr) {
struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr;

const size_t maxlen = sizeof(un->sun_path);
const size_t path_len = strnlen(uri->path, maxlen);

This comment has been minimized.

Copy link
@markdroth

markdroth Feb 23, 2017

Member

Doesn't look like the logic actually changed here. But upon further review, I think this is correct: if the NUL byte is at maxlen, strnlen() will return maxlen - 1, which is fine, and if the NUL byte is at maxlen + 1, we don't have enough room in the buffer, so we should fail.

This comment has been minimized.

Copy link
@dgquintas

dgquintas Feb 23, 2017

Author

Yes, exactly.

David Garcia Quintas
@dgquintas

This comment has been minimized.

Copy link
Author

commented Feb 28, 2017

@dgquintas dgquintas merged commit 5f13f48 into grpc:master Feb 28, 2017

11 of 15 checks passed

Basic Tests Linux Build finished.
Details
Bazel Full Build & Tests Build finished.
Details
gRPC_interop_pull_requests Build finished.
Details
gRPC_performance_pull_requests Build finished.
Details
Asan C Build finished.
Details
Asan C++ Build finished.
Details
Basic Tests Mac Build finished.
Details
Basic Tests Windows Build finished.
Details
Bazel Basic Build Build finished.
Details
Msan C Build finished.
Details
Portability Tests Linux Build finished.
Details
Portability Tests Windows Build finished.
Details
Tsan C Build finished.
Details
Tsan C++ Build finished.
Details
cla/google All necessary CLAs are signed

@lock lock bot locked as resolved and limited conversation to collaborators Jan 24, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.