libselinux: fix avc_netlink_loop() error caused by nonblocking mode.

avc_open() creates the netlink socket in nonblocking mode.  If the
application later takes control of the netlink socket with
avc_netlink_acquire_fd() and then calls avc_netlink_loop(), it
will fail with EWOULDBLOCK.

To remedy this, remove the O_NONBLOCK flag from the netlink socket
at the start of avc_netlink_loop().  Also, with this fix, there is
no need for avc_open() to ever create a blocking socket, so change
that and update the man page.

-v2: use poll() in avc_netlink_check_nb().  This makes both
avc_netlink_loop() and avc_netlink_check_nb() independent of the
O_NONBLOCK flag.

-v3: move poll() to avc_receive() internal function; patch by
KaiGai Kohei <kaigai@kaigai.gr.jp>

Signed-off-by: Eamon Walsh <ewalsh@tycho.nsa.gov>
This commit is contained in:
Eamon Walsh 2010-02-26 15:18:38 -05:00
parent a73f32c3e3
commit 61d005b739
3 changed files with 21 additions and 12 deletions

View File

@ -41,12 +41,9 @@ descriptor is stored internally; use
.BR avc_netlink_acquire_fd (3) .BR avc_netlink_acquire_fd (3)
to take ownership of it in application code. The to take ownership of it in application code. The
.I blocking .I blocking
argument specifies whether read operations on the socket will block. argument controls whether the O_NONBLOCK flag is set on the socket descriptor.
.BR avc_open (3) .BR avc_open (3)
calls this function internally, specifying non-blocking behavior (unless calls this function internally, specifying non-blocking behavior.
threading callbacks were explicitly set using the deprecated
.BR avc_init (3)
interface, in which case blocking behavior is set).
.B avc_netlink_close .B avc_netlink_close
closes the netlink socket. This function is called automatically by closes the netlink socket. This function is called automatically by
@ -66,9 +63,7 @@ checks the netlink socket for pending messages and processes them.
Callbacks for policyload and enforcing changes will be called; Callbacks for policyload and enforcing changes will be called;
see see
.BR selinux_set_callback (3). .BR selinux_set_callback (3).
This function does not block unless This function does not block.
.BR avc_netlink_open (3)
specified blocking behavior.
.B avc_netlink_loop .B avc_netlink_loop
enters a loop blocking on the netlink socket and processing messages as they enters a loop blocking on the netlink socket and processing messages as they

View File

@ -222,7 +222,7 @@ int avc_init(const char *prefix,
avc_enforcing = rc; avc_enforcing = rc;
} }
rc = avc_netlink_open(avc_using_threads); rc = avc_netlink_open(0);
if (rc < 0) { if (rc < 0) {
avc_log(SELINUX_ERROR, avc_log(SELINUX_ERROR,
"%s: can't open netlink socket: %d (%s)\n", "%s: can't open netlink socket: %d (%s)\n",

View File

@ -15,6 +15,7 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <poll.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <linux/types.h> #include <linux/types.h>
@ -92,13 +93,26 @@ void avc_netlink_close(void)
close(fd); close(fd);
} }
static int avc_netlink_receive(char *buf, unsigned buflen) static int avc_netlink_receive(char *buf, unsigned buflen, int blocking)
{ {
int rc; int rc;
struct pollfd pfd = { fd, POLLIN | POLLPRI, 0 };
struct sockaddr_nl nladdr; struct sockaddr_nl nladdr;
socklen_t nladdrlen = sizeof nladdr; socklen_t nladdrlen = sizeof nladdr;
struct nlmsghdr *nlh = (struct nlmsghdr *)buf; struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
rc = poll(&pfd, 1, (blocking ? -1 : 0));
if (rc == 0 && !blocking) {
errno = EWOULDBLOCK;
return -1;
}
else if (rc < 1) {
avc_log(SELINUX_ERROR, "%s: netlink poll: error %d\n",
avc_prefix, errno);
return rc;
}
rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr, rc = recvfrom(fd, buf, buflen, 0, (struct sockaddr *)&nladdr,
&nladdrlen); &nladdrlen);
if (rc < 0) if (rc < 0)
@ -208,7 +222,7 @@ int avc_netlink_check_nb(void)
while (1) { while (1) {
errno = 0; errno = 0;
rc = avc_netlink_receive(buf, sizeof(buf)); rc = avc_netlink_receive(buf, sizeof(buf), 0);
if (rc < 0) { if (rc < 0) {
if (errno == EWOULDBLOCK) if (errno == EWOULDBLOCK)
return 0; return 0;
@ -235,7 +249,7 @@ void avc_netlink_loop(void)
while (1) { while (1) {
errno = 0; errno = 0;
rc = avc_netlink_receive(buf, sizeof(buf)); rc = avc_netlink_receive(buf, sizeof(buf), 1);
if (rc < 0) { if (rc < 0) {
if (errno == 0 || errno == EINTR) if (errno == 0 || errno == EINTR)
continue; continue;