mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Implement file descriptor passing.
This change refactors the Unix socket / pipe backend to have a ring buffer containing segments, where each segment has an optional leading ancillary buffer containing control messages followed by a normal data buffer. The SCM_RIGHTS control message has been implemented which transfers file descriptors to the receiving process. File descriptors are reference counted and cycles are prevented using the following restrictions: 1) Unix sockets cannot be sent on themselves (on either end). 2) Unix sockets themselves being sent cannot be sent on. 3) Unix sockets cannot send a Unix socket being sent on. This is a compatible ABI change.
This commit is contained in:
parent
b9898086c6
commit
3c43f71084
23 changed files with 1440 additions and 151 deletions
2
Makefile
2
Makefile
|
@ -177,7 +177,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers
|
|||
echo 'ID=sortix' && \
|
||||
echo 'VERSION_ID="$(VERSION)"' && \
|
||||
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
|
||||
echo 'SORTIX_ABI=1.1' && \
|
||||
echo 'SORTIX_ABI=1.2' && \
|
||||
true) > "$(SYSROOT)/etc/sortix-release"
|
||||
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
|
||||
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
|
||||
|
|
|
@ -232,6 +232,16 @@ bool Descriptor::IsSeekable()
|
|||
return seekable;
|
||||
}
|
||||
|
||||
bool Descriptor::pass()
|
||||
{
|
||||
return vnode->pass();
|
||||
}
|
||||
|
||||
void Descriptor::unpass()
|
||||
{
|
||||
vnode->unpass();
|
||||
}
|
||||
|
||||
int Descriptor::sync(ioctx_t* ctx)
|
||||
{
|
||||
// TODO: Possible denial-of-service attack if someone opens the file without
|
||||
|
|
|
@ -195,6 +195,8 @@ class Unode : public Inode
|
|||
public:
|
||||
Unode(Ref<Server> server, ino_t ino, mode_t type);
|
||||
virtual ~Unode();
|
||||
virtual bool pass();
|
||||
virtual void unpass();
|
||||
virtual void linked();
|
||||
virtual void unlinked();
|
||||
virtual int sync(ioctx_t* ctx);
|
||||
|
@ -766,6 +768,15 @@ void Unode::UnexpectedResponse(Channel* channel, struct fsm_msg_header* hdr)
|
|||
errno = EIO;
|
||||
}
|
||||
|
||||
bool Unode::pass()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unode::unpass()
|
||||
{
|
||||
}
|
||||
|
||||
void Unode::linked()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2012-2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -58,6 +58,8 @@ public:
|
|||
Ref<Descriptor> Fork();
|
||||
bool SetFlags(int new_dflags);
|
||||
int GetFlags();
|
||||
bool pass();
|
||||
void unpass();
|
||||
int sync(ioctx_t* ctx);
|
||||
int stat(ioctx_t* ctx, struct stat* st);
|
||||
int statvfs(ioctx_t* ctx, struct statvfs* stvfs);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2012-2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -54,6 +54,8 @@ public: /* These must never change after construction and is read-only. */
|
|||
|
||||
public:
|
||||
virtual ~Inode() { }
|
||||
virtual bool pass() = 0;
|
||||
virtual void unpass() = 0;
|
||||
virtual void linked() = 0;
|
||||
virtual void unlinked() = 0;
|
||||
virtual int sync(ioctx_t* ctx) = 0;
|
||||
|
@ -165,6 +167,8 @@ protected:
|
|||
public:
|
||||
AbstractInode();
|
||||
virtual ~AbstractInode();
|
||||
virtual bool pass();
|
||||
virtual void unpass();
|
||||
virtual void linked();
|
||||
virtual void unlinked();
|
||||
virtual int sync(ioctx_t* ctx);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2011, 2012, 2013, 2014, 2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -43,6 +43,8 @@ public:
|
|||
bool SetSIGPIPEDelivery(bool deliver_sigpipe);
|
||||
size_t Size();
|
||||
bool Resize(size_t new_size);
|
||||
bool pass();
|
||||
void unpass();
|
||||
ssize_t readv(ioctx_t* ctx, const struct iovec* iov, int iovcnt);
|
||||
ssize_t recv(ioctx_t* ctx, uint8_t* buf, size_t count, int flags);
|
||||
ssize_t recvmsg(ioctx_t* ctx, struct msghdr* msg, int flags);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2012, 2013, 2014, 2017 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -124,6 +124,21 @@ public:
|
|||
size_t Refcount() const { return obj ? obj->Refcount : 0; }
|
||||
bool IsUnique() const { return obj->IsUnique(); }
|
||||
|
||||
// Leak a reference and allow recreating it later from an integer.
|
||||
uintptr_t Export()
|
||||
{
|
||||
if ( obj )
|
||||
obj->Refer_Renamed();
|
||||
return (uintptr_t) obj;
|
||||
}
|
||||
|
||||
// Restore a leaked reference from an integer.
|
||||
void Import(uintptr_t ptr)
|
||||
{
|
||||
Reset();
|
||||
obj = (T*) ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T* obj;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2012-2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -55,6 +55,8 @@ public: /* These must never change after construction and is read-only. */
|
|||
public:
|
||||
Vnode(Ref<Inode> inode, Ref<Vnode> mountedat, ino_t rootino, dev_t rootdev);
|
||||
virtual ~Vnode();
|
||||
bool pass();
|
||||
void unpass();
|
||||
int sync(ioctx_t* ctx);
|
||||
int stat(ioctx_t* ctx, struct stat* st);
|
||||
int statvfs(ioctx_t* ctx, struct statvfs* stvfs);
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
#define S_IFFACTORY 0x10000
|
||||
/* Don't run the factory method if simply stat'ing the inode. */
|
||||
#define S_IFFACTORY_NOSTAT 0x20000
|
||||
/* The file object must never be wrapped in another file object. */
|
||||
#define S_IFNEVERWRAP 0x40000
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -62,6 +62,15 @@ AbstractInode::~AbstractInode()
|
|||
{
|
||||
}
|
||||
|
||||
bool AbstractInode::pass()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AbstractInode::unpass()
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractInode::linked()
|
||||
{
|
||||
InterlockedIncrement(&stat_nlink);
|
||||
|
|
|
@ -836,6 +836,8 @@ int sys_mkpartition(int fd, off_t start, off_t length, int flags)
|
|||
Ref<Inode> inner_inode = desc->vnode->inode;
|
||||
desc.Reset();
|
||||
|
||||
if ( inner_inode->type & S_IFNEVERWRAP )
|
||||
return errno = EPERM, -1;
|
||||
if ( !S_ISBLK(inner_inode->type) && !S_ISREG(inner_inode->type) )
|
||||
return errno = EPERM, -1;
|
||||
if ( start < 0 || length < 0 )
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2016, 2017 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2013, 2014, 2016, 2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -83,6 +83,8 @@ class StreamSocket : public AbstractInode
|
|||
public:
|
||||
StreamSocket(uid_t owner, gid_t group, mode_t mode, Ref<Manager> manager);
|
||||
virtual ~StreamSocket();
|
||||
virtual bool pass();
|
||||
virtual void unpass();
|
||||
virtual Ref<Inode> accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrsize,
|
||||
int flags);
|
||||
virtual int bind(ioctx_t* ctx, const uint8_t* addr, size_t addrsize);
|
||||
|
@ -163,7 +165,10 @@ StreamSocket::StreamSocket(uid_t owner, gid_t group, mode_t mode,
|
|||
inode_type = INODE_TYPE_STREAM;
|
||||
dev = (dev_t) manager.Get();
|
||||
ino = (ino_t) this;
|
||||
this->type = S_IFSOCK;
|
||||
// Never allow wrapping filesystem sockets as they need to be able to
|
||||
// recognize themselves when passing filesystems, to prevent reference
|
||||
// cycle loops.
|
||||
this->type = S_IFSOCK | S_IFNEVERWRAP;
|
||||
this->stat_uid = owner;
|
||||
this->stat_gid = group;
|
||||
this->stat_mode = (mode & S_SETABLE) | this->type;
|
||||
|
@ -191,6 +196,23 @@ StreamSocket::~StreamSocket()
|
|||
free(bound_address);
|
||||
}
|
||||
|
||||
bool StreamSocket::pass()
|
||||
{
|
||||
if ( outgoing.pass() )
|
||||
{
|
||||
if ( incoming.pass() )
|
||||
return true;
|
||||
outgoing.unpass();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void StreamSocket::unpass()
|
||||
{
|
||||
outgoing.unpass();
|
||||
incoming.unpass();
|
||||
}
|
||||
|
||||
Ref<Inode> StreamSocket::accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrsize,
|
||||
int flags)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
|
||||
* Copyright (c) 2013, 2015, 2017 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -39,6 +39,7 @@ Partition::Partition(Ref<Inode> inner_inode, off_t start, off_t length)
|
|||
assert(0 <= start);
|
||||
assert(0 <= length);
|
||||
assert(length <= OFF_MAX - start);
|
||||
assert(!(inner_inode->type & S_IFNEVERWRAP));
|
||||
assert(S_ISBLK(inner_inode->type) || S_ISREG(inner_inode->type));
|
||||
this->dev = (dev_t) this;
|
||||
this->ino = (ino_t) this;
|
||||
|
|
1012
kernel/pipe.cpp
1012
kernel/pipe.cpp
File diff suppressed because it is too large
Load diff
|
@ -216,6 +216,16 @@ int Vnode::unmount(ioctx_t* ctx, const char* filename, int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool Vnode::pass()
|
||||
{
|
||||
return inode->pass();
|
||||
}
|
||||
|
||||
void Vnode::unpass()
|
||||
{
|
||||
inode->unpass();
|
||||
}
|
||||
|
||||
int Vnode::sync(ioctx_t* ctx)
|
||||
{
|
||||
return inode->sync(ctx);
|
||||
|
|
|
@ -97,9 +97,21 @@ struct cmsghdr
|
|||
|
||||
#define SCM_RIGHTS 1
|
||||
|
||||
/* TODO: CMSG_DATA(cmsg) */
|
||||
/* TODO: CMSG_NXTHDR(cmsg) */
|
||||
/* TODO: CMSH_FIRSTHDR(cmsg) */
|
||||
#define CMSG_ALIGN(value) \
|
||||
(-(-(size_t)(value) & ~(__alignof__(struct cmsghdr) - 1)))
|
||||
#define CMSG_SPACE(size) (sizeof(struct cmsghdr) + CMSG_ALIGN(size))
|
||||
#define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
|
||||
#define CMSG_DATA(cmsg) ((unsigned char*) ((struct cmsghdr*) (cmsg) + 1))
|
||||
#define CMSG_FIRSTHDR(mhdr) \
|
||||
((mhdr)->msg_controllen < sizeof(struct cmsghdr) ? \
|
||||
(struct cmsghdr*) 0 : \
|
||||
(struct cmsghdr*) (mhdr)->msg_control)
|
||||
#define CMSG_NXTHDR(mhdr, cmsg) \
|
||||
((cmsg)->cmsg_len < sizeof(struct cmsghdr) || \
|
||||
(char*) (mhdr)->msg_control + (mhdr)->msg_controllen - (char*) (cmsg) <= \
|
||||
CMSG_ALIGN((cmsg)->cmsg_len) ? \
|
||||
(struct cmsghdr*) 0 : \
|
||||
(struct cmsghdr*) (((char*) (cmsg)) + CMSG_ALIGN((cmsg)->cmsg_len)))
|
||||
|
||||
struct linger
|
||||
{
|
||||
|
@ -140,6 +152,7 @@ struct linger
|
|||
#define MSG_WAITALL (1<<7)
|
||||
#define MSG_DONTWAIT (1<<8)
|
||||
#define MSG_CMSG_CLOEXEC (1<<9)
|
||||
#define MSG_CMSG_CLOFORK (1<<10)
|
||||
|
||||
#define AF_UNSPEC 0
|
||||
#define AF_INET 1
|
||||
|
|
|
@ -25,6 +25,10 @@ test-pthread-once \
|
|||
test-pthread-self \
|
||||
test-pthread-tls \
|
||||
test-signal-raise \
|
||||
test-unix-socket-fd-cycle \
|
||||
test-unix-socket-fd-leak \
|
||||
test-unix-socket-fd-pass \
|
||||
test-unix-socket-fd-trunc \
|
||||
test-unix-socket-name \
|
||||
test-unix-socket-shutdown \
|
||||
|
||||
|
|
124
regress/test-unix-socket-fd-cycle.c
Normal file
124
regress/test-unix-socket-fd-cycle.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* test-unix-socket-fd-cycle.c
|
||||
* Tests whether Unix socket file descriptor passing cycles are rejected.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int a_fds[2];
|
||||
test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, a_fds) == 0);
|
||||
int b_fds[2];
|
||||
test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, b_fds) == 0);
|
||||
|
||||
struct msghdr mhdr;
|
||||
char buf[1] = { 0 };
|
||||
struct iovec iov;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
alignas(struct cmsghdr) char cmsgdata[CMSG_SPACE(sizeof(int))];
|
||||
ssize_t amount;
|
||||
struct cmsghdr* cmsg;
|
||||
|
||||
// Passing a Unix socket on itself isn't permitted.
|
||||
buf[0] = 'X';
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
int* cdata = (int*) CMSG_DATA(cmsg);
|
||||
*cdata = a_fds[1];
|
||||
amount = sendmsg(a_fds[1], &mhdr, 0);
|
||||
test_assertx(amount < 0);
|
||||
test_assert(errno == EPERM);
|
||||
|
||||
// Passing a Unix socket on its other end isn't permitted.
|
||||
*cdata = a_fds[0];
|
||||
amount = sendmsg(a_fds[1], &mhdr, 0);
|
||||
test_assertx(amount < 0);
|
||||
test_assert(errno == EPERM);
|
||||
|
||||
// Passing a Unix socket (with no fds passed) on another Unix socket (which
|
||||
// itself isn't being passed) is allowed.
|
||||
*cdata = b_fds[1];
|
||||
amount = sendmsg(a_fds[1], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
|
||||
// A Unix socket (itself being passed) is not permitted to pass fds.
|
||||
// b_fds[1] is already being sent on a_fds[1] (to a_fds[0]).
|
||||
FILE* file;
|
||||
test_assert((file = tmpfile()));
|
||||
*cdata = fileno(file);
|
||||
amount = sendmsg(b_fds[1], &mhdr, 0);
|
||||
test_assertx(amount < 0);
|
||||
test_assert(errno == EPERM);
|
||||
fclose(file);
|
||||
|
||||
// A Unix socket is not permitted to send a socket with fds being sent.
|
||||
// b_fds[1] is already being sent on a_fds[1] (to a_fds[0]).
|
||||
*cdata = a_fds[1];
|
||||
amount = sendmsg(b_fds[0], &mhdr, 0);
|
||||
test_assertx(amount < 0);
|
||||
test_assert(errno == EPERM);
|
||||
|
||||
// Receive b_fds[1] being sent on a_fds[1] to a_fds[0].
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
amount = recvmsg(a_fds[0], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
test_assertx(buf[0] == 'X');
|
||||
test_assertx(!(mhdr.msg_flags & MSG_CTRUNC));
|
||||
test_assertx(!mhdr.msg_flags);
|
||||
test_assertx(mhdr.msg_controllen);
|
||||
cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
test_assertx(cmsg);
|
||||
test_assertx(cmsg->cmsg_level == SOL_SOCKET);
|
||||
test_assertx(cmsg->cmsg_type == SCM_RIGHTS);
|
||||
test_assertx(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
|
||||
cdata = (int*) CMSG_DATA(cmsg);
|
||||
int file_fd = *cdata;
|
||||
test_assertx(0 <= file_fd);
|
||||
struct stat gotten_st;
|
||||
test_assert(fstat(file_fd, &gotten_st) == 0);
|
||||
struct stat expected_st;
|
||||
test_assert(fstat(b_fds[1], &expected_st) == 0);
|
||||
test_assertx(gotten_st.st_ino == expected_st.st_ino);
|
||||
test_assertx(gotten_st.st_dev == expected_st.st_dev);
|
||||
test_assertx(!CMSG_NXTHDR(&mhdr, cmsg));
|
||||
close(file_fd);
|
||||
|
||||
return 0;
|
||||
}
|
66
regress/test-unix-socket-fd-leak.c
Normal file
66
regress/test-unix-socket-fd-leak.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* test-unix-socket-fd-leak.c
|
||||
* Tests whether leaking file descriptors over a Unix socket works.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fds[2];
|
||||
test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
|
||||
|
||||
FILE* file;
|
||||
test_assert((file = tmpfile()));
|
||||
|
||||
struct msghdr mhdr;
|
||||
char buf[1] = { 0 };
|
||||
struct iovec iov;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
alignas(struct cmsghdr) char cmsgdata[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
buf[0] = 'X';
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
int* cdata = (int*) CMSG_DATA(cmsg);
|
||||
*cdata = fileno(file);
|
||||
ssize_t amount = sendmsg(fds[1], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
|
||||
fclose(file);
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
return 0;
|
||||
}
|
144
regress/test-unix-socket-fd-pass.c
Normal file
144
regress/test-unix-socket-fd-pass.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* test-unix-socket-fd-pass.c
|
||||
* Tests whether passing a file descriptor over an Unix socket works.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fds[2];
|
||||
test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
|
||||
|
||||
FILE* file;
|
||||
test_assert((file = tmpfile()));
|
||||
|
||||
struct stat expected_st;
|
||||
test_assert(fstat(fileno(file), &expected_st) == 0);
|
||||
|
||||
struct msghdr mhdr;
|
||||
char buf[1] = { 0 };
|
||||
struct iovec iov;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
alignas(struct cmsghdr) char cmsgdata[CMSG_SPACE(sizeof(int))];
|
||||
|
||||
pid_t child_pid;
|
||||
test_assert(0 <= (child_pid = fork()));
|
||||
|
||||
if ( child_pid == 0 )
|
||||
{
|
||||
close(fds[0]);
|
||||
buf[0] = 'X';
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
int* cdata = (int*) CMSG_DATA(cmsg);
|
||||
*cdata = fileno(file);
|
||||
ssize_t amount = sendmsg(fds[1], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
fclose(file);
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
ssize_t amount = recvmsg(fds[0], &mhdr, MSG_PEEK);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
test_assertx(buf[0] == 'X');
|
||||
test_assertx(!(mhdr.msg_flags & MSG_CTRUNC));
|
||||
test_assertx(!mhdr.msg_flags);
|
||||
test_assertx(mhdr.msg_controllen);
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
test_assertx(cmsg);
|
||||
test_assertx(cmsg->cmsg_level == SOL_SOCKET);
|
||||
test_assertx(cmsg->cmsg_type == SCM_RIGHTS);
|
||||
test_assertx(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
|
||||
int* cdata = (int*) CMSG_DATA(cmsg);
|
||||
int file_fd = *cdata;
|
||||
test_assertx(0 <= file_fd);
|
||||
struct stat gotten_st;
|
||||
test_assert(fstat(file_fd, &gotten_st) == 0);
|
||||
test_assertx(gotten_st.st_ino == expected_st.st_ino);
|
||||
test_assertx(gotten_st.st_dev == expected_st.st_dev);
|
||||
test_assertx(!CMSG_NXTHDR(&mhdr, cmsg));
|
||||
close(file_fd);
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
amount = recvmsg(fds[0], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
test_assertx(buf[0] == 'X');
|
||||
test_assertx(!(mhdr.msg_flags & MSG_CTRUNC));
|
||||
test_assertx(!mhdr.msg_flags);
|
||||
test_assertx(mhdr.msg_controllen);
|
||||
cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
test_assertx(cmsg);
|
||||
test_assertx(cmsg->cmsg_level == SOL_SOCKET);
|
||||
test_assertx(cmsg->cmsg_type == SCM_RIGHTS);
|
||||
test_assertx(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
|
||||
cdata = (int*) CMSG_DATA(cmsg);
|
||||
file_fd = *cdata;
|
||||
test_assertx(0 <= file_fd);
|
||||
test_assert(fstat(file_fd, &gotten_st) == 0);
|
||||
test_assertx(gotten_st.st_ino == expected_st.st_ino);
|
||||
test_assertx(gotten_st.st_dev == expected_st.st_dev);
|
||||
test_assertx(!CMSG_NXTHDR(&mhdr, cmsg));
|
||||
close(file_fd);
|
||||
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
amount = recvmsg(fds[0], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 0);
|
||||
test_assertx(!mhdr.msg_flags);
|
||||
test_assertx(!mhdr.msg_controllen);
|
||||
|
||||
int code;
|
||||
test_assert(waitpid(child_pid, &code, 0) == child_pid);
|
||||
test_assert(WIFEXITED(code) && WEXITSTATUS(code) == 0);
|
||||
|
||||
return 0;
|
||||
}
|
103
regress/test-unix-socket-fd-trunc.c
Normal file
103
regress/test-unix-socket-fd-trunc.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* test-unix-socket-fd-trunc.c
|
||||
* Tests having too little control data when passing file descriptors.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <stdalign.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fds[2];
|
||||
test_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
|
||||
|
||||
FILE* file1;
|
||||
test_assert((file1 = tmpfile()));
|
||||
FILE* file2;
|
||||
test_assert((file2 = tmpfile()));
|
||||
|
||||
struct stat expected_st;
|
||||
test_assert(fstat(fileno(file1), &expected_st) == 0);
|
||||
|
||||
struct msghdr mhdr;
|
||||
char buf[1] = { 0 };
|
||||
struct iovec iov;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
alignas(struct cmsghdr) char cmsgdata[CMSG_SPACE(sizeof(int) * 2)];
|
||||
|
||||
buf[0] = 'X';
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdata;
|
||||
mhdr.msg_controllen = sizeof(cmsgdata);
|
||||
struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 2);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
int* cdata = (int*) CMSG_DATA(cmsg);
|
||||
cdata[0] = fileno(file1);
|
||||
cdata[1] = fileno(file2);
|
||||
ssize_t amount = sendmsg(fds[1], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
|
||||
fclose(file1);
|
||||
fclose(file2);
|
||||
|
||||
alignas(struct cmsghdr)
|
||||
char cmsgdatasmall[CMSG_ALIGN(sizeof(struct cmsghdr)) + sizeof(int)];
|
||||
memset(&mhdr, 0, sizeof(mhdr));
|
||||
mhdr.msg_iov = &iov;
|
||||
mhdr.msg_iovlen = 1;
|
||||
mhdr.msg_control = cmsgdatasmall;
|
||||
mhdr.msg_controllen = sizeof(cmsgdatasmall);
|
||||
amount = recvmsg(fds[0], &mhdr, 0);
|
||||
test_assert(0 <= amount);
|
||||
test_assertx(amount == 1);
|
||||
test_assertx(buf[0] == 'X');
|
||||
test_assertx(mhdr.msg_flags == MSG_CTRUNC);
|
||||
test_assertx(mhdr.msg_controllen);
|
||||
test_assertx(mhdr.msg_controllen == sizeof(cmsgdatasmall));
|
||||
cmsg = CMSG_FIRSTHDR(&mhdr);
|
||||
test_assertx(cmsg);
|
||||
test_assertx(cmsg->cmsg_level == SOL_SOCKET);
|
||||
test_assertx(cmsg->cmsg_type == SCM_RIGHTS);
|
||||
test_assertx(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
|
||||
cdata = (int*) CMSG_DATA(cmsg);
|
||||
int file_fd = *cdata;
|
||||
test_assertx(0 <= file_fd);
|
||||
struct stat gotten_st;
|
||||
test_assert(fstat(file_fd, &gotten_st) == 0);
|
||||
test_assertx(gotten_st.st_ino == expected_st.st_ino);
|
||||
test_assertx(gotten_st.st_dev == expected_st.st_dev);
|
||||
test_assertx(!CMSG_NXTHDR(&mhdr, cmsg));
|
||||
close(file_fd);
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#define TEST_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
|
|
@ -69,6 +69,14 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
|
|||
.Xr grep 1
|
||||
for it after a release.
|
||||
.Sh CHANGES
|
||||
.Ss Implement file descriptor passing
|
||||
The
|
||||
.Dv SCM_RIGHTS
|
||||
control message have been implemented, allowing file descriptors to be passed
|
||||
over
|
||||
.Dv AF_UNIX
|
||||
sockets.
|
||||
This is a minor compatible ABI change.
|
||||
.Ss Implement threading primitives that truly sleep
|
||||
The
|
||||
.Xr futex 2
|
||||
|
|
Loading…
Add table
Reference in a new issue