From d4c9a98527afdbd67b25a1300339a42b9e0a24c2 Mon Sep 17 00:00:00 2001 From: Paul Floyd Date: Fri, 8 Sep 2023 23:40:12 +0200 Subject: [PATCH] FreeBSD: add syscall wrapper for close_range --- .gitignore | 1 + NEWS | 3 +- coregrind/m_syswrap/priv_syswrap-freebsd.h | 2 +- coregrind/m_syswrap/syswrap-freebsd.c | 73 +++++++++++++++++- include/vki/vki-freebsd.h | 1 + memcheck/tests/freebsd/Makefile.am | 60 +++++++-------- memcheck/tests/freebsd/close_range.c | 77 +++++++++++++++++++ memcheck/tests/freebsd/close_range.stderr.exp | 12 +++ memcheck/tests/freebsd/close_range.vgtest | 2 + memcheck/tests/freebsd/scalar_13_plus.c | 4 + .../tests/freebsd/scalar_13_plus.stderr.exp | 12 +++ 11 files changed, 213 insertions(+), 34 deletions(-) create mode 100644 memcheck/tests/freebsd/close_range.c create mode 100644 memcheck/tests/freebsd/close_range.stderr.exp create mode 100644 memcheck/tests/freebsd/close_range.vgtest diff --git a/.gitignore b/.gitignore index 6aeb41df94..48730f2193 100644 --- a/.gitignore +++ b/.gitignore @@ -1351,6 +1351,7 @@ /memcheck/tests/freebsd/capsicum /memcheck/tests/freebsd/chflags /memcheck/tests/freebsd/chmod_chown +/memcheck/tests/freebsd/close_range /memcheck/tests/freebsd/delete_sized_mismatch /memcheck/tests/freebsd/errno_aligned_allocs /memcheck/tests/freebsd/eventfd1 diff --git a/NEWS b/NEWS index 4459ddd7cf..c3ee77d516 100644 --- a/NEWS +++ b/NEWS @@ -20,7 +20,8 @@ AMD64/macOS 10.13 and nanoMIPS/Linux. * ================== PLATFORM CHANGES ================= -* support has been added for FreeBSD 14 and FreeBSD 15. +* Support has been added for FreeBSD 14 and FreeBSD 15. +* Add support for the FreeBSD close_range system call. * ==================== TOOL CHANGES =================== diff --git a/coregrind/m_syswrap/priv_syswrap-freebsd.h b/coregrind/m_syswrap/priv_syswrap-freebsd.h index 8822f22f21..844da82a45 100644 --- a/coregrind/m_syswrap/priv_syswrap-freebsd.h +++ b/coregrind/m_syswrap/priv_syswrap-freebsd.h @@ -541,7 +541,7 @@ DECL_TEMPLATE(freebsd, sys_sigfastblock) // 573 DECL_TEMPLATE(freebsd, sys___realpathat) // 574 #endif -// unimpl __NR_close_range 575 +DECL_TEMPLATE(freebsd, sys_close_range) // 575 #endif diff --git a/coregrind/m_syswrap/syswrap-freebsd.c b/coregrind/m_syswrap/syswrap-freebsd.c index a9a77c3f45..4f304091e5 100644 --- a/coregrind/m_syswrap/syswrap-freebsd.c +++ b/coregrind/m_syswrap/syswrap-freebsd.c @@ -6642,7 +6642,7 @@ POST(sys_shm_open2) } } -// SYS_sigfastblock +// SYS_sigfastblock 573 // int sigfastblock(int cmd, void *ptr); PRE(sys_sigfastblock) { @@ -6667,6 +6667,75 @@ PRE(sys___realpathat) PRE_MEM_WRITE("__realpathat(buf)", (Addr)ARG3, ARG4); } +// SYS_sys_close_range 575 +// int close_range(close_range(u_int lowfd, u_int highfd, int flags); +PRE(sys_close_range) +{ + SysRes res = VG_(mk_SysRes_Success)(0); + unsigned int lowfd = ARG1; + unsigned int fd_counter; // will count from lowfd to highfd + unsigned int highfd = ARG2; + + /* on linux the may lock if futexes are used + * there is a lock in the kernel but I assume it's just + * a spinlock */ + PRINT("sys_close_range ( %" FMT_REGWORD "u, %" FMT_REGWORD "u, %" + FMT_REGWORD "d )", ARG1, ARG2, SARG3); + PRE_REG_READ3(int, "close_range", + unsigned int, lowfd, unsigned int, highfd, + int, flags); + + if (lowfd > highfd) { + SET_STATUS_Failure( VKI_EINVAL ); + return; + } + + if (highfd >= VG_(fd_hard_limit)) + highfd = VG_(fd_hard_limit) - 1; + + if (lowfd > highfd) { + SET_STATUS_Success ( 0 ); + return; + } + + fd_counter = lowfd; + do { + if (fd_counter > highfd + || (fd_counter == 2U/*stderr*/ && VG_(debugLog_getLevel)() > 0) + || fd_counter == VG_(log_output_sink).fd + || fd_counter == VG_(xml_output_sink).fd) { + /* Split the range if it contains a file descriptor we're not + * supposed to close. */ + if (fd_counter - 1 >= lowfd) { + res = VG_(do_syscall3)(__NR_close_range, (UWord)lowfd, (UWord)fd_counter - 1, ARG3 ); + } + lowfd = fd_counter + 1; + } + } while (fd_counter++ <= highfd); + + /* If it failed along the way, it's presumably the flags being wrong. */ + SET_STATUS_from_SysRes (res); +} + +POST(sys_close_range) +{ + unsigned int fd; + unsigned int last = ARG2; + + if (!VG_(clo_track_fds) + || (ARG3 & VKI_CLOSE_RANGE_CLOEXEC) != 0) + return; + + if (last >= VG_(fd_hard_limit)) + last = VG_(fd_hard_limit) - 1; + + for (fd = ARG1; fd <= last; fd++) + if ((fd != 2/*stderr*/ || VG_(debugLog_getLevel)() == 0) + && fd != VG_(log_output_sink).fd + && fd != VG_(xml_output_sink).fd) + ML_(record_fd_close)(fd); +} + POST(sys___realpathat) { POST_MEM_WRITE((Addr)ARG3, ARG4); @@ -7402,7 +7471,7 @@ const SyscallTableEntry ML_(syscall_table)[] = { BSDX_(__NR_sigfastblock, sys_sigfastblock), // 573 BSDXY( __NR___realpathat, sys___realpathat), // 574 #endif - // unimpl __NR_close_range 575 + BSDXY(__NR_close_range, sys_close_range), // 575 #endif #if (FREEBSD_VERS >= FREEBSD_13_0) diff --git a/include/vki/vki-freebsd.h b/include/vki/vki-freebsd.h index b30b2933ec..eee094d340 100644 --- a/include/vki/vki-freebsd.h +++ b/include/vki/vki-freebsd.h @@ -1610,6 +1610,7 @@ struct vki_dirent { #define VKI_RFSPAWN (1U<<31U) +#define VKI_CLOSE_RANGE_CLOEXEC (1<<2) //---------------------------------------------------------------------- // From sys/msg.h diff --git a/memcheck/tests/freebsd/Makefile.am b/memcheck/tests/freebsd/Makefile.am index d7aae0fbf0..f842feb7e0 100644 --- a/memcheck/tests/freebsd/Makefile.am +++ b/memcheck/tests/freebsd/Makefile.am @@ -5,50 +5,50 @@ dist_noinst_SCRIPTS = filter_stderr filter_pts dump_stdout filter_sigwait \ filter_scalar filter_realpathat filter_fstat filter_eventfd2 EXTRA_DIST = \ - scalar.h \ - statfs.vgtest \ - statfs.stderr.exp \ - pdfork_pdkill.vgtest \ - pdfork_pdkill.stderr.exp \ - getfsstat.vgtest \ - getfsstat.stderr.exp \ - getfsstat.supp \ - getfsstat.stderr.exp-x86 \ - supponlyobj.vgtest \ - supponlyobj.stderr.exp \ - supponlyobj.supp \ - extattr.vgtest \ - extattr.stderr.exp \ - sigwait.vgtest \ - sigwait.stdout.exp \ - sigwait.stderr.exp \ - sigwait.stderr.exp-x86 \ + capsicum.vgtest \ + capsicum.stderr.exp \ chflags.vgtest\ chflags.stderr.exp \ chflags.stderr.exp-x86 \ + close_range.vgtest close_range.stderr.exp \ + extattr.vgtest \ + extattr.stderr.exp \ get_set_login.vgtest \ get_set_login.stderr.exp \ + getfsstat.vgtest \ + getfsstat.stderr.exp \ + getfsstat.supp \ + getfsstat.stderr.exp-x86 \ + pdfork_pdkill.vgtest \ + pdfork_pdkill.stderr.exp \ revoke.vgtest \ revoke.stderr.exp \ - scalar.vgtest \ + scalar.h scalar.vgtest \ scalar.stderr.exp \ scalar.stderr.exp-x86 \ - capsicum.vgtest \ - capsicum.stderr.exp \ - getfh.vgtest \ - getfh.stderr.exp \ - linkat.vgtest \ - linkat.stderr.exp \ - scalar_fork.vgtest \ - scalar_fork.stderr.exp \ - scalar_thr_exit.vgtest \ - scalar_thr_exit.stderr.exp \ scalar_abort2.vgtest \ scalar_abort2.stderr.exp \ + scalar_fork.vgtest \ + scalar_fork.stderr.exp \ scalar_pdfork.vgtest \ scalar_pdfork.stderr.exp \ + scalar_thr_exit.vgtest \ + scalar_thr_exit.stderr.exp \ scalar_vfork.vgtest \ scalar_vfork.stderr.exp \ + sigwait.vgtest \ + sigwait.stdout.exp \ + sigwait.stderr.exp \ + sigwait.stderr.exp-x86 \ + statfs.vgtest \ + statfs.stderr.exp \ + supponlyobj.vgtest \ + supponlyobj.stderr.exp \ + supponlyobj.supp \ + getfh.vgtest \ + getfh.stderr.exp \ + linkat.vgtest \ + linkat.stderr.exp \ stat.vgtest \ stat.stderr.exp \ stat.stderr.exp-x86 \ @@ -116,7 +116,7 @@ EXTRA_DIST = \ delete_sized_mismatch_xml.stderr.exp check_PROGRAMS = \ - statfs pdfork_pdkill getfsstat inlinfo inlinfo_nested.so extattr \ + close_range statfs pdfork_pdkill getfsstat inlinfo inlinfo_nested.so extattr \ sigwait chflags get_set_login revoke scalar capsicum getfh \ linkat scalar_fork scalar_thr_exit scalar_abort2 scalar_pdfork \ scalar_vfork stat file_locking_wait6 utimens access chmod_chown \ diff --git a/memcheck/tests/freebsd/close_range.c b/memcheck/tests/freebsd/close_range.c new file mode 100644 index 0000000000..5490c80ef6 --- /dev/null +++ b/memcheck/tests/freebsd/close_range.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + struct rlimit rl; + // I'm assuming opens start at 3 and get recycled + int fd1 = open("close_range.c", O_RDONLY); + int fd2 = open("close_range.vgtest", O_RDONLY); + int fd3 = open("close_range.stderr.exp", O_RDONLY); + + // all open + close_range(fd1, fd3, 0); + // all closed + close_range(fd1, fd3, 0); + + fd1 = open("close_range.c", O_RDONLY); + fd2 = open("close_range.vgtest", O_RDONLY); + + // 3 and 4 open 5 closed + close_range(fd1, fd3, 0); + + fd1 = open("close_range.c", O_RDONLY); + fd3 = open("close_range.stderr.exp", O_RDONLY); + + // 3 and 5 open 4 closed + close_range(fd1, fd3, 0); + + fd1 = open("close_range.c", O_RDONLY); + fd2 = open("close_range.vgtest", O_RDONLY); + fd3 = open("close_range.stderr.exp", O_RDONLY); + + // good flag + close_range(fd1, fd3, CLOSE_RANGE_CLOEXEC); + close_range(fd1, fd3, 0); + + fd1 = open("close_range.c", O_RDONLY); + fd2 = open("close_range.vgtest", O_RDONLY); + fd3 = open("close_range.stderr.exp", O_RDONLY); + + errno = 0; + // bad flag + close_range(fd1, fd3, 2); + assert(errno = EINVAL); + + errno = 0; + // wrong order + close_range(fd3, fd1, 2); + assert(errno = EINVAL); + + errno = 0; + getrlimit(RLIMIT_NOFILE, &rl); + + // should do nothing + close_range(rl.rlim_cur+100, rl.rlim_cur+200, 0); + + close_range(3, rl.rlim_cur, 0); + + fd1 = open("close_range.c", O_RDONLY); + fd2 = open("close_range.vgtest", O_RDONLY); + fd3 = open("close_range.stderr.exp", O_RDONLY); + + close_range(3, rl.rlim_cur+1, 0); + + { + unsigned a; + unsigned b; + int c; + close_range(a, b, c); + } +} + diff --git a/memcheck/tests/freebsd/close_range.stderr.exp b/memcheck/tests/freebsd/close_range.stderr.exp new file mode 100644 index 0000000000..a7e9db6378 --- /dev/null +++ b/memcheck/tests/freebsd/close_range.stderr.exp @@ -0,0 +1,12 @@ +Syscall param close_range(lowfd) contains uninitialised byte(s) + at 0x........: close_range (in /...libc...) + by 0x........: main (close_range.c:74) + +Syscall param close_range(highfd) contains uninitialised byte(s) + at 0x........: close_range (in /...libc...) + by 0x........: main (close_range.c:74) + +Syscall param close_range(flags) contains uninitialised byte(s) + at 0x........: close_range (in /...libc...) + by 0x........: main (close_range.c:74) + diff --git a/memcheck/tests/freebsd/close_range.vgtest b/memcheck/tests/freebsd/close_range.vgtest new file mode 100644 index 0000000000..f47fde8e41 --- /dev/null +++ b/memcheck/tests/freebsd/close_range.vgtest @@ -0,0 +1,2 @@ +prog: close_range +vgopts: -q diff --git a/memcheck/tests/freebsd/scalar_13_plus.c b/memcheck/tests/freebsd/scalar_13_plus.c index b22152ebb2..6e58e418fb 100644 --- a/memcheck/tests/freebsd/scalar_13_plus.c +++ b/memcheck/tests/freebsd/scalar_13_plus.c @@ -16,6 +16,10 @@ int main(void) /* SYS___realpathat 574 */ GO(SYS___realpathat, " 5s 2m"); SY(SYS___realpathat, x0+0xffff, x0, x0, x0+100, x0+2); FAIL; + + /* SYS_close_range 575 */ + GO(SYS_close_range, "3s 0m"); + SY(SYS_close_range, x0+5, x0+10, x0); SUCC; /* SYS___specialfd 577 */ GO(SYS___specialfd, "3s 1m"); diff --git a/memcheck/tests/freebsd/scalar_13_plus.stderr.exp b/memcheck/tests/freebsd/scalar_13_plus.stderr.exp index 489646810b..43b904e6f4 100644 --- a/memcheck/tests/freebsd/scalar_13_plus.stderr.exp +++ b/memcheck/tests/freebsd/scalar_13_plus.stderr.exp @@ -72,6 +72,18 @@ Syscall param __realpathat(buf) points to unaddressable byte(s) ... Address 0x........ is not stack'd, malloc'd or (recently) free'd +--------------------------------------------------------- +575: SYS_close_range 3s 0m +--------------------------------------------------------- +Syscall param close_range(lowfd) contains uninitialised byte(s) + ... + +Syscall param close_range(highfd) contains uninitialised byte(s) + ... + +Syscall param close_range(flags) contains uninitialised byte(s) + ... + --------------------------------------------------------- 577: SYS___specialfd 3s 1m --------------------------------------------------------- -- 2.43.5