mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-15 10:12:24 +00:00
Merge pull request #1539 from brauner/2017-05-06/fix_abstract_unix_sockets
bugfixes: {segfaults, hashes, abstract unix sockets}
This commit is contained in:
commit
218a8306c2
@ -55,8 +55,9 @@ int lxc_abstract_unix_open(const char *path, int type, int flags)
|
|||||||
|
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
len = strlen(&path[1]) + 1;
|
len = strlen(&path[1]);
|
||||||
if (len >= sizeof(addr.sun_path) - 1) {
|
/* do not enforce \0-termination */
|
||||||
|
if (len >= sizeof(addr.sun_path)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = ENAMETOOLONG;
|
errno = ENAMETOOLONG;
|
||||||
return -1;
|
return -1;
|
||||||
@ -64,7 +65,7 @@ int lxc_abstract_unix_open(const char *path, int type, int flags)
|
|||||||
/* addr.sun_path[0] has already been set to 0 by memset() */
|
/* addr.sun_path[0] has already been set to 0 by memset() */
|
||||||
strncpy(&addr.sun_path[1], &path[1], strlen(&path[1]));
|
strncpy(&addr.sun_path[1], &path[1], strlen(&path[1]));
|
||||||
|
|
||||||
if (bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len)) {
|
if (bind(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1)) {
|
||||||
int tmp = errno;
|
int tmp = errno;
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = tmp;
|
errno = tmp;
|
||||||
@ -109,8 +110,9 @@ int lxc_abstract_unix_connect(const char *path)
|
|||||||
|
|
||||||
addr.sun_family = AF_UNIX;
|
addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
len = strlen(&path[1]) + 1;
|
len = strlen(&path[1]);
|
||||||
if (len >= sizeof(addr.sun_path) - 1) {
|
/* do not enforce \0-termination */
|
||||||
|
if (len >= sizeof(addr.sun_path)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
errno = ENAMETOOLONG;
|
errno = ENAMETOOLONG;
|
||||||
return -1;
|
return -1;
|
||||||
@ -118,7 +120,7 @@ int lxc_abstract_unix_connect(const char *path)
|
|||||||
/* addr.sun_path[0] has already been set to 0 by memset() */
|
/* addr.sun_path[0] has already been set to 0 by memset() */
|
||||||
strncpy(&addr.sun_path[1], &path[1], strlen(&path[1]));
|
strncpy(&addr.sun_path[1], &path[1], strlen(&path[1]));
|
||||||
|
|
||||||
if (connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len)) {
|
if (connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len + 1)) {
|
||||||
int tmp = errno;
|
int tmp = errno;
|
||||||
/* special case to connect to older containers */
|
/* special case to connect to older containers */
|
||||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
|
||||||
@ -136,8 +138,8 @@ int lxc_abstract_unix_send_fd(int fd, int sendfd, void *data, size_t size)
|
|||||||
struct msghdr msg = { 0 };
|
struct msghdr msg = { 0 };
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0};
|
||||||
char buf[1];
|
char buf[1] = {0};
|
||||||
int *val;
|
int *val;
|
||||||
|
|
||||||
msg.msg_control = cmsgbuf;
|
msg.msg_control = cmsgbuf;
|
||||||
@ -166,9 +168,9 @@ int lxc_abstract_unix_recv_fd(int fd, int *recvfd, void *data, size_t size)
|
|||||||
struct msghdr msg = { 0 };
|
struct msghdr msg = { 0 };
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
||||||
char buf[1];
|
|
||||||
int ret, *val;
|
int ret, *val;
|
||||||
|
char cmsgbuf[CMSG_SPACE(sizeof(int))] = {0};
|
||||||
|
char buf[1] = {0};
|
||||||
|
|
||||||
msg.msg_name = NULL;
|
msg.msg_name = NULL;
|
||||||
msg.msg_namelen = 0;
|
msg.msg_namelen = 0;
|
||||||
@ -210,8 +212,8 @@ int lxc_abstract_unix_send_credential(int fd, void *data, size_t size)
|
|||||||
.uid = getuid(),
|
.uid = getuid(),
|
||||||
.gid = getgid(),
|
.gid = getgid(),
|
||||||
};
|
};
|
||||||
char cmsgbuf[CMSG_SPACE(sizeof(cred))];
|
char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0};
|
||||||
char buf[1];
|
char buf[1] = {0};
|
||||||
|
|
||||||
msg.msg_control = cmsgbuf;
|
msg.msg_control = cmsgbuf;
|
||||||
msg.msg_controllen = sizeof(cmsgbuf);
|
msg.msg_controllen = sizeof(cmsgbuf);
|
||||||
@ -239,9 +241,9 @@ int lxc_abstract_unix_rcv_credential(int fd, void *data, size_t size)
|
|||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
struct ucred cred;
|
struct ucred cred;
|
||||||
char cmsgbuf[CMSG_SPACE(sizeof(cred))];
|
|
||||||
char buf[1];
|
|
||||||
int ret;
|
int ret;
|
||||||
|
char cmsgbuf[CMSG_SPACE(sizeof(cred))] = {0};
|
||||||
|
char buf[1] = {0};
|
||||||
|
|
||||||
msg.msg_name = NULL;
|
msg.msg_name = NULL;
|
||||||
msg.msg_namelen = 0;
|
msg.msg_namelen = 0;
|
||||||
|
@ -24,8 +24,10 @@
|
|||||||
#ifndef __LXC_AF_UNIX_H
|
#ifndef __LXC_AF_UNIX_H
|
||||||
#define __LXC_AF_UNIX_H
|
#define __LXC_AF_UNIX_H
|
||||||
|
|
||||||
|
/* does not enforce \0-termination */
|
||||||
extern int lxc_abstract_unix_open(const char *path, int type, int flags);
|
extern int lxc_abstract_unix_open(const char *path, int type, int flags);
|
||||||
extern int lxc_abstract_unix_close(int fd);
|
extern int lxc_abstract_unix_close(int fd);
|
||||||
|
/* does not enforce \0-termination */
|
||||||
extern int lxc_abstract_unix_connect(const char *path);
|
extern int lxc_abstract_unix_connect(const char *path);
|
||||||
extern int lxc_abstract_unix_send_fd(int fd, int sendfd, void *data, size_t size);
|
extern int lxc_abstract_unix_send_fd(int fd, int sendfd, void *data, size_t size);
|
||||||
extern int lxc_abstract_unix_recv_fd(int fd, int *recvfd, void *data, size_t size);
|
extern int lxc_abstract_unix_recv_fd(int fd, int *recvfd, void *data, size_t size);
|
||||||
|
@ -74,14 +74,19 @@
|
|||||||
|
|
||||||
lxc_log_define(lxc_commands, lxc);
|
lxc_log_define(lxc_commands, lxc);
|
||||||
|
|
||||||
static int fill_sock_name(char *path, int len, const char *name,
|
static int fill_sock_name(char *path, int len, const char *lxcname,
|
||||||
const char *lxcpath, const char *hashed_sock_name)
|
const char *lxcpath, const char *hashed_sock_name)
|
||||||
{
|
{
|
||||||
|
const char *name;
|
||||||
char *tmppath;
|
char *tmppath;
|
||||||
size_t tmplen;
|
size_t tmplen;
|
||||||
uint64_t hash;
|
uint64_t hash;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
name = lxcname;
|
||||||
|
if (!name)
|
||||||
|
name = "";
|
||||||
|
|
||||||
if (hashed_sock_name != NULL) {
|
if (hashed_sock_name != NULL) {
|
||||||
ret = snprintf(path, len, "lxc/%s/command", hashed_sock_name);
|
ret = snprintf(path, len, "lxc/%s/command", hashed_sock_name);
|
||||||
if (ret < 0 || ret >= len) {
|
if (ret < 0 || ret >= len) {
|
||||||
@ -193,8 +198,11 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
|
|||||||
rsp->data = rspdata;
|
rsp->data = rspdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp->datalen == 0)
|
if (rsp->datalen == 0) {
|
||||||
|
DEBUG("command %s response data length is 0",
|
||||||
|
lxc_cmd_str(cmd->req.cmd));
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
if (rsp->datalen > LXC_CMD_DATA_MAX) {
|
if (rsp->datalen > LXC_CMD_DATA_MAX) {
|
||||||
ERROR("Command %s response data %d too long.",
|
ERROR("Command %s response data %d too long.",
|
||||||
lxc_cmd_str(cmd->req.cmd), rsp->datalen);
|
lxc_cmd_str(cmd->req.cmd), rsp->datalen);
|
||||||
@ -274,7 +282,7 @@ static int lxc_cmd(const char *name, struct lxc_cmd_rr *cmd, int *stopped,
|
|||||||
int sock, ret = -1;
|
int sock, ret = -1;
|
||||||
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
|
char path[sizeof(((struct sockaddr_un *)0)->sun_path)] = { 0 };
|
||||||
char *offset = &path[1];
|
char *offset = &path[1];
|
||||||
int len;
|
size_t len;
|
||||||
int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE;
|
int stay_connected = cmd->req.cmd == LXC_CMD_CONSOLE;
|
||||||
|
|
||||||
*stopped = 0;
|
*stopped = 0;
|
||||||
@ -982,7 +990,7 @@ int lxc_cmd_init(const char *name, struct lxc_handler *handler,
|
|||||||
* Although null termination isn't required by the API, we do it anyway
|
* Although null termination isn't required by the API, we do it anyway
|
||||||
* because we print the sockname out sometimes.
|
* because we print the sockname out sometimes.
|
||||||
*/
|
*/
|
||||||
len = sizeof(path)-2;
|
len = sizeof(path) - 2;
|
||||||
if (fill_sock_name(offset, len, name, lxcpath, NULL))
|
if (fill_sock_name(offset, len, name, lxcpath, NULL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -4370,7 +4370,10 @@ int list_active_containers(const char *lxcpath, char ***nret,
|
|||||||
*p2 = '\0';
|
*p2 = '\0';
|
||||||
|
|
||||||
if (is_hashed) {
|
if (is_hashed) {
|
||||||
if (strncmp(lxcpath, lxc_cmd_get_lxcpath(p), lxcpath_len) != 0)
|
char *recvpath = lxc_cmd_get_lxcpath(p);
|
||||||
|
if (!recvpath)
|
||||||
|
continue;
|
||||||
|
if (strncmp(lxcpath, recvpath, lxcpath_len) != 0)
|
||||||
continue;
|
continue;
|
||||||
p = lxc_cmd_get_name(p);
|
p = lxc_cmd_get_name(p);
|
||||||
}
|
}
|
||||||
|
@ -153,36 +153,52 @@ int lxc_monitor_close(int fd)
|
|||||||
return close(fd);
|
return close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enforces \0-termination for the abstract unix socket. This is not required
|
||||||
|
* but allows us to print it out.
|
||||||
|
*
|
||||||
|
* Older version of liblxc only allowed for 105 bytes to be used for the
|
||||||
|
* abstract unix domain socket name because the code for our abstract unix
|
||||||
|
* socket handling performed invalid checks. Since we \0-terminate we could now
|
||||||
|
* have a maximum of 106 chars. But to not break backwards compatibility we keep
|
||||||
|
* the limit at 105.
|
||||||
|
*/
|
||||||
int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) {
|
int lxc_monitor_sock_name(const char *lxcpath, struct sockaddr_un *addr) {
|
||||||
size_t len;
|
size_t len;
|
||||||
int ret;
|
int ret;
|
||||||
char *sockname;
|
|
||||||
char *path;
|
char *path;
|
||||||
uint64_t hash;
|
uint64_t hash;
|
||||||
|
|
||||||
/* addr.sun_path is only 108 bytes, so we hash the full name and
|
/* addr.sun_path is only 108 bytes, so we hash the full name and
|
||||||
* then append as much of the name as we can fit.
|
* then append as much of the name as we can fit.
|
||||||
*/
|
*/
|
||||||
sockname = &addr->sun_path[1];
|
|
||||||
memset(addr, 0, sizeof(*addr));
|
memset(addr, 0, sizeof(*addr));
|
||||||
addr->sun_family = AF_UNIX;
|
addr->sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
/* strlen("lxc/") + strlen("/monitor-sock") + 1 = 18 */
|
||||||
len = strlen(lxcpath) + 18;
|
len = strlen(lxcpath) + 18;
|
||||||
path = alloca(len);
|
path = alloca(len);
|
||||||
ret = snprintf(path, len, "lxc/%s/monitor-sock", lxcpath);
|
ret = snprintf(path, len, "lxc/%s/monitor-sock", lxcpath);
|
||||||
if (ret < 0 || (size_t)ret >= len) {
|
if (ret < 0 || (size_t)ret >= len) {
|
||||||
ERROR("Failed to create path for monitor.");
|
ERROR("failed to create name for monitor socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Note: snprintf() will \0-terminate addr->sun_path on the 106th byte
|
||||||
|
* and so the abstract socket name has 105 "meaningful" characters. This
|
||||||
|
* is absolutely intentional. For further info read the comment for this
|
||||||
|
* function above!
|
||||||
|
*/
|
||||||
len = sizeof(addr->sun_path) - 1;
|
len = sizeof(addr->sun_path) - 1;
|
||||||
hash = fnv_64a_buf(path, ret, FNV1A_64_INIT);
|
hash = fnv_64a_buf(path, ret, FNV1A_64_INIT);
|
||||||
ret = snprintf(sockname, len, "lxc/%016" PRIx64 "/%s", hash, lxcpath);
|
ret = snprintf(addr->sun_path, len, "@lxc/%016" PRIx64 "/%s", hash, lxcpath);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
|
ERROR("failed to create hashed name for monitor socket");
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
sockname[sizeof(addr->sun_path)-3] = '\0';
|
/* replace @ with \0 */
|
||||||
INFO("Using monitor socket name \"%s\".", sockname);
|
addr->sun_path[0] = '\0';
|
||||||
|
INFO("using monitor socket name \"%s\" (length of socket name %zu must be <= %zu)", &addr->sun_path[1], strlen(&addr->sun_path[1]), sizeof(addr->sun_path) - 3);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -193,7 +209,8 @@ int lxc_monitor_open(const char *lxcpath)
|
|||||||
int fd;
|
int fd;
|
||||||
size_t retry;
|
size_t retry;
|
||||||
size_t len;
|
size_t len;
|
||||||
int ret = 0, backoff_ms[] = {10, 50, 100};
|
int ret = -1;
|
||||||
|
int backoff_ms[] = {10, 50, 100};
|
||||||
|
|
||||||
if (lxc_monitor_sock_name(lxcpath, &addr) < 0)
|
if (lxc_monitor_sock_name(lxcpath, &addr) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -201,28 +218,32 @@ int lxc_monitor_open(const char *lxcpath)
|
|||||||
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
ERROR("Failed to create socket: %s.", strerror(errno));
|
ERROR("Failed to create socket: %s.", strerror(errno));
|
||||||
return -1;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlen(&addr.sun_path[1]) + 1;
|
len = strlen(&addr.sun_path[1]);
|
||||||
|
DEBUG("opening monitor socket %s with len %zu", &addr.sun_path[1], len);
|
||||||
if (len >= sizeof(addr.sun_path) - 1) {
|
if (len >= sizeof(addr.sun_path) - 1) {
|
||||||
ret = -1;
|
|
||||||
errno = ENAMETOOLONG;
|
errno = ENAMETOOLONG;
|
||||||
|
ret = -errno;
|
||||||
|
ERROR("name of monitor socket too long (%zu bytes): %s", len, strerror(errno));
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (retry = 0; retry < sizeof(backoff_ms) / sizeof(backoff_ms[0]); retry++) {
|
for (retry = 0; retry < sizeof(backoff_ms) / sizeof(backoff_ms[0]); retry++) {
|
||||||
ret = connect(fd, (struct sockaddr *)&addr, offsetof(struct sockaddr_un, sun_path) + len);
|
fd = lxc_abstract_unix_connect(addr.sun_path);
|
||||||
if (ret == 0 || errno != ECONNREFUSED)
|
if (fd < 0 || errno != ECONNREFUSED)
|
||||||
break;
|
break;
|
||||||
ERROR("Failed to connect to monitor socket. Retrying in %d ms.", backoff_ms[retry]);
|
ERROR("Failed to connect to monitor socket. Retrying in %d ms: %s", backoff_ms[retry], strerror(errno));
|
||||||
usleep(backoff_ms[retry] * 1000);
|
usleep(backoff_ms[retry] * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if (fd < 0) {
|
||||||
|
ret = -errno;
|
||||||
ERROR("Failed to connect to monitor socket: %s.", strerror(errno));
|
ERROR("Failed to connect to monitor socket: %s.", strerror(errno));
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user