1e5a5dd6cSEnji Cooper // Tests for POSIX message queue functionality.
2e5a5dd6cSEnji Cooper
3e5a5dd6cSEnji Cooper #include <time.h>
4e5a5dd6cSEnji Cooper #include <fcntl.h>
5e5a5dd6cSEnji Cooper #include <sys/stat.h>
6e5a5dd6cSEnji Cooper #include <mqueue.h>
7e5a5dd6cSEnji Cooper
8e5a5dd6cSEnji Cooper #include <string>
9e5a5dd6cSEnji Cooper
10e5a5dd6cSEnji Cooper #include "capsicum.h"
11e5a5dd6cSEnji Cooper #include "syscalls.h"
12e5a5dd6cSEnji Cooper #include "capsicum-test.h"
13e5a5dd6cSEnji Cooper
14e5a5dd6cSEnji Cooper // Run a test case in a forked process, possibly cleaning up a
15e5a5dd6cSEnji Cooper // message after completion
16e5a5dd6cSEnji Cooper #define FORK_TEST_ON_MQ(test_case_name, test_name, test_mq) \
17e5a5dd6cSEnji Cooper static void test_case_name##_##test_name##_ForkTest(); \
18e5a5dd6cSEnji Cooper TEST(test_case_name, test_name ## Forked) { \
19e5a5dd6cSEnji Cooper _RUN_FORKED_FN(test_case_name##_##test_name##_ForkTest, \
20e5a5dd6cSEnji Cooper #test_case_name, #test_name); \
21e5a5dd6cSEnji Cooper const char *mqname = test_mq; \
22e5a5dd6cSEnji Cooper if (mqname) mq_unlink_(mqname); \
23e5a5dd6cSEnji Cooper } \
24e5a5dd6cSEnji Cooper static void test_case_name##_##test_name##_ForkTest()
25e5a5dd6cSEnji Cooper
26e5a5dd6cSEnji Cooper static bool invoked;
seen_it_done_it(int)2733d7e3eeSEnji Cooper void seen_it_done_it(int) {
28e5a5dd6cSEnji Cooper invoked = true;
29e5a5dd6cSEnji Cooper }
30e5a5dd6cSEnji Cooper
31b6973c8fSAlex Richardson FORK_TEST_ON_MQ(PosixMqueue, CapModeIfMqOpenAvailable, "/cap_mq") {
32e5a5dd6cSEnji Cooper int mq = mq_open_("/cap_mq", O_RDWR|O_CREAT, 0644, NULL);
33e5a5dd6cSEnji Cooper // On FreeBSD, turn on message queue support with:
34e5a5dd6cSEnji Cooper // - 'kldload mqueuefs'
35e5a5dd6cSEnji Cooper // - 'options P1003_1B_MQUEUE' in kernel build config.
36e5a5dd6cSEnji Cooper if (mq < 0 && errno == ENOSYS) {
37b6973c8fSAlex Richardson GTEST_SKIP() << "mq_open -> -ENOSYS";
38e5a5dd6cSEnji Cooper }
39e5a5dd6cSEnji Cooper EXPECT_OK(mq);
40e5a5dd6cSEnji Cooper cap_rights_t r_read;
41e5a5dd6cSEnji Cooper cap_rights_init(&r_read, CAP_READ);
42e5a5dd6cSEnji Cooper cap_rights_t r_write;
43e5a5dd6cSEnji Cooper cap_rights_init(&r_write, CAP_WRITE);
44e5a5dd6cSEnji Cooper cap_rights_t r_poll;
45e5a5dd6cSEnji Cooper cap_rights_init(&r_poll, CAP_EVENT);
46e5a5dd6cSEnji Cooper
47e5a5dd6cSEnji Cooper int cap_read_mq = dup(mq);
48e5a5dd6cSEnji Cooper EXPECT_OK(cap_read_mq);
49e5a5dd6cSEnji Cooper EXPECT_OK(cap_rights_limit(cap_read_mq, &r_read));
50e5a5dd6cSEnji Cooper int cap_write_mq = dup(mq);
51e5a5dd6cSEnji Cooper EXPECT_OK(cap_write_mq);
52e5a5dd6cSEnji Cooper EXPECT_OK(cap_rights_limit(cap_write_mq, &r_write));
53e5a5dd6cSEnji Cooper int cap_poll_mq = dup(mq);
54e5a5dd6cSEnji Cooper EXPECT_OK(cap_poll_mq);
55e5a5dd6cSEnji Cooper EXPECT_OK(cap_rights_limit(cap_poll_mq, &r_poll));
56e5a5dd6cSEnji Cooper EXPECT_OK(mq_close_(mq));
57e5a5dd6cSEnji Cooper
58e5a5dd6cSEnji Cooper signal(SIGUSR2, seen_it_done_it);
59e5a5dd6cSEnji Cooper
60e5a5dd6cSEnji Cooper EXPECT_OK(cap_enter()); // Enter capability mode
61e5a5dd6cSEnji Cooper
62e5a5dd6cSEnji Cooper // Can no longer access the message queue via the POSIX IPC namespace.
63e5a5dd6cSEnji Cooper EXPECT_CAPMODE(mq_open_("/cap_mw", O_RDWR|O_CREAT, 0644, NULL));
64e5a5dd6cSEnji Cooper
65e5a5dd6cSEnji Cooper struct sigevent se;
66e5a5dd6cSEnji Cooper se.sigev_notify = SIGEV_SIGNAL;
67e5a5dd6cSEnji Cooper se.sigev_signo = SIGUSR2;
68e5a5dd6cSEnji Cooper EXPECT_OK(mq_notify_(cap_poll_mq, &se));
69e5a5dd6cSEnji Cooper EXPECT_NOTCAPABLE(mq_notify_(cap_read_mq, &se));
70e5a5dd6cSEnji Cooper EXPECT_NOTCAPABLE(mq_notify_(cap_write_mq, &se));
71e5a5dd6cSEnji Cooper
72e5a5dd6cSEnji Cooper const unsigned int kPriority = 10;
73e5a5dd6cSEnji Cooper const char* message = "xyzzy";
74e5a5dd6cSEnji Cooper struct timespec ts;
75e5a5dd6cSEnji Cooper ts.tv_sec = 1;
76e5a5dd6cSEnji Cooper ts.tv_nsec = 0;
77e5a5dd6cSEnji Cooper EXPECT_OK(mq_timedsend_(cap_write_mq, message, strlen(message) + 1, kPriority, &ts));
78e5a5dd6cSEnji Cooper EXPECT_NOTCAPABLE(mq_timedsend_(cap_read_mq, message, strlen(message) + 1, kPriority, &ts));
79e5a5dd6cSEnji Cooper
80e5a5dd6cSEnji Cooper sleep(1); // Give the notification a chance to arrive.
81e5a5dd6cSEnji Cooper EXPECT_TRUE(invoked);
82e5a5dd6cSEnji Cooper
83e5a5dd6cSEnji Cooper struct mq_attr mqa;
84e5a5dd6cSEnji Cooper EXPECT_OK(mq_getattr_(cap_poll_mq, &mqa));
85e5a5dd6cSEnji Cooper EXPECT_OK(mq_setattr_(cap_poll_mq, &mqa, NULL));
86e5a5dd6cSEnji Cooper EXPECT_NOTCAPABLE(mq_getattr_(cap_write_mq, &mqa));
87e5a5dd6cSEnji Cooper
88e5a5dd6cSEnji Cooper char* buffer = (char *)malloc(mqa.mq_msgsize);
89e5a5dd6cSEnji Cooper unsigned int priority;
90e5a5dd6cSEnji Cooper EXPECT_NOTCAPABLE(mq_timedreceive_(cap_write_mq, buffer, mqa.mq_msgsize, &priority, &ts));
91e5a5dd6cSEnji Cooper EXPECT_OK(mq_timedreceive_(cap_read_mq, buffer, mqa.mq_msgsize, &priority, &ts));
92e5a5dd6cSEnji Cooper EXPECT_EQ(std::string(message), std::string(buffer));
93e5a5dd6cSEnji Cooper EXPECT_EQ(kPriority, priority);
94e5a5dd6cSEnji Cooper free(buffer);
95e5a5dd6cSEnji Cooper
96e5a5dd6cSEnji Cooper close(cap_read_mq);
97e5a5dd6cSEnji Cooper close(cap_write_mq);
98e5a5dd6cSEnji Cooper close(cap_poll_mq);
99e5a5dd6cSEnji Cooper }
100