fd: add close_range(..., CLOSE_RANGE_CLOEXEC)

For compatibility with Linux.

MFC after:	3 days
Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D34424
This commit is contained in:
Mateusz Guzik 2022-03-03 13:45:11 +01:00
parent 33cbbf268f
commit f3f3e3c44d
12 changed files with 125 additions and 22 deletions

View file

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 12, 2020
.Dd March 3, 2022
.Dt CLOSEFROM 2
.Os
.Sh NAME
@ -56,8 +56,12 @@ and
.Fa highfd
inclusive, clamped to the range of open file descriptors.
Any errors encountered while closing file descriptors are ignored.
There are currently no defined
.Fa flags .
Supported
.Fa flags :
.Bl -tag -width ".Dv CLOSE_RANGE_CLOEXEC"
.It Dv CLOSE_RANGE_CLOEXEC
Set the close-on-exec flag on descriptors in the range instead of closing them.
.El
.Sh RETURN VALUES
Upon successful completion,
.Fn close_range

View file

@ -400,6 +400,13 @@ sysdecode_cap_fcntlrights(FILE *fp, uint32_t rights, uint32_t *rem)
return (print_mask_int(fp, capfcntl, rights, rem));
}
bool
sysdecode_close_range_flags(FILE *fp, int flags, int *rem)
{
return (print_mask_int(fp, closerangeflags, flags, rem));
}
const char *
sysdecode_extattrnamespace(int namespace)
{

View file

@ -94,6 +94,7 @@ gen_table "accessmode" "[A-Z]_OK[[:space:]]+0?x?[0-9A-Fa-f]+" "sys/
gen_table "acltype" "ACL_TYPE_[A-Z4_]+[[:space:]]+0x[0-9]+" "sys/acl.h"
gen_table "atflags" "AT_[A-Z_]+[[:space:]]+0x[0-9]+" "sys/fcntl.h"
gen_table "capfcntl" "CAP_FCNTL_[A-Z]+[[:space:]]+\(1" "sys/capsicum.h"
gen_table "closerangeflags" "CLOSE_RANGE_[A-Z]+[[:space:]]+\([0-9]+<<[0-9]+\)" "sys/unistd.h"
gen_table "extattrns" "EXTATTR_NAMESPACE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/extattr.h"
gen_table "fadvisebehav" "POSIX_FADV_[A-Z]+[[:space:]]+[0-9]+" "sys/fcntl.h"
gen_table "openflags" "O_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/fcntl.h" "O_RDONLY|O_RDWR|O_WRONLY"

View file

@ -43,6 +43,7 @@ const char *sysdecode_atfd(int _fd);
bool sysdecode_atflags(FILE *_fp, int _flags, int *_rem);
bool sysdecode_cap_fcntlrights(FILE *_fp, uint32_t _rights, uint32_t *_rem);
void sysdecode_cap_rights(FILE *_fp, cap_rights_t *_rightsp);
bool sysdecode_close_range_flags(FILE *_fp, int _flags, int *_rem);
const char *sysdecode_cmsg_type(int _cmsg_level, int _cmsg_type);
const char *sysdecode_extattrnamespace(int _namespace);
const char *sysdecode_fadvice(int _advice);

View file

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd January 16, 2018
.Dd March 3, 2022
.Dt sysdecode_mask 3
.Os
.Sh NAME
@ -32,6 +32,7 @@
.Nm sysdecode_accessmode ,
.Nm sysdecode_atflags ,
.Nm sysdecode_capfcntlrights ,
.Nm sysdecode_close_range_flags ,
.Nm sysdecode_fcntl_fileflags ,
.Nm sysdecode_fileflags ,
.Nm sysdecode_filemode ,
@ -74,6 +75,8 @@
.Ft bool
.Fn sysdecode_cap_fcntlrights "FILE *fp" "uint32_t rights" "uint32_t *rem"
.Ft bool
.Fn sysdecode_close_range_flags "FILE *fp" "int flags" "int *rem"
.Ft bool
.Fn sysdecode_fcntl_fileflags "FILE *fp" "int flags" "int *rem"
.Ft bool
.Fn sysdecode_fileflags "FILE *fp" "fflags_t flags" "fflags_t *rem"

View file

@ -1393,24 +1393,40 @@ kern_close(struct thread *td, int fd)
return (closefp(fdp, fd, fp, td, true, true));
}
int
kern_close_range(struct thread *td, u_int lowfd, u_int highfd)
static int
close_range_cloexec(struct thread *td, u_int lowfd, u_int highfd)
{
struct filedesc *fdp;
struct fdescenttbl *fdt;
struct filedescent *fde;
int fd;
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
fdt = atomic_load_ptr(&fdp->fd_files);
highfd = MIN(highfd, fdt->fdt_nfiles - 1);
fd = lowfd;
if (__predict_false(fd > highfd)) {
goto out_locked;
}
for (; fd <= highfd; fd++) {
fde = &fdt->fdt_ofiles[fd];
if (fde->fde_file != NULL)
fde->fde_flags |= UF_EXCLOSE;
}
out_locked:
FILEDESC_XUNLOCK(fdp);
return (0);
}
static int
close_range_impl(struct thread *td, u_int lowfd, u_int highfd)
{
struct filedesc *fdp;
const struct fdescenttbl *fdt;
struct file *fp;
int fd;
/*
* Check this prior to clamping; closefrom(3) with only fd 0, 1, and 2
* open should not be a usage error. From a close_range() perspective,
* close_range(3, ~0U, 0) in the same scenario should also likely not
* be a usage error as all fd above 3 are in-fact already closed.
*/
if (highfd < lowfd) {
return (EINVAL);
}
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
fdt = atomic_load_ptr(&fdp->fd_files);
@ -1440,6 +1456,26 @@ kern_close_range(struct thread *td, u_int lowfd, u_int highfd)
return (0);
}
int
kern_close_range(struct thread *td, int flags, u_int lowfd, u_int highfd)
{
/*
* Check this prior to clamping; closefrom(3) with only fd 0, 1, and 2
* open should not be a usage error. From a close_range() perspective,
* close_range(3, ~0U, 0) in the same scenario should also likely not
* be a usage error as all fd above 3 are in-fact already closed.
*/
if (highfd < lowfd) {
return (EINVAL);
}
if ((flags & CLOSE_RANGE_CLOEXEC) != 0)
return (close_range_cloexec(td, lowfd, highfd));
return (close_range_impl(td, lowfd, highfd));
}
#ifndef _SYS_SYSPROTO_H_
struct close_range_args {
u_int lowfd;
@ -1455,10 +1491,9 @@ sys_close_range(struct thread *td, struct close_range_args *uap)
AUDIT_ARG_CMD(uap->highfd);
AUDIT_ARG_FFLAGS(uap->flags);
/* No flags currently defined */
if (uap->flags != 0)
if ((uap->flags & ~(CLOSE_RANGE_CLOEXEC)) != 0)
return (EINVAL);
return (kern_close_range(td, uap->lowfd, uap->highfd));
return (kern_close_range(td, uap->flags, uap->lowfd, uap->highfd));
}
#ifdef COMPAT_FREEBSD12
@ -1483,7 +1518,7 @@ freebsd12_closefrom(struct thread *td, struct freebsd12_closefrom_args *uap)
* closefrom(0) which closes all files.
*/
lowfd = MAX(0, uap->lowfd);
return (kern_close_range(td, lowfd, ~0U));
return (kern_close_range(td, 0, lowfd, ~0U));
}
#endif /* COMPAT_FREEBSD12 */

