|
|
a3e2b5 |
From 8798fcef9052d2946637fa2d4376844fad6b5f8c Mon Sep 17 00:00:00 2001
|
|
|
a3e2b5 |
From: Filipe Brandenburger <filbranden@google.com>
|
|
|
a3e2b5 |
Date: Tue, 24 Jul 2018 20:15:55 -0700
|
|
|
a3e2b5 |
Subject: [PATCH] test-socket-util: Add tests for receive_fd_iov() and friends.
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Test it when sending an FD without any contents, or an FD and some contents,
|
|
|
a3e2b5 |
or only contents and no FD (using a bare send().)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Also fix the previous test which forked but was missing an _exit() at the
|
|
|
a3e2b5 |
end of the child execution code.
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
(cherry picked from commit 8a3386ab4fea9c4efa9c72e7c149cf510a46f03e)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
Resolves: #1683319
|
|
|
a3e2b5 |
---
|
|
|
a3e2b5 |
src/test/test-socket-util.c | 215 ++++++++++++++++++++++++++++++++++++
|
|
|
a3e2b5 |
1 file changed, 215 insertions(+)
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c
|
|
|
a3e2b5 |
index 588485d881..19c5395b92 100644
|
|
|
a3e2b5 |
--- a/src/test/test-socket-util.c
|
|
|
a3e2b5 |
+++ b/src/test/test-socket-util.c
|
|
|
a3e2b5 |
@@ -6,8 +6,11 @@
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
#include "alloc-util.h"
|
|
|
a3e2b5 |
#include "async.h"
|
|
|
a3e2b5 |
+#include "exit-status.h"
|
|
|
a3e2b5 |
#include "fd-util.h"
|
|
|
a3e2b5 |
+#include "fileio.h"
|
|
|
a3e2b5 |
#include "in-addr-util.h"
|
|
|
a3e2b5 |
+#include "io-util.h"
|
|
|
a3e2b5 |
#include "log.h"
|
|
|
a3e2b5 |
#include "macro.h"
|
|
|
a3e2b5 |
#include "process-util.h"
|
|
|
a3e2b5 |
@@ -484,9 +487,215 @@ static void test_getpeercred_getpeergroups(void) {
|
|
|
a3e2b5 |
}
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
safe_close_pair(pair);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
}
|
|
|
a3e2b5 |
}
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
+static void test_passfd_read(void) {
|
|
|
a3e2b5 |
+ static const char file_contents[] = "test contents for passfd";
|
|
|
a3e2b5 |
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
|
a3e2b5 |
+ int r;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ r = safe_fork("(passfd_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (r == 0) {
|
|
|
a3e2b5 |
+ /* Child */
|
|
|
a3e2b5 |
+ char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX";
|
|
|
a3e2b5 |
+ _cleanup_close_ int tmpfd = -1;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[0] = safe_close(pair[0]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ tmpfd = mkostemp_safe(tmpfile);
|
|
|
a3e2b5 |
+ assert_se(tmpfd >= 0);
|
|
|
a3e2b5 |
+ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents));
|
|
|
a3e2b5 |
+ tmpfd = safe_close(tmpfd);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ tmpfd = open(tmpfile, O_RDONLY);
|
|
|
a3e2b5 |
+ assert_se(tmpfd >= 0);
|
|
|
a3e2b5 |
+ assert_se(unlink(tmpfile) == 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(send_one_fd(pair[1], tmpfd, MSG_DONTWAIT) == 0);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
+ }
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* Parent */
|
|
|
a3e2b5 |
+ char buf[64];
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ _cleanup_close_ int fd = -1;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[1] = safe_close(pair[1]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd) == 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(fd >= 0);
|
|
|
a3e2b5 |
+ r = read(fd, buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+ buf[r] = 0;
|
|
|
a3e2b5 |
+ assert_se(streq(buf, file_contents));
|
|
|
a3e2b5 |
+}
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+static void test_passfd_contents_read(void) {
|
|
|
a3e2b5 |
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
|
a3e2b5 |
+ static const char file_contents[] = "test contents in the file";
|
|
|
a3e2b5 |
+ static const char wire_contents[] = "test contents on the wire";
|
|
|
a3e2b5 |
+ int r;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (r == 0) {
|
|
|
a3e2b5 |
+ /* Child */
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT_STRING(wire_contents);
|
|
|
a3e2b5 |
+ char tmpfile[] = "/tmp/test-socket-util-passfd-contents-read-XXXXXX";
|
|
|
a3e2b5 |
+ _cleanup_close_ int tmpfd = -1;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[0] = safe_close(pair[0]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ tmpfd = mkostemp_safe(tmpfile);
|
|
|
a3e2b5 |
+ assert_se(tmpfd >= 0);
|
|
|
a3e2b5 |
+ assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents));
|
|
|
a3e2b5 |
+ tmpfd = safe_close(tmpfd);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ tmpfd = open(tmpfile, O_RDONLY);
|
|
|
a3e2b5 |
+ assert_se(tmpfd >= 0);
|
|
|
a3e2b5 |
+ assert_se(unlink(tmpfile) == 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(send_one_fd_iov(pair[1], tmpfd, &iov, 1, MSG_DONTWAIT) > 0);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
+ }
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* Parent */
|
|
|
a3e2b5 |
+ char buf[64];
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ _cleanup_close_ int fd = -1;
|
|
|
a3e2b5 |
+ ssize_t k;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[1] = safe_close(pair[1]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd;;
|
|
|
a3e2b5 |
+ assert_se(k > 0);
|
|
|
a3e2b5 |
+ buf[k] = 0;
|
|
|
a3e2b5 |
+ assert_se(streq(buf, wire_contents));
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(fd >= 0);
|
|
|
a3e2b5 |
+ r = read(fd, buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+ buf[r] = 0;
|
|
|
a3e2b5 |
+ assert_se(streq(buf, file_contents));
|
|
|
a3e2b5 |
+}
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+static void test_receive_nopassfd(void) {
|
|
|
a3e2b5 |
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
|
a3e2b5 |
+ static const char wire_contents[] = "no fd passed here";
|
|
|
a3e2b5 |
+ int r;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ r = safe_fork("(receive_nopassfd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (r == 0) {
|
|
|
a3e2b5 |
+ /* Child */
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT_STRING(wire_contents);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[0] = safe_close(pair[0]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) > 0);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
+ }
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* Parent */
|
|
|
a3e2b5 |
+ char buf[64];
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ int fd = -999;
|
|
|
a3e2b5 |
+ ssize_t k;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[1] = safe_close(pair[1]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd;;
|
|
|
a3e2b5 |
+ assert_se(k > 0);
|
|
|
a3e2b5 |
+ buf[k] = 0;
|
|
|
a3e2b5 |
+ assert_se(streq(buf, wire_contents));
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* no fd passed here, confirm it was reset */
|
|
|
a3e2b5 |
+ assert_se(fd == -1);
|
|
|
a3e2b5 |
+}
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+static void test_send_nodata_nofd(void) {
|
|
|
a3e2b5 |
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
|
a3e2b5 |
+ int r;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ r = safe_fork("(send_nodata_nofd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (r == 0) {
|
|
|
a3e2b5 |
+ /* Child */
|
|
|
a3e2b5 |
+ pair[0] = safe_close(pair[0]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(send_one_fd_iov(pair[1], -1, NULL, 0, MSG_DONTWAIT) == -EINVAL);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
+ }
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* Parent */
|
|
|
a3e2b5 |
+ char buf[64];
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ int fd = -999;
|
|
|
a3e2b5 |
+ ssize_t k;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[1] = safe_close(pair[1]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd;;
|
|
|
a3e2b5 |
+ /* recvmsg() will return errno EAGAIN if nothing was sent */
|
|
|
a3e2b5 |
+ assert_se(k == -EAGAIN);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */
|
|
|
a3e2b5 |
+ assert_se(fd == -999);
|
|
|
a3e2b5 |
+}
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+static void test_send_emptydata(void) {
|
|
|
a3e2b5 |
+ _cleanup_close_pair_ int pair[2] = { -1, -1 };
|
|
|
a3e2b5 |
+ int r;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ r = safe_fork("(send_emptydata)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
|
|
|
a3e2b5 |
+ assert_se(r >= 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ if (r == 0) {
|
|
|
a3e2b5 |
+ /* Child */
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT_STRING(""); /* zero-length iov */
|
|
|
a3e2b5 |
+ assert_se(iov.iov_len == 0);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[0] = safe_close(pair[0]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* This will succeed, since iov is set. */
|
|
|
a3e2b5 |
+ assert_se(send_one_fd_iov(pair[1], -1, &iov, 1, MSG_DONTWAIT) == 0);
|
|
|
a3e2b5 |
+ _exit(EXIT_SUCCESS);
|
|
|
a3e2b5 |
+ }
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* Parent */
|
|
|
a3e2b5 |
+ char buf[64];
|
|
|
a3e2b5 |
+ struct iovec iov = IOVEC_INIT(buf, sizeof(buf)-1);
|
|
|
a3e2b5 |
+ int fd = -999;
|
|
|
a3e2b5 |
+ ssize_t k;
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ pair[1] = safe_close(pair[1]);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ k = receive_one_fd_iov(pair[0], &iov, 1, MSG_DONTWAIT, &fd;;
|
|
|
a3e2b5 |
+ /* receive_one_fd_iov() returns -EIO if an fd is not found and no data was returned. */
|
|
|
a3e2b5 |
+ assert_se(k == -EIO);
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
+ /* receive_one_fd_iov returned error, so confirm &fd wasn't touched */
|
|
|
a3e2b5 |
+ assert_se(fd == -999);
|
|
|
a3e2b5 |
+}
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
int main(int argc, char *argv[]) {
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
log_set_max_level(LOG_DEBUG);
|
|
|
a3e2b5 |
@@ -515,5 +724,11 @@ int main(int argc, char *argv[]) {
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
test_getpeercred_getpeergroups();
|
|
|
a3e2b5 |
|
|
|
a3e2b5 |
+ test_passfd_read();
|
|
|
a3e2b5 |
+ test_passfd_contents_read();
|
|
|
a3e2b5 |
+ test_receive_nopassfd();
|
|
|
a3e2b5 |
+ test_send_nodata_nofd();
|
|
|
a3e2b5 |
+ test_send_emptydata();
|
|
|
a3e2b5 |
+
|
|
|
a3e2b5 |
return 0;
|
|
|
a3e2b5 |
}
|