Skip to content

Commit 876e480

Browse files
keesjgunthorpe
authored andcommitted
RDMA/cma: Distinguish between sockaddr_in and sockaddr_in6 by size
Clang can do some aggressive inlining, which provides it with greater visibility into the sizes of various objects that are passed into helpers. Specifically, compare_netdev_and_ip() can see through the type given to the "sa" argument, which means it can generate code for "struct sockaddr_in" that would have been passed to ipv6_addr_cmp() (that expects to operate on the larger "struct sockaddr_in6"), which would result in a compile-time buffer overflow condition detected by memcmp(). Logically, this state isn't reachable due to the sa_family assignment two callers above and the check in compare_netdev_and_ip(). Instead, provide a compile-time check on sizes so the size-mismatched code will be elided when inlining. Avoids the following warning from Clang: ../include/linux/fortify-string.h:652:4: error: call to '__read_overflow' declared with 'error' attribute: detected read beyond size of object (1st parameter) __read_overflow(); ^ note: In function 'cma_netevent_callback' note: which inlined function 'node_from_ndev_ip' 1 error generated. When the underlying object size is not known (e.g. with GCC and older Clang), the result of __builtin_object_size() is SIZE_MAX, which will also compile away, leaving the code as it was originally. Link: https://lore.kernel.org/r/[email protected] Link: ClangBuiltLinux#1687 Signed-off-by: Kees Cook <[email protected]> Tested-by: Nathan Chancellor <[email protected]> # build Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 5ff31df commit 876e480

File tree

1 file changed

+12
-5
lines changed
  • drivers/infiniband/core

1 file changed

+12
-5
lines changed

drivers/infiniband/core/cma.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,20 @@ static int compare_netdev_and_ip(int ifindex_a, struct sockaddr *sa,
479479
if (sa->sa_family != sb->sa_family)
480480
return sa->sa_family - sb->sa_family;
481481

482-
if (sa->sa_family == AF_INET)
483-
return memcmp((char *)&((struct sockaddr_in *)sa)->sin_addr,
484-
(char *)&((struct sockaddr_in *)sb)->sin_addr,
482+
if (sa->sa_family == AF_INET &&
483+
__builtin_object_size(sa, 0) >= sizeof(struct sockaddr_in)) {
484+
return memcmp(&((struct sockaddr_in *)sa)->sin_addr,
485+
&((struct sockaddr_in *)sb)->sin_addr,
485486
sizeof(((struct sockaddr_in *)sa)->sin_addr));
487+
}
488+
489+
if (sa->sa_family == AF_INET6 &&
490+
__builtin_object_size(sa, 0) >= sizeof(struct sockaddr_in6)) {
491+
return ipv6_addr_cmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
492+
&((struct sockaddr_in6 *)sb)->sin6_addr);
493+
}
486494

487-
return ipv6_addr_cmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
488-
&((struct sockaddr_in6 *)sb)->sin6_addr);
495+
return -1;
489496
}
490497

491498
static int cma_add_id_to_tree(struct rdma_id_private *node_id_priv)

0 commit comments

Comments
 (0)