859 lines
27 KiB
C
859 lines
27 KiB
C
/*
|
|
* Check decoding of struct msghdr ancillary data.
|
|
*
|
|
* Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
|
|
* Copyright (c) 2016-2018 The strace developers.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "tests.h"
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "xlat.h"
|
|
#include "xlat/scmvals.h"
|
|
|
|
#ifndef SOL_IP
|
|
# define SOL_IP 0
|
|
#endif
|
|
#ifndef SOL_TCP
|
|
# define SOL_TCP 6
|
|
#endif
|
|
|
|
#ifndef SCM_SECURITY
|
|
# define SCM_SECURITY 3
|
|
#endif
|
|
|
|
#define MIN_SIZE_OF(type, member) \
|
|
(offsetof(type, member) + sizeof(((type *) 0)->member))
|
|
|
|
static struct cmsghdr *
|
|
get_cmsghdr(void *const page, const size_t len)
|
|
{
|
|
return page - CMSG_ALIGN(len);
|
|
}
|
|
|
|
static void
|
|
print_fds(const struct cmsghdr *const cmsg, const size_t cmsg_len)
|
|
{
|
|
size_t nfd = cmsg_len > CMSG_LEN(0)
|
|
? (cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0;
|
|
if (!nfd)
|
|
return;
|
|
|
|
printf(", cmsg_data=[");
|
|
int *fdp = (int *) CMSG_DATA(cmsg);
|
|
size_t i;
|
|
for (i = 0; i < nfd; ++i) {
|
|
if (i)
|
|
printf(", ");
|
|
#if !VERBOSE
|
|
if (i >= DEFAULT_STRLEN) {
|
|
printf("...");
|
|
break;
|
|
}
|
|
#endif
|
|
printf("%d", fdp[i]);
|
|
}
|
|
printf("]");
|
|
}
|
|
|
|
static void
|
|
test_scm_rights1(struct msghdr *const mh,
|
|
const size_t msg_controllen,
|
|
void *const page,
|
|
const void *const src,
|
|
const size_t cmsg_len)
|
|
{
|
|
const size_t aligned_cms_len =
|
|
cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
|
|
if (cmsg_len >= CMSG_LEN(0)
|
|
&& aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
|
|
return;
|
|
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
|
|
|
|
if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
|
|
cmsg->cmsg_len = cmsg_len;
|
|
if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
size_t src_len =
|
|
cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
|
|
if (src_len > CMSG_LEN(0))
|
|
memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = msg_controllen;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
int saved_errno = errno;
|
|
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0");
|
|
if (msg_controllen < CMSG_LEN(0)) {
|
|
if (msg_controllen)
|
|
printf(", msg_control=%p", cmsg);
|
|
} else {
|
|
printf(", msg_control=[{cmsg_len=%lu, cmsg_level=SOL_SOCKET"
|
|
", cmsg_type=SCM_RIGHTS", (unsigned long) cmsg_len);
|
|
print_fds(cmsg, src_len);
|
|
printf("}");
|
|
if (aligned_cms_len < msg_controllen)
|
|
printf(", ... /* %p */", (void *) cmsg + aligned_cms_len);
|
|
printf("]");
|
|
}
|
|
|
|
errno = saved_errno;
|
|
printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned long) msg_controllen, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_scm_rights2(struct msghdr *const mh,
|
|
const size_t msg_controllen,
|
|
void *const page,
|
|
const int *const *const src,
|
|
const size_t *const cmsg_len)
|
|
{
|
|
const size_t aligned_cms_len[2] = {
|
|
cmsg_len[0] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[0]) : CMSG_LEN(0),
|
|
cmsg_len[1] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[1]) : CMSG_LEN(0)
|
|
};
|
|
if (cmsg_len[0] < CMSG_LEN(0)
|
|
|| aligned_cms_len[0] + CMSG_LEN(0) > msg_controllen
|
|
|| aligned_cms_len[0] + aligned_cms_len[1] + CMSG_LEN(0) <= msg_controllen)
|
|
return;
|
|
|
|
struct cmsghdr *const cmsg[2] = {
|
|
get_cmsghdr(page, msg_controllen),
|
|
(void *) get_cmsghdr(page, msg_controllen) + aligned_cms_len[0]
|
|
};
|
|
cmsg[0]->cmsg_len = cmsg_len[0];
|
|
cmsg[0]->cmsg_level = SOL_SOCKET;
|
|
cmsg[0]->cmsg_type = SCM_RIGHTS;
|
|
if (cmsg_len[0] > CMSG_LEN(0))
|
|
memcpy(CMSG_DATA(cmsg[0]), src[0], cmsg_len[0] - CMSG_LEN(0));
|
|
|
|
const size_t msg_controllen1 = msg_controllen - aligned_cms_len[0];
|
|
if (msg_controllen1 >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
|
|
cmsg[1]->cmsg_len = cmsg_len[1];
|
|
if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
|
|
cmsg[1]->cmsg_level = SOL_SOCKET;
|
|
if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
|
|
cmsg[1]->cmsg_type = SCM_RIGHTS;
|
|
size_t src1_len =
|
|
cmsg_len[1] < msg_controllen1 ? cmsg_len[1] : msg_controllen1;
|
|
if (src1_len > CMSG_LEN(0))
|
|
memcpy(CMSG_DATA(cmsg[1]), src[1], src1_len - CMSG_LEN(0));
|
|
|
|
mh->msg_control = cmsg[0];
|
|
mh->msg_controllen = msg_controllen;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
int saved_errno = errno;
|
|
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%lu"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
|
|
(unsigned long) cmsg_len[0]);
|
|
print_fds(cmsg[0], cmsg_len[0]);
|
|
printf("}, {cmsg_len=%lu, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
|
|
(unsigned long) cmsg_len[1]);
|
|
print_fds(cmsg[1], src1_len);
|
|
printf("}");
|
|
if (aligned_cms_len[1] < msg_controllen1)
|
|
printf(", ... /* %p */", (void *) cmsg[1] + aligned_cms_len[1]);
|
|
printf("]");
|
|
|
|
errno = saved_errno;
|
|
printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned long) msg_controllen, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_scm_rights3(struct msghdr *const mh, void *const page, const size_t nfds)
|
|
{
|
|
const size_t len = CMSG_SPACE(sizeof(int) * nfds);
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * nfds);
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
int *fdp = (int *) CMSG_DATA(cmsg);
|
|
size_t i;
|
|
for (i = 0; i < nfds; ++i)
|
|
fdp[i] = i;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
|
|
(unsigned) cmsg->cmsg_len);
|
|
print_fds(cmsg, cmsg->cmsg_len);
|
|
printf("}], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned long) len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_scm_timestamp(struct msghdr *const mh, void *const page)
|
|
{
|
|
size_t len = CMSG_SPACE(sizeof(struct timeval));
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct timeval));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMP;
|
|
struct timeval *tv = (struct timeval *) CMSG_DATA(cmsg);
|
|
tv->tv_sec = 123456789;
|
|
tv->tv_usec = 987654;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMP"
|
|
", cmsg_data={tv_sec=%lld, tv_usec=%llu}}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len,
|
|
(long long) tv->tv_sec, zero_extend_signed_to_ull(tv->tv_usec),
|
|
(unsigned long) len, rc, errno2name());
|
|
|
|
len = CMSG_SPACE(sizeof(struct timeval) - sizeof(long));
|
|
cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct timeval) - sizeof(long));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMP;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMP, cmsg_data=?}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len,
|
|
(unsigned long) len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_scm_timestampns(struct msghdr *const mh, void *const page)
|
|
{
|
|
size_t len = CMSG_SPACE(sizeof(struct timespec));
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct timespec));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMPNS;
|
|
struct timespec *ts = (struct timespec *) CMSG_DATA(cmsg);
|
|
ts->tv_sec = 123456789;
|
|
ts->tv_nsec = 987654321;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPNS"
|
|
", cmsg_data={tv_sec=%lld, tv_nsec=%llu}}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len,
|
|
(long long) ts->tv_sec, zero_extend_signed_to_ull(ts->tv_nsec),
|
|
(unsigned long) len, rc, errno2name());
|
|
|
|
len = CMSG_SPACE(sizeof(struct timespec) - sizeof(long));
|
|
cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct timespec) - sizeof(long));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMPNS;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPNS"
|
|
", cmsg_data=?}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len,
|
|
(unsigned long) len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_scm_timestamping(struct msghdr *const mh, void *const page)
|
|
{
|
|
size_t len = CMSG_SPACE(3 * sizeof(struct timespec));
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(3 * sizeof(struct timespec));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMPING;
|
|
struct timespec *ts = (struct timespec *) CMSG_DATA(cmsg);
|
|
ts[0].tv_sec = 123456789;
|
|
ts[0].tv_nsec = 987654321;
|
|
ts[1].tv_sec = 123456790;
|
|
ts[1].tv_nsec = 987654320;
|
|
ts[2].tv_sec = 123456791;
|
|
ts[2].tv_nsec = 987654319;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPING"
|
|
", cmsg_data=[{tv_sec=%lld, tv_nsec=%llu}"
|
|
", {tv_sec=%lld, tv_nsec=%llu}, {tv_sec=%lld, tv_nsec=%llu}]}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, (long long) ts[0].tv_sec,
|
|
zero_extend_signed_to_ull(ts[0].tv_nsec),
|
|
(long long) ts[1].tv_sec,
|
|
zero_extend_signed_to_ull(ts[1].tv_nsec),
|
|
(long long) ts[2].tv_sec,
|
|
zero_extend_signed_to_ull(ts[2].tv_nsec),
|
|
(unsigned long) len, rc, errno2name());
|
|
|
|
len = CMSG_SPACE(3 * sizeof(struct timespec) - sizeof(long));
|
|
cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(3 * sizeof(struct timespec) - sizeof(long));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_TIMESTAMPING;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPING"
|
|
", cmsg_data=?}]"
|
|
", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len,
|
|
(unsigned long) len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
print_security(const struct cmsghdr *const cmsg, const size_t cmsg_len)
|
|
{
|
|
int n = cmsg_len > CMSG_LEN(0) ? cmsg_len - CMSG_LEN(0) : 0;
|
|
if (!n)
|
|
return;
|
|
|
|
printf(", cmsg_data=\"%.*s\"", n, CMSG_DATA(cmsg));
|
|
}
|
|
|
|
static void
|
|
test_scm_security(struct msghdr *const mh,
|
|
const size_t msg_controllen,
|
|
void *const page,
|
|
const void *const src,
|
|
const size_t cmsg_len,
|
|
const int cmsg_level,
|
|
const char *const cmsg_level_str)
|
|
{
|
|
const size_t aligned_cms_len =
|
|
cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
|
|
if (cmsg_len >= CMSG_LEN(0)
|
|
&& aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
|
|
return;
|
|
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
|
|
|
|
cmsg->cmsg_len = cmsg_len;
|
|
cmsg->cmsg_level = cmsg_level;
|
|
cmsg->cmsg_type = SCM_SECURITY;
|
|
|
|
size_t src_len =
|
|
cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
|
|
if (src_len > CMSG_LEN(0))
|
|
memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = msg_controllen;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
int saved_errno = errno;
|
|
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%lu, cmsg_level=%s"
|
|
", cmsg_type=SCM_SECURITY",
|
|
(unsigned long) cmsg_len, cmsg_level_str);
|
|
print_security(cmsg, src_len);
|
|
printf("}");
|
|
if (aligned_cms_len < msg_controllen)
|
|
printf(", ... /* %p */", (void *) cmsg + aligned_cms_len);
|
|
printf("]");
|
|
|
|
errno = saved_errno;
|
|
printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned long) msg_controllen, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_unknown_type(struct msghdr *const mh,
|
|
void *const page,
|
|
const int cmsg_level,
|
|
const char *const cmsg_level_str,
|
|
const char *const cmsg_type_str)
|
|
{
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(0);
|
|
cmsg->cmsg_level = cmsg_level;
|
|
cmsg->cmsg_type = 0xfacefeed;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = cmsg->cmsg_len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
|
|
", cmsg_type=%#x /* %s */}], msg_controllen=%u, msg_flags=0}"
|
|
", 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_level_str, cmsg->cmsg_type,
|
|
cmsg_type_str, (unsigned) mh->msg_controllen, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_sol_socket(struct msghdr *const mh, void *const page)
|
|
{
|
|
static const int fds0[] = { -10, -11, -12, -13 };
|
|
static const int fds1[] = { -15, -16, -17, -18 };
|
|
size_t msg_controllen, max_msg_controllen;
|
|
|
|
max_msg_controllen = CMSG_SPACE(sizeof(fds0)) + sizeof(*fds0) - 1;
|
|
for (msg_controllen = 0;
|
|
msg_controllen <= max_msg_controllen;
|
|
msg_controllen++) {
|
|
size_t cmsg_len;
|
|
|
|
for (cmsg_len = 0;
|
|
cmsg_len <= msg_controllen + CMSG_LEN(0);
|
|
cmsg_len++) {
|
|
test_scm_rights1(mh, msg_controllen,
|
|
page, fds0, cmsg_len);
|
|
}
|
|
}
|
|
|
|
max_msg_controllen =
|
|
CMSG_SPACE(sizeof(fds0)) + CMSG_SPACE(sizeof(fds1)) +
|
|
sizeof(*fds0) - 1;
|
|
for (msg_controllen = CMSG_LEN(0) * 2;
|
|
msg_controllen <= max_msg_controllen;
|
|
msg_controllen++) {
|
|
static const int *const fdps[] = { fds0, fds1 };
|
|
size_t cmsg_len[2];
|
|
|
|
for (cmsg_len[0] = CMSG_LEN(0);
|
|
CMSG_ALIGN(cmsg_len[0]) + CMSG_LEN(0) <= msg_controllen
|
|
&& CMSG_ALIGN(cmsg_len[0]) <= CMSG_SPACE(sizeof(fds0));
|
|
cmsg_len[0]++) {
|
|
const size_t msg_controllen1 =
|
|
msg_controllen - CMSG_ALIGN(cmsg_len[0]);
|
|
|
|
for (cmsg_len[1] = 0;
|
|
cmsg_len[1] <= msg_controllen1 + CMSG_LEN(0);
|
|
cmsg_len[1]++) {
|
|
test_scm_rights2(mh, msg_controllen,
|
|
page, fdps, cmsg_len);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const char text[16] = "0123456789abcdef";
|
|
max_msg_controllen = CMSG_SPACE(sizeof(text)) + CMSG_LEN(0) - 1;
|
|
for (msg_controllen = CMSG_LEN(0);
|
|
msg_controllen <= max_msg_controllen;
|
|
msg_controllen++) {
|
|
size_t cmsg_len;
|
|
|
|
for (cmsg_len = 0;
|
|
cmsg_len <= msg_controllen + CMSG_LEN(0)
|
|
&& cmsg_len <= CMSG_LEN(sizeof(text));
|
|
cmsg_len++) {
|
|
test_scm_security(mh, msg_controllen,
|
|
page, text, cmsg_len,
|
|
ARG_STR(SOL_SOCKET));
|
|
}
|
|
}
|
|
|
|
test_scm_rights3(mh, page, DEFAULT_STRLEN - 1);
|
|
test_scm_rights3(mh, page, DEFAULT_STRLEN);
|
|
test_scm_rights3(mh, page, DEFAULT_STRLEN + 1);
|
|
|
|
test_scm_timestamp(mh, page);
|
|
test_scm_timestampns(mh, page);
|
|
test_scm_timestamping(mh, page);
|
|
|
|
test_unknown_type(mh, page, ARG_STR(SOL_SOCKET), "SCM_???");
|
|
}
|
|
|
|
static void
|
|
test_ip_pktinfo(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str)
|
|
{
|
|
const unsigned int len = CMSG_SPACE(sizeof(struct in_pktinfo));
|
|
struct cmsghdr *const cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
|
|
struct in_pktinfo *const info = (struct in_pktinfo *) CMSG_DATA(cmsg);
|
|
info->ipi_ifindex = ifindex_lo();
|
|
info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
|
|
info->ipi_addr.s_addr = inet_addr("5.6.7.8");
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
|
|
", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
|
|
", ipi_spec_dst=inet_addr(\"%s\")"
|
|
", ipi_addr=inet_addr(\"%s\")}}]"
|
|
", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str,
|
|
IFINDEX_LO_STR, "1.2.3.4", "5.6.7.8", len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_ip_uint(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str)
|
|
{
|
|
const unsigned int len = CMSG_SPACE(sizeof(int));
|
|
struct cmsghdr *const cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
|
|
unsigned int *u = (void *) CMSG_DATA(cmsg);
|
|
*u = 0xfacefeed;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
|
|
", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
|
|
rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_ip_uint8_t(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str)
|
|
{
|
|
const unsigned int len = CMSG_SPACE(1);
|
|
struct cmsghdr *const cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(1);
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
*CMSG_DATA(cmsg) = 'A';
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
|
|
", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str,
|
|
(unsigned) (uint8_t) 'A', len, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
|
|
{
|
|
const unsigned char *const opts = cmsg_data;
|
|
unsigned int i;
|
|
for (i = 0; i < data_len; ++i) {
|
|
if (i)
|
|
printf(", ");
|
|
#if !VERBOSE
|
|
if (i >= DEFAULT_STRLEN) {
|
|
printf("...");
|
|
break;
|
|
}
|
|
#endif
|
|
printf("0x%02x", opts[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_ip_opts(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str,
|
|
const unsigned int opts_len)
|
|
{
|
|
unsigned int len = CMSG_SPACE(opts_len);
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(opts_len);
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
unsigned int i;
|
|
for (i = 0; i < opts_len; ++i)
|
|
CMSG_DATA(cmsg)[i] = 'A' + i;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str);
|
|
print_ip_opts(CMSG_DATA(cmsg), opts_len);
|
|
printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
len, rc, errno2name());
|
|
}
|
|
|
|
#ifdef IP_CHECKSUM
|
|
struct sock_ee {
|
|
uint32_t ee_errno;
|
|
uint8_t ee_origin;
|
|
uint8_t ee_type;
|
|
uint8_t ee_code;
|
|
uint8_t ee_pad;
|
|
uint32_t ee_info;
|
|
uint32_t ee_data;
|
|
struct sockaddr_in offender;
|
|
};
|
|
|
|
static void
|
|
test_ip_recverr(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str)
|
|
{
|
|
const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
|
|
struct cmsghdr *const cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
|
|
struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
|
|
e->ee_errno = 0xdeadbeef;
|
|
e->ee_origin = 2;
|
|
e->ee_type = 3;
|
|
e->ee_code = 4;
|
|
e->ee_info = 0xfacefeed;
|
|
e->ee_data = 0xbadc0ded;
|
|
e->offender.sin_family = AF_INET,
|
|
e->offender.sin_port = htons(12345),
|
|
e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
|
|
", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
|
|
", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
|
|
", offender={sa_family=AF_INET, sin_port=htons(%hu)"
|
|
", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
|
|
", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str,
|
|
e->ee_errno, e->ee_origin, e->ee_type,
|
|
e->ee_code, e->ee_info, e->ee_data,
|
|
ntohs(e->offender.sin_port),
|
|
len, rc, errno2name());
|
|
}
|
|
#endif
|
|
|
|
#ifdef IP_ORIGDSTADDR
|
|
static void
|
|
test_ip_origdstaddr(struct msghdr *const mh, void *const page,
|
|
const int cmsg_type, const char *const cmsg_type_str)
|
|
{
|
|
const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
|
|
struct cmsghdr *const cmsg = get_cmsghdr(page, len);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
|
|
cmsg->cmsg_level = SOL_IP;
|
|
cmsg->cmsg_type = cmsg_type;
|
|
|
|
struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
|
|
sin->sin_family = AF_INET,
|
|
sin->sin_port = htons(12345),
|
|
sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
|
|
", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
|
|
", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
|
|
", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, cmsg_type_str,
|
|
ntohs(sin->sin_port), len, rc, errno2name());
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
test_sol_ip(struct msghdr *const mh, void *const page)
|
|
{
|
|
test_ip_pktinfo(mh, page, ARG_STR(IP_PKTINFO));
|
|
test_ip_uint(mh, page, ARG_STR(IP_TTL));
|
|
test_ip_uint8_t(mh, page, ARG_STR(IP_TOS));
|
|
test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 1);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 2);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 3);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 4);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 5);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 6);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 7);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 8);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN - 1);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN);
|
|
test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN + 1);
|
|
#ifdef IP_CHECKSUM
|
|
test_ip_recverr(mh, page, ARG_STR(IP_RECVERR));
|
|
#endif
|
|
#ifdef IP_ORIGDSTADDR
|
|
test_ip_origdstaddr(mh, page, ARG_STR(IP_ORIGDSTADDR));
|
|
#endif
|
|
#ifdef IP_CHECKSUM
|
|
test_ip_uint(mh, page, ARG_STR(IP_CHECKSUM));
|
|
#endif
|
|
test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
|
|
ARG_STR(SOL_IP));
|
|
test_unknown_type(mh, page, ARG_STR(SOL_IP), "IP_???");
|
|
}
|
|
|
|
static void
|
|
test_unknown_level(struct msghdr *const mh, void *const page)
|
|
{
|
|
struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(0);
|
|
cmsg->cmsg_level = SOL_TCP;
|
|
cmsg->cmsg_type = 0xdeadbeef;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = cmsg->cmsg_len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
|
|
", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
|
|
", 0) = %d %s (%m)\n",
|
|
(unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
|
|
(unsigned) mh->msg_controllen, rc, errno2name());
|
|
}
|
|
|
|
static void
|
|
test_big_len(struct msghdr *const mh)
|
|
{
|
|
int optmem_max;
|
|
|
|
if (read_int_from_file("/proc/sys/net/core/optmem_max", &optmem_max)
|
|
|| optmem_max <= 0 || optmem_max > 0x100000)
|
|
optmem_max = sizeof(long long) * (2 * IOV_MAX + 512);
|
|
optmem_max = (optmem_max + sizeof(long long) - 1)
|
|
& ~(sizeof(long long) - 1);
|
|
|
|
const size_t len = optmem_max * 2;
|
|
struct cmsghdr *const cmsg = tail_alloc(len);
|
|
cmsg->cmsg_len = len;
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
mh->msg_control = cmsg;
|
|
mh->msg_controllen = len;
|
|
|
|
int rc = sendmsg(-1, mh, 0);
|
|
if (EBADF != errno)
|
|
perror_msg_and_skip("sendmsg");
|
|
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=[{cmsg_len=%u"
|
|
", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
|
|
(unsigned) cmsg->cmsg_len);
|
|
print_fds(cmsg, optmem_max);
|
|
printf("}, ...], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
|
|
(unsigned long) len, rc, errno2name());
|
|
}
|
|
|
|
int main(int ac, const char **av)
|
|
{
|
|
int rc = sendmsg(-1, 0, 0);
|
|
printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
|
|
|
|
TAIL_ALLOC_OBJECT_CONST_PTR(struct msghdr, mh);
|
|
memset(mh, 0, sizeof(*mh));
|
|
test_big_len(mh);
|
|
|
|
rc = sendmsg(-1, mh + 1, 0);
|
|
printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
|
|
mh + 1, rc, errno2name());
|
|
|
|
void *page = tail_alloc(1) + 1;
|
|
mh->msg_control = page;
|
|
mh->msg_controllen = CMSG_LEN(0);
|
|
rc = sendmsg(-1, mh, 0);
|
|
printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
|
|
", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
|
|
", msg_flags=0}, 0) = %d %s (%m)\n",
|
|
page, (unsigned) CMSG_LEN(0), rc, errno2name());
|
|
|
|
test_sol_socket(mh, page);
|
|
test_sol_ip(mh, page);
|
|
test_unknown_level(mh, page);
|
|
|
|
puts("+++ exited with 0 +++");
|
|
return 0;
|
|
}
|