util: Protect system calls against signals

Most system calls will fail if interrupted by a SIGALRM. Make sure we
block SIGALRM prior to do the syscall, and restore the set of signals
afterwards.

Signed-off-by: Olivier Fourdan <ofourdan@redhat.com>
This commit is contained in:
Olivier Fourdan 2022-12-06 14:21:16 +01:00 committed by Peter Hutterer
parent 085f96c3bb
commit 0ac9b1b02d
2 changed files with 48 additions and 20 deletions

View file

@ -47,7 +47,9 @@ xread_with_fds(int fd, void *buf, size_t count, int **fds)
.msg_controllen = sizeof(control),
};
sigset_t old_mask = signals_block();
int received = xerrno(recvmsg(fd, &msg, 0));
signals_release(old_mask);
if (received > 0) {
*fds = NULL;
@ -110,7 +112,11 @@ xsend_with_fd(int fd, const void *buf, size_t len, int *fds)
header->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(CMSG_FIRSTHDR(&msg)), fds, nfds * sizeof(int));
return xerrno(sendmsg(fd, &msg, MSG_NOSIGNAL));
sigset_t old_mask = signals_block();
int sent = xerrno(sendmsg(fd, &msg, MSG_NOSIGNAL));
signals_release(old_mask);
return sent;
}
/* consider this struct opaque */

View file

@ -43,7 +43,7 @@
/**
* Blocks the SIGALRM signal.
*
* @return the previous set of signals to pass to release_signals()
* @return the previous set of signals to pass to signals_release()
*/
static inline sigset_t
signals_block(void)
@ -61,7 +61,7 @@ signals_block(void)
/**
* Release the SIGALRM signal.
*
* @param mask: The previous set of signals as returned by block_signals().
* @param mask: The previous set of signals as returned by signals_block().
*/
static inline void
signals_release(sigset_t mask)
@ -81,12 +81,15 @@ xerrno(int value) {
}
/**
* Wrapper around close(). It checks for fd != -1 to satisfy coverity and
* friends and always returns -1.
* Wrapper around close() that blocks the SIGALRM signal. It checks for
* fd != -1 to satisfy coverity and friends and always returns -1.
*/
static inline int
xclose(int fd) {
sigset_t old_mask = signals_block();
if (fd != -1) close(fd);
signals_release(old_mask);
return -1;
}
@ -97,42 +100,56 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FILE *, fclose);
#define _cleanup_fclose_ _cleanup_(fclosep)
/**
* Wrapper around read(). Returns the number of bytes read or a negative
* errno on failure.
* Wrapper around read() that blocks the SIGALRM signal.
* Returns the number of bytes read or a negative errno on failure.
*/
static inline int
xread(int fd, void *buf, size_t count)
{
return xerrno(read(fd, buf, count));
sigset_t old_mask = signals_block();
int n = xerrno(read(fd, buf, count));
signals_release(old_mask);
return n;
}
/**
* Wrapper around read(). Returns the number of bytes read or a negative
* errno on failure. Any fds passed along with the message
* are stored in the -1-terminated allocated fds array, to be freed by the
* caller. Where no fds were passed, the array is NULL.
* Wrapper around read() that blocks the SIGALRM signal.
* Returns the number of bytes read or a negative errno on failure.
* Any fds passed along with the message are stored in the -1-terminated
* allocated fds array, to be freed by the caller. Where no fds were
* passed, the array is NULL.
*/
int
xread_with_fds(int fd, void *buf, size_t count, int **fds);
/**
* Wrapper around write(). Returns the number of bytes written or a negative
* errno on failure.
* Wrapper around write() that blocks the SIGALRM signal.
* Returns the number of bytes written or a negative errno on failure.
*/
static inline int
xwrite(int fd, const void *buf, size_t count)
{
return xerrno(write(fd, buf, count));
sigset_t old_mask = signals_block();
int n = xerrno(write(fd, buf, count));
signals_release(old_mask);
return n;
}
/**
* Wrapper around send() that always sets MSG_NOSIGNAL. Returns the number
* of bytes written or a negative errno on failure.
* Wrapper around send() that always sets MSG_NOSIGNAL and blocks the
* SIGALRM signal.
* Returns the number of bytes written or a negative errno on failure.
*/
static inline int
xsend(int fd, const void *buf, size_t len)
{
return xerrno(send(fd, buf, len, MSG_NOSIGNAL));
sigset_t old_mask = signals_block();
int n = xerrno(send(fd, buf, len, MSG_NOSIGNAL));
signals_release(old_mask);
return n;
}
/**
@ -158,14 +175,19 @@ xconnect(const char *path)
if (!xsnprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path))
return -EINVAL;
sigset_t old_mask = signals_block();
int sockfd = socket(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0);
if (sockfd == -1)
return -errno;
goto fail;
if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
return -errno;
goto fail;
signals_release(old_mask);
return sockfd;
fail:
signals_release(old_mask);
return -errno;
}
/**