Merge pull request #10412 from poettering/sockaddr-sun-path

various fixes related to struct sockaddr_un handling
This commit is contained in:
Yu Watanabe
2018-10-17 01:25:58 +09:00
committed by GitHub
41 changed files with 378 additions and 341 deletions
+15
View File
@@ -46,6 +46,21 @@ static inline void *mfree(void *memory) {
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l) _alloc_(2);
#define memdupa(p, l) \
({ \
void *_q_; \
_q_ = alloca(l); \
memcpy(_q_, p, l); \
})
#define memdupa_suffix0(p, l) \
({ \
void *_q_; \
_q_ = alloca(l + 1); \
((uint8_t*) _q_)[l] = 0; \
memcpy(_q_, p, l); \
})
static inline void freep(void *p) {
free(*(void**) p);
}
+5 -9
View File
@@ -35,7 +35,7 @@ int socket_address_listen(
_cleanup_close_ int fd = -1;
const char *p;
int r, one;
int r;
assert(a);
@@ -74,26 +74,22 @@ int socket_address_listen(
return -errno;
if (reuse_port) {
one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &const_int_one, sizeof(const_int_one)) < 0)
log_warning_errno(errno, "SO_REUSEPORT failed: %m");
}
if (free_bind) {
one = 1;
if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &const_int_one, sizeof(const_int_one)) < 0)
log_warning_errno(errno, "IP_FREEBIND failed: %m");
}
if (transparent) {
one = 1;
if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &const_int_one, sizeof(const_int_one)) < 0)
log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
}
}
one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_one, sizeof(const_int_one)) < 0)
return -errno;
p = socket_address_get_path(a);
+100 -27
View File
@@ -57,8 +57,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
assert(a);
assert(s);
zero(*a);
a->type = SOCK_STREAM;
*a = (SocketAddress) {
.type = SOCK_STREAM,
};
if (*s == '[') {
uint16_t port;
@@ -96,7 +97,9 @@ int socket_address_parse(SocketAddress *a, const char *s) {
size_t l;
l = strlen(s);
if (l >= sizeof(a->sockaddr.un.sun_path))
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
* parsing (the kernel itself is less strict here in what it
* accepts) */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
@@ -108,7 +111,11 @@ int socket_address_parse(SocketAddress *a, const char *s) {
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here
* when parsing, even though abstract namespace sockets
* explicitly allow embedded NUL bytes and don't consider
* them special. But it's simply annoying to debug such
* sockets. */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
@@ -286,19 +293,28 @@ int socket_address_verify(const SocketAddress *a) {
case AF_UNIX:
if (a->size < offsetof(struct sockaddr_un, sun_path))
return -EINVAL;
if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will
* append a NUL byte if we have path sockets that are above
* sun_path' full size */
return -EINVAL;
if (a->size > offsetof(struct sockaddr_un, sun_path)) {
if (a->size > offsetof(struct sockaddr_un, sun_path) &&
a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */
if (a->sockaddr.un.sun_path[0] != 0) {
char *e;
/* path */
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
if (!e)
return -EINVAL;
const char *e;
e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
if (e) {
/* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */
if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
return -EINVAL;
} else {
/* If there's no embedded NUL byte, then then the size needs to match the whole
* structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
* and considers both equivalent: getsockname() even extends sockaddr_un beyond its
* size if the path is non NUL terminated.)*/
if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1))
return -EINVAL;
}
}
@@ -482,6 +498,10 @@ const char* socket_address_get_path(const SocketAddress *a) {
if (a->sockaddr.un.sun_path[0] == 0)
return NULL;
/* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un
* structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the
* full sun_path space. */
assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1);
return a->sockaddr.un.sun_path;
}
@@ -747,21 +767,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret)
return 0;
}
int socket_address_unlink(SocketAddress *a) {
assert(a);
if (socket_address_family(a) != AF_UNIX)
return 0;
if (a->sockaddr.un.sun_path[0] == 0)
return 0;
if (unlink(a->sockaddr.un.sun_path) < 0)
return -errno;
return 1;
}
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
@@ -1246,3 +1251,71 @@ int socket_ioctl_fd(void) {
return fd;
}
int sockaddr_un_unlink(const struct sockaddr_un *sa) {
const char *p, * nul;
assert(sa);
if (sa->sun_family != AF_UNIX)
return -EPROTOTYPE;
if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */
return 0;
/* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */
nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path));
if (nul)
p = sa->sun_path;
else
p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path));
if (unlink(p) < 0)
return -errno;
return 1;
}
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
size_t l;
assert(ret);
assert(path);
/* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as
* abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept
* anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to
* reference paths in the abstract namespace that include NUL bytes in the name. */
l = strlen(path);
if (l == 0)
return -EINVAL;
if (!IN_SET(path[0], '/', '@'))
return -EINVAL;
if (path[1] == 0)
return -EINVAL;
/* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
* the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket
* addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that
* do not expect non-NUL terminated file system path*/
if (l+1 > sizeof(ret->sun_path))
return -EINVAL;
*ret = (struct sockaddr_un) {
.sun_family = AF_UNIX,
};
if (path[0] == '@') {
/* Abstract namespace socket */
memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */
return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */
} else {
assert(path[0] == '/');
/* File system socket */
memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */
return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */
}
}
+9 -2
View File
@@ -71,7 +71,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
int socket_address_print(const SocketAddress *a, char **p);
int socket_address_verify(const SocketAddress *a) _pure_;
int socket_address_unlink(SocketAddress *a);
int sockaddr_un_unlink(const struct sockaddr_un *sa);
static inline int socket_address_unlink(const SocketAddress *a) {
return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0;
}
bool socket_address_can_accept(const SocketAddress *a) _pure_;
@@ -179,7 +184,9 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
offsetof(struct sockaddr_un, sun_path) + \
(_sa->sun_path[0] == 0 ? \
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
})
int socket_ioctl_fd(void);
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
+7 -9
View File
@@ -31,12 +31,11 @@ size_t strpcpy(char **dest, size_t size, const char *src) {
if (size > 1)
*dest = mempcpy(*dest, src, size-1);
size = 0;
} else {
if (len > 0) {
*dest = mempcpy(*dest, src, len);
size -= len;
}
} else if (len > 0) {
*dest = mempcpy(*dest, src, len);
size -= len;
}
*dest[0] = '\0';
return size;
}
@@ -56,9 +55,8 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
if (i < (int)size) {
*dest += i;
size -= i;
} else {
} else
size = 0;
}
va_end(va);
return size;
}
@@ -73,7 +71,7 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
do {
size = strpcpy(dest, size, src);
src = va_arg(va, char *);
} while (src != NULL);
} while (src);
va_end(va);
return size;
}
@@ -100,7 +98,7 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
do {
size = strpcpy(&s, size, src);
src = va_arg(va, char *);
} while (src != NULL);
} while (src);
va_end(va);
return size;
+3
View File
@@ -51,6 +51,9 @@ int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
const int const_int_zero = 0;
const int const_int_one = 1;
size_t page_size(void) {
static thread_local size_t pgsz = 0;
long r;
+3
View File
@@ -232,3 +232,6 @@ int version(void);
int str_verscmp(const char *s1, const char *s2);
void disable_coredumps(void);
extern const int const_int_zero;
extern const int const_int_one;