Add accept4() system call.
The accept4() function, compared to accept(), allows setting the new file descriptor atomically close-on-exec and explicitly controlling the non-blocking status on the new socket. (Note that the latter point means that accept() is not equivalent to any form of accept4().) The linuxulator's accept4 implementation leaves a race window where the new file descriptor is not close-on-exec because it calls sys_accept(). This implementation leaves no such race window (by using falloc() flags). The linuxulator could be fixed and simplified by using the new code. Like accept(), accept4() is async-signal-safe, a cancellation point and permitted in capability mode.
This commit is contained in:
parent
14f525595c
commit
da7d2afb6d
@ -270,6 +270,7 @@ MAN+= sctp_generic_recvmsg.2 \
|
|||||||
wait.2 \
|
wait.2 \
|
||||||
write.2
|
write.2
|
||||||
|
|
||||||
|
MLINKS+=accept.2 accept4.2
|
||||||
MLINKS+=access.2 eaccess.2 \
|
MLINKS+=access.2 eaccess.2 \
|
||||||
access.2 faccessat.2
|
access.2 faccessat.2
|
||||||
MLINKS+=brk.2 sbrk.2
|
MLINKS+=brk.2 sbrk.2
|
||||||
|
@ -378,6 +378,7 @@ FBSD_1.2 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
FBSD_1.3 {
|
FBSD_1.3 {
|
||||||
|
accept4;
|
||||||
bindat;
|
bindat;
|
||||||
cap_fcntls_get;
|
cap_fcntls_get;
|
||||||
cap_fcntls_limit;
|
cap_fcntls_limit;
|
||||||
@ -461,6 +462,8 @@ FBSDprivate_1.0 {
|
|||||||
__sys_abort2;
|
__sys_abort2;
|
||||||
_accept;
|
_accept;
|
||||||
__sys_accept;
|
__sys_accept;
|
||||||
|
_accept4;
|
||||||
|
__sys_accept4;
|
||||||
_access;
|
_access;
|
||||||
__sys_access;
|
__sys_access;
|
||||||
_acct;
|
_acct;
|
||||||
|
@ -41,6 +41,8 @@
|
|||||||
.In sys/socket.h
|
.In sys/socket.h
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn accept "int s" "struct sockaddr * restrict addr" "socklen_t * restrict addrlen"
|
.Fn accept "int s" "struct sockaddr * restrict addr" "socklen_t * restrict addrlen"
|
||||||
|
.Ft int
|
||||||
|
.Fn accept4 "int s" "struct sockaddr * restrict addr" "socklen_t * restrict addrlen" "int flags"
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The argument
|
The argument
|
||||||
.Fa s
|
.Fa s
|
||||||
@ -66,6 +68,26 @@ and
|
|||||||
signals from the original socket
|
signals from the original socket
|
||||||
.Fa s .
|
.Fa s .
|
||||||
.Pp
|
.Pp
|
||||||
|
The
|
||||||
|
.Fn accept4
|
||||||
|
system call is similar,
|
||||||
|
but the
|
||||||
|
.Dv O_NONBLOCK
|
||||||
|
property of the new socket is instead determined by the
|
||||||
|
.Dv SOCK_NONBLOCK
|
||||||
|
flag in the
|
||||||
|
.Fa flags
|
||||||
|
argument,
|
||||||
|
the
|
||||||
|
.Dv O_ASYNC
|
||||||
|
property is cleared,
|
||||||
|
the signal destination is cleared
|
||||||
|
and the close-on-exec flag on the new file descriptor can be set via the
|
||||||
|
.Dv SOCK_CLOEXEC
|
||||||
|
flag in the
|
||||||
|
.Fa flags
|
||||||
|
argument.
|
||||||
|
.Pp
|
||||||
If no pending connections are
|
If no pending connections are
|
||||||
present on the queue, and the original socket
|
present on the queue, and the original socket
|
||||||
is not marked as non-blocking,
|
is not marked as non-blocking,
|
||||||
@ -141,13 +163,15 @@ properties and the signal destination being inherited,
|
|||||||
but should set them explicitly using
|
but should set them explicitly using
|
||||||
.Xr fcntl 2 .
|
.Xr fcntl 2 .
|
||||||
.Sh RETURN VALUES
|
.Sh RETURN VALUES
|
||||||
The call returns \-1 on error.
|
These calls return \-1 on error.
|
||||||
If it succeeds, it returns a non-negative
|
If they succeed, they return a non-negative
|
||||||
integer that is a descriptor for the accepted socket.
|
integer that is a descriptor for the accepted socket.
|
||||||
.Sh ERRORS
|
.Sh ERRORS
|
||||||
The
|
The
|
||||||
.Fn accept
|
.Fn accept
|
||||||
system call will fail if:
|
and
|
||||||
|
.Fn accept4
|
||||||
|
system calls will fail if:
|
||||||
.Bl -tag -width Er
|
.Bl -tag -width Er
|
||||||
.It Bq Er EBADF
|
.It Bq Er EBADF
|
||||||
The descriptor is invalid.
|
The descriptor is invalid.
|
||||||
@ -176,6 +200,16 @@ are present to be accepted.
|
|||||||
A connection arrived, but it was closed while waiting
|
A connection arrived, but it was closed while waiting
|
||||||
on the listen queue.
|
on the listen queue.
|
||||||
.El
|
.El
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Fn accept4
|
||||||
|
system call will also fail if:
|
||||||
|
.Bl -tag -width Er
|
||||||
|
.It Bq Er EINVAL
|
||||||
|
The
|
||||||
|
.Fa flags
|
||||||
|
argument is invalid.
|
||||||
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr bind 2 ,
|
.Xr bind 2 ,
|
||||||
.Xr connect 2 ,
|
.Xr connect 2 ,
|
||||||
@ -190,3 +224,8 @@ The
|
|||||||
.Fn accept
|
.Fn accept
|
||||||
system call appeared in
|
system call appeared in
|
||||||
.Bx 4.2 .
|
.Bx 4.2 .
|
||||||
|
.Pp
|
||||||
|
The
|
||||||
|
.Fn accept4
|
||||||
|
system call appeared in
|
||||||
|
.Fx 10.0 .
|
||||||
|
@ -181,6 +181,7 @@ FBSDprivate_1.0 {
|
|||||||
___wait;
|
___wait;
|
||||||
___waitpid;
|
___waitpid;
|
||||||
__accept;
|
__accept;
|
||||||
|
__accept4;
|
||||||
__aio_suspend;
|
__aio_suspend;
|
||||||
__close;
|
__close;
|
||||||
__connect;
|
__connect;
|
||||||
@ -408,3 +409,7 @@ FBSD_1.2 {
|
|||||||
setcontext;
|
setcontext;
|
||||||
swapcontext;
|
swapcontext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FBSD_1.3 {
|
||||||
|
accept4;
|
||||||
|
};
|
||||||
|
@ -101,6 +101,7 @@ extern pid_t __waitpid(pid_t, int *, int);
|
|||||||
extern int __sys_aio_suspend(const struct aiocb * const[], int,
|
extern int __sys_aio_suspend(const struct aiocb * const[], int,
|
||||||
const struct timespec *);
|
const struct timespec *);
|
||||||
extern int __sys_accept(int, struct sockaddr *, socklen_t *);
|
extern int __sys_accept(int, struct sockaddr *, socklen_t *);
|
||||||
|
extern int __sys_accept4(int, struct sockaddr *, socklen_t *, int);
|
||||||
extern int __sys_connect(int, const struct sockaddr *, socklen_t);
|
extern int __sys_connect(int, const struct sockaddr *, socklen_t);
|
||||||
extern int __sys_fsync(int);
|
extern int __sys_fsync(int);
|
||||||
extern int __sys_msync(void *, size_t, int);
|
extern int __sys_msync(void *, size_t, int);
|
||||||
@ -129,6 +130,7 @@ int ___usleep(useconds_t useconds);
|
|||||||
pid_t ___wait(int *);
|
pid_t ___wait(int *);
|
||||||
pid_t ___waitpid(pid_t, int *, int);
|
pid_t ___waitpid(pid_t, int *, int);
|
||||||
int __accept(int, struct sockaddr *, socklen_t *);
|
int __accept(int, struct sockaddr *, socklen_t *);
|
||||||
|
int __accept4(int, struct sockaddr *, socklen_t *, int);
|
||||||
int __aio_suspend(const struct aiocb * const iocbs[], int,
|
int __aio_suspend(const struct aiocb * const iocbs[], int,
|
||||||
const struct timespec *);
|
const struct timespec *);
|
||||||
int __close(int);
|
int __close(int);
|
||||||
@ -176,6 +178,26 @@ __accept(int s, struct sockaddr *addr, socklen_t *addrlen)
|
|||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__weak_reference(__accept4, accept4);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancellation behavior:
|
||||||
|
* If thread is canceled, no socket is created.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
__accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags)
|
||||||
|
{
|
||||||
|
struct pthread *curthread;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
curthread = _get_curthread();
|
||||||
|
_thr_cancel_enter(curthread);
|
||||||
|
ret = __sys_accept4(s, addr, addrlen, flags);
|
||||||
|
_thr_cancel_leave(curthread, ret == -1);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
__weak_reference(__aio_suspend, aio_suspend);
|
__weak_reference(__aio_suspend, aio_suspend);
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1022,3 +1022,7 @@
|
|||||||
int namelen); }
|
int namelen); }
|
||||||
540 AUE_CHFLAGSAT NOPROTO { int chflagsat(int fd, const char *path, \
|
540 AUE_CHFLAGSAT NOPROTO { int chflagsat(int fd, const char *path, \
|
||||||
u_long flags, int atflag); }
|
u_long flags, int atflag); }
|
||||||
|
541 AUE_ACCEPT NOPROTO { int accept4(int s, \
|
||||||
|
struct sockaddr * __restrict name, \
|
||||||
|
__socklen_t * __restrict anamelen, \
|
||||||
|
int flags); }
|
||||||
|
@ -78,6 +78,7 @@ abort2
|
|||||||
## relies on existing bindings on a socket, subject to capability rights.
|
## relies on existing bindings on a socket, subject to capability rights.
|
||||||
##
|
##
|
||||||
accept
|
accept
|
||||||
|
accept4
|
||||||
|
|
||||||
##
|
##
|
||||||
## Allow AIO operations by file descriptor, subject to capability rights.
|
## Allow AIO operations by file descriptor, subject to capability rights.
|
||||||
|
@ -972,5 +972,9 @@
|
|||||||
int namelen); }
|
int namelen); }
|
||||||
540 AUE_CHFLAGSAT STD { int chflagsat(int fd, const char *path, \
|
540 AUE_CHFLAGSAT STD { int chflagsat(int fd, const char *path, \
|
||||||
u_long flags, int atflag); }
|
u_long flags, int atflag); }
|
||||||
|
541 AUE_ACCEPT STD { int accept4(int s, \
|
||||||
|
struct sockaddr * __restrict name, \
|
||||||
|
__socklen_t * __restrict anamelen, \
|
||||||
|
int flags); }
|
||||||
; Please copy any additions and changes to the following compatability tables:
|
; Please copy any additions and changes to the following compatability tables:
|
||||||
; sys/compat/freebsd32/syscalls.master
|
; sys/compat/freebsd32/syscalls.master
|
||||||
|
@ -97,10 +97,18 @@ __FBSDID("$FreeBSD$");
|
|||||||
#endif /* SCTP */
|
#endif /* SCTP */
|
||||||
#endif /* INET || INET6 */
|
#endif /* INET || INET6 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flags for accept1() and kern_accept4(), in addition to SOCK_CLOEXEC
|
||||||
|
* and SOCK_NONBLOCK.
|
||||||
|
*/
|
||||||
|
#define ACCEPT4_INHERIT 0x1
|
||||||
|
#define ACCEPT4_COMPAT 0x2
|
||||||
|
|
||||||
static int sendit(struct thread *td, int s, struct msghdr *mp, int flags);
|
static int sendit(struct thread *td, int s, struct msghdr *mp, int flags);
|
||||||
static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp);
|
static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp);
|
||||||
|
|
||||||
static int accept1(struct thread *td, struct accept_args *uap, int compat);
|
static int accept1(struct thread *td, int s, struct sockaddr *uname,
|
||||||
|
socklen_t *anamelen, int flags);
|
||||||
static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat);
|
static int do_sendfile(struct thread *td, struct sendfile_args *uap, int compat);
|
||||||
static int getsockname1(struct thread *td, struct getsockname_args *uap,
|
static int getsockname1(struct thread *td, struct getsockname_args *uap,
|
||||||
int compat);
|
int compat);
|
||||||
@ -317,49 +325,46 @@ sys_listen(td, uap)
|
|||||||
* accept1()
|
* accept1()
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
accept1(td, uap, compat)
|
accept1(td, s, uname, anamelen, flags)
|
||||||
struct thread *td;
|
struct thread *td;
|
||||||
struct accept_args /* {
|
int s;
|
||||||
int s;
|
struct sockaddr *uname;
|
||||||
struct sockaddr * __restrict name;
|
socklen_t *anamelen;
|
||||||
socklen_t * __restrict anamelen;
|
int flags;
|
||||||
} */ *uap;
|
|
||||||
int compat;
|
|
||||||
{
|
{
|
||||||
struct sockaddr *name;
|
struct sockaddr *name;
|
||||||
socklen_t namelen;
|
socklen_t namelen;
|
||||||
struct file *fp;
|
struct file *fp;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (uap->name == NULL)
|
if (uname == NULL)
|
||||||
return (kern_accept(td, uap->s, NULL, NULL, NULL));
|
return (kern_accept4(td, s, NULL, NULL, flags, NULL));
|
||||||
|
|
||||||
error = copyin(uap->anamelen, &namelen, sizeof (namelen));
|
error = copyin(anamelen, &namelen, sizeof (namelen));
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
error = kern_accept(td, uap->s, &name, &namelen, &fp);
|
error = kern_accept4(td, s, &name, &namelen, flags, &fp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* return a namelen of zero for older code which might
|
* return a namelen of zero for older code which might
|
||||||
* ignore the return value from accept.
|
* ignore the return value from accept.
|
||||||
*/
|
*/
|
||||||
if (error) {
|
if (error) {
|
||||||
(void) copyout(&namelen,
|
(void) copyout(&namelen, anamelen, sizeof(*anamelen));
|
||||||
uap->anamelen, sizeof(*uap->anamelen));
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == 0 && name != NULL) {
|
if (error == 0 && uname != NULL) {
|
||||||
#ifdef COMPAT_OLDSOCK
|
#ifdef COMPAT_OLDSOCK
|
||||||
if (compat)
|
if (flags & ACCEPT4_COMPAT)
|
||||||
((struct osockaddr *)name)->sa_family =
|
((struct osockaddr *)name)->sa_family =
|
||||||
name->sa_family;
|
name->sa_family;
|
||||||
#endif
|
#endif
|
||||||
error = copyout(name, uap->name, namelen);
|
error = copyout(name, uname, namelen);
|
||||||
}
|
}
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
error = copyout(&namelen, uap->anamelen,
|
error = copyout(&namelen, anamelen,
|
||||||
sizeof(namelen));
|
sizeof(namelen));
|
||||||
if (error)
|
if (error)
|
||||||
fdclose(td->td_proc->p_fd, fp, td->td_retval[0], td);
|
fdclose(td->td_proc->p_fd, fp, td->td_retval[0], td);
|
||||||
@ -371,6 +376,13 @@ accept1(td, uap, compat)
|
|||||||
int
|
int
|
||||||
kern_accept(struct thread *td, int s, struct sockaddr **name,
|
kern_accept(struct thread *td, int s, struct sockaddr **name,
|
||||||
socklen_t *namelen, struct file **fp)
|
socklen_t *namelen, struct file **fp)
|
||||||
|
{
|
||||||
|
return (kern_accept4(td, s, name, namelen, ACCEPT4_INHERIT, fp));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
kern_accept4(struct thread *td, int s, struct sockaddr **name,
|
||||||
|
socklen_t *namelen, int flags, struct file **fp)
|
||||||
{
|
{
|
||||||
struct filedesc *fdp;
|
struct filedesc *fdp;
|
||||||
struct file *headfp, *nfp = NULL;
|
struct file *headfp, *nfp = NULL;
|
||||||
@ -400,7 +412,7 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
|
|||||||
if (error != 0)
|
if (error != 0)
|
||||||
goto done;
|
goto done;
|
||||||
#endif
|
#endif
|
||||||
error = falloc(td, &nfp, &fd, 0);
|
error = falloc(td, &nfp, &fd, (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0);
|
||||||
if (error)
|
if (error)
|
||||||
goto done;
|
goto done;
|
||||||
ACCEPT_LOCK();
|
ACCEPT_LOCK();
|
||||||
@ -441,7 +453,10 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
|
|||||||
|
|
||||||
TAILQ_REMOVE(&head->so_comp, so, so_list);
|
TAILQ_REMOVE(&head->so_comp, so, so_list);
|
||||||
head->so_qlen--;
|
head->so_qlen--;
|
||||||
so->so_state |= (head->so_state & SS_NBIO);
|
if (flags & ACCEPT4_INHERIT)
|
||||||
|
so->so_state |= (head->so_state & SS_NBIO);
|
||||||
|
else
|
||||||
|
so->so_state |= (flags & SOCK_NONBLOCK) ? SS_NBIO : 0;
|
||||||
so->so_qstate &= ~SQ_COMP;
|
so->so_qstate &= ~SQ_COMP;
|
||||||
so->so_head = NULL;
|
so->so_head = NULL;
|
||||||
|
|
||||||
@ -454,9 +469,15 @@ kern_accept(struct thread *td, int s, struct sockaddr **name,
|
|||||||
/* connection has been removed from the listen queue */
|
/* connection has been removed from the listen queue */
|
||||||
KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0);
|
KNOTE_UNLOCKED(&head->so_rcv.sb_sel.si_note, 0);
|
||||||
|
|
||||||
pgid = fgetown(&head->so_sigio);
|
if (flags & ACCEPT4_INHERIT) {
|
||||||
if (pgid != 0)
|
pgid = fgetown(&head->so_sigio);
|
||||||
fsetown(pgid, &so->so_sigio);
|
if (pgid != 0)
|
||||||
|
fsetown(pgid, &so->so_sigio);
|
||||||
|
} else {
|
||||||
|
fflag &= ~(FNONBLOCK | FASYNC);
|
||||||
|
if (flags & SOCK_NONBLOCK)
|
||||||
|
fflag |= FNONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
|
finit(nfp, fflag, DTYPE_SOCKET, so, &socketops);
|
||||||
/* Sync socket nonblocking/async state with file flags */
|
/* Sync socket nonblocking/async state with file flags */
|
||||||
@ -527,7 +548,18 @@ sys_accept(td, uap)
|
|||||||
struct accept_args *uap;
|
struct accept_args *uap;
|
||||||
{
|
{
|
||||||
|
|
||||||
return (accept1(td, uap, 0));
|
return (accept1(td, uap->s, uap->name, uap->anamelen, ACCEPT4_INHERIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sys_accept4(td, uap)
|
||||||
|
struct thread *td;
|
||||||
|
struct accept4_args *uap;
|
||||||
|
{
|
||||||
|
if (uap->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
return (accept1(td, uap->s, uap->name, uap->anamelen, uap->flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COMPAT_OLDSOCK
|
#ifdef COMPAT_OLDSOCK
|
||||||
@ -537,7 +569,8 @@ oaccept(td, uap)
|
|||||||
struct accept_args *uap;
|
struct accept_args *uap;
|
||||||
{
|
{
|
||||||
|
|
||||||
return (accept1(td, uap, 1));
|
return (accept1(td, uap->s, uap->name, uap->anamelen,
|
||||||
|
ACCEPT4_INHERIT | ACCEPT4_COMPAT));
|
||||||
}
|
}
|
||||||
#endif /* COMPAT_OLDSOCK */
|
#endif /* COMPAT_OLDSOCK */
|
||||||
|
|
||||||
|
@ -634,6 +634,7 @@ int accept(int, struct sockaddr * __restrict, socklen_t * __restrict);
|
|||||||
int bind(int, const struct sockaddr *, socklen_t);
|
int bind(int, const struct sockaddr *, socklen_t);
|
||||||
int connect(int, const struct sockaddr *, socklen_t);
|
int connect(int, const struct sockaddr *, socklen_t);
|
||||||
#if __BSD_VISIBLE
|
#if __BSD_VISIBLE
|
||||||
|
int accept4(int, struct sockaddr * __restrict, socklen_t * __restrict, int);
|
||||||
int bindat(int, int, const struct sockaddr *, socklen_t);
|
int bindat(int, int, const struct sockaddr *, socklen_t);
|
||||||
int connectat(int, int, const struct sockaddr *, socklen_t);
|
int connectat(int, int, const struct sockaddr *, socklen_t);
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,6 +60,8 @@ int kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg,
|
|||||||
u_int buflen);
|
u_int buflen);
|
||||||
int kern_accept(struct thread *td, int s, struct sockaddr **name,
|
int kern_accept(struct thread *td, int s, struct sockaddr **name,
|
||||||
socklen_t *namelen, struct file **fp);
|
socklen_t *namelen, struct file **fp);
|
||||||
|
int kern_accept4(struct thread *td, int s, struct sockaddr **name,
|
||||||
|
socklen_t *namelen, int flags, struct file **fp);
|
||||||
int kern_access(struct thread *td, char *path, enum uio_seg pathseg,
|
int kern_access(struct thread *td, char *path, enum uio_seg pathseg,
|
||||||
int flags);
|
int flags);
|
||||||
int kern_accessat(struct thread *td, int fd, char *path,
|
int kern_accessat(struct thread *td, int fd, char *path,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user