View file

@ -111,7 +111,7 @@ int kern_clock_settime(struct thread *td, clockid_t clock_id,
struct timespec *ats);
void kern_thread_cputime(struct thread *targettd, struct timespec *ats);
void kern_process_cputime(struct proc *targetp, struct timespec *ats);
int kern_close_range(struct thread *td, u_int lowfd, u_int highfd);
int kern_close_range(struct thread *td, int flags, u_int lowfd, u_int highfd);
int kern_close(struct thread *td, int fd);
int kern_connectat(struct thread *td, int dirfd, int fd,
struct sockaddr *sa);

View file

@ -200,6 +200,11 @@
#define SWAPOFF_FORCE 0x00000001
/*
* close_range() options.
*/
#define CLOSE_RANGE_CLOEXEC (1<<2)
#endif /* __BSD_VISIBLE */
#endif /* !_SYS_UNISTD_H_ */

View file

@ -144,7 +144,7 @@ main(void)
{
struct shared_info *info;
pid_t pid;
int fd, i, start;
int fd, flags, i, start;
printf("1..20\n");
@ -325,5 +325,38 @@ main(void)
fail(info->tag, "%s", info->message);
ok(info->tag);
/* test CLOSE_RANGE_CLOEXEC */
for (i = 0; i < 8; i++)
(void)devnull();
fd = highest_fd();
start = fd - 8;
if (close_range(start + 1, start + 4, CLOSE_RANGE_CLOEXEC) < 0)
fail_err("close_range(..., CLOSE_RANGE_CLOEXEC)");
flags = fcntl(start, F_GETFD);
if (flags < 0)
fail_err("fcntl(.., F_GETFD)");
if ((flags & FD_CLOEXEC) != 0)
fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
"when it should not have on fd %d", start);
for (i = start + 1; i <= start + 4; i++) {
flags = fcntl(i, F_GETFD);
if (flags < 0)
fail_err("fcntl(.., F_GETFD)");
if ((flags & FD_CLOEXEC) == 0)
fail("close_range", "CLOSE_RANGE_CLOEXEC did not set "
"close-on-exec on fd %d", i);
}
for (; i < start + 8; i++) {
flags = fcntl(i, F_GETFD);
if (flags < 0)
fail_err("fcntl(.., F_GETFD)");
if ((flags & FD_CLOEXEC) != 0)
fail("close_range", "CLOSE_RANGE_CLOEXEC set close-on-exec "
"when it should not have on fd %d", i);
}
if (close_range(start, start + 8, 0) < 0)
fail_err("close_range");
ok("close_range(..., CLOSE_RANGE_CLOEXEC)");
return (0);
}

