Linux supports abstract addresses for AF_UNIX. These have .sun_path starting with '\0'. When presented in human-readable form they have a leading '@' instead. The length of the sockaddr must not include any trailing zeroes after the abstract name, as they will treated as part of the name and cause address matching to fail. This patch makes various changes to code that works with sun_path to ensure that abstract addresses work correctly. In particular it fixes a bug in __rpc_sockisbound() which incorrectly determines that a socket bound to an abstract address is in fact not bound. This prevents sockets with abstract addresses being used even when created outside of the library. Signed-off-by: NeilBrown <neilb@xxxxxxx> --- src/rpc_com.h | 6 ++++++ src/rpc_generic.c | 18 ++++++++++++------ src/rpc_soc.c | 6 +++++- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/rpc_com.h b/src/rpc_com.h index 76badefcfe90..ded72d1a647e 100644 --- a/src/rpc_com.h +++ b/src/rpc_com.h @@ -60,6 +60,12 @@ bool_t __xdrrec_getrec(XDR *, enum xprt_stat *, bool_t); void __xprt_unregister_unlocked(SVCXPRT *); void __xprt_set_raddr(SVCXPRT *, const struct sockaddr_storage *); +/* Evaluate to actual length of the `sockaddr_un' structure, whether + * abstract or not. + */ +#include <stddef.h> +#define SUN_LEN_A(ptr) (offsetof(struct sockaddr_un, sun_path) \ + + 1 + strlen((ptr)->sun_path + 1)) extern int __svc_maxrec; diff --git a/src/rpc_generic.c b/src/rpc_generic.c index aabbe4be896c..ee44c8d6eac4 100644 --- a/src/rpc_generic.c +++ b/src/rpc_generic.c @@ -650,7 +650,8 @@ __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf) if (path_len < 0) return NULL; - if (asprintf(&ret, "%.*s", path_len, sun->sun_path) < 0) + if (asprintf(&ret, "%c%.*s", sun->sun_path[0] ?: '@', + path_len - 1, sun->sun_path + 1) < 0) return (NULL); break; default: @@ -682,9 +683,10 @@ __rpc_uaddr2taddr_af(int af, const char *uaddr) /* * AF_LOCAL addresses are expected to be absolute - * pathnames, anything else will be AF_INET or AF_INET6. + * pathnames or abstract names, anything else will be + * AF_INET or AF_INET6. */ - if (*addrstr != '/') { + if (*addrstr != '/' && *addrstr != '@') { p = strrchr(addrstr, '.'); if (p == NULL) goto out; @@ -747,6 +749,9 @@ __rpc_uaddr2taddr_af(int af, const char *uaddr) strncpy(sun->sun_path, addrstr, sizeof(sun->sun_path) - 1); ret->len = SUN_LEN(sun); ret->maxlen = sizeof(struct sockaddr_un); + if (sun->sun_path[0] == '@') + /* Abstract address */ + sun->sun_path[0] = '\0'; ret->buf = sun; break; default: @@ -834,6 +839,7 @@ __rpc_sockisbound(int fd) struct sockaddr_un usin; } u_addr; socklen_t slen; + int path_len; slen = sizeof (struct sockaddr_storage); if (getsockname(fd, (struct sockaddr *)(void *)&ss, &slen) < 0) @@ -849,9 +855,9 @@ __rpc_sockisbound(int fd) return (u_addr.sin6.sin6_port != 0); #endif case AF_LOCAL: - /* XXX check this */ - memcpy(&u_addr.usin, &ss, sizeof(u_addr.usin)); - return (u_addr.usin.sun_path[0] != 0); + memcpy(&u_addr.usin, &ss, sizeof(u_addr.usin)); + path_len = slen - offsetof(struct sockaddr_un, sun_path); + return path_len > 0; default: break; } diff --git a/src/rpc_soc.c b/src/rpc_soc.c index fde121db75cf..c6c93b50337d 100644 --- a/src/rpc_soc.c +++ b/src/rpc_soc.c @@ -701,7 +701,11 @@ svcunix_create(sock, sendsize, recvsize, path) memset(&sun, 0, sizeof sun); sun.sun_family = AF_LOCAL; strncpy(sun.sun_path, path, (sizeof(sun.sun_path)-1)); - addrlen = sizeof(struct sockaddr_un); + if (sun.sun_path[0] == '@') + /* abstract address */ + sun.sun_path[0] = '\0'; + + addrlen = SUN_LEN_A(&sun); sa = (struct sockaddr *)&sun; if (bind(sock, sa, addrlen) < 0) -- 2.43.0