Improve system call polyfills

- Polyfill open() w/ O_CLOEXEC on RHEL5
- Remove old workaround from rmdir() on the New Technology
- preadv() and pwritev() are now smarter about demodernization
- preadv() and pwritev() are now available on the New Technology
This commit is contained in:
Justine Tunney 2021-03-08 10:07:02 -08:00
parent 816b0e1851
commit 0ad609268f
21 changed files with 260 additions and 117 deletions

View file

@ -31,7 +31,7 @@ char *sys_getcwd_xnu(char *res, size_t size) {
int fd;
struct stat st[2];
char buf[XNU_MAXPATHLEN], *ret = NULL;
if ((fd = sys_openat(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY)) != -1) {
if ((fd = sys_openat(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
if (sys_fstat(fd, &st[0]) != -1) {
if (st[0].st_dev && st[0].st_ino) {
if (sys_fcntl(fd, XNU_F_GETPATH, buf) != -1) {

View file

@ -108,6 +108,7 @@ i32 __sys_dup3(i32, i32, i32) hidden;
i32 __sys_execve(const char *, char *const[], char *const[]) hidden;
i32 __sys_fstat(i32, struct stat *) hidden;
i32 __sys_fstatat(i32, const char *, struct stat *, i32) hidden;
i32 __sys_openat(i32, const char *, i32, u32) hidden;
i32 __sys_pipe2(i32[hasatleast 2], u32) hidden;
i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden;
i32 getdents(i32, char *, u32, i64 *) hidden;
@ -153,7 +154,7 @@ i32 sys_mprotect(void *, u64, i32) hidden;
i32 sys_msync(void *, u64, i32) hidden;
i32 sys_munmap(void *, u64) hidden;
i32 sys_nanosleep(const struct timespec *, struct timespec *) hidden;
i32 sys_openat(i32, const char *, i32, ...) hidden;
i32 sys_openat(i32, const char *, i32, u32) hidden;
i32 sys_pause(void) hidden;
i32 sys_pipe(i32[hasatleast 2]) hidden;
i32 sys_pipe2(i32[hasatleast 2], u32) hidden;
@ -218,6 +219,7 @@ void __sigenter_xnu(void *, i32, i32, struct __darwin_siginfo *,
int gethostname_linux(char *, size_t) hidden;
int gethostname_bsd(char *, size_t) hidden;
int gethostname_nt(char *, size_t) hidden;
size_t __iovec_size(const struct iovec *, size_t) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » windows nt » veneers

27
libc/calls/iovecsize.c Normal file
View file

@ -0,0 +1,27 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or 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.
*/
#include "libc/calls/internal.h"
size_t __iovec_size(const struct iovec *v, size_t n) {
size_t i, sum;
for (sum = i = 0; i < n; ++i) {
sum += v[i].iov_len;
}
return sum;
}

44
libc/calls/openat-sysv.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or 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.
*/
#include "libc/calls/internal.h"
#include "libc/errno.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/o.h"
int sys_openat(int dirfd, const char *file, int flags, unsigned mode) {
int fd, err;
err = errno;
fd = __sys_openat(dirfd, file, flags, mode);
/*
* RHEL5 doesn't support O_CLOEXEC
* What on earth is it doing here?
* It returns -530!
*/
if (IsLinux() && fd == -1 && errno > 255) {
errno = err;
fd = __sys_openat(dirfd, file, flags & ~O_CLOEXEC, mode);
if (fd != -1 && (flags & O_CLOEXEC)) {
sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
}
}
return fd;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
@ -25,54 +26,84 @@
#include "libc/macros.internal.h"
#include "libc/sysv/consts/iov.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
#define __NR_preadv_linux 0x0127
/**
* Reads data from multiple buffers from file descriptor at offset.
* Reads with maximum generality.
*
* @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX
* then the extra buffers are simply ignored
* @return number of bytes actually read, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
ssize_t preadv(int fd, struct iovec *iovec, int count, int64_t off) {
ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
static bool once, demodernize;
int olderr;
int i, err;
ssize_t rc;
if (!count) return 0;
if ((count = MIN(count, IOV_MAX)) < 0) return einval();
size_t got, toto;
if (fd < 0) return einval();
if (iovlen < 0) return einval();
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return weaken(__zipos_read)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off);
} else if (IsWindows()) {
if (fd < g_fds.n) {
return sys_read_nt(g_fds.p + fd, iov, iovlen, off);
} else {
return ebadf();
}
} else if (IsMetal()) {
return enosys();
}
/*
* NT, XNU, and 2007-era Linux don't support this system call.
*/
if (!once) {
once = true;
if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_preadv_linux)) {
/*
* Read size is too large to detect older kernels safely without
* introducing nontrivial mechanics. We'll try again later.
*/
once = false;
err = errno;
rc = sys_preadv(fd, iov, iovlen, off, off);
if (rc == -1 && errno == ENOSYS) {
errno = err;
once = true;
demodernize = true;
} else {
olderr = errno;
rc = sys_preadv(fd, iovec, count, off, off);
if (rc == -1 && errno == ENOSYS) {
errno = olderr;
demodernize = true;
} else if (IsLinux() && rc == __NR_preadv_linux /*RHEL5:CVE-2010-3301*/) {
demodernize = true;
} else if (IsLinux() && rc == __NR_preadv_linux) {
if (__iovec_size(iov, iovlen) < __NR_preadv_linux) {
demodernize = true; /*RHEL5:CVE-2010-3301*/
once = true;
} else {
return rc;
}
} else {
once = true;
return rc;
}
}
if (!demodernize) {
return sys_preadv(fd, iovec, count, off, off);
} else {
return pread(fd, iovec[0].iov_base, iovec[0].iov_len, off);
return sys_preadv(fd, iov, iovlen, off, off);
}
if (!iovlen) {
return sys_pread(fd, NULL, 0, off, off);
}
for (toto = i = 0; i < iovlen; ++i) {
rc = sys_pread(fd, iov[i].iov_base, iov[i].iov_len, off, off);
if (rc == -1) {
if (toto && (errno == EINTR || errno == EAGAIN)) {
return toto;
} else {
return -1;
}
}
got = rc;
toto += got;
if (got != iov[i].iov_len) {
break;
}
}
return toto;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
@ -24,6 +25,7 @@
#include "libc/macros.internal.h"
#include "libc/sysv/consts/iov.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
#define __NR_pwritev_linux 0x0128
@ -35,49 +37,77 @@
* been committed. It can also happen if we need to polyfill this system
* call using pwrite().
*
* @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX
* then the extra buffers are simply ignored
* @return number of bytes actually sent, or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
*/
ssize_t pwritev(int fd, const struct iovec *iovec, int count, int64_t off) {
ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) {
static bool once, demodernize;
int olderr;
int i, err;
ssize_t rc;
if (!count) return 0;
if ((count = MIN(count, IOV_MAX)) < 0) return einval();
size_t sent, toto;
if (fd < 0) return einval();
if (iovlen < 0) return einval();
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return weaken(__zipos_write)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off);
} else if (IsWindows()) {
if (fd < g_fds.n) {
return sys_write_nt(g_fds.p + fd, iov, iovlen, off);
} else {
return ebadf();
}
} else if (IsMetal()) {
return enosys();
}
/*
* NT, XNU, and 2007-era Linux don't support this system call.
*/
if (!once) {
once = true;
if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_pwritev_linux)) {
/*
* Write size is too large to detect older kernels safely without
* introducing nontrivial mechanics. We'll try again later.
*/
once = false;
err = errno;
rc = sys_pwritev(fd, iov, iovlen, off, off);
if (rc == -1 && errno == ENOSYS) {
errno = err;
once = true;
demodernize = true;
} else {
olderr = errno;
rc = sys_pwritev(fd, iovec, count, off, off);
if (rc == -1 && errno == ENOSYS) {
errno = olderr;
demodernize = true;
} else if (IsLinux() &&
rc == __NR_pwritev_linux /*RHEL5:CVE-2010-3301*/) {
demodernize = true;
} else if (IsLinux() && rc == __NR_pwritev_linux) {
if (__iovec_size(iov, iovlen) < __NR_pwritev_linux) {
demodernize = true; /*RHEL5:CVE-2010-3301*/
once = true;
} else {
return rc;
}
} else {
once = true;
return rc;
}
}
if (!demodernize) {
return sys_pwritev(fd, iovec, count, off, off);
} else {
return pwrite(fd, iovec[0].iov_base, iovec[0].iov_len, off);
return sys_pwritev(fd, iov, iovlen, off, off);
}
if (!iovlen) {
return sys_pwrite(fd, NULL, 0, off, off);
}
for (toto = i = 0; i < iovlen; ++i) {
rc = sys_pwrite(fd, iov[i].iov_base, iov[i].iov_len, off, off);
if (rc == -1) {
if (toto && (errno == EINTR || errno == EAGAIN)) {
return toto;
} else {
return -1;
}
}
sent = rc;
toto += sent;
if (sent != iov[i].iov_len) {
break;
}
}
return toto;
}

View file

@ -33,25 +33,11 @@ static textwindows int sys_unlink_nt(const char16_t *path) {
}
static textwindows int sys_rmdir_nt(const char16_t *path) {
int e, ms;
for (ms = 1;; ms *= 2) {
if (RemoveDirectory(path)) return 0;
/*
* Files can linger, for absolutely no reason.
* Possibly some Windows Defender bug on Win7.
* Sleep for up to one second w/ expo backoff.
* Alternative is use Microsoft internal APIs.
* Never could have imagined it'd be this bad.
*/
if ((e = GetLastError()) == kNtErrorDirNotEmpty && ms <= 512) {
Sleep(ms);
continue;
} else {
break;
}
if (RemoveDirectory(path)) {
return 0;
} else {
return __winerr();
}
errno = e;
return -1;
}
textwindows int sys_unlinkat_nt(int dirfd, const char *path, int flags) {

View file

@ -25,14 +25,6 @@
#include "libc/nt/struct/overlapped.h"
#include "libc/sysv/errfuns.h"
static textwindows size_t SumIovecLen(const struct iovec *v, size_t n) {
size_t i, sum;
for (sum = i = 0; i < n; ++i) {
sum += v[i].iov_len;
}
return sum;
}
textwindows ssize_t sys_write_nt(struct Fd *fd, const struct iovec *iov,
size_t iovlen, ssize_t opt_offset) {
size_t i, total;
@ -52,7 +44,7 @@ textwindows ssize_t sys_write_nt(struct Fd *fd, const struct iovec *iov,
return __winerr();
}
}
if (!total) assert(!SumIovecLen(iov, iovlen));
if (!total) assert(!__iovec_size(iov, iovlen));
return total;
} else {
if (WriteFile(fd->handle, NULL, 0, &wrote,