View file

@ -864,6 +864,14 @@ ktrsyscall(struct ktr_syscall *ktr, u_int sv_flags)
ip++;
narg--;
break;
case SYS_close_range:
print_number(ip, narg, c);
print_number(ip, narg, c);
putchar(',');
print_mask_arg(sysdecode_close_range_flags, *ip);
ip += 3;
narg -= 3;
break;
case SYS_open:
case SYS_openat:
print_number(ip, narg, c);

View file

@ -91,6 +91,7 @@ enum Argtype {
Atfd,
Atflags,
CapFcntlRights,
Closerangeflags,
Extattrnamespace,
Fadvice,
Fcntl,

View file

@ -183,6 +183,8 @@ static const struct syscall_decode decoded_syscalls[] = {
.args = { { Int, 0 } } },
{ .name = "closefrom", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
{ .name = "close_range", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Int, 1 }, { Closerangeflags, 2 } } },
{ .name = "compat11.fstat", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Stat11 | OUT, 1 } } },
{ .name = "compat11.fstatat", .ret_type = 1, .nargs = 4,
@ -1983,6 +1985,9 @@ print_arg(struct syscall_arg *sc, unsigned long *args, register_t *retval,
case Fcntl:
print_integer_arg(sysdecode_fcntl_cmd, fp, args[sc->offset]);
break;
case Closerangeflags:
print_mask_arg(sysdecode_close_range_flags, fp, args[sc->offset]);
break;
case Mprot:
print_mask_arg(sysdecode_mmap_prot, fp, args[sc->offset]);
break;