22 struct cmsghdr alignment;
23 unsigned char bytes[CMSG_SPACE(2 *
sizeof(
int))];
26static socklen_t unix_address(
const char* path,
struct sockaddr_un* address) {
27 memset(address, 0,
sizeof(*address));
28 address->sun_family = AF_UNIX;
29 if (strlen(path) >=
sizeof(address->sun_path))
31 strcpy(address->sun_path, path);
32 return (socklen_t)(offsetof(
struct sockaddr_un, sun_path) + strlen(path) + 1);
35static ssize_t send_rights(
int socketFd,
const struct sockaddr_un* address, socklen_t addressLength,
36 const int* descriptors,
size_t descriptorCount,
char payload) {
38 struct iovec vector = {&payload,
sizeof(payload)};
40 .msg_name = (
void*)address,
41 .msg_namelen = addressLength,
44 .msg_control = control.bytes,
45 .msg_controllen = CMSG_SPACE(descriptorCount *
sizeof(
int)),
47 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
48 header->cmsg_len = CMSG_LEN(descriptorCount *
sizeof(
int));
49 header->cmsg_level = SOL_SOCKET;
50 header->cmsg_type = SCM_RIGHTS;
51 memcpy(CMSG_DATA(header), descriptors, descriptorCount *
sizeof(
int));
52 return sendmsg(socketFd, &
message, 0);
55static int receive_rights(
int socketFd,
int flags,
size_t controlCapacity,
int expectedCount,
56 int* received,
int* outputFlags,
char expectedPayload) {
59 struct iovec vector = {&payload,
sizeof(payload)};
63 .msg_control = control.bytes,
64 .msg_controllen = controlCapacity,
67 if (recvmsg(socketFd, &
message, flags) != 1 || payload != expectedPayload)
69 *outputFlags =
message.msg_flags;
71 return message.msg_controllen == 0 ? 0 : -1;
74 struct cmsghdr* header = CMSG_FIRSTHDR(&
message);
75 if (!header || header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS ||
76 header->cmsg_len != CMSG_LEN((
size_t)expectedCount *
sizeof(
int)))
78 memcpy(received, CMSG_DATA(header), (
size_t)expectedCount *
sizeof(
int));
82void test_scm_rights(
void) {
83 static const char socketPath[] =
"/scm-rights.sock";
84 struct sockaddr_un address;
85 socklen_t addressLength = unix_address(socketPath, &address);
90 puts(
"Testing AF_UNIX datagram descriptor passing... ");
92 (void)unlink(socketPath);
93 receiver = socket(AF_UNIX, SOCK_DGRAM, 0);
94 sender = socket(AF_UNIX, SOCK_DGRAM, 0);
95 if (receiver < 0 || sender < 0 || bind(receiver, (
struct sockaddr*)&address, addressLength))
99 if (pipe(firstPipe) || fcntl(firstPipe[0], F_SETFD, FD_CLOEXEC) ||
100 send_rights(sender, &address, addressLength, &firstPipe[0], 1,
'a') != 1 ||
104 int received[2] = {-1, -1};
106 if (receive_rights(receiver, 0, CMSG_SPACE(
sizeof(
int)), 1, received, &outputFlags,
'a') ||
107 outputFlags || fcntl(received[0], F_GETFD) != 0 || write(firstPipe[1],
"A", 1) != 1)
110 if (read(received[0], &
byte, 1) != 1 ||
byte !=
'A' || close(received[0]) || close(firstPipe[1]))
114 if (pipe(cloexecPipe) ||
115 send_rights(sender, &address, addressLength, &cloexecPipe[0], 1,
'b') != 1 ||
116 close(cloexecPipe[0]))
118 if (receive_rights(receiver, MSG_CMSG_CLOEXEC, CMSG_SPACE(
sizeof(
int)), 1, received, &outputFlags,
120 fcntl(received[0], F_GETFD) != FD_CLOEXEC || close(received[0]) || close(cloexecPipe[1]))
125 if (pipe(truncated[0]) || pipe(truncated[1]))
127 sent[0] = truncated[0][0];
128 sent[1] = truncated[1][0];
129 if (send_rights(sender, &address, addressLength, sent, 2,
'c') != 1 || close(sent[0]) ||
132 if (receive_rights(receiver, 0, CMSG_LEN(
sizeof(
int)), 1, received, &outputFlags,
'c') ||
133 !(outputFlags & MSG_CTRUNC) || write(truncated[0][1],
"C", 1) != 1)
136 if (read(received[0], &
byte, 1) != 1 ||
byte !=
'C' || close(received[0]) ||
137 close(truncated[0][1]))
139 void (*previousSigpipe)(int) = signal(SIGPIPE, SIG_IGN);
140 if (previousSigpipe == SIG_ERR)
143 int hiddenWrite = (int)write(truncated[1][1],
"x", 1);
144 int hiddenWriteError = errno;
145 int hiddenClose = close(truncated[1][1]);
146 if (signal(SIGPIPE, previousSigpipe) == SIG_ERR || hiddenWrite != -1 ||
147 hiddenWriteError != EPIPE || hiddenClose)
151 if (pipe(faultPipe) || send_rights(sender, &address, addressLength, &faultPipe[0], 1,
'd') != 1 ||
155 struct iovec vector = {&payload,
sizeof(payload)};
156 struct msghdr badReceive = {
159 .msg_control = (
void*)-1,
160 .msg_controllen = CMSG_SPACE(
sizeof(
int)),
163 if (recvmsg(receiver, &badReceive, 0) != -1 || errno != EFAULT ||
164 receive_rights(receiver, 0, CMSG_SPACE(
sizeof(
int)), 1, received, &outputFlags,
'd') ||
165 close(received[0]) || close(faultPipe[1]))
168 int unsupportedPipe[2];
169 if (pipe(unsupportedPipe))
172 if (send_rights(sender, &address, addressLength, &sender, 1,
'e') != -1 || errno != EOPNOTSUPP)
174 stream = socket(AF_UNIX, SOCK_STREAM, 0);
176 if (stream < 0 || send_rights(stream, 0, 0, &unsupportedPipe[0], 1,
'f') != -1 ||
180 char malformedPayload =
'm';
181 struct iovec malformedVector = {&malformedPayload,
sizeof(malformedPayload)};
183 struct msghdr malformedMessage = {
184 .msg_name = &address,
185 .msg_namelen = addressLength,
186 .msg_iov = &malformedVector,
188 .msg_control = malformedControl.bytes,
189 .msg_controllen = CMSG_SPACE(
sizeof(
int)),
191 struct cmsghdr* malformedHeader = CMSG_FIRSTHDR(&malformedMessage);
192 malformedHeader->cmsg_len = CMSG_LEN(
sizeof(
int)) - 1;
193 malformedHeader->cmsg_level = SOL_SOCKET;
194 malformedHeader->cmsg_type = SCM_RIGHTS;
195 memcpy(CMSG_DATA(malformedHeader), &unsupportedPipe[0],
sizeof(
int));
197 if (sendmsg(sender, &malformedMessage, 0) != -1 || errno != EINVAL)
200 malformedHeader->cmsg_len = CMSG_LEN(
sizeof(
int));
201 malformedHeader->cmsg_level = SOL_SOCKET;
202 malformedHeader->cmsg_type = 0x7fffffff;
204 if (sendmsg(sender, &malformedMessage, 0) != -1 || errno != EOPNOTSUPP)
208 malformedMessage.msg_control = misalignedControl.bytes + 1;
209 malformedMessage.msg_controllen = CMSG_LEN(
sizeof(
int));
210 struct cmsghdr alignedHeader = {
211 .cmsg_len = CMSG_LEN(
sizeof(
int)),
212 .cmsg_level = SOL_SOCKET,
213 .cmsg_type = SCM_RIGHTS,
215 memcpy(misalignedControl.bytes + 1, &alignedHeader,
sizeof(alignedHeader));
216 memcpy(misalignedControl.bytes + 1 + CMSG_LEN(0), &unsupportedPipe[0],
sizeof(
int));
218 if (sendmsg(sender, &malformedMessage, 0) != 1 ||
219 receive_rights(receiver, 0, CMSG_SPACE(
sizeof(
int)), 1, received, &outputFlags,
'm') ||
223 if (send_rights(sender, &address, addressLength, &unsupportedPipe[0], 1,
'n') != 1)
226 char misalignedPayload = 0;
227 struct iovec misalignedVector = {&misalignedPayload,
sizeof(misalignedPayload)};
228 struct msghdr misalignedReceive = {
229 .msg_iov = &misalignedVector,
231 .msg_control = misalignedReceiveControl.bytes + 1,
232 .msg_controllen = CMSG_LEN(
sizeof(
int)),
234 if (recvmsg(receiver, &misalignedReceive, 0) != 1 || misalignedPayload !=
'n' ||
235 misalignedReceive.msg_controllen != CMSG_LEN(
sizeof(
int)))
237 struct cmsghdr receivedHeader = {0};
238 int misalignedReceived = -1;
239 memcpy(&receivedHeader, misalignedReceiveControl.bytes + 1,
sizeof(receivedHeader));
240 memcpy(&misalignedReceived, misalignedReceiveControl.bytes + 1 + CMSG_LEN(0),
sizeof(
int));
241 if (receivedHeader.cmsg_len != CMSG_LEN(
sizeof(
int)) || receivedHeader.cmsg_level != SOL_SOCKET ||
242 receivedHeader.cmsg_type != SCM_RIGHTS || misalignedReceived < 0 || close(misalignedReceived))
247 if (send_rights(sender, &address, addressLength, &invalid, 1,
'g') != -1 || errno != EBADF)
250 if (close(unsupportedPipe[0]) || close(unsupportedPipe[1]) || close(stream) || close(sender) ||
253 (void)unlink(socketPath);