diff options
-rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_socket.c | 193 |
1 files changed, 103 insertions, 90 deletions
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c index 180cffe6c6..d91c9cdad9 100644 --- a/usr/src/uts/common/brand/lx/syscall/lx_socket.c +++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c @@ -44,6 +44,7 @@ #include <sys/vmsystm.h> #include <sys/limits.h> #include <sys/fcntl.h> +#include <sys/sysmacros.h> #include <netpacket/packet.h> #include <sockcommon.h> #include <socktpi_impl.h> @@ -175,9 +176,10 @@ static const int stol_family[LX_AF_MAX + 1] = { * directory with .ABSK_ prefixed to their names. */ #define ABST_PRFX "/tmp/.ABSK_" -#define ABST_PRFX_LEN 11 +#define ABST_PRFX_LEN (sizeof (ABST_PRFX) - 1) #define LX_DEV_LOG "/dev/log" +#define LX_DEV_LOG_LEN (sizeof (LX_DEV_LOG) - 1) #define LX_DEV_LOG_REDIRECT "/var/run/.dev_log_redirect" typedef enum { @@ -298,51 +300,112 @@ lx_xlate_sock_flags(int inflags, lx_xlate_dir_t dir) return (outflags); } +typedef enum lx_sun_type { + LX_SUN_NORMAL, + LX_SUN_ABSTRACT, + LX_SUN_DEVLOG +} lx_sun_type_t; + static void -convert_abst_path(const struct sockaddr *inaddr, socklen_t len, - struct sockaddr *outaddr) +ltos_sockaddr_ux(const struct sockaddr *inaddr, const socklen_t inlen, + struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type) { - int idx, odx; struct sockaddr_un buf; + /* Calculate size of (sun_family + any padding) in sockaddr */ + int sizediff = (sizeof (buf) - sizeof (buf.sun_path)); + int len = inlen - sizediff; - /* - * len is the entire size of the sockaddr data structure, including the - * sa_family, so we need to subtract this out. - */ - len -= sizeof (sa_family_t); - - /* Add our abstract prefix */ - (void) strcpy(buf.sun_path, ABST_PRFX); - for (idx = 1, odx = ABST_PRFX_LEN; - idx < len && odx < sizeof (buf.sun_path); idx++, odx++) { - char c = inaddr->sa_data[idx]; - if (c == '\0' || c == '/') { - buf.sun_path[odx] = '_'; + VERIFY(len > 0); + VERIFY(len <= sizeof (buf.sun_path)); + bzero(&buf, sizeof(buf)); + + if (inaddr->sa_data[0] == '\0') { + /* + * Linux supports abstract Unix sockets, which are simply + * sockets that do not exist on the file system. These sockets + * are denoted by beginning the path with a NULL character. To + * support these, we strip out the leading NULL character and + * change the path to point to a real place in /tmp directory, + * by prepending ABST_PRFX and replacing all illegal characters + * with * '_'. + * + * Since these sockets are supposed to exist outside the + * filesystem, they must be cleaned up after use. This removal + * is performed during bind(). + */ + int idx, odx; + + /* Add our abstract prefix */ + (void) strcpy(buf.sun_path, ABST_PRFX); + for (idx = 1, odx = ABST_PRFX_LEN; + idx < len && odx < sizeof (buf.sun_path); + idx++, odx++) { + char c = inaddr->sa_data[idx]; + if (c == '\0' || c == '/') { + buf.sun_path[odx] = '_'; + } else { + buf.sun_path[odx] = c; + } + } + + /* + * Since abstract socket addresses might not be NUL terminated, + * we must explicitly NUL terminate the translated path. + * Care is taken not to overflow the buffer. + */ + if (odx == sizeof (buf.sun_path)) { + buf.sun_path[odx - 1] = '\0'; } else { - buf.sun_path[odx] = c; + buf.sun_path[odx] = '\0'; } - } - /* - * Since abstract socket paths may not be NULL terminated, we must - * explicitly NULL terminate our string. Don't overflow the buffer if - * the path is exactly that size. - */ - if (odx == sizeof (buf.sun_path)) { - buf.sun_path[odx - 1] = '\0'; + if (sun_type != NULL) { + *sun_type = LX_SUN_ABSTRACT; + } + } else if (len > LX_DEV_LOG_LEN && + strcmp(inaddr->sa_data, LX_DEV_LOG) == 0) { + /* + * In order to support /dev/log -- a Unix domain socket used + * for logging that has had its path hard-coded far and wide -- + * we need to relocate the socket into a writable filesystem. + * + * Since this path should be reusable after being closed (but + * not unlinked), it is implicitly cleaned up during bind(). + */ + + (void) strcpy(buf.sun_path, LX_DEV_LOG_REDIRECT); + + if (sun_type != NULL) { + *sun_type = LX_SUN_DEVLOG; + } } else { - buf.sun_path[odx] = '\0'; + /* Copy the address directly, minding termination */ + (void) strncpy(buf.sun_path, inaddr->sa_data, len); + len = strnlen(buf.sun_path, len); + if (len == sizeof (buf.sun_path)) { + buf.sun_path[len - 1] = '\0'; + } else { + VERIFY(len < sizeof (buf.sun_path)); + buf.sun_path[len] = '\0'; + } + + if (sun_type != NULL) { + *sun_type = LX_SUN_NORMAL; + } } + buf.sun_family = AF_UNIX; + *outlen = strlen(buf.sun_path) + 1 + sizediff; + VERIFY(*outlen <= sizeof (struct sockaddr_un)); - (void) strcpy(outaddr->sa_data, buf.sun_path); + *outaddr = kmem_alloc(*outlen, KM_SLEEP); + bcopy(&buf, *outaddr, *outlen); } -typedef enum lx_sun_type { - LX_SUN_NORMAL, - LX_SUN_ABSTRACT, - LX_SUN_DEVLOG -} lx_sun_type_t; - +/* + * Copy in a Linux-native socket address from userspace and convert it into + * illumos format. When successful, it will allocate an appropriately sized + * struct to be freed by the caller. + */ static long ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen, struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type) @@ -380,63 +443,9 @@ ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen, error = EINVAL; break; } + ltos_sockaddr_ux(laddr, inlen, outaddr, outlen, + sun_type); - /* - * Since the address may expand during translation, - * allocate a full sockaddr_un structure for output. - * Use of kmem_zalloc prevents garbage from appearing - * in the output if the address is shorter than the - * maximum. - */ - *outlen = sizeof (struct sockaddr_un); - *outaddr = kmem_zalloc(*outlen, KM_SLEEP); - (*outaddr)->sa_family = AF_UNIX; - - if (strcmp(laddr->sa_data, LX_DEV_LOG) == 0) { - /* - * In order to support /dev/log -- a Unix - * domain socket used for logging that has had - * its path hard-coded far and wide -- we need - * to relocate the socket into a writable - * filesystem. - * - * Since this path should be reusable after - * being closed (but not unlinked), it is - * implicitly cleaned up during bind(). - */ - (void) strcpy((*outaddr)->sa_data, - LX_DEV_LOG_REDIRECT); - if (sun_type != NULL) { - *sun_type = LX_SUN_DEVLOG; - } - } else if (laddr->sa_data[0] == '\0') { - /* - * Linux supports abstract Unix sockets, which - * are simply sockets that do not exist on the - * file system. These sockets are denoted by - * beginning the path with a NULL character. To - * support these, we strip out the leading NULL - * character and change the path to point to a - * real place in /tmp directory, by prepending - * ABST_PRFX and replacing all illegal - * characters with * '_'. - * - * Since these sockets are supposed to exist - * outside the filesystem, they must be cleaned - * up after use. This removal is performed - * during bind(). - */ - convert_abst_path(laddr, inlen, *outaddr); - if (sun_type != NULL) { - *sun_type = LX_SUN_ABSTRACT; - } - } else { - bcopy(laddr->sa_data, (*outaddr)->sa_data, - inlen - sizeof (sa_family_t)); - if (sun_type != NULL) { - *sun_type = LX_SUN_NORMAL; - } - } /* AF_UNIX bypasses the standard copy logic */ kmem_free(laddr, inlen); return (0); @@ -503,6 +512,10 @@ ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen, return (error); } +/* + * Convert an illumos-native socket address into Linux format and copy it out + * to userspace. + */ static long stol_sockaddr_copyout(struct sockaddr *inaddr, socklen_t inlen, struct sockaddr *outaddr, socklen_t *outlen, socklen_t orig) |