mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 22:35:15 +00:00
socket-util-unix: Fix umask race in bind_unix_socket().
The umask is a process-wide value, so bind_unix_socket() races with file creation in other Open vSwitch threads. This fixes the race. The workaround for non-Linux systems is not ideal, but I do not know any other general solution. I tested the workaround only on Linux. CC: YAMAMOTO Takashi <yamamoto@valinux.co.jp> Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Flavio Leitner <fbl@redhat.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "fatal-signal.h"
|
#include "fatal-signal.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
@@ -261,11 +262,44 @@ free_sockaddr_un(int dirfd, const char *linkname)
|
|||||||
static int
|
static int
|
||||||
bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len)
|
bind_unix_socket(int fd, struct sockaddr *sun, socklen_t sun_len)
|
||||||
{
|
{
|
||||||
/* According to _Unix Network Programming_, umask should affect bind(). */
|
const mode_t mode = 0700;
|
||||||
mode_t old_umask = umask(0077);
|
if (LINUX) {
|
||||||
int error = bind(fd, sun, sun_len) ? errno : 0;
|
/* On Linux, the fd's permissions become the file's permissions.
|
||||||
umask(old_umask);
|
* fchmod() does not affect other files, like umask() does. */
|
||||||
return error;
|
if (fchmod(fd, mode)) {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Must be after fchmod(). */
|
||||||
|
if (bind(fd, sun, sun_len)) {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* On FreeBSD and NetBSD, only the umask affects permissions. The
|
||||||
|
* umask is process-wide rather than thread-specific, so we have to use
|
||||||
|
* a subprocess for safety. */
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (!pid) {
|
||||||
|
umask(mode ^ 0777);
|
||||||
|
_exit(bind(fd, sun, sun_len) ? errno : 0);
|
||||||
|
} else if (pid > 0) {
|
||||||
|
int status;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
do {
|
||||||
|
error = waitpid(pid, &status, 0) < 0 ? errno : 0;
|
||||||
|
} while (error == EINTR);
|
||||||
|
|
||||||
|
return (error ? error
|
||||||
|
: WIFEXITED(status) ? WEXITSTATUS(status)
|
||||||
|
: WIFSIGNALED(status) ? EINTR
|
||||||
|
: ECHILD /* WTF? */);
|
||||||
|
} else {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
|
/* Creates a Unix domain socket in the given 'style' (either SOCK_DGRAM or
|
||||||
|
Reference in New Issue
Block a user