From 27c899af56c941c4cc4f233b1a07201677ac423e Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 3 Feb 2021 00:10:12 -0800 Subject: [PATCH] Make mmap() work better - Mapping file offsets now works on Windows - Mapping stack memory now works on OpenBSD --- libc/calls/open-nt.c | 19 +---------------- libc/runtime/directmapnt.c | 4 +++- libc/runtime/mmap.c | 14 +++++++++---- libc/sysv/consts.sh | 6 +++--- libc/sysv/consts/O_ACCMODE.s | 2 +- libc/sysv/consts/O_RDONLY.s | 2 +- libc/sysv/consts/O_RDWR.s | 2 +- test/libc/runtime/mmap_test.c | 36 ++++++++++++++++++++++++++++++++ third_party/chibicc/test/test.mk | 19 ++++++----------- third_party/chibicc/tokenize.c | 23 ++++++++------------ 10 files changed, 71 insertions(+), 56 deletions(-) diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 5ada3e843..7bde0dc0d 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -42,16 +42,7 @@ static textwindows int64_t open$nt$impl(int dirfd, const char *path, char16_t path16[PATH_MAX]; if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1; if ((handle = CreateFile( - path16, - (flags & 0xf000000f) | - (/* this is needed if we mmap(rwx+cow) - nt is choosy about open() access */ - (flags & O_APPEND) - ? kNtFileAppendData - : (flags & O_ACCMODE) == O_RDONLY - ? kNtGenericExecute | kNtFileGenericRead - : kNtGenericExecute | kNtFileGenericRead | - kNtFileGenericWrite), + path16, flags & 0xf000000f, /* see consts.sh */ (flags & O_EXCL) ? kNtFileShareExclusive : kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, @@ -75,14 +66,6 @@ static textwindows int64_t open$nt$impl(int dirfd, const char *path, kNtFileFlagBackupSemantics | kNtFileFlagPosixSemantics | kNtFileAttributeTemporary)))), 0)) != -1) { - if (flags & O_SPARSE) { - /* - * TODO(jart): Can all files be sparse files? That seems to be the - * way Linux behaves out of the box. - * TODO(jart): Wow why does sparse wreak havoc? - */ - DeviceIoControl(handle, kNtFsctlSetSparse, NULL, 0, NULL, 0, &br, NULL); - } return handle; } else if (GetLastError() == kNtErrorFileExists && ((flags & O_CREAT) && diff --git a/libc/runtime/directmapnt.c b/libc/runtime/directmapnt.c index 490c0ce35..fb540a153 100644 --- a/libc/runtime/directmapnt.c +++ b/libc/runtime/directmapnt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/nt/enum/filemapflags.h" #include "libc/nt/enum/pageflags.h" @@ -30,7 +31,8 @@ textwindows struct DirectMap __mmap$nt(void *addr, size_t size, unsigned prot, if ((dm.maphandle = CreateFileMappingNuma( handle, &kNtIsInheritable, (prot & PROT_WRITE) ? kNtPageExecuteReadwrite : kNtPageExecuteRead, - size >> 32, size, NULL, kNtNumaNoPreferredNode))) { + handle != -1 ? 0 : size >> 32, handle != -1 ? 0 : size, NULL, + kNtNumaNoPreferredNode))) { if (!(dm.addr = MapViewOfFileExNuma( dm.maphandle, (prot & PROT_WRITE) diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index e375eb25d..a7bfc537c 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -52,13 +52,14 @@ * @param flags can have MAP_ANONYMOUS, MAP_SHARED, MAP_PRIVATE, etc. * @param fd is an open()'d file descriptor whose contents shall be * mapped, and is ignored if MAP_ANONYMOUS is specified - * @param offset specifies absolute byte index of fd's file for mapping, - * and should be zero if MAP_ANONYMOUS is specified + * @param off specifies absolute byte index of fd's file for mapping, + * should be zero if MAP_ANONYMOUS is specified, and sadly needs + * to be 64kb aligned too * @return virtual base address of new mapping, or MAP_FAILED w/ errno */ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { - int i, x, n, a, b; struct DirectMap dm; + int i, x, n, a, b, f; if (!size) return VIP(einval()); if (!ALIGNED(off)) return VIP(einval()); if (!ALIGNED(addr)) return VIP(einval()); @@ -84,7 +85,12 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { } addr = (void *)(intptr_t)((int64_t)x << 16); } - dm = __mmap(addr, size, prot, flags | MAP_FIXED, fd, off); + f = flags | MAP_FIXED; + if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ + dm = __mmap(addr, size, prot, f & ~MAP_GROWSDOWN, fd, off); + if (dm.addr == MAP_FAILED) return MAP_FAILED; + } + dm = __mmap(addr, size, prot, f, fd, off); if (dm.addr == MAP_FAILED || dm.addr != addr) { return MAP_FAILED; } diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 3c3d458c4..451a081b5 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -205,10 +205,10 @@ syscon sig SIGRTMIN 0 0 65 0 0 # │││ │ ┌┴───dwDesiredAccess # N │││ │ │ # group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD T │││┌─┴┐│ Commentary -syscon open O_RDONLY 0 0 0 0 0x80000000 # unix consensus & kNtGenericRead +syscon open O_RDONLY 0 0 0 0 0xA0000000 # unix consensus & kNtGenericRead|kNtGenericExecute syscon open O_WRONLY 1 1 1 1 0x40000000 # unix consensus & kNtGenericWrite -syscon open O_RDWR 2 2 2 2 0xc0000000 # unix consensus & kNtGenericRead|kNtGenericWrite -syscon open O_ACCMODE 3 3 3 3 0xc0000000 # O_RDONLY|O_WRONLY|O_RDWR +syscon open O_RDWR 2 2 2 2 0xE0000000 # unix consensus & kNtGenericRead|kNtGenericWrite|kNtGenericExecute +syscon open O_ACCMODE 3 3 3 3 0xE0000000 # O_RDONLY|O_WRONLY|O_RDWR syscon open O_APPEND 0x0400 8 8 8 0x00000004 # bsd consensus & kNtFileAppendData; won't pose issues w/ mknod(S_IFIFO) syscon open O_CREAT 0x40 0x0200 0x0200 0x0200 0x00000040 # bsd consensus & NT faked as Linux syscon open O_EXCL 0x80 0x0800 0x0800 0x0800 0x00000080 # bsd consensus & NT faked as Linux diff --git a/libc/sysv/consts/O_ACCMODE.s b/libc/sysv/consts/O_ACCMODE.s index ae0a401e6..d46a77153 100644 --- a/libc/sysv/consts/O_ACCMODE.s +++ b/libc/sysv/consts/O_ACCMODE.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_ACCMODE 3 3 3 3 0xc0000000 +.syscon open O_ACCMODE 3 3 3 3 0xE0000000 diff --git a/libc/sysv/consts/O_RDONLY.s b/libc/sysv/consts/O_RDONLY.s index b6ca5801a..5bb4e01e7 100644 --- a/libc/sysv/consts/O_RDONLY.s +++ b/libc/sysv/consts/O_RDONLY.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_RDONLY 0 0 0 0 0x80000000 +.syscon open O_RDONLY 0 0 0 0 0xA0000000 diff --git a/libc/sysv/consts/O_RDWR.s b/libc/sysv/consts/O_RDWR.s index b98945420..1a1dd54ba 100644 --- a/libc/sysv/consts/O_RDWR.s +++ b/libc/sysv/consts/O_RDWR.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_RDWR 2 2 2 2 0xc0000000 +.syscon open O_RDWR 2 2 2 2 0xE0000000 diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 9d4975a02..5977f46ea 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -88,6 +88,42 @@ TEST(mmap, testMapFixed_destroysEverythingInItsPath) { EXPECT_NE(-1, munmap((void *)kFixedmapStart, FRAMESIZE * 3)); } +TEST(mmap, customStackMemory_isAuthorized) { + char *stack; + uintptr_t w, r; + ASSERT_NE(MAP_FAILED, + (stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0))); + asm("mov\t%%rsp,%0\n\t" + "mov\t%2,%%rsp\n\t" + "push\t%3\n\t" + "pop\t%1\n\t" + "mov\t%0,%%rsp" + : "=&r"(w), "=&r"(r) + : "rm"(stack + STACKSIZE - 8), "i"(123)); + ASSERT_EQ(123, r); +} + +TEST(mmap, fileOffset) { + int fd; + char *map; + char testdir[PATH_MAX]; + sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); + ASSERT_NE(-1, makedirs(testdir, 0755)); + ASSERT_NE(-1, chdir(testdir)); + ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644))); + EXPECT_NE(-1, ftruncate(fd, FRAMESIZE * 2)); + EXPECT_NE(-1, pwrite(fd, "hello", 5, FRAMESIZE * 0)); + EXPECT_NE(-1, pwrite(fd, "there", 5, FRAMESIZE * 1)); + ASSERT_NE(MAP_FAILED, (map = mmap(NULL, FRAMESIZE, PROT_READ, MAP_PRIVATE, fd, + FRAMESIZE))); + EXPECT_EQ(0, memcmp(map, "there", 5), "%#.*s", 5, map); + EXPECT_NE(-1, munmap(map, FRAMESIZE)); + EXPECT_NE(-1, close(fd)); + ASSERT_NE(-1, chdir("../../..")); + ASSERT_NE(-1, rmrf(testdir)); +} + TEST(isheap, nullPtr) { ASSERT_FALSE(isheap(NULL)); } diff --git a/third_party/chibicc/test/test.mk b/third_party/chibicc/test/test.mk index 35c916ddb..142846f09 100644 --- a/third_party/chibicc/test/test.mk +++ b/third_party/chibicc/test/test.mk @@ -21,13 +21,8 @@ THIRD_PARTY_CHIBICC_TEST_HDRS = $(filter %.h,$(THIRD_PARTY_CHIBICC_TEST_FILES)) THIRD_PARTY_CHIBICC_TEST_TESTS = $(THIRD_PARTY_CHIBICC_TEST_COMS:%=%.ok) THIRD_PARTY_CHIBICC_TEST_COMS = \ - $(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%.c=o/$(MODE)/%.com) - -# TODO(jart): make chibicc compiled chibicc work with asan runtime -ifneq ($(MODE),dbg) -THIRD_PARTY_CHIBICC_TEST_COMS += \ - $(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%.c=o/$(MODE)/%2.com) -endif + $(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%_test.c=o/$(MODE)/%_test.com) \ + $(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%_test.c=o/$(MODE)/%_test2.com) THIRD_PARTY_CHIBICC_TEST_OBJS = \ $(THIRD_PARTY_CHIBICC_TEST_SRCS:%.c=o/$(MODE)/%.chibicc.o) @@ -62,21 +57,19 @@ THIRD_PARTY_CHIBICC_TEST_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x)))) $(THIRD_PARTY_CHIBICC_TEST_A): \ - third_party/chibicc/test/ \ $(THIRD_PARTY_CHIBICC_TEST_A).pkg \ - $(THIRD_PARTY_CHIBICC_TEST_OBJS) + o/$(MODE)/third_party/chibicc/test/common.chibicc.o $(THIRD_PARTY_CHIBICC_TEST2_A): \ - third_party/chibicc/test/ \ $(THIRD_PARTY_CHIBICC_TEST2_A).pkg \ - $(THIRD_PARTY_CHIBICC_TEST2_OBJS) + o/$(MODE)/third_party/chibicc/test/common.chibicc2.o $(THIRD_PARTY_CHIBICC_TEST_A).pkg: \ - $(THIRD_PARTY_CHIBICC_TEST_OBJS) \ + o/$(MODE)/third_party/chibicc/test/common.chibicc.o \ $(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x)_A).pkg) $(THIRD_PARTY_CHIBICC_TEST2_A).pkg: \ - $(THIRD_PARTY_CHIBICC_TEST2_OBJS) \ + o/$(MODE)/third_party/chibicc/test/common.chibicc2.o \ $(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x)_A).pkg) o/$(MODE)/third_party/chibicc/test/%.com.dbg: \ diff --git a/third_party/chibicc/tokenize.c b/third_party/chibicc/tokenize.c index 568fafbc8..5f12b6367 100644 --- a/third_party/chibicc/tokenize.c +++ b/third_party/chibicc/tokenize.c @@ -136,21 +136,16 @@ static int read_ident(char *start) { } } -static int from_hex(char c) { - if ('0' <= c && c <= '9') return c - '0'; - if ('a' <= c && c <= 'f') return c - 'a' + 10; - return c - 'A' + 10; -} - // Read a punctuator token from p and returns its length. int read_punct(char *p) { - static char kw[][4] = {"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", - "+=", "-=", "*=", "/=", "++", "--", "%=", "&=", - "|=", "^=", "&&", "||", "<<", ">>", "##"}; - for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) { + static const char kPunct[][4] = { + "<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=", "-=", "*=", "/=", + "++", "--", "%=", "&=", "|=", "^=", "&&", "||", "<<", ">>", "##", + }; + for (int i = 0; i < sizeof(kPunct) / sizeof(*kPunct); i++) { for (int j = 0;;) { - if (p[j] != kw[i][j]) break; - if (!kw[i][++j]) return j; + if (p[j] != kPunct[i][j]) break; + if (!kPunct[i][++j]) return j; } } return ispunct(*p) ? 1 : 0; @@ -200,7 +195,7 @@ int read_escaped_char(char **new_pos, char *p) { if (!isxdigit(*p)) error_at(p, "invalid hex escape sequence"); unsigned c = 0; for (; isxdigit(*p); p++) { - c = (c << 4) + from_hex(*p); /* TODO(jart): overflow here unicode_test */ + c = (c << 4) + hextoint(*p); /* TODO(jart): overflow here unicode_test */ } *new_pos = p; return c; @@ -628,7 +623,7 @@ static uint32_t read_universal_char(char *p, int len) { uint32_t c = 0; for (int i = 0; i < len; i++) { if (!isxdigit(p[i])) return 0; - c = (c << 4) | from_hex(p[i]); + c = (c << 4) | hextoint(p[i]); } return c; }