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
2
Makefile
2
Makefile
|
@ -177,7 +177,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers
|
||||||
echo 'ID=sortix' && \
|
echo 'ID=sortix' && \
|
||||||
echo 'VERSION_ID="$(VERSION)"' && \
|
echo 'VERSION_ID="$(VERSION)"' && \
|
||||||
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
|
echo 'PRETTY_NAME="Sortix $(VERSION)"' && \
|
||||||
echo 'SORTIX_ABI=1.1' && \
|
echo 'SORTIX_ABI=1.2' && \
|
||||||
true) > "$(SYSROOT)/etc/sortix-release"
|
true) > "$(SYSROOT)/etc/sortix-release"
|
||||||
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
|
echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system"
|
||||||
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
|
ln -sf sortix-release "$(SYSROOT)/etc/os-release"
|
||||||
|
|
|
@ -232,6 +232,16 @@ bool Descriptor::IsSeekable()
|
||||||
return seekable;
|
return seekable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Descriptor::pass()
|
||||||
|
{
|
||||||
|
return vnode->pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Descriptor::unpass()
|
||||||
|
{
|
||||||
|
vnode->unpass();
|
||||||
|
}
|
||||||
|
|
||||||
int Descriptor::sync(ioctx_t* ctx)
|
int Descriptor::sync(ioctx_t* ctx)
|
||||||
{
|
{
|
||||||
// TODO: Possible denial-of-service attack if someone opens the file without
|
// TODO: Possible denial-of-service attack if someone opens the file without
|
||||||
|
|
|
@ -195,6 +195,8 @@ class Unode : public Inode
|
||||||
public:
|
public:
|
||||||
Unode(Ref<Server> server, ino_t ino, mode_t type);
|
Unode(Ref<Server> server, ino_t ino, mode_t type);
|
||||||
virtual ~Unode();
|
virtual ~Unode();
|
||||||
|
virtual bool pass();
|
||||||
|
virtual void unpass();
|
||||||
virtual void linked();
|
virtual void linked();
|
||||||
virtual void unlinked();
|
virtual void unlinked();
|
||||||
virtual int sync(ioctx_t* ctx);
|
virtual int sync(ioctx_t* ctx);
|
||||||
|
@ -766,6 +768,15 @@ void Unode::UnexpectedResponse(Channel* channel, struct fsm_msg_header* hdr)
|
||||||
errno = EIO;
|
errno = EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Unode::pass()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unode::unpass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Unode::linked()
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -58,6 +58,8 @@ public:
|
||||||
Ref<Descriptor> Fork();
|
Ref<Descriptor> Fork();
|
||||||
bool SetFlags(int new_dflags);
|
bool SetFlags(int new_dflags);
|
||||||
int GetFlags();
|
int GetFlags();
|
||||||
|
bool pass();
|
||||||
|
void unpass();
|
||||||
int sync(ioctx_t* ctx);
|
int sync(ioctx_t* ctx);
|
||||||
int stat(ioctx_t* ctx, struct stat* st);
|
int stat(ioctx_t* ctx, struct stat* st);
|
||||||
int statvfs(ioctx_t* ctx, struct statvfs* stvfs);
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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:
|
public:
|
||||||
virtual ~Inode() { }
|
virtual ~Inode() { }
|
||||||
|
virtual bool pass() = 0;
|
||||||
|
virtual void unpass() = 0;
|
||||||
virtual void linked() = 0;
|
virtual void linked() = 0;
|
||||||
virtual void unlinked() = 0;
|
virtual void unlinked() = 0;
|
||||||
virtual int sync(ioctx_t* ctx) = 0;
|
virtual int sync(ioctx_t* ctx) = 0;
|
||||||
|
@ -165,6 +167,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
AbstractInode();
|
AbstractInode();
|
||||||
virtual ~AbstractInode();
|
virtual ~AbstractInode();
|
||||||
|
virtual bool pass();
|
||||||
|
virtual void unpass();
|
||||||
virtual void linked();
|
virtual void linked();
|
||||||
virtual void unlinked();
|
virtual void unlinked();
|
||||||
virtual int sync(ioctx_t* ctx);
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -43,6 +43,8 @@ public:
|
||||||
bool SetSIGPIPEDelivery(bool deliver_sigpipe);
|
bool SetSIGPIPEDelivery(bool deliver_sigpipe);
|
||||||
size_t Size();
|
size_t Size();
|
||||||
bool Resize(size_t new_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 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 recv(ioctx_t* ctx, uint8_t* buf, size_t count, int flags);
|
||||||
ssize_t recvmsg(ioctx_t* ctx, struct msghdr* msg, 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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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; }
|
size_t Refcount() const { return obj ? obj->Refcount : 0; }
|
||||||
bool IsUnique() const { return obj->IsUnique(); }
|
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:
|
private:
|
||||||
T* obj;
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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:
|
public:
|
||||||
Vnode(Ref<Inode> inode, Ref<Vnode> mountedat, ino_t rootino, dev_t rootdev);
|
Vnode(Ref<Inode> inode, Ref<Vnode> mountedat, ino_t rootino, dev_t rootdev);
|
||||||
virtual ~Vnode();
|
virtual ~Vnode();
|
||||||
|
bool pass();
|
||||||
|
void unpass();
|
||||||
int sync(ioctx_t* ctx);
|
int sync(ioctx_t* ctx);
|
||||||
int stat(ioctx_t* ctx, struct stat* st);
|
int stat(ioctx_t* ctx, struct stat* st);
|
||||||
int statvfs(ioctx_t* ctx, struct statvfs* stvfs);
|
int statvfs(ioctx_t* ctx, struct statvfs* stvfs);
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
#define S_IFFACTORY 0x10000
|
#define S_IFFACTORY 0x10000
|
||||||
/* Don't run the factory method if simply stat'ing the inode. */
|
/* Don't run the factory method if simply stat'ing the inode. */
|
||||||
#define S_IFFACTORY_NOSTAT 0x20000
|
#define S_IFFACTORY_NOSTAT 0x20000
|
||||||
|
/* The file object must never be wrapped in another file object. */
|
||||||
|
#define S_IFNEVERWRAP 0x40000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -62,6 +62,15 @@ AbstractInode::~AbstractInode()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AbstractInode::pass()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractInode::unpass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractInode::linked()
|
void AbstractInode::linked()
|
||||||
{
|
{
|
||||||
InterlockedIncrement(&stat_nlink);
|
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;
|
Ref<Inode> inner_inode = desc->vnode->inode;
|
||||||
desc.Reset();
|
desc.Reset();
|
||||||
|
|
||||||
|
if ( inner_inode->type & S_IFNEVERWRAP )
|
||||||
|
return errno = EPERM, -1;
|
||||||
if ( !S_ISBLK(inner_inode->type) && !S_ISREG(inner_inode->type) )
|
if ( !S_ISBLK(inner_inode->type) && !S_ISREG(inner_inode->type) )
|
||||||
return errno = EPERM, -1;
|
return errno = EPERM, -1;
|
||||||
if ( start < 0 || length < 0 )
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -83,6 +83,8 @@ class StreamSocket : public AbstractInode
|
||||||
public:
|
public:
|
||||||
StreamSocket(uid_t owner, gid_t group, mode_t mode, Ref<Manager> manager);
|
StreamSocket(uid_t owner, gid_t group, mode_t mode, Ref<Manager> manager);
|
||||||
virtual ~StreamSocket();
|
virtual ~StreamSocket();
|
||||||
|
virtual bool pass();
|
||||||
|
virtual void unpass();
|
||||||
virtual Ref<Inode> accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrsize,
|
virtual Ref<Inode> accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrsize,
|
||||||
int flags);
|
int flags);
|
||||||
virtual int bind(ioctx_t* ctx, const uint8_t* addr, size_t addrsize);
|
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;
|
inode_type = INODE_TYPE_STREAM;
|
||||||
dev = (dev_t) manager.Get();
|
dev = (dev_t) manager.Get();
|
||||||
ino = (ino_t) this;
|
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_uid = owner;
|
||||||
this->stat_gid = group;
|
this->stat_gid = group;
|
||||||
this->stat_mode = (mode & S_SETABLE) | this->type;
|
this->stat_mode = (mode & S_SETABLE) | this->type;
|
||||||
|
@ -191,6 +196,23 @@ StreamSocket::~StreamSocket()
|
||||||
free(bound_address);
|
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,
|
Ref<Inode> StreamSocket::accept4(ioctx_t* ctx, uint8_t* addr, size_t* addrsize,
|
||||||
int flags)
|
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
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* 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 <= start);
|
||||||
assert(0 <= length);
|
assert(0 <= length);
|
||||||
assert(length <= OFF_MAX - start);
|
assert(length <= OFF_MAX - start);
|
||||||
|
assert(!(inner_inode->type & S_IFNEVERWRAP));
|
||||||
assert(S_ISBLK(inner_inode->type) || S_ISREG(inner_inode->type));
|
assert(S_ISBLK(inner_inode->type) || S_ISREG(inner_inode->type));
|
||||||
this->dev = (dev_t) this;
|
this->dev = (dev_t) this;
|
||||||
this->ino = (ino_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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Vnode::pass()
|
||||||
|
{
|
||||||
|
return inode->pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Vnode::unpass()
|
||||||
|
{
|
||||||
|
inode->unpass();
|
||||||
|
}
|
||||||
|
|
||||||
int Vnode::sync(ioctx_t* ctx)
|
int Vnode::sync(ioctx_t* ctx)
|
||||||
{
|
{
|
||||||
return inode->sync(ctx);
|
return inode->sync(ctx);
|
||||||
|
|
|
@ -97,9 +97,21 @@ struct cmsghdr
|
||||||
|
|
||||||
#define SCM_RIGHTS 1
|
#define SCM_RIGHTS 1
|
||||||
|
|
||||||
/* TODO: CMSG_DATA(cmsg) */
|
#define CMSG_ALIGN(value) \
|
||||||
/* TODO: CMSG_NXTHDR(cmsg) */
|
(-(-(size_t)(value) & ~(__alignof__(struct cmsghdr) - 1)))
|
||||||
/* TODO: CMSH_FIRSTHDR(cmsg) */
|
#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
|
struct linger
|
||||||
{
|
{
|
||||||
|
@ -140,6 +152,7 @@ struct linger
|
||||||
#define MSG_WAITALL (1<<7)
|
#define MSG_WAITALL (1<<7)
|
||||||
#define MSG_DONTWAIT (1<<8)
|
#define MSG_DONTWAIT (1<<8)
|
||||||
#define MSG_CMSG_CLOEXEC (1<<9)
|
#define MSG_CMSG_CLOEXEC (1<<9)
|
||||||
|
#define MSG_CMSG_CLOFORK (1<<10)
|
||||||
|
|
||||||
#define AF_UNSPEC 0
|
#define AF_UNSPEC 0
|
||||||
#define AF_INET 1
|
#define AF_INET 1
|
||||||
|
|
|
@ -25,6 +25,10 @@ test-pthread-once \
|
||||||
test-pthread-self \
|
test-pthread-self \
|
||||||
test-pthread-tls \
|
test-pthread-tls \
|
||||||
test-signal-raise \
|
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-name \
|
||||||
test-unix-socket-shutdown \
|
test-unix-socket-shutdown \
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
#define TEST_H
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <error.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
|
@ -69,6 +69,14 @@ releasing Sortix x.y, foo." to allow the maintainer to easily
|
||||||
.Xr grep 1
|
.Xr grep 1
|
||||||
for it after a release.
|
for it after a release.
|
||||||
.Sh CHANGES
|
.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
|
.Ss Implement threading primitives that truly sleep
|
||||||
The
|
The
|
||||||
.Xr futex 2
|
.Xr futex 2
|
||||||
|
|
Loading…
Reference in New Issue