Make C memory safe like Rust

This change enables Address Sanitizer systemically w/ `make MODE=dbg`.
Our version of Rust's `unsafe` keyword is named `noasan` which is used
for two functions that do aligned memory chunking, like `strcpy.c` and
we need to fix the tiny DEFLATE code, but that's it everything else is
fabulous you can have all the fischer price security blankets you need

Best of all is we're now able to use the ASAN data in Blinkenlights to
colorize the memory dumps. See the screenshot below of a test program:

  https://justine.lol/blinkenlights/asan.png

Which is operating on float arrays stored on the stack, with red areas
indicating poisoned memory, and the green areas indicate valid memory.
This commit is contained in:
Justine Tunney 2021-02-01 03:33:13 -08:00
parent fdc3fa9148
commit 1ff9ab95ac
153 changed files with 2545 additions and 2077 deletions

View file

@ -19,8 +19,13 @@ APE_LIB_A_OBJS = \
$(APE_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \ $(APE_LIB_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(APE_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o) $(APE_LIB_A_SRCS_C:%.c=o/$(MODE)/%.o)
APE_LIB_A_DIRECTDEPS = \
LIBC_NEXGEN32E \
LIBC_INTRIN \
LIBC_STR \
LIBC_STUBS
APE_LIB_A_CHECKS = $(APE_LIB_A_HDRS:%=o/$(MODE)/%.ok) APE_LIB_A_CHECKS = $(APE_LIB_A_HDRS:%=o/$(MODE)/%.ok)
APE_LIB_A_DIRECTDEPS = LIBC_STR LIBC_NEXGEN32E LIBC_STUBS
APE_LIB_A_DEPS = $(call uniq,$(foreach x,$(APE_LIB_A_DIRECTDEPS),$($(x)))) APE_LIB_A_DEPS = $(call uniq,$(foreach x,$(APE_LIB_A_DIRECTDEPS),$($(x))))
$(APE_LIB_A): ape/lib/ $(APE_LIB_A).pkg $(APE_LIB_A_OBJS) $(APE_LIB_A): ape/lib/ $(APE_LIB_A).pkg $(APE_LIB_A_OBJS)

View file

@ -86,6 +86,9 @@ for x; do
-mno-vzeroupper) -mno-vzeroupper)
set -- "$@" "$x" -Wa,-msse2avx -D__MNO_VZEROUPPER__ set -- "$@" "$x" -Wa,-msse2avx -D__MNO_VZEROUPPER__
;; ;;
-fsanitize=address)
set -- "$@" "$x" -D__FSANITIZE_ADDRESS__
;;
-fsanitize=undefined) -fsanitize=undefined)
set -- "$@" "$x" -D__FSANITIZE_UNDEFINED__ set -- "$@" "$x" -D__FSANITIZE_UNDEFINED__
COUNTERMAND="$COUNTERMAND -fno-data-sections" # sqlite.o COUNTERMAND="$COUNTERMAND -fno-data-sections" # sqlite.o

View file

@ -94,15 +94,13 @@ CONFIG_CPPFLAGS += \
CONFIG_CCFLAGS += \ CONFIG_CCFLAGS += \
$(BACKTRACES) \ $(BACKTRACES) \
$(FTRACE) \ $(FTRACE) \
-O1 \
-fno-inline -fno-inline
CONFIG_COPTS += \ CONFIG_COPTS += \
$(SECURITY_BLANKETS) \ $(SECURITY_BLANKETS) \
$(SANITIZER) $(SANITIZER)
CONFIG_COPTS += \
-ftrapv
TARGET_ARCH ?= \ TARGET_ARCH ?= \
-msse3 -msse3

View file

@ -104,14 +104,15 @@ FTRACE = \
SANITIZER = \ SANITIZER = \
-fsanitize=leak \ -fsanitize=leak \
-fsanitize=undefined \ -fsanitize=address \
-fsanitize=implicit-signed-integer-truncation \ -fsanitize=implicit-signed-integer-truncation \
-fsanitize=implicit-integer-sign-change -fsanitize=implicit-integer-sign-change
NO_MAGIC = \ NO_MAGIC = \
-mno-fentry \ -mno-fentry \
-fno-stack-protector \ -fno-stack-protector \
-fno-sanitize=all -fno-sanitize=all \
-fwrapv
OLD_CODE = \ OLD_CODE = \
-fno-strict-aliasing \ -fno-strict-aliasing \

View file

@ -2,16 +2,16 @@
#-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐ #-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐
#───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘ #───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘
if [ -x "o/$MODE/tool/build/mkdeps.com" ]; then #if [ -x "o/$MODE/tool/build/mkdeps.com" ]; then
set -- "o/$MODE/tool/build/mkdeps.com" "$@" # set -- "o/$MODE/tool/build/mkdeps.com" "$@"
else #else
if [ ! -x o/build/bootstrap/mkdeps.com ]; then if [ ! -x o/build/bootstrap/mkdeps.com ]; then
mkdir -p o/build/bootstrap && mkdir -p o/build/bootstrap &&
cp -a build/bootstrap/mkdeps.com \ cp -a build/bootstrap/mkdeps.com \
o/build/bootstrap/mkdeps.com || exit o/build/bootstrap/mkdeps.com || exit
fi fi
set -- o/build/bootstrap/mkdeps.com "$@" set -- o/build/bootstrap/mkdeps.com "$@"
fi #fi
if [ "$SILENT" = "0" ]; then if [ "$SILENT" = "0" ]; then
printf "%s\n" "$*" >&2 printf "%s\n" "$*" >&2

View file

@ -26,6 +26,7 @@ DSP_CORE_A_CHECKS = \
DSP_CORE_A_DIRECTDEPS = \ DSP_CORE_A_DIRECTDEPS = \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_MEM \ LIBC_MEM \
LIBC_INTRIN \
LIBC_TINYMATH \ LIBC_TINYMATH \
LIBC_STUBS LIBC_STUBS
@ -57,12 +58,6 @@ o/$(MODE)/dsp/core/det3.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
-ffast-math -ffast-math
# ifeq (,$(MODE))
# $(DSP_CORE_OBJS): \
# OVERRIDE_CFLAGS += \
# -fsanitize=address
# endif
DSP_CORE_LIBS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x))) DSP_CORE_LIBS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)))
DSP_CORE_SRCS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_SRCS)) DSP_CORE_SRCS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_SRCS))
DSP_CORE_HDRS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_HDRS)) DSP_CORE_HDRS = $(foreach x,$(DSP_CORE_ARTIFACTS),$($(x)_HDRS))

View file

@ -30,7 +30,7 @@
static int ttysetcursor(int fd, bool visible) { static int ttysetcursor(int fd, bool visible) {
struct NtConsoleCursorInfo ntcursor; struct NtConsoleCursorInfo ntcursor;
char code[8] = "\e[?25l"; char code[8] = "\e[?25l";
if (isterminalinarticulate()) return 0; if (IsTerminalInarticulate()) return 0;
if (visible) code[5] = 'h'; if (visible) code[5] = 'h';
if (SupportsWindows()) { if (SupportsWindows()) {
GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor); GetConsoleCursorInfo(GetStdHandle(kNtStdOutputHandle), &ntcursor);

View file

@ -59,12 +59,6 @@ o/$(MODE)/dsp/tty/ttyraster.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
$(MATHEMATICAL) $(MATHEMATICAL)
# ifeq (,$(MODE))
# $(DSP_TTY_OBJS): \
# OVERRIDE_CFLAGS += \
# -fsanitize=address
# endif
DSP_TTY_LIBS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x))) DSP_TTY_LIBS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)))
DSP_TTY_SRCS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_SRCS)) DSP_TTY_SRCS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_SRCS))
DSP_TTY_HDRS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_HDRS)) DSP_TTY_HDRS = $(foreach x,$(DSP_TTY_ARTIFACTS),$($(x)_HDRS))

View file

@ -32,7 +32,7 @@ windex: .quad 0
ezlea windex$sse4,dx ezlea windex$sse4,dx
testb X86_HAVE(AVX2)+kCpuids(%rip) testb X86_HAVE(AVX2)+kCpuids(%rip)
cmovz %rdx,%rax cmovz %rdx,%rax
#endif /* AVX */ #endif /* AVX2 */
#if !X86_NEED(SSE4_2) #if !X86_NEED(SSE4_2)
ezlea windex$k8,dx ezlea windex$k8,dx
testb X86_HAVE(SSE4_2)+kCpuids(%rip) testb X86_HAVE(SSE4_2)+kCpuids(%rip)

View file

@ -86,9 +86,9 @@ int main(int argc, char *argv[]) {
"movd\t%rax,%xmm14\n\t" "movd\t%rax,%xmm14\n\t"
"mov\t$0xffffffffffffffff,%rax\n\t" "mov\t$0xffffffffffffffff,%rax\n\t"
"movd\t%rax,%xmm15\n\t" "movd\t%rax,%xmm15\n\t"
"fldpi\n\t"); "fldpi\n\t"
"xor\t%eax,%eax\n\t"
res = *(int *)(intptr_t)boo / boo; "div\t%eax\n\t");
return res; return res;
} }

View file

@ -48,7 +48,6 @@ EXAMPLES_DIRECTDEPS = \
LIBC_FMT \ LIBC_FMT \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_MEM \ LIBC_MEM \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_NT_KERNEL32 \ LIBC_NT_KERNEL32 \

View file

@ -16,10 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/sysv/errfuns.h"
/** /**
* Sets current directory. * Sets current directory.

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
/** /**
@ -35,5 +36,5 @@
* @asyncsignalsafe * @asyncsignalsafe
*/ */
nodiscard int creat(const char *file, uint32_t mode) { nodiscard int creat(const char *file, uint32_t mode) {
return open(file, O_CREAT | O_WRONLY | O_TRUNC, mode); return openat(AT_FDCWD, file, O_CREAT | O_WRONLY | O_TRUNC, mode);
} }

View file

@ -33,10 +33,8 @@
* unless it's equal to oldfd, in which case dup2() is a no-op * unless it's equal to oldfd, in which case dup2() is a no-op
* @flags can have O_CLOEXEC * @flags can have O_CLOEXEC
* @see dup(), dup2() * @see dup(), dup2()
* @syscall
*/ */
int dup3(int oldfd, int newfd, int flags) { int dup3(int oldfd, int newfd, int flags) {
if (oldfd == newfd) return einval();
if (!IsWindows()) { if (!IsWindows()) {
return dup3$sysv(oldfd, newfd, flags); return dup3$sysv(oldfd, newfd, flags);
} else { } else {

39
libc/calls/getppid-nt.c Normal file
View file

@ -0,0 +1,39 @@
/*-*- 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/dce.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/ntdll.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
textwindows int getppid$nt(void) {
struct NtProcessBasicInformation ProcessInformation;
uint32_t gotsize = 0;
if (!NtError(
NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
sizeof(ProcessInformation), &gotsize)) &&
gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
/* TODO(jart): Fix type mismatch and do we need to close this? */
return ProcessInformation.InheritedFromUniqueProcessId;
}
return GetCurrentProcessId();
}

View file

@ -16,28 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/ntdll.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
static textwindows noinline int32_t getppid$nt(void) {
struct NtProcessBasicInformation ProcessInformation;
uint32_t gotsize = 0;
if (!NtError(
NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
sizeof(ProcessInformation), &gotsize)) &&
gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
/* TODO(jart): Fix type mismatch and do we need to close this? */
return ProcessInformation.InheritedFromUniqueProcessId;
}
return GetCurrentProcessId();
}
/** /**
* Returns parent process id. * Returns parent process id.

View file

@ -240,6 +240,7 @@ int fork$nt(void) hidden;
int fstat$nt(i64, struct stat *) hidden; int fstat$nt(i64, struct stat *) hidden;
int fstatat$nt(int, const char *, struct stat *, uint32_t) hidden; int fstatat$nt(int, const char *, struct stat *, uint32_t) hidden;
int ftruncate$nt(int, u64) hidden; int ftruncate$nt(int, u64) hidden;
int getppid$nt(void) hidden;
int getpriority$nt(int) hidden; int getpriority$nt(int) hidden;
int getrusage$nt(int, struct rusage *) hidden; int getrusage$nt(int, struct rusage *) hidden;
int gettimeofday$nt(struct timeval *, struct timezone *) hidden; int gettimeofday$nt(struct timeval *, struct timezone *) hidden;

View file

@ -53,7 +53,7 @@ _start: test %rdi,%rdi
repnz scasq repnz scasq
mov %rdi,%rcx # auxv mov %rdi,%rcx # auxv
mov %ebx,%edi mov %ebx,%edi
call _executive call cosmo
9: ud2 9: ud2
.endfn _start,weak,hidden .endfn _start,weak,hidden

View file

@ -1,308 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
Copyright 2020 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/notice.inc"
#include "libc/macros.h"
.source __FILE__
/ Embeds ASCII names for errno constants into binary.
.section .rodata,"aS",@progbits
kErrnoNames:
/ <SORTED-ORDER-C>
.asciz "2BIG"
.asciz "ACCES"
.asciz "ADDRINUSE"
.asciz "ADDRNOTAVAIL"
.asciz "ADV"
.asciz "AFNOSUPPORT"
.asciz "AGAIN"
.asciz "ALREADY"
.asciz "BADE"
.asciz "BADF"
.asciz "BADFD"
.asciz "BADMSG"
.asciz "BADR"
.asciz "BADRQC"
.asciz "BADSLT"
.asciz "BFONT"
.asciz "BUSY"
.asciz "CANCELED"
.asciz "CHILD"
.asciz "CHRNG"
.asciz "COMM"
.asciz "CONNABORTED"
.asciz "CONNREFUSED"
.asciz "CONNRESET"
.asciz "DEADLK"
.asciz "DESTADDRREQ"
.asciz "DOM"
.asciz "DOTDOT"
.asciz "DQUOT"
.asciz "EXIST"
.asciz "FAULT"
.asciz "FBIG"
.asciz "HOSTDOWN"
.asciz "HOSTUNREACH"
.asciz "HWPOISON"
.asciz "IDRM"
.asciz "ILSEQ"
.asciz "INPROGRESS"
.asciz "INTR"
.asciz "INVAL"
.asciz "IO"
.asciz "ISCONN"
.asciz "ISDIR"
.asciz "ISNAM"
.asciz "KEYEXPIRED"
.asciz "KEYREJECTED"
.asciz "KEYREVOKED"
.asciz "L2HLT"
.asciz "L2NSYNC"
.asciz "L3HLT"
.asciz "L3RST"
.asciz "LIBACC"
.asciz "LIBBAD"
.asciz "LIBEXEC"
.asciz "LIBMAX"
.asciz "LIBSCN"
.asciz "LNRNG"
.asciz "LOOP"
.asciz "MEDIUMTYPE"
.asciz "MFILE"
.asciz "MLINK"
.asciz "MSGSIZE"
.asciz "MULTIHOP"
.asciz "NAMETOOLONG"
.asciz "NAVAIL"
.asciz "NETDOWN"
.asciz "NETRESET"
.asciz "NETUNREACH"
.asciz "NFILE"
.asciz "NOANO"
.asciz "NOBUFS"
.asciz "NOCSI"
.asciz "NODATA"
.asciz "NODEV"
.asciz "NOENT"
.asciz "NOEXEC"
.asciz "NOKEY"
.asciz "NOLCK"
.asciz "NOLINK"
.asciz "NOMEDIUM"
.asciz "NOMEM"
.asciz "NOMSG"
.asciz "NONET"
.asciz "NOPKG"
.asciz "NOPROTOOPT"
.asciz "NOSPC"
.asciz "NOSR"
.asciz "NOSTR"
.asciz "NOSYS"
.asciz "NOTBLK"
.asciz "NOTCONN"
.asciz "NOTDIR"
.asciz "NOTEMPTY"
.asciz "NOTNAM"
.asciz "NOTRECOVERABLE"
.asciz "NOTSOCK"
.asciz "NOTSUP"
.asciz "NOTTY"
.asciz "NOTUNIQ"
.asciz "NXIO"
.asciz "OPNOTSUPP"
.asciz "OVERFLOW"
.asciz "OWNERDEAD"
.asciz "PERM"
.asciz "PFNOSUPPORT"
.asciz "PIPE"
.asciz "PROTO"
.asciz "PROTONOSUPPORT"
.asciz "PROTOTYPE"
.asciz "RANGE"
.asciz "REMCHG"
.asciz "REMOTE"
.asciz "REMOTEIO"
.asciz "RESTART"
.asciz "RFKILL"
.asciz "ROFS"
.asciz "SHUTDOWN"
.asciz "SOCKTNOSUPPORT"
.asciz "SPIPE"
.asciz "SRCH"
.asciz "SRMNT"
.asciz "STALE"
.asciz "STRPIPE"
.asciz "TIME"
.asciz "TIMEDOUT"
.asciz "TOOMANYREFS"
.asciz "TXTBSY"
.asciz "UCLEAN"
.asciz "UNATCH"
.asciz "USERS"
.asciz "XDEV"
.asciz "XFULL"
/ </SORTED-ORDER-C>
.byte 0
.endobj kErrnoNames,globl,hidden
.previous
/ Pulls errno constants into linkage.
/
/ @assume linker relocates these in sorted order
/ @assume linker invoked as LC_ALL=C ld ...
/ @see libc/sysv/systemfive.S
/ @see libc/sysv/consts/syscon.h
.yoink E2BIG
.yoink EACCES
.yoink EADDRINUSE
.yoink EADDRNOTAVAIL
.yoink EADV
.yoink EAFNOSUPPORT
.yoink EAGAIN
.yoink EALREADY
.yoink EBADE
.yoink EBADF
.yoink EBADFD
.yoink EBADMSG
.yoink EBADR
.yoink EBADRQC
.yoink EBADSLT
.yoink EBFONT
.yoink EBUSY
.yoink ECANCELED
.yoink ECHILD
.yoink ECHRNG
.yoink ECOMM
.yoink ECONNABORTED
.yoink ECONNREFUSED
.yoink ECONNRESET
.yoink EDEADLK
.yoink EDESTADDRREQ
.yoink EDOM
.yoink EDOTDOT
.yoink EDQUOT
.yoink EEXIST
.yoink EFAULT
.yoink EFBIG
.yoink EHOSTDOWN
.yoink EHOSTUNREACH
.yoink EHWPOISON
.yoink EIDRM
.yoink EILSEQ
.yoink EINPROGRESS
.yoink EINTR
.yoink EINVAL
.yoink EIO
.yoink EISCONN
.yoink EISDIR
.yoink EISNAM
.yoink EKEYEXPIRED
.yoink EKEYREJECTED
.yoink EKEYREVOKED
.yoink EL2HLT
.yoink EL2NSYNC
.yoink EL3HLT
.yoink EL3RST
.yoink ELIBACC
.yoink ELIBBAD
.yoink ELIBEXEC
.yoink ELIBMAX
.yoink ELIBSCN
.yoink ELNRNG
.yoink ELOOP
.yoink EMEDIUMTYPE
.yoink EMFILE
.yoink EMLINK
.yoink EMSGSIZE
.yoink EMULTIHOP
.yoink ENAMETOOLONG
.yoink ENAVAIL
.yoink ENETDOWN
.yoink ENETRESET
.yoink ENETUNREACH
.yoink ENFILE
.yoink ENOANO
.yoink ENOBUFS
.yoink ENOCSI
.yoink ENODATA
.yoink ENODEV
.yoink ENOENT
.yoink ENOEXEC
.yoink ENOKEY
.yoink ENOLCK
.yoink ENOLINK
.yoink ENOMEDIUM
.yoink ENOMEM
.yoink ENOMSG
.yoink ENONET
.yoink ENOPKG
.yoink ENOPROTOOPT
.yoink ENOSPC
.yoink ENOSR
.yoink ENOSTR
.yoink ENOSYS
.yoink ENOTBLK
.yoink ENOTCONN
.yoink ENOTDIR
.yoink ENOTEMPTY
.yoink ENOTNAM
.yoink ENOTRECOVERABLE
.yoink ENOTSOCK
.yoink ENOTSUP
.yoink ENOTTY
.yoink ENOTUNIQ
.yoink ENXIO
.yoink EOPNOTSUPP
.yoink EOVERFLOW
.yoink EOWNERDEAD
.yoink EPERM
.yoink EPFNOSUPPORT
.yoink EPIPE
.yoink EPROTO
.yoink EPROTONOSUPPORT
.yoink EPROTOTYPE
.yoink ERANGE
.yoink EREMCHG
.yoink EREMOTE
.yoink EREMOTEIO
.yoink ERESTART
.yoink ERFKILL
.yoink EROFS
.yoink ESHUTDOWN
.yoink ESOCKTNOSUPPORT
.yoink ESPIPE
.yoink ESRCH
.yoink ESRMNT
.yoink ESTALE
.yoink ESTRPIPE
.yoink ETIME
.yoink ETIMEDOUT
.yoink ETOOMANYREFS
.yoink ETXTBSY
.yoink EUCLEAN
.yoink EUNATCH
.yoink EUSERS
.yoink EXDEV
.yoink EXFULL
.type kErrnoStart,@object
.type kErrnoEnd,@object
.globl kErrnoStart, kErrnoEnd, EXFULL, E2BIG
.hidden kErrnoStart, kErrnoEnd
kErrnoStart = E2BIG
kErrnoEnd = EXFULL + 8

View file

@ -26,8 +26,275 @@
#include "libc/nt/runtime.h" #include "libc/nt/runtime.h"
#include "libc/str/str.h" #include "libc/str/str.h"
const char *geterrname(int code) { STATIC_YOINK("E2BIG");
extern const char kErrnoNames[]; STATIC_YOINK("EACCES");
STATIC_YOINK("EADDRINUSE");
STATIC_YOINK("EADDRNOTAVAIL");
STATIC_YOINK("EADV");
STATIC_YOINK("EAFNOSUPPORT");
STATIC_YOINK("EAGAIN");
STATIC_YOINK("EALREADY");
STATIC_YOINK("EBADE");
STATIC_YOINK("EBADF");
STATIC_YOINK("EBADFD");
STATIC_YOINK("EBADMSG");
STATIC_YOINK("EBADR");
STATIC_YOINK("EBADRQC");
STATIC_YOINK("EBADSLT");
STATIC_YOINK("EBFONT");
STATIC_YOINK("EBUSY");
STATIC_YOINK("ECANCELED");
STATIC_YOINK("ECHILD");
STATIC_YOINK("ECHRNG");
STATIC_YOINK("ECOMM");
STATIC_YOINK("ECONNABORTED");
STATIC_YOINK("ECONNREFUSED");
STATIC_YOINK("ECONNRESET");
STATIC_YOINK("EDEADLK");
STATIC_YOINK("EDESTADDRREQ");
STATIC_YOINK("EDOM");
STATIC_YOINK("EDOTDOT");
STATIC_YOINK("EDQUOT");
STATIC_YOINK("EEXIST");
STATIC_YOINK("EFAULT");
STATIC_YOINK("EFBIG");
STATIC_YOINK("EHOSTDOWN");
STATIC_YOINK("EHOSTUNREACH");
STATIC_YOINK("EHWPOISON");
STATIC_YOINK("EIDRM");
STATIC_YOINK("EILSEQ");
STATIC_YOINK("EINPROGRESS");
STATIC_YOINK("EINTR");
STATIC_YOINK("EINVAL");
STATIC_YOINK("EIO");
STATIC_YOINK("EISCONN");
STATIC_YOINK("EISDIR");
STATIC_YOINK("EISNAM");
STATIC_YOINK("EKEYEXPIRED");
STATIC_YOINK("EKEYREJECTED");
STATIC_YOINK("EKEYREVOKED");
STATIC_YOINK("EL2HLT");
STATIC_YOINK("EL2NSYNC");
STATIC_YOINK("EL3HLT");
STATIC_YOINK("EL3RST");
STATIC_YOINK("ELIBACC");
STATIC_YOINK("ELIBBAD");
STATIC_YOINK("ELIBEXEC");
STATIC_YOINK("ELIBMAX");
STATIC_YOINK("ELIBSCN");
STATIC_YOINK("ELNRNG");
STATIC_YOINK("ELOOP");
STATIC_YOINK("EMEDIUMTYPE");
STATIC_YOINK("EMFILE");
STATIC_YOINK("EMLINK");
STATIC_YOINK("EMSGSIZE");
STATIC_YOINK("EMULTIHOP");
STATIC_YOINK("ENAMETOOLONG");
STATIC_YOINK("ENAVAIL");
STATIC_YOINK("ENETDOWN");
STATIC_YOINK("ENETRESET");
STATIC_YOINK("ENETUNREACH");
STATIC_YOINK("ENFILE");
STATIC_YOINK("ENOANO");
STATIC_YOINK("ENOBUFS");
STATIC_YOINK("ENOCSI");
STATIC_YOINK("ENODATA");
STATIC_YOINK("ENODEV");
STATIC_YOINK("ENOENT");
STATIC_YOINK("ENOEXEC");
STATIC_YOINK("ENOKEY");
STATIC_YOINK("ENOLCK");
STATIC_YOINK("ENOLINK");
STATIC_YOINK("ENOMEDIUM");
STATIC_YOINK("ENOMEM");
STATIC_YOINK("ENOMSG");
STATIC_YOINK("ENONET");
STATIC_YOINK("ENOPKG");
STATIC_YOINK("ENOPROTOOPT");
STATIC_YOINK("ENOSPC");
STATIC_YOINK("ENOSR");
STATIC_YOINK("ENOSTR");
STATIC_YOINK("ENOSYS");
STATIC_YOINK("ENOTBLK");
STATIC_YOINK("ENOTCONN");
STATIC_YOINK("ENOTDIR");
STATIC_YOINK("ENOTEMPTY");
STATIC_YOINK("ENOTNAM");
STATIC_YOINK("ENOTRECOVERABLE");
STATIC_YOINK("ENOTSOCK");
STATIC_YOINK("ENOTSUP");
STATIC_YOINK("ENOTTY");
STATIC_YOINK("ENOTUNIQ");
STATIC_YOINK("ENXIO");
STATIC_YOINK("EOPNOTSUPP");
STATIC_YOINK("EOVERFLOW");
STATIC_YOINK("EOWNERDEAD");
STATIC_YOINK("EPERM");
STATIC_YOINK("EPFNOSUPPORT");
STATIC_YOINK("EPIPE");
STATIC_YOINK("EPROTO");
STATIC_YOINK("EPROTONOSUPPORT");
STATIC_YOINK("EPROTOTYPE");
STATIC_YOINK("ERANGE");
STATIC_YOINK("EREMCHG");
STATIC_YOINK("EREMOTE");
STATIC_YOINK("EREMOTEIO");
STATIC_YOINK("ERESTART");
STATIC_YOINK("ERFKILL");
STATIC_YOINK("EROFS");
STATIC_YOINK("ESHUTDOWN");
STATIC_YOINK("ESOCKTNOSUPPORT");
STATIC_YOINK("ESPIPE");
STATIC_YOINK("ESRCH");
STATIC_YOINK("ESRMNT");
STATIC_YOINK("ESTALE");
STATIC_YOINK("ESTRPIPE");
STATIC_YOINK("ETIME");
STATIC_YOINK("ETIMEDOUT");
STATIC_YOINK("ETOOMANYREFS");
STATIC_YOINK("ETXTBSY");
STATIC_YOINK("EUCLEAN");
STATIC_YOINK("EUNATCH");
STATIC_YOINK("EUSERS");
STATIC_YOINK("EXDEV");
STATIC_YOINK("EXFULL");
_Alignas(char) static const char kErrnoNames[] = "\
2BIG\000\
ACCES\000\
ADDRINUSE\000\
ADDRNOTAVAIL\000\
ADV\000\
AFNOSUPPORT\000\
AGAIN\000\
ALREADY\000\
BADE\000\
BADF\000\
BADFD\000\
BADMSG\000\
BADR\000\
BADRQC\000\
BADSLT\000\
BFONT\000\
BUSY\000\
CANCELED\000\
CHILD\000\
CHRNG\000\
COMM\000\
CONNABORTED\000\
CONNREFUSED\000\
CONNRESET\000\
DEADLK\000\
DESTADDRREQ\000\
DOM\000\
DOTDOT\000\
DQUOT\000\
EXIST\000\
FAULT\000\
FBIG\000\
HOSTDOWN\000\
HOSTUNREACH\000\
HWPOISON\000\
IDRM\000\
ILSEQ\000\
INPROGRESS\000\
INTR\000\
INVAL\000\
IO\000\
ISCONN\000\
ISDIR\000\
ISNAM\000\
KEYEXPIRED\000\
KEYREJECTED\000\
KEYREVOKED\000\
L2HLT\000\
L2NSYNC\000\
L3HLT\000\
L3RST\000\
LIBACC\000\
LIBBAD\000\
LIBEXEC\000\
LIBMAX\000\
LIBSCN\000\
LNRNG\000\
LOOP\000\
MEDIUMTYPE\000\
MFILE\000\
MLINK\000\
MSGSIZE\000\
MULTIHOP\000\
NAMETOOLONG\000\
NAVAIL\000\
NETDOWN\000\
NETRESET\000\
NETUNREACH\000\
NFILE\000\
NOANO\000\
NOBUFS\000\
NOCSI\000\
NODATA\000\
NODEV\000\
NOENT\000\
NOEXEC\000\
NOKEY\000\
NOLCK\000\
NOLINK\000\
NOMEDIUM\000\
NOMEM\000\
NOMSG\000\
NONET\000\
NOPKG\000\
NOPROTOOPT\000\
NOSPC\000\
NOSR\000\
NOSTR\000\
NOSYS\000\
NOTBLK\000\
NOTCONN\000\
NOTDIR\000\
NOTEMPTY\000\
NOTNAM\000\
NOTRECOVERABLE\000\
NOTSOCK\000\
NOTSUP\000\
NOTTY\000\
NOTUNIQ\000\
NXIO\000\
OPNOTSUPP\000\
OVERFLOW\000\
OWNERDEAD\000\
PERM\000\
PFNOSUPPORT\000\
PIPE\000\
PROTO\000\
PROTONOSUPPORT\000\
PROTOTYPE\000\
RANGE\000\
REMCHG\000\
REMOTE\000\
REMOTEIO\000\
RESTART\000\
RFKILL\000\
ROFS\000\
SHUTDOWN\000\
SOCKTNOSUPPORT\000\
SPIPE\000\
SRCH\000\
SRMNT\000\
STALE\000\
STRPIPE\000\
TIME\000\
TIMEDOUT\000\
TOOMANYREFS\000\
TXTBSY\000\
UCLEAN\000\
UNATCH\000\
USERS\000\
XDEV\000\
XFULL\000\
\000";
const char *geterrname(long code) {
const long *e; const long *e;
size_t i, n; size_t i, n;
e = &E2BIG; e = &E2BIG;

View file

@ -43,6 +43,7 @@ static const int kCp437iMultimappings[] = {
u'' << 8 | 0xEE, // ELEMENT-OF SIGN u'' << 8 | 0xEE, // ELEMENT-OF SIGN
u'β' << 8 | 0xE1, // GREEK SMALL BETA u'β' << 8 | 0xE1, // GREEK SMALL BETA
u'ſ' << 8 | 0xF4, // LATIN SMALL LETTER LONG S u'ſ' << 8 | 0xF4, // LATIN SMALL LETTER LONG S
u'·' << 8 | 0xFA // MIDDLE DOT
}; };
static int g_cp437i[256 + ARRAYLEN(kCp437iMultimappings)]; static int g_cp437i[256 + ARRAYLEN(kCp437iMultimappings)];

View file

@ -597,6 +597,13 @@ typedef uint64_t uintmax_t;
#define nocallersavedregisters "need modern compiler" #define nocallersavedregisters "need modern compiler"
#endif #endif
#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 408 || \
__has_attribute(__no_sanitize_address__)
#define noasan __attribute__((__no_sanitize_address__))
#else
#define noasan
#endif
#ifndef unreachable #ifndef unreachable
#define unreachable __builtin_unreachable() #define unreachable __builtin_unreachable()
#endif #endif

View file

@ -57,7 +57,7 @@
#endif #endif
#define BIGPAGESIZE 0x200000 #define BIGPAGESIZE 0x200000
#define STACKSIZE 0x10000 #define STACKSIZE 0x100000
#define FRAMESIZE 0x10000 /* 8086 */ #define FRAMESIZE 0x10000 /* 8086 */
#define PAGESIZE 0x1000 /* i386+ */ #define PAGESIZE 0x1000 /* i386+ */
#define BUFSIZ 0x1000 /* best stdio default */ #define BUFSIZ 0x1000 /* best stdio default */

805
libc/intrin/asan.c Normal file
View file

@ -0,0 +1,805 @@
/*-*- 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 2020 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/alg/reverse.h"
#include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/intrin/asan.internal.h"
#include "libc/linux/exit.h"
#include "libc/linux/write.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/hook/hook.h"
#include "libc/runtime/directmap.h"
#include "libc/runtime/memtrack.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "third_party/dlmalloc/dlmalloc.internal.h"
STATIC_YOINK("_init_asan");
/**
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
*
* Someone brilliant at Google figured out a way to improve upon memory
* protection. Rather than invent another Java or Rust they changed GCC
* so it can emit fast code, that checks the validity of each memory op
* with byte granularity, by probing shadow memory.
*
* - AddressSanitizer dedicates one-eighth of the virtual address space
* to its shadow memory and uses a direct mapping with a scale and
* offset to translate an application address to its corresponding
* shadow address. Given the application memory address Addr, the
* address of the shadow byte is computed as (Addr>>3)+Offset."
*
* - We use the following encoding for each shadow byte: 0 means that
* all 8 bytes of the corresponding application memory region are
* addressable; k (1 k 7) means that the first k bytes are
* addressible; any negative value indicates that the entire 8-byte
* word is unaddressable. We use different negative values to
* distinguish between different kinds of unaddressable memory (heap
* redzones, stack redzones, global redzones, freed memory).
*
* Here's what the generated code looks like for 64-bit reads:
*
* movq %addr,%tmp
* shrq $3,%tmp
* cmpb $0,0x7fff8000(%tmp)
* jnz abort
* movq (%addr),%dst
*/
#define HOOK(HOOK, IMPL) \
do { \
if (weaken(HOOK)) { \
*weaken(HOOK) = IMPL; \
} \
} while (0)
#define REQUIRE(FUNC) \
do { \
if (!weaken(FUNC)) { \
__asan_write_string("asan needs " #FUNC "\n"); \
__asan_exit(100); \
} \
} while (0)
struct AsanSourceLocation {
const char *filename;
int line;
int column;
};
struct AsanAccessInfo {
const uintptr_t addr;
const uintptr_t first_bad_addr;
size_t size;
bool iswrite;
unsigned long ip;
};
struct AsanGlobal {
const uintptr_t addr;
size_t size;
size_t size_with_redzone;
const void *name;
const void *module_name;
unsigned long has_cxx_init;
struct AsanSourceLocation *location;
char *odr_indicator;
};
struct AsanMorgue {
unsigned i;
void *p[16];
};
static struct AsanMorgue __asan_morgue;
static uint64_t __asan_bsrl(uint64_t x) {
return __builtin_clzll(x) ^ 63;
}
static uint64_t __asan_roundup2pow(uint64_t x) {
return x > 1 ? 1ull << (__asan_bsrl(x - 1) + 1) : x ? 1 : 0;
}
static uint64_t __asan_rounddown2pow(uint64_t x) {
return x ? 1ull << __asan_bsrl(x) : 0;
}
static size_t __asan_strlen(const char *s) {
size_t n = 0;
while (*s++) ++n;
return n;
}
static int __asan_strcmp(const char *l, const char *r) {
size_t i = 0;
while (l[i] == r[i] && r[i]) ++i;
return (l[i] & 0xff) - (r[i] & 0xff);
}
static char *__asan_stpcpy(char *d, const char *s) {
size_t i;
for (i = 0;; ++i) {
if (!(d[i] = s[i])) {
return d + i;
}
}
}
static void *__asan_repstosb(void *di, int al, size_t cx) {
asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
: "0"(di), "1"(cx), "a"(al));
return di;
}
static void *__asan_memset(void *p, int c, size_t n) {
char *b;
size_t i;
uint64_t x;
b = p;
x = 0x0101010101010101 * (c & 0xff);
switch (n) {
case 0:
return p;
case 1:
__builtin_memcpy(b, &x, 1);
return p;
case 2:
__builtin_memcpy(b, &x, 2);
return p;
case 3:
__builtin_memcpy(b, &x, 2);
__builtin_memcpy(b + 1, &x, 2);
return p;
case 4:
__builtin_memcpy(b, &x, 4);
return p;
case 5:
case 6:
case 7:
__builtin_memcpy(b, &x, 4);
__builtin_memcpy(b + n - 4, &x, 4);
return p;
case 8:
__builtin_memcpy(b, &x, 8);
return p;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
__builtin_memcpy(b, &x, 8);
__builtin_memcpy(b + n - 8, &x, 8);
return p;
default:
if (n <= 64) {
i = 0;
do {
__builtin_memcpy(b + i, &x, 8);
asm volatile("" ::: "memory");
__builtin_memcpy(b + i + 8, &x, 8);
} while ((i += 16) + 16 <= n);
for (; i < n; ++i) b[i] = x;
} else {
__asan_repstosb(p, c, n);
}
return p;
}
}
static void *__asan_mempcpy(void *dst, const void *src, size_t n) {
size_t i;
char *d, *s;
uint64_t a, b;
d = dst;
s = src;
switch (n) {
case 0:
return d;
case 1:
*d = *s;
return d + 1;
case 2:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(d, &a, 2);
return d + 2;
case 3:
__builtin_memcpy(&a, s, 2);
__builtin_memcpy(&b, s + 1, 2);
__builtin_memcpy(d, &a, 2);
__builtin_memcpy(d + 1, &b, 2);
return d + 3;
case 4:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(d, &a, 4);
return d + 4;
case 5:
case 6:
case 7:
__builtin_memcpy(&a, s, 4);
__builtin_memcpy(&b, s + n - 4, 4);
__builtin_memcpy(d, &a, 4);
__builtin_memcpy(d + n - 4, &b, 4);
return d + n;
case 8:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(d, &a, 8);
return d + 8;
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
__builtin_memcpy(&a, s, 8);
__builtin_memcpy(&b, s + n - 8, 8);
__builtin_memcpy(d, &a, 8);
__builtin_memcpy(d + n - 8, &b, 8);
return d + n;
default:
i = 0;
do {
__builtin_memcpy(&a, s + i, 8);
asm volatile("" ::: "memory");
__builtin_memcpy(d + i, &a, 8);
} while ((i += 8) + 8 <= n);
for (; i < n; ++i) d[i] = s[i];
return d + i;
}
}
static void *__asan_memcpy(void *dst, const void *src, size_t n) {
__asan_mempcpy(dst, src, n);
return dst;
}
static void *__asan_memrchr(void *p, int c, size_t n) {
uint8_t *b;
for (c &= 0xff, b = p; n--;) {
if (b[n] == c) {
return b + n;
}
}
return NULL;
}
static size_t __asan_int2hex(uint64_t x, char b[17], uint8_t k) {
int i;
char *p;
for (p = b; k > 0;) {
*p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15];
}
*p = '\0';
return p - b;
}
static size_t __asan_uint2str(uint64_t i, char *a) {
size_t j;
j = 0;
do {
a[j++] = i % 10 + '0';
i /= 10;
} while (i > 0);
a[j] = '\0';
reverse(a, j);
return j;
}
static size_t __asan_int2str(int64_t i, char *a) {
if (i >= 0) return __asan_uint2str(i, a);
*a++ = '-';
return 1 + __asan_uint2str(-i, a);
}
void __asan_poison(uintptr_t p, size_t n, int kind) {
int k;
char *s;
if (!n) return;
if (p & 7) {
k = MIN(8 - (p & 7), n);
s = SHADOW(p);
if (*s == 0 || *s > (p & 7)) {
*s = p & 7;
}
n -= k;
p += k;
}
__asan_memset(SHADOW(p), kind, n >> 3);
if ((k = n & 7)) {
s = SHADOW(p + n);
if (*s < 0 || (*s > 0 && *s >= k)) {
*s = kind;
}
}
}
void __asan_unpoison(uintptr_t p, size_t n) {
int k;
char *s;
if (!n) return;
if (p & 7) {
k = MIN(8 - (p & 7), n);
s = SHADOW(p);
*s = 0;
n -= k;
p += k;
}
__asan_memset(SHADOW(p), 0, n >> 3);
if ((k = n & 7)) {
s = SHADOW(p + n);
if (*s && *s < k) {
*s = k;
}
}
}
static const char *__asan_dscribe_heap_poison(long c) {
switch (c) {
case kAsanHeapFree:
return "heap double free";
case kAsanStackFree:
return "stack double free";
case kAsanRelocated:
return "free after relocate";
default:
return "this corruption";
}
}
static const char *__asan_describe_access_poison(int c) {
switch (c) {
case kAsanHeapFree:
return "heap use after free";
case kAsanStackFree:
return "stack use after release";
case kAsanRelocated:
return "heap use after relocate";
case kAsanHeapUnderrun:
return "heap underrun";
case kAsanHeapOverrun:
return "heap overrun";
case kAsanGlobalOverrun:
return "global overrun";
case kAsanGlobalUnregistered:
return "global unregistered";
case kAsanStackUnderrun:
return "stack underflow";
case kAsanStackOverrun:
return "stack overflow";
case kAsanAllocaUnderrun:
return "alloca underflow";
case kAsanAllocaOverrun:
return "alloca overflow";
case kAsanUnscoped:
return "unscoped";
case kAsanUnmapped:
return "unmapped";
default:
return "poisoned";
}
}
static ssize_t __asan_write(const void *data, size_t size) {
ssize_t rc;
if (weaken(write)) {
if ((rc = weaken(write)(2, data, size)) != -1) {
return rc;
}
}
return LinuxWrite(2, data, size);
}
static ssize_t __asan_write_string(const char *s) {
return __asan_write(s, __asan_strlen(s));
}
static wontreturn void __asan_exit(int rc) {
if (weaken(_Exit)) {
weaken(_Exit)(rc);
} else {
LinuxExit(rc);
}
for (;;) asm("hlt");
}
static wontreturn void __asan_abort(void) {
if (weaken(abort)) weaken(abort)();
__asan_exit(134);
}
static wontreturn void __asan_die(const char *msg) {
__asan_write_string(msg);
if (weaken(__die)) weaken(__die)();
__asan_abort();
}
static char *__asan_report_start(char *p) {
bool ansi;
const char *term;
term = weaken(getenv) ? weaken(getenv)("TERM") : NULL;
ansi = !term || __asan_strcmp(term, "dumb") != 0;
if (ansi) p = __asan_stpcpy(p, "\r\e[J\e[1;91m");
p = __asan_stpcpy(p, "asan error");
if (ansi) p = __asan_stpcpy(p, "\e[0m");
return __asan_stpcpy(p, ": ");
}
static wontreturn void __asan_report_heap_fault(void *addr, long c) {
char *p, ibuf[21], buf[256];
p = __asan_report_start(buf);
p = __asan_stpcpy(p, __asan_dscribe_heap_poison(c));
p = __asan_stpcpy(p, " ");
p = __asan_mempcpy(p, ibuf, __asan_int2str(c, ibuf));
p = __asan_stpcpy(p, " at 0x");
p = __asan_mempcpy(p, ibuf, __asan_int2hex((intptr_t)addr, ibuf, 48));
p = __asan_stpcpy(p, " shadow 0x");
p = __asan_mempcpy(p, ibuf, __asan_int2hex((intptr_t)SHADOW(addr), ibuf, 48));
p = __asan_stpcpy(p, "\r\n");
__asan_die(buf);
}
static wontreturn void __asan_report_memory_fault(uint8_t *addr, int size,
const char *kind) {
char *p, ibuf[21], buf[256];
p = __asan_report_start(buf);
p = __asan_stpcpy(p, __asan_describe_access_poison(*SHADOW(addr)));
p = __asan_stpcpy(p, " ");
p = __asan_mempcpy(p, ibuf, __asan_int2str(size, ibuf));
p = __asan_stpcpy(p, "-byte ");
p = __asan_stpcpy(p, kind);
p = __asan_stpcpy(p, " at 0x");
p = __asan_mempcpy(p, ibuf, __asan_int2hex((intptr_t)addr, ibuf, 48));
p = __asan_stpcpy(p, " shadow 0x");
p = __asan_mempcpy(p, ibuf, __asan_int2hex((intptr_t)SHADOW(addr), ibuf, 48));
p = __asan_stpcpy(p, "\r\n");
__asan_die(buf);
}
const void *__asan_morgue_add(void *p) {
void *r;
unsigned i, j;
for (;;) {
i = __asan_morgue.i;
j = (i + 1) & (ARRAYLEN(__asan_morgue.p) - 1);
if (cmpxchg(&__asan_morgue.i, i, j)) {
r = __asan_morgue.p[i];
__asan_morgue.p[i] = p;
return r;
}
}
}
static void __asan_morgue_flush(void) {
void *p;
unsigned i;
for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) {
p = __asan_morgue.p[i];
if (cmpxchg(__asan_morgue.p + i, p, NULL)) {
if (weaken(dlfree)) {
weaken(dlfree)(p);
}
}
}
}
static size_t __asan_heap_size(size_t n) {
if (n < -8) {
return __asan_roundup2pow(ROUNDUP(n, 8) + 8);
} else {
return -1;
}
}
static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun) {
char *p;
size_t c;
if ((p = weaken(dlmemalign)(a, __asan_heap_size(n)))) {
c = weaken(dlmalloc_usable_size)(p);
__asan_unpoison((uintptr_t)p, n);
__asan_poison((uintptr_t)p - 16, 16, underrun); /* see dlmalloc design */
__asan_poison((uintptr_t)p + n, c - n, overrun);
WRITE64BE(p + c - sizeof(n), n);
}
return p;
}
static size_t __asan_malloc_usable_size(const void *p) {
size_t c, n;
if ((c = weaken(dlmalloc_usable_size)(p)) >= 8) {
if ((n = READ64BE((char *)p + c - sizeof(n))) <= c) {
return n;
} else {
__asan_report_heap_fault(p, n);
}
} else {
__asan_report_heap_fault(p, 0);
}
}
static void __asan_deallocate(char *p, long kind) {
size_t c, n;
if ((c = weaken(dlmalloc_usable_size)(p)) >= 8) {
if ((n = READ64BE((char *)p + c - sizeof(n))) <= c) {
WRITE64BE((char *)p + c - sizeof(n), kind);
__asan_poison((uintptr_t)p, n, kind);
if (weaken(dlfree)) {
weaken(dlfree)(__asan_morgue_add(p));
}
} else {
__asan_report_heap_fault(p, n);
}
} else {
__asan_report_heap_fault(p, 0);
}
}
static void __asan_free(void *p) {
if (!p) return;
__asan_deallocate(p, kAsanHeapFree);
}
static void *__asan_memalign(size_t align, size_t size) {
return __asan_allocate(align, size, kAsanHeapUnderrun, kAsanHeapOverrun);
}
static void *__asan_malloc(size_t size) {
return __asan_memalign(16, size);
}
static void *__asan_calloc(size_t nelem, size_t elsize) {
char *p;
size_t n;
if (__builtin_mul_overflow(nelem, elsize, &n)) n = -1;
if ((p = __asan_malloc(n))) __asan_memset(p, 0, n);
return p;
}
static void *__asan_realloc(void *p, size_t n) {
char *p2;
size_t c, m;
if (p) {
if (n) {
if ((c = weaken(dlmalloc_usable_size)(p)) < 8)
__asan_report_heap_fault(p, 0);
if ((m = READ64BE((char *)p + c - sizeof(n))) > c)
__asan_report_heap_fault(p, m);
if (n <= m) { /* shrink */
__asan_poison((uintptr_t)p + n, m - n, kAsanHeapOverrun);
WRITE64BE((char *)p + c - sizeof(n), n);
p2 = p;
} else if (n <= c - 8) { /* small growth */
__asan_unpoison((uintptr_t)p + m, n - m);
WRITE64BE((char *)p + c - sizeof(n), n);
p2 = p;
} else if ((p2 = __asan_malloc(n))) { /* exponential growth */
__asan_memcpy(p2, p, m);
__asan_deallocate(p, kAsanRelocated);
}
} else {
__asan_free(p);
p2 = NULL;
}
} else {
p2 = __asan_malloc(n);
}
return p2;
}
static void *__asan_valloc(size_t n) {
return __asan_memalign(PAGESIZE, n);
}
static void *__asan_pvalloc(size_t n) {
return __asan_valloc(ROUNDUP(n, PAGESIZE));
}
static int __asan_malloc_trim(size_t pad) {
__asan_morgue_flush();
if (weaken(dlmalloc_trim)) {
return weaken(dlmalloc_trim)(pad);
} else {
return 0;
}
}
void *__asan_stack_malloc(size_t size, int classid) {
return __asan_allocate(32, size, kAsanStackUnderrun, kAsanStackOverrun);
}
void __asan_stack_free(char *p, size_t size, int classid) {
__asan_deallocate(p, kAsanStackFree);
}
void __asan_handle_no_return_impl(uintptr_t rsp) {
__asan_unpoison(rsp, ROUNDUP(rsp, STACKSIZE) - rsp);
}
void __asan_register_globals(struct AsanGlobal g[], int n) {
int i;
for (i = 0; i < n; ++i) {
__asan_unpoison(g[i].addr, g[i].size);
__asan_poison(g[i].addr + g[i].size, g[i].size_with_redzone - g[i].size,
kAsanGlobalOverrun);
}
}
void __asan_unregister_globals(struct AsanGlobal g[], int n) {
int i;
for (i = 0; i < n; ++i) {
__asan_poison(g[i].addr, g[i].size_with_redzone, kAsanGlobalUnregistered);
}
}
void __asan_report_load_impl(uint8_t *addr, int size) {
__asan_report_memory_fault(addr, size, "load");
}
void __asan_report_store_impl(uint8_t *addr, int size) {
__asan_report_memory_fault(addr, size, "store");
}
void __asan_alloca_poison(uintptr_t addr, size_t size) {
/* TODO(jart): Make sense of this function. */
/* __asan_poison(addr - 32, 32, kAsanAllocaUnderrun); */
__asan_poison(ROUNDUP(addr + size, 32), 32, kAsanAllocaOverrun);
__asan_unpoison(addr, ROUNDUP(addr + size, 32) - (addr + size) + 32 + size);
}
void __asan_allocas_unpoison(uintptr_t x, uintptr_t y) {
if (x && x > y) __asan_unpoison(x, y - x);
}
void *__asan_addr_is_in_fake_stack(void *fakestack, void *addr, void **beg,
void **end) {
return NULL;
}
void *__asan_get_current_fake_stack(void) {
return NULL;
}
void __asan_install_malloc_hooks(void) {
HOOK(hook$free, __asan_free);
HOOK(hook$malloc, __asan_malloc);
HOOK(hook$calloc, __asan_calloc);
HOOK(hook$valloc, __asan_valloc);
HOOK(hook$pvalloc, __asan_pvalloc);
HOOK(hook$realloc, __asan_realloc);
HOOK(hook$memalign, __asan_memalign);
HOOK(hook$malloc_trim, __asan_malloc_trim);
HOOK(hook$malloc_usable_size, __asan_malloc_usable_size);
}
static bool __asan_is_mapped(int x) {
int i;
struct MemoryIntervals *m;
m = weaken(_mmi);
i = weaken(FindMemoryInterval)(m, x);
return i < m->i && x >= m->p[i].x && x <= m->p[i].y;
}
void __asan_map_shadow(uintptr_t p, size_t n) {
int i, x, a, b;
struct DirectMap sm;
struct MemoryIntervals *m;
if (0x7fff8000 <= p && p < 0x100080000000) {
__asan_die("asan error: mmap can't shadow a shadow\r\n");
}
m = weaken(_mmi);
a = (uintptr_t)SHADOW(p) >> 16;
b = ROUNDUP((uintptr_t)SHADOW(ROUNDUP((uintptr_t)p + n, 8)), 1 << 16) >> 16;
for (; a < b; ++a) {
if (!__asan_is_mapped(a)) {
sm = weaken(__mmap)(
(void *)((uintptr_t)a << 16), 1 << 16, PROT_READ | PROT_WRITE,
MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED, -1, 0);
if (sm.addr == MAP_FAILED ||
weaken(TrackMemoryInterval)(
m, a, a, sm.maphandle, PROT_READ | PROT_WRITE,
MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED) == -1) {
__asan_abort();
}
__asan_repstosb((void *)((uintptr_t)a << 16), kAsanUnmapped, 1 << 16);
}
}
__asan_unpoison((uintptr_t)p, n);
}
static textstartup void __asan_shadow_string(char *s) {
__asan_map_shadow((uintptr_t)s, __asan_strlen(s) + 1);
}
static textstartup void __asan_shadow_auxv(intptr_t *auxv) {
size_t i;
for (i = 0; auxv[i]; i += 2) {
if (weaken(AT_RANDOM) && auxv[i] == *weaken(AT_RANDOM)) {
__asan_map_shadow(auxv[i + 1], 16);
} else if (weaken(AT_EXECFN) && auxv[i] == *weaken(AT_EXECFN)) {
__asan_shadow_string((char *)auxv[i + 1]);
} else if (weaken(AT_PLATFORM) && auxv[i] == *weaken(AT_PLATFORM)) {
__asan_shadow_string((char *)auxv[i + 1]);
}
}
__asan_map_shadow((uintptr_t)auxv, (i + 2) * sizeof(intptr_t));
}
static textstartup void __asan_shadow_string_list(char **list) {
size_t i;
for (i = 0; list[i]; ++i) {
__asan_shadow_string(list[i]);
}
__asan_map_shadow((uintptr_t)list, (i + 1) * sizeof(char *));
}
static textstartup void __asan_shadow_existing_mappings(void) {
size_t i;
struct MemoryIntervals m;
__asan_memcpy(&m, weaken(_mmi), sizeof(m));
for (i = 0; i < m.i; ++i) {
__asan_map_shadow((uintptr_t)m.p[i].x << 16,
(uintptr_t)(m.p[i].y - m.p[i].x + 1) << 16);
}
}
static textstartup bool IsMemoryManagementRuntimeLinked(void) {
return weaken(_mmi) && weaken(__mmap) && weaken(MAP_ANONYMOUS) &&
weaken(FindMemoryInterval) && weaken(TrackMemoryInterval);
}
textstartup void __asan_init(int argc, char **argv, char **envp,
intptr_t *auxv) {
static bool once;
if (!cmpxchg(&once, false, true)) return;
REQUIRE(_mmi);
REQUIRE(__mmap);
REQUIRE(MAP_ANONYMOUS);
REQUIRE(FindMemoryInterval);
REQUIRE(TrackMemoryInterval);
if (weaken(hook$malloc) || weaken(hook$calloc) || weaken(hook$realloc) ||
weaken(hook$pvalloc) || weaken(hook$valloc) || weaken(hook$free) ||
weaken(hook$malloc_usable_size)) {
REQUIRE(dlmemalign);
REQUIRE(dlmalloc_usable_size);
}
__asan_shadow_existing_mappings();
__asan_map_shadow((uintptr_t)_base, _end - _base);
__asan_shadow_string_list(argv);
__asan_shadow_string_list(envp);
__asan_shadow_auxv(auxv);
__asan_install_malloc_hooks();
}
static textstartup void __asan_ctor(void) {
if (weaken(__cxa_atexit)) {
weaken(__cxa_atexit)(__asan_morgue_flush, NULL, NULL);
}
}
const void *const g_asan_ctor[] initarray = {__asan_ctor};

View file

@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_ASAN_H_
#define COSMOPOLITAN_LIBC_INTRIN_ASAN_H_
#define kAsanScale 3
#define kAsanMagic 0x7fff8000
#define kAsanHeapFree -1
#define kAsanStackFree -2
#define kAsanRelocated -3
#define kAsanHeapUnderrun -4
#define kAsanHeapOverrun -5
#define kAsanGlobalOverrun -6
#define kAsanGlobalUnregistered -7
#define kAsanStackUnderrun -8
#define kAsanStackOverrun -9
#define kAsanAllocaUnderrun -10
#define kAsanAllocaOverrun -11
#define kAsanUnscoped -12
#define kAsanUnmapped -13
#define SHADOW(x) ((char *)(((uintptr_t)(x) >> kAsanScale) + kAsanMagic))
void __asan_map_shadow(uintptr_t, size_t);
void __asan_poison(uintptr_t, size_t, int);
void __asan_unpoison(uintptr_t, size_t);
#endif /* COSMOPOLITAN_LIBC_INTRIN_ASAN_H_ */

View file

@ -41,7 +41,12 @@ $(LIBC_INTRIN_A).pkg: \
$(LIBC_INTRIN_A_OBJS): \ $(LIBC_INTRIN_A_OBJS): \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
-fwrapv -O3 $(NO_MAGIC) \
-O3
o/$(MODE)/libc/intrin/asan.o: \
OVERRIDE_CFLAGS += \
-mgeneral-regs-only
LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x))) LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)))
LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS)) LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS))

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/macros.h" #include "libc/macros.h"
.privileged
.source __FILE__ .source __FILE__
/ @fileoverview Address Sanitizer Thunks / @fileoverview Address Sanitizer Thunks
@ -48,9 +47,12 @@ __asan_report_load16:
.endfn __asan_report_load16,globl .endfn __asan_report_load16,globl
OnReportLoad: OnReportLoad:
pop %rsi pop %rsi
ezlea __asan_report_load_n,ax / 𝑠𝑙𝑖𝑑𝑒
jmp __asan_report_noreentry
.endfn OnReportLoad .endfn OnReportLoad
__asan_report_load_n:
lea __asan_report_load_impl(%rip),%r11
jmp __asan_report_noreentry
.endfn __asan_report_load_n,globl
__asan_report_store1: __asan_report_store1:
push $1 push $1
@ -78,21 +80,24 @@ __asan_report_store32:
.endfn __asan_report_store32,globl .endfn __asan_report_store32,globl
ReportStore: ReportStore:
pop %rsi pop %rsi
ezlea __asan_report_store_n,ax
/ 𝑠𝑙𝑖𝑑𝑒 / 𝑠𝑙𝑖𝑑𝑒
.endfn ReportStore .endfn ReportStore
__asan_report_store_n:
lea __asan_report_store_impl(%rip),%r11
/ 𝑠𝑙𝑖𝑑𝑒
.endfn __asan_report_store_n,globl
__asan_report_noreentry: __asan_report_noreentry:
push %rbp push %rbp
mov %rsp,%rbp mov %rsp,%rbp
cmpb $0,noreentry(%rip) xor %eax,%eax
mov $1,%r10b
cmpxchg %r10b,__asan_noreentry(%rip)
jnz 2f jnz 2f
incb noreentry(%rip) call *%r11
call *%rax decb __asan_noreentry(%rip)
decb noreentry(%rip) 2: pop %rbp
pop %rbp
ret ret
2: call abort
.endfn __asan_report_noreentry .endfn __asan_report_noreentry
__asan_stack_free_0: __asan_stack_free_0:
@ -194,14 +199,27 @@ OnStackMalloc:
.endfn OnStackMalloc .endfn OnStackMalloc
__asan_handle_no_return: __asan_handle_no_return:
push %rbp
mov %rsp,%rbp
lea 8(%rsp),%rdi
call __asan_handle_no_return_impl
pop %rbp
ret ret
.endfn __asan_handle_no_return,globl .endfn __asan_handle_no_return,globl
__asan_before_dynamic_init: __asan_before_dynamic_init:
push %rbp
mov %rsp,%rbp
ud2
pop %rbp
ret ret
.endfn __asan_before_dynamic_init,globl .endfn __asan_before_dynamic_init,globl
__asan_after_dynamic_init: __asan_after_dynamic_init:
push %rbp
mov %rsp,%rbp
ud2
pop %rbp
ret ret
.endfn __asan_after_dynamic_init,globl .endfn __asan_after_dynamic_init,globl
@ -229,7 +247,7 @@ __asan_option_detect_stack_use_after_return:
.previous .previous
.bss .bss
noreentry: __asan_noreentry:
.byte 0 .byte 0
.endobj noreentry .endobj __asan_noreentry
.previous .previous

View file

@ -1,453 +0,0 @@
/*-*- 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 2020 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/bits/safemacros.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/fmt/itoa.h"
#include "libc/log/asan.internal.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/log.h"
#include "libc/mem/hook/hook.h"
#include "libc/runtime/directmap.h"
#include "libc/runtime/memtrack.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "third_party/dlmalloc/dlmalloc.internal.h"
STATIC_YOINK("_init_asan");
/**
* @fileoverview Cosmopolitan Address Sanitizer Runtime.
*
* Someone brilliant at Google figured out a way to improve upon memory
* protection. Rather than invent another Java or Rust they changed GCC
* so it can emit fast code, that checks the validity of each memory op
* with byte granularity, by probing shadow memory.
*
* - AddressSanitizer dedicates one-eighth of the virtual address space
* to its shadow memory and uses a direct mapping with a scale and
* offset to translate an application address to its corresponding
* shadow address. Given the application memory address Addr, the
* address of the shadow byte is computed as (Addr>>3)+Offset."
*
* - We use the following encoding for each shadow byte: 0 means that
* all 8 bytes of the corresponding application memory region are
* addressable; k (1 k 7) means that the first k bytes are
* addressible; any negative value indicates that the entire 8-byte
* word is unaddressable. We use different negative values to
* distinguish between different kinds of unaddressable memory (heap
* redzones, stack redzones, global redzones, freed memory).
*
* Here's what the generated code looks like for 64-bit reads:
*
* movq %addr,%tmp
* shrq $3,%tmp
* cmpb $0,0x7fff8000(%tmp)
* jnz abort
* movq (%addr),%dst
*/
#define HOOK(HOOK, IMPL) \
if (weaken(HOOK)) { \
*weaken(HOOK) = IMPL; \
}
struct AsanSourceLocation {
const char *filename;
int line;
int column;
};
struct AsanAccessInfo {
const char *addr;
const char *first_bad_addr;
size_t size;
bool iswrite;
unsigned long ip;
};
struct AsanGlobal {
const char *addr;
size_t size;
size_t size_with_redzone;
const void *name;
const void *module_name;
unsigned long has_cxx_init;
struct AsanSourceLocation *location;
char *odr_indicator;
};
struct AsanMorgue {
unsigned i;
void *p[16];
};
static struct AsanMorgue __asan_morgue;
static const char *__asan_dscribe_free_poison(int c) {
switch (c) {
case kAsanHeapFree:
return "heap double free";
case kAsanRelocated:
return "free after relocate";
case kAsanStackFree:
return "stack double free";
default:
return "invalid pointer";
}
}
static const char *__asan_describe_access_poison(int c) {
switch (c) {
case kAsanHeapFree:
return "heap use after free";
case kAsanStackFree:
return "stack use after release";
case kAsanRelocated:
return "heap use after relocate";
case kAsanHeapUnderrun:
return "heap underrun";
case kAsanHeapOverrun:
return "heap overrun";
case kAsanGlobalOverrun:
return "global overrun";
case kAsanGlobalUnregistered:
return "global unregistered";
case kAsanStackUnderrun:
return "stack underflow";
case kAsanStackOverrun:
return "stack overflow";
case kAsanAllocaOverrun:
return "alloca overflow";
case kAsanUnscoped:
return "unscoped";
default:
return "poisoned";
}
}
static wontreturn void __asan_die(const char *msg, size_t size) {
write(STDERR_FILENO, msg, size);
__die();
}
static char *__asan_report_start(char *p) {
bool ansi;
const char *term;
term = getenv("TERM");
ansi = !term || strcmp(term, "dumb") != 0;
if (ansi) p = stpcpy(p, "\r\e[J\e[1;91m");
p = stpcpy(p, "asan error");
if (ansi) p = stpcpy(p, "\e[0m");
return stpcpy(p, ": ");
}
static wontreturn void __asan_report_deallocate_fault(void *addr, int c) {
char *p, ibuf[21], buf[256];
p = __asan_report_start(buf);
p = stpcpy(p, __asan_dscribe_free_poison(c));
p = stpcpy(p, " ");
p = mempcpy(p, ibuf, int64toarray_radix10(c, ibuf));
p = stpcpy(p, " at 0x");
p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48));
p = stpcpy(p, "\r\n");
__asan_die(buf, p - buf);
}
static wontreturn void __asan_report_memory_fault(uint8_t *addr, int size,
const char *kind) {
char *p, ibuf[21], buf[256];
p = __asan_report_start(buf);
p = stpcpy(p, __asan_describe_access_poison(*(char *)SHADOW((intptr_t)addr)));
p = stpcpy(p, " ");
p = mempcpy(p, ibuf, uint64toarray_radix10(size, ibuf));
p = stpcpy(p, "-byte ");
p = stpcpy(p, kind);
p = stpcpy(p, " at 0x");
p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48));
p = stpcpy(p, "\r\n");
__asan_die(buf, p - buf);
}
static const void *__asan_morgue_add(void *p) {
void *r;
r = __asan_morgue.p[__asan_morgue.i];
__asan_morgue.p[__asan_morgue.i] = p;
__asan_morgue.i += 1;
__asan_morgue.i &= ARRAYLEN(__asan_morgue.p) - 1;
return r;
}
static void __asan_morgue_flush(void) {
void *p;
unsigned i;
for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) {
p = __asan_morgue.p[i];
__asan_morgue.p[i] = NULL;
dlfree(p);
}
}
static void *__asan_allocate(size_t align, size_t size, int underrun,
int overrun) {
char *p, *s;
size_t q, r, i;
if (!(p = dlmemalign(align, ROUNDUP(size, 8) + 16))) return NULL;
s = (char *)SHADOW((intptr_t)p - 16);
q = size / 8;
r = size % 8;
*s++ = underrun;
*s++ = underrun;
memset(s, 0, q);
s += q;
if (r) *s++ = r;
*s++ = overrun;
*s++ = overrun;
return p;
}
static void __asan_deallocate(char *p, int kind) {
char *s;
s = (char *)SHADOW((intptr_t)p);
if ((*s < 0 && *s != kAsanHeapOverrun) || *s >= 8) {
__asan_report_deallocate_fault(p, *s);
}
memset(s, kind, dlmalloc_usable_size(p) >> 3);
dlfree(__asan_morgue_add(p));
}
static void __asan_poison_redzone(intptr_t addr, size_t size, size_t redsize,
int kind) {
char *s;
intptr_t p;
size_t a, b, w;
w = (intptr_t)addr & 7;
p = (intptr_t)addr - w;
a = w + size;
b = w + redsize;
s = (char *)SHADOW(p + a);
if (a & 7) *s++ = a & 7;
memset(s, kind, (b - ROUNDUP(a, 8)) >> 3);
}
static size_t __asan_malloc_usable_size(const void *vp) {
char *s;
size_t n;
for (n = 0, s = (char *)SHADOW((intptr_t)vp);; ++s) {
if (!*s) {
n += 8;
} else if (*s > 0) {
n += *s & 7;
} else {
break;
}
}
return n;
}
static void __asan_free(void *p) {
if (!p) return;
__asan_deallocate(p, kAsanHeapFree);
}
static void *__asan_memalign(size_t align, size_t size) {
return __asan_allocate(align, size, kAsanHeapUnderrun, kAsanHeapOverrun);
}
static void *__asan_malloc(size_t size) {
return __asan_memalign(16, size);
}
static void *__asan_calloc(size_t n, size_t m) {
char *p;
size_t size;
if (__builtin_mul_overflow(n, m, &size)) size = -1;
if ((p = __asan_malloc(size))) memset(p, 0, size);
return p;
}
static void *__asan_realloc(void *p, size_t n) {
char *p2;
if (p) {
if (n) {
if ((p2 = __asan_malloc(n))) {
memcpy(p2, p, min(n, dlmalloc_usable_size(p)));
__asan_deallocate(p, kAsanRelocated);
}
} else {
__asan_free(p);
p2 = NULL;
}
} else {
p2 = __asan_malloc(n);
}
return p2;
}
static void *__asan_valloc(size_t n) {
return __asan_memalign(PAGESIZE, n);
}
static void *__asan_pvalloc(size_t n) {
return __asan_valloc(ROUNDUP(n, PAGESIZE));
}
static int __asan_malloc_trim(size_t pad) {
__asan_morgue_flush();
return dlmalloc_trim(pad);
}
void __asan_register_globals(struct AsanGlobal g[], int n) {
unsigned i;
for (i = 0; i < n; ++i) {
__asan_poison_redzone((intptr_t)g[i].addr, g[i].size,
g[i].size_with_redzone, kAsanGlobalOverrun);
}
}
void __asan_unregister_globals(struct AsanGlobal g[], int n) {
unsigned i;
intptr_t a, b;
for (i = 0; i < n; ++i) {
a = ROUNDUP((intptr_t)g[i].addr, 8);
b = ROUNDDOWN((intptr_t)g[i].addr + g[i].size_with_redzone, 8);
if (b > a) {
memset((char *)SHADOW(a), kAsanGlobalUnregistered, (b - a) >> 3);
}
}
}
void *__asan_stack_malloc(size_t size, int classid) {
return __asan_allocate(32, size, kAsanStackUnderrun, kAsanStackOverrun);
}
void __asan_stack_free(char *p, size_t size, int classid) {
dlfree(p);
}
void __asan_report_load_n(uint8_t *addr, int size) {
__asan_report_memory_fault(addr, size, "load");
}
void __asan_report_store_n(uint8_t *addr, int size) {
__asan_report_memory_fault(addr, size, "store");
}
void __asan_poison_stack_memory(uintptr_t p, size_t n) {
memset((char *)SHADOW(p), kAsanUnscoped, n >> 3);
if (n & 7) *(char *)SHADOW(p + n) = 8 - (n & 7);
}
void __asan_unpoison_stack_memory(uintptr_t p, size_t n) {
memset((char *)SHADOW(p), 0, n >> 3);
if (n & 7) *(char *)SHADOW(p + n) = n & 7;
}
void __asan_alloca_poison(intptr_t addr, size_t size) {
__asan_poison_redzone(addr, size, size + 32, kAsanAllocaOverrun);
}
void __asan_allocas_unpoison(uintptr_t top, uintptr_t bottom) {
memset((char *)SHADOW(top), 0, (bottom - top) >> 3);
}
void *__asan_addr_is_in_fake_stack(void *fakestack, void *addr, void **beg,
void **end) {
return NULL;
}
void *__asan_get_current_fake_stack(void) {
return NULL;
}
void __asan_install_malloc_hooks(void) {
HOOK(hook$free, __asan_free);
HOOK(hook$malloc, __asan_malloc);
HOOK(hook$calloc, __asan_calloc);
HOOK(hook$valloc, __asan_valloc);
HOOK(hook$pvalloc, __asan_pvalloc);
HOOK(hook$realloc, __asan_realloc);
HOOK(hook$memalign, __asan_memalign);
HOOK(hook$malloc_trim, __asan_malloc_trim);
HOOK(hook$malloc_usable_size, __asan_malloc_usable_size);
}
static bool __asan_is_mapped(int x) {
int i = FindMemoryInterval(&_mmi, x);
return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y;
}
void __asan_map_shadow(void *p, size_t n) {
int i, x, a, b;
struct DirectMap sm;
a = SHADOW((uintptr_t)p) >> 16;
b = ROUNDUP(SHADOW(ROUNDUP((uintptr_t)p + n, 8)), 1 << 16) >> 16;
for (; a < b; ++a) {
if (!__asan_is_mapped(a)) {
sm = __mmap((void *)((uintptr_t)a << 16), 1 << 16, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (sm.addr == MAP_FAILED ||
TrackMemoryInterval(&_mmi, a, a, sm.maphandle, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED) == -1) {
abort();
}
}
}
}
static char *__asan_get_stack_base(void) {
uintptr_t rsp;
asm("mov\t%%rsp,%0" : "=r"(rsp));
return (char *)ROUNDDOWN(ROUNDDOWN(rsp, STACKSIZE), FRAMESIZE);
}
static textstartup size_t __asan_get_auxv_size(intptr_t *auxv) {
unsigned i;
for (i = 0;; i += 2) {
if (!auxv[i]) break;
}
return (i + 2) * sizeof(intptr_t);
}
static textstartup void __asan_shadow_string_list(char **list) {
for (; *list; ++list) {
__asan_map_shadow(*list, strlen(*list) + 1);
}
}
textstartup void __asan_init(int argc, char **argv, char **envp,
intptr_t *auxv) {
static bool once;
if (once) return;
__asan_map_shadow(_base, _end - _base);
__asan_map_shadow(__asan_get_stack_base(), STACKSIZE);
__asan_shadow_string_list(argv);
__asan_shadow_string_list(envp);
__asan_map_shadow(auxv, __asan_get_auxv_size(auxv));
__asan_install_malloc_hooks();
}
static textstartup void __asan_ctor(void) {
__cxa_atexit(__asan_morgue_flush, NULL, NULL);
}
const void *const g_asan_ctor[] initarray = {__asan_ctor};

View file

@ -1,22 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_LOG_ASAN_H_
#define COSMOPOLITAN_LIBC_LOG_ASAN_H_
#define kAsanScale 3
#define kAsanMagic 0x7fff8000
#define kAsanHeapFree -1
#define kAsanStackFree -2
#define kAsanRelocated -3
#define kAsanHeapUnderrun -4
#define kAsanHeapOverrun -5
#define kAsanGlobalOverrun -6
#define kAsanGlobalUnregistered -7
#define kAsanStackUnderrun -8
#define kAsanStackOverrun -9
#define kAsanAllocaOverrun -10
#define kAsanUnscoped -11
#define SHADOW(x) (((x) >> kAsanScale) + kAsanMagic)
void __asan_map_shadow(void *, size_t);
#endif /* COSMOPOLITAN_LIBC_LOG_ASAN_H_ */

View file

@ -21,10 +21,12 @@
#include "libc/bits/safemacros.h" #include "libc/bits/safemacros.h"
#include "libc/bits/weaken.h" #include "libc/bits/weaken.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/nexgen32e/gc.internal.h" #include "libc/nexgen32e/gc.internal.h"
@ -33,9 +35,10 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#define kBacktraceMaxFrames 128 #define kBacktraceMaxFrames 128
#define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (16 + 1)) #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1))
static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
ssize_t got; ssize_t got;
@ -43,7 +46,9 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
size_t i, j, gi; size_t i, j, gi;
int ws, pid, pipefds[2]; int ws, pid, pipefds[2];
struct Garbages *garbage; struct Garbages *garbage;
sigset_t chldmask, savemask;
const struct StackFrame *frame; const struct StackFrame *frame;
struct sigaction ignore, saveint, savequit;
const char *debugbin, *p1, *p2, *p3, *addr2line; const char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
if (IsOpenbsd()) return -1; if (IsOpenbsd()) return -1;
@ -66,12 +71,25 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
--gi; --gi;
} while ((addr = garbage->p[gi].ret) == weakaddr("__gc")); } while ((addr = garbage->p[gi].ret) == weakaddr("__gc"));
} }
argv[i++] = &buf[j]; argv[i++] = buf + j;
j += snprintf(&buf[j], 17, "%#x", addr - 1) + 1; buf[j++] = '0';
buf[j++] = 'x';
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
} }
argv[i++] = NULL; argv[i++] = NULL;
ignore.sa_flags = 0;
ignore.sa_handler = SIG_IGN;
sigemptyset(&ignore.sa_mask);
sigaction(SIGINT, &ignore, &saveint);
sigaction(SIGQUIT, &ignore, &savequit);
sigemptyset(&chldmask);
sigaddset(&chldmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &chldmask, &savemask);
pipe(pipefds); pipe(pipefds);
if (!(pid = vfork())) { if (!(pid = vfork())) {
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
dup2(pipefds[1], 1); dup2(pipefds[1], 1);
close(pipefds[0]); close(pipefds[0]);
close(pipefds[1]); close(pipefds[1]);
@ -106,6 +124,9 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
if (errno == EINTR) continue; if (errno == EINTR) continue;
return -1; return -1;
} }
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
if (WIFEXITED(ws) && !WEXITSTATUS(ws)) { if (WIFEXITED(ws) && !WEXITSTATUS(ws)) {
return 0; return 0;
} else { } else {

View file

@ -56,10 +56,10 @@ relegated void __check_fail(const char *suffix, const char *opstr,
gethostname(hostname, sizeof(hostname)); gethostname(hostname, sizeof(hostname));
(dprintf)(STDERR_FILENO, (dprintf)(STDERR_FILENO,
"check failed on %s pid %d\n" "check failed on %s pid %d\r\n"
"\tCHECK_%s(%s, %s);\n" "\tCHECK_%s(%s, %s);\r\n"
"\t\t → %#lx (%s)\n" "\t\t → %#lx (%s)\r\n"
"\t\t%s %#lx (%s)\n", "\t\t%s %#lx (%s)\r\n",
hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr, hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr,
got, gotstr); got, gotstr);
@ -68,19 +68,19 @@ relegated void __check_fail(const char *suffix, const char *opstr,
va_start(va, fmt); va_start(va, fmt);
(vdprintf)(STDERR_FILENO, fmt, va); (vdprintf)(STDERR_FILENO, fmt, va);
va_end(va); va_end(va);
(dprintf)(STDERR_FILENO, "\n"); (dprintf)(STDERR_FILENO, "\r\n");
} }
(dprintf)(STDERR_FILENO, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE, (dprintf)(STDERR_FILENO, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE,
getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET); getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET);
for (i = 1; i < g_argc; ++i) { for (i = 1; i < g_argc; ++i) {
(dprintf)(STDERR_FILENO, "\t\t%s%s\n", g_argv[i], (dprintf)(STDERR_FILENO, "\t\t%s%s\r\n", g_argv[i],
i < g_argc - 1 ? " \\" : ""); i < g_argc - 1 ? " \\" : "");
} }
if (!IsTiny() && lasterr == ENOMEM) { if (!IsTiny() && lasterr == ENOMEM) {
(dprintf)(STDERR_FILENO, "\n"); (dprintf)(STDERR_FILENO, "\r\n");
PrintMemoryIntervals(STDERR_FILENO, &_mmi); PrintMemoryIntervals(STDERR_FILENO, &_mmi);
} }

View file

@ -46,5 +46,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got,
__print(bx, uint64toarray_radix16(got, bx)); __print(bx, uint64toarray_radix16(got, bx));
__print_string(" ("); __print_string(" (");
__print(bx, int64toarray_radix10(lasterr, bx)); __print(bx, int64toarray_radix10(lasterr, bx));
__print_string(")\n"); __print_string(")\r\n");
} }

View file

@ -17,12 +17,12 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/bits/weaken.h"
#include "libc/dce.h"
#include "libc/log/backtrace.internal.h" #include "libc/log/backtrace.internal.h"
#include "libc/log/log.h" #include "libc/log/log.h"
#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
/** /**
* Aborts process after printing a backtrace. * Aborts process after printing a backtrace.
@ -31,15 +31,14 @@
*/ */
relegated wontreturn void __die(void) { relegated wontreturn void __die(void) {
static bool once; static bool once;
if (!once) { if (cmpxchg(&once, false, true)) {
once = true; if (weaken(fflush)) {
weaken(fflush)(NULL);
}
if (!IsTiny()) { if (!IsTiny()) {
if (IsDebuggerPresent(false)) DebugBreak(); if (IsDebuggerPresent(false)) DebugBreak();
ShowBacktrace(STDERR_FILENO, NULL); ShowBacktrace(2, NULL);
} }
exit(EXIT_FAILURE);
unreachable;
} }
abort(); _exit(77);
unreachable;
} }

View file

@ -34,7 +34,7 @@
* @returns -1 on error or something else on success * @returns -1 on error or something else on success
*/ */
int getttysize(int fd, struct winsize *out) { int getttysize(int fd, struct winsize *out) {
if (isterminalinarticulate()) { if (IsTerminalInarticulate()) {
out->ws_col = strtoimax(firstnonnull(getenv("COLUMNS"), "80"), NULL, 0); out->ws_col = strtoimax(firstnonnull(getenv("COLUMNS"), "80"), NULL, 0);
out->ws_row = strtoimax(firstnonnull(getenv("ROWS"), "40"), NULL, 0); out->ws_row = strtoimax(firstnonnull(getenv("ROWS"), "40"), NULL, 0);
out->ws_xpixel = 0; out->ws_xpixel = 0;

View file

@ -22,4 +22,6 @@
/** /**
* Returns true if current process was spawned by GNU Make. * Returns true if current process was spawned by GNU Make.
*/ */
bool isrunningundermake(void) { return !!getenv("MAKEFLAGS"); } bool IsRunningUnderMake(void) {
return !!getenv("MAKEFLAGS");
}

View file

@ -24,6 +24,6 @@
/** /**
* Checks if we're probably running inside Emacs. * Checks if we're probably running inside Emacs.
*/ */
bool isterminalinarticulate(void) { bool IsTerminalInarticulate(void) {
return strcmp(nulltoempty(getenv("TERM")), "dumb") == 0; return strcmp(nulltoempty(getenv("TERM")), "dumb") == 0;
} }

View file

@ -39,7 +39,7 @@ void meminfo(int); /* shows malloc statistics &c. */
void memsummary(int); /* light version of same thing */ void memsummary(int); /* light version of same thing */
uint16_t getttycols(uint16_t); uint16_t getttycols(uint16_t);
int getttysize(int, struct winsize *) paramsnonnull(); int getttysize(int, struct winsize *) paramsnonnull();
bool isterminalinarticulate(void) nosideeffect; bool IsTerminalInarticulate(void) nosideeffect;
char *commandvenv(const char *, const char *) nodiscard; char *commandvenv(const char *, const char *) nodiscard;
const char *GetAddr2linePath(void); const char *GetAddr2linePath(void);
const char *GetGdbPath(void); const char *GetGdbPath(void);
@ -47,7 +47,7 @@ const char *GetGdbPath(void);
void showcrashreports(void); void showcrashreports(void);
void callexitontermination(struct sigset *); void callexitontermination(struct sigset *);
bool32 IsDebuggerPresent(bool); bool32 IsDebuggerPresent(bool);
bool isrunningundermake(void); bool IsRunningUnderMake(void);
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § liblog » logging cosmopolitan § liblog » logging

View file

@ -58,15 +58,21 @@ $(LIBC_LOG_A).pkg: \
$(LIBC_LOG_A_OBJS) \ $(LIBC_LOG_A_OBJS) \
$(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x)_A).pkg) $(foreach x,$(LIBC_LOG_A_DIRECTDEPS),$($(x)_A).pkg)
$(LIBC_LOG_A_OBJS): \ o/$(MODE)/libc/log/attachdebugger.o \
o/$(MODE)/libc/log/backtrace2.o \
o/$(MODE)/libc/log/backtrace3.o \
o/$(MODE)/libc/log/checkaligned.o \
o/$(MODE)/libc/log/checkfail.o \
o/$(MODE)/libc/log/checkfail_ndebug.o \
o/$(MODE)/libc/log/getsymboltable.o \
o/$(MODE)/libc/log/oncrash.o \
o/$(MODE)/libc/log/onkill.o \
o/$(MODE)/libc/log/startfatal.o \
o/$(MODE)/libc/log/startfatal_ndebug.o \
o/$(MODE)/libc/log/ubsan.o \
o/$(MODE)/libc/log/die.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
$(NO_MAGIC) \ $(NO_MAGIC)
-fwrapv
# ifeq (,$(MODE))
# LIBC_LOG_ASAN = o/$(MODE)/libc/log/asan.o
# endif
LIBC_LOG_ASAN_A = o/$(MODE)/libc/log/log.a
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x))) LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS)) LIBC_LOG_SRCS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)_SRCS))

View file

@ -182,8 +182,9 @@ relegated static void ShowCrashReport(int err, int fd, int sig,
write(fd, "\r\n", 2); write(fd, "\r\n", 2);
for (i = 0; i < g_argc; ++i) { for (i = 0; i < g_argc; ++i) {
write(fd, g_argv[i], strlen(g_argv[i])); write(fd, g_argv[i], strlen(g_argv[i]));
write(fd, "\r\n", 2); write(fd, " ", 1);
} }
write(fd, "\r\n", 2);
} }
relegated static void RestoreDefaultCrashSignalHandlers(void) { relegated static void RestoreDefaultCrashSignalHandlers(void) {
@ -221,9 +222,9 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
rip = ctx ? ctx->uc_mcontext.rip : 0; rip = ctx ? ctx->uc_mcontext.rip : 0;
if ((gdbpid = IsDebuggerPresent(true))) { if ((gdbpid = IsDebuggerPresent(true))) {
DebugBreak(); DebugBreak();
} else if (isterminalinarticulate() || isrunningundermake()) { } else if (IsTerminalInarticulate() || IsRunningUnderMake()) {
gdbpid = -1; gdbpid = -1;
} else { } else if (FindDebugBinary()) {
RestoreDefaultCrashSignalHandlers(); RestoreDefaultCrashSignalHandlers();
gdbpid = gdbpid =
attachdebugger(((sig == SIGTRAP || sig == SIGQUIT) && attachdebugger(((sig == SIGTRAP || sig == SIGQUIT) &&

View file

@ -90,6 +90,6 @@ kCp437:
.short 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x03bc,0x03c4 #e0:αßΓπΣσμτ .short 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x03bc,0x03c4 #e0:αßΓπΣσμτ
.short 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229 #e8:ΦΘΩδφε .short 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229 #e8:ΦΘΩδφε
.short 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248 #f0:±÷ .short 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248 #f0:±÷
.short 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x03bb #f8:°·²λ .short 0x00b0,0x2219,0x00d7,0x221a,0x207f,0x00b2,0x25a0,0x03bb #f8:°×²λ
.endobj kCp437,globl .endobj kCp437,globl
.previous .previous

View file

@ -40,10 +40,9 @@
/ @return original rdi copied to rax / @return original rdi copied to rax
/ @mode long / @mode long
/ @asyncsignalsafe / @asyncsignalsafe
.align 16
.source __FILE__
memcpy: mov %rdi,%rax memcpy: mov %rdi,%rax
/ 𝑠𝑙𝑖𝑑𝑒 / 𝑠𝑙𝑖𝑑𝑒
.align 16
.endfn memcpy,globl .endfn memcpy,globl
/ Copies memory w/ minimal impact ABI. / Copies memory w/ minimal impact ABI.
@ -53,7 +52,6 @@ memcpy: mov %rdi,%rax
/ @param rdx is number of bytes / @param rdx is number of bytes
/ @clob flags,rcx,xmm3,xmm4 / @clob flags,rcx,xmm3,xmm4
/ @mode long / @mode long
.align 16
MemCpy: .leafprologue MemCpy: .leafprologue
.profilable .profilable
mov $.Lmemcpytab.ro.size,%ecx mov $.Lmemcpytab.ro.size,%ecx
@ -141,6 +139,7 @@ MemCpy: .leafprologue
pxor %xmm3,%xmm3 pxor %xmm3,%xmm3
jmp .L0 jmp .L0
.endfn MemCpy,globl,hidden .endfn MemCpy,globl,hidden
.source __FILE__
.initro 300,_init_memcpy .initro 300,_init_memcpy
memcpytab.ro: memcpytab.ro:

View file

@ -23,7 +23,6 @@
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/nexgen32e/macros.h" #include "libc/nexgen32e/macros.h"
#include "libc/macros.h" #include "libc/macros.h"
.source __FILE__
/ Sets memory. / Sets memory.
/ /
@ -34,7 +33,8 @@
/ @mode long / @mode long
/ @asyncsignalsafe / @asyncsignalsafe
memset: mov %rdi,%rax memset: mov %rdi,%rax
/ fallthrough / 𝑠𝑙𝑖𝑑𝑒
.align 16
.endfn memset,globl .endfn memset,globl
/ Sets memory w/ minimal-impact ABI. / Sets memory w/ minimal-impact ABI.
@ -98,6 +98,7 @@ MemSet: .leafprologue
pop %rax pop %rax
jmp .L0 jmp .L0
.endfn MemSet,globl,hidden .endfn MemSet,globl,hidden
.source __FILE__
.rodata.cst8 .rodata.cst8
.Lb8: .quad 0x0101010101010101 .Lb8: .quad 0x0101010101010101

View file

@ -25,22 +25,29 @@
.text.startup .text.startup
.source __FILE__ .source __FILE__
/ Stack frame that owns process from spawn to exit. / Cosmopolitan runtime.
/ /
/ @param edi is argc / @param edi is argc
/ @param rsi is argv / @param rsi is argv
/ @param rdx is environ / @param rdx is environ
/ @param rcx is auxv / @param rcx is auxv
/ @noreturn / @noreturn
_executive: cosmo: push %rbp
push %rbp
mov %rsp,%rbp mov %rsp,%rbp
ezlea _base,bx ezlea _base,bx
mov %edi,%r12d mov %edi,%r12d
mov %rsi,%r13 mov %rsi,%r13
mov %rdx,%r14 mov %rdx,%r14
mov %rcx,%r15 mov %rcx,%r15
call _spawn #ifdef __FAST_MATH__
call __fast_math
#endif
call _init
call _construct
#if !IsTrustworthy()
mov $PROT_READ,%edi
call _piro
#endif
mov %r12d,%edi mov %r12d,%edi
mov %r13,%rsi mov %r13,%rsi
mov %r14,%rdx mov %r14,%rdx
@ -49,7 +56,7 @@ _executive:
call main call main
xchg %eax,%edi xchg %eax,%edi
call exit call exit
.endfn _executive,weak,hidden .endfn cosmo,weak,hidden
ud2 ud2
#ifdef __PG__ #ifdef __PG__

View file

@ -36,6 +36,7 @@ const char *FindDebugBinary(void) {
char buf[2][PATH_MAX]; char buf[2][PATH_MAX];
static char res[PATH_MAX]; static char res[PATH_MAX];
const char *bins[4], *pwd; const char *bins[4], *pwd;
if (res[0]) return res;
bins[0] = program_invocation_name; bins[0] = program_invocation_name;
bins[1] = (const char *)getauxval(AT_EXECFN); bins[1] = (const char *)getauxval(AT_EXECFN);
pwd = emptytonull(getenv("PWD")); pwd = emptytonull(getenv("PWD"));
@ -59,6 +60,7 @@ const char *FindDebugBinary(void) {
return res; return res;
} }
} }
res[0] = '\0';
errno = ENOENT; errno = ENOENT;
return NULL; return NULL;
} }

View file

@ -19,7 +19,7 @@ extern hidden void *g_stacktop;
void _init(void) hidden; void _init(void) hidden;
void _piro(int) hidden; void _piro(int) hidden;
void *__cxa_finalize(void *) hidden; void *__cxa_finalize(void *) hidden;
void _executive(int, char **, char **, long (*)[2]) hidden wontreturn; void cosmo(int, char **, char **, long (*)[2]) hidden wontreturn;
void __stack_chk_fail(void) wontreturn relegated; void __stack_chk_fail(void) wontreturn relegated;
void __stack_chk_fail_local(void) wontreturn relegated hidden; void __stack_chk_fail_local(void) wontreturn relegated hidden;
void _jmpstack(void *, void *, ...) hidden wontreturn; void _jmpstack(void *, void *, ...) hidden wontreturn;

View file

@ -23,7 +23,7 @@ COSMOPOLITAN_C_START_
#define kFixedmapStart MEMTRACK_ADDRESS(_kFixedmapStart, 0x40000000) #define kFixedmapStart MEMTRACK_ADDRESS(_kFixedmapStart, 0x40000000)
struct MemoryIntervals { struct MemoryIntervals {
int i; long i;
struct MemoryInterval { struct MemoryInterval {
int x; int x;
int y; int y;

View file

@ -21,7 +21,7 @@
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/internal.h" #include "libc/calls/internal.h"
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/log/asan.internal.h" #include "libc/intrin/asan.internal.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/rand/rand.h" #include "libc/rand/rand.h"
#include "libc/runtime/directmap.h" #include "libc/runtime/directmap.h"
@ -94,7 +94,7 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
abort(); abort();
} }
if (weaken(__asan_map_shadow)) { if (weaken(__asan_map_shadow)) {
weaken(__asan_map_shadow)(dm.addr, size); weaken(__asan_map_shadow)((uintptr_t)dm.addr, size);
} }
return dm.addr; return dm.addr;
} }

View file

@ -56,7 +56,23 @@ $(LIBC_RUNTIME_A).pkg: \
$(LIBC_RUNTIME_A_OBJS) \ $(LIBC_RUNTIME_A_OBJS) \
$(foreach x,$(LIBC_RUNTIME_A_DIRECTDEPS),$($(x)_A).pkg) $(foreach x,$(LIBC_RUNTIME_A_DIRECTDEPS),$($(x)_A).pkg)
$(LIBC_RUNTIME_A_OBJS): \ o/$(MODE)/libc/runtime/abort-nt.o \
o/$(MODE)/libc/runtime/assertfail.o \
o/$(MODE)/libc/runtime/memtrack.o \
o/$(MODE)/libc/runtime/memtracknt.o \
o/$(MODE)/libc/runtime/findmemoryinterval.o \
o/$(MODE)/libc/runtime/arememoryintervalsok.o \
o/$(MODE)/libc/runtime/isheap.o \
o/$(MODE)/libc/runtime/directmap.o \
o/$(MODE)/libc/runtime/directmapnt.o \
o/$(MODE)/libc/runtime/stackchkfail.o \
o/$(MODE)/libc/runtime/stackchkfaillocal.o \
o/$(MODE)/libc/runtime/hook.greg.o \
o/$(MODE)/libc/runtime/print.greg.o \
o/$(MODE)/libc/runtime/ftrace.greg.o \
o/$(MODE)/libc/runtime/getdosargv.o \
o/$(MODE)/libc/runtime/getdosenviron.o \
o/$(MODE)/libc/runtime/winmain.greg.o: \
OVERRIDE_CFLAGS += \ OVERRIDE_CFLAGS += \
$(NO_MAGIC) $(NO_MAGIC)

View file

@ -49,8 +49,6 @@
* TODO: How can we ensure we never overlap with KERNEL32.DLL? * TODO: How can we ensure we never overlap with KERNEL32.DLL?
*/ */
#define WINSTACK 0x100000
struct WinArgs { struct WinArgs {
char *argv[4096]; char *argv[4096];
char *envp[4096]; char *envp[4096];
@ -109,7 +107,7 @@ static textwindows wontreturn void WinMainNew(void) {
NormalizeCmdExe(); NormalizeCmdExe();
*(/*unconst*/ int *)&__hostos = WINDOWS; *(/*unconst*/ int *)&__hostos = WINDOWS;
addr = NtGetVersion() < kNtVersionWindows10 ? 0xff00000 : 0x777000000000; addr = NtGetVersion() < kNtVersionWindows10 ? 0xff00000 : 0x777000000000;
size = ROUNDUP(WINSTACK + sizeof(struct WinArgs), FRAMESIZE); size = ROUNDUP(STACKSIZE + sizeof(struct WinArgs), FRAMESIZE);
_mmi.p[0].h = _mmi.p[0].h =
__mmap$nt((char *)addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, -1, 0) __mmap$nt((char *)addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, -1, 0)
.maphandle; .maphandle;
@ -130,8 +128,7 @@ static textwindows wontreturn void WinMainNew(void) {
FreeEnvironmentStrings(env16); FreeEnvironmentStrings(env16);
auxv[0][0] = pushpop(0L); auxv[0][0] = pushpop(0L);
auxv[0][1] = pushpop(0L); auxv[0][1] = pushpop(0L);
_jmpstack((char *)addr + WINSTACK, _executive, count, wa->argv, wa->envp, _jmpstack((char *)addr + STACKSIZE, cosmo, count, wa->argv, wa->envp, auxv);
auxv);
} }
/** /**

View file

@ -45,7 +45,8 @@
*/ */
void *memccpy(void *d, const void *s, int c, size_t n) { void *memccpy(void *d, const void *s, int c, size_t n) {
const char *p, *pe; const char *p, *pe;
if ((pe = memchr((p = s), c, n))) { p = s;
if ((pe = memchr(p, c, n))) {
return mempcpy(d, s, pe - p + 1); return mempcpy(d, s, pe - p + 1);
} else { } else {
memcpy(d, s, n); memcpy(d, s, n);

View file

@ -20,26 +20,8 @@
#include "libc/intrin/pmovmskb.h" #include "libc/intrin/pmovmskb.h"
#include "libc/str/str.h" #include "libc/str/str.h"
/** static noasan size_t stpcpy$sse2(char *d, const char *s, size_t i) {
* Copies bytes from 𝑠 to 𝑑 until a NUL is encountered.
*
* @param 𝑑 is destination memory
* @param 𝑠 is a NUL-terminated string
* @note 𝑑 and 𝑠 can't overlap
* @return pointer to nul byte
* @see strcpy(), memccpy()
* @asyncsignalsafe
*/
char *stpcpy(char *d, const char *s) {
size_t i;
uint8_t v1[16], v2[16], vz[16]; uint8_t v1[16], v2[16], vz[16];
i = 0;
while (((uintptr_t)(s + i) & 15)) {
if (!(d[i] = s[i])) {
return d + i;
}
++i;
}
for (;;) { for (;;) {
memset(vz, 0, 16); memset(vz, 0, 16);
memcpy(v1, s + i, 16); memcpy(v1, s + i, 16);
@ -51,6 +33,26 @@ char *stpcpy(char *d, const char *s) {
break; break;
} }
} }
return i;
}
/**
* Copies bytes from 𝑠 to 𝑑 until a NUL is encountered.
*
* @param 𝑑 is destination memory
* @param 𝑠 is a NUL-terminated string
* @note 𝑑 and 𝑠 can't overlap
* @return pointer to nul byte
* @asyncsignalsafe
*/
char *stpcpy(char *d, const char *s) {
size_t i;
for (i = 0; (uintptr_t)(s + i) & 15; ++i) {
if (!(d[i] = s[i])) {
return d + i;
}
}
i = stpcpy$sse2(d, s, i);
for (;;) { for (;;) {
if (!(d[i] = s[i])) { if (!(d[i] = s[i])) {
return d + i; return d + i;

View file

@ -90,6 +90,7 @@ void *memeqmask(void *, const void *, const void *, size_t) memcpyesque;
size_t strlen(const char *) strlenesque; size_t strlen(const char *) strlenesque;
size_t strnlen(const char *, size_t) strlenesque; size_t strnlen(const char *, size_t) strlenesque;
size_t strnlen_s(const char *, size_t); size_t strnlen_s(const char *, size_t);
size_t strlen$pure(const char *) strlenesque;
char *strchr(const char *, int) strlenesque; char *strchr(const char *, int) strlenesque;
char *index(const char *, int) strlenesque; char *index(const char *, int) strlenesque;
void *memchr(const void *, int, size_t) strlenesque; void *memchr(const void *, int, size_t) strlenesque;
@ -360,6 +361,17 @@ char *strsignal(int) returnsnonnull libcesque;
}) })
#endif /* hosted/sse2/unbloat */ #endif /* hosted/sse2/unbloat */
#ifdef __FSANITIZE_ADDRESS__
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § strings » address sanitizer
*/
#ifdef strlen
#undef strlen
#endif
#define strlen(s) strlen$pure(s)
#endif /* __FSANITIZE_ADDRESS__ */
#endif /* __GNUC__ && !__STRICT_ANSI__ */ #endif /* __GNUC__ && !__STRICT_ANSI__ */
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -42,8 +42,8 @@ $(LIBC_STR_A).pkg: \
$(LIBC_STR_A_OBJS) \ $(LIBC_STR_A_OBJS) \
$(foreach x,$(LIBC_STR_A_DIRECTDEPS),$($(x)_A).pkg) $(foreach x,$(LIBC_STR_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/str/memmem.o: \ o/$(MODE)/libc/str/memmem.o: \
OVERRIDE_CPPFLAGS += \ OVERRIDE_CPPFLAGS += \
-DSTACK_FRAME_UNLIMITED -DSTACK_FRAME_UNLIMITED
LIBC_STR_LIBS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x))) LIBC_STR_LIBS = $(foreach x,$(LIBC_STR_ARTIFACTS),$($(x)))

View file

@ -20,26 +20,8 @@
#include "libc/intrin/pmovmskb.h" #include "libc/intrin/pmovmskb.h"
#include "libc/str/str.h" #include "libc/str/str.h"
/** static noasan size_t strcpy$sse2(char *d, const char *s, size_t i) {
* Copies bytes from 𝑠 to 𝑑 until a NUL is encountered.
*
* @param 𝑑 is destination memory
* @param 𝑠 is a NUL-terminated string
* @note 𝑑 and 𝑠 can't overlap
* @return original dest
* @see memccpy()
* @asyncsignalsafe
*/
char *strcpy(char *d, const char *s) {
size_t i;
uint8_t v1[16], v2[16], vz[16]; uint8_t v1[16], v2[16], vz[16];
i = 0;
while (((uintptr_t)(s + i) & 15)) {
if (!(d[i] = s[i])) {
return d;
}
++i;
}
for (;;) { for (;;) {
memset(vz, 0, 16); memset(vz, 0, 16);
memcpy(v1, s + i, 16); memcpy(v1, s + i, 16);
@ -51,6 +33,26 @@ char *strcpy(char *d, const char *s) {
break; break;
} }
} }
return i;
}
/**
* Copies bytes from 𝑠 to 𝑑 until a NUL is encountered.
*
* @param 𝑑 is destination memory
* @param 𝑠 is a NUL-terminated string
* @note 𝑑 and 𝑠 can't overlap
* @return original dest
* @asyncsignalsafe
*/
char *strcpy(char *d, const char *s) {
size_t i;
for (i = 0; (uintptr_t)(s + i) & 15; ++i) {
if (!(d[i] = s[i])) {
return d;
}
}
i = strcpy$sse2(d, s, i);
for (;;) { for (;;) {
if (!(d[i] = s[i])) { if (!(d[i] = s[i])) {
return d; return d;

50
libc/str/strlen-pure.c Normal file
View file

@ -0,0 +1,50 @@
/*-*- 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/str/str.h"
noasan static const char *strlen$pure$x64(const char *p) {
uint64_t w;
for (;;) {
w = *(uint64_t *)p;
if (~w & (w - 0x0101010101010101) & 0x8080808080808080) {
break;
} else {
p += 8;
}
}
return p;
}
/**
* Returns length of NUL-terminated string.
*/
size_t strlen$pure(const char *s) {
const char *p;
p = s;
while ((uintptr_t)p & 7) {
if (*p) {
++p;
} else {
return p - s;
}
}
p = strlen$pure$x64(p);
while (*p) ++p;
return p - s;
}

View file

@ -27,6 +27,25 @@
static const int16_t kDel16[8] = {127, 127, 127, 127, 127, 127, 127, 127}; static const int16_t kDel16[8] = {127, 127, 127, 127, 127, 127, 127, 127};
/* 10x speedup for ascii */
static noasan axdx_t tprecode16to8$sse2(char *dst, size_t dstsize,
const char16_t *src, axdx_t r) {
int16_t v1[8], v2[8], v3[8], vz[8];
memset(vz, 0, 16);
while (r.ax + 8 < dstsize) {
memcpy(v1, src + r.dx, 16);
pcmpgtw(v2, v1, vz);
pcmpgtw(v3, v1, kDel16);
pandn((void *)v2, (void *)v3, (void *)v2);
if (pmovmskb((void *)v2) != 0xFFFF) break;
packsswb((void *)v1, v1, v1);
memcpy(dst + r.ax, v1, 8);
r.ax += 8;
r.dx += 8;
}
return r;
}
/** /**
* Transcodes UTF-16 to UTF-8. * Transcodes UTF-16 to UTF-8.
* *
@ -40,24 +59,11 @@ axdx_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) {
axdx_t r; axdx_t r;
uint64_t w; uint64_t w;
wint_t x, y; wint_t x, y;
int16_t v1[8], v2[8], v3[8], vz[8];
r.ax = 0; r.ax = 0;
r.dx = 0; r.dx = 0;
for (;;) { for (;;) {
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
/* 10x speedup for ascii */ r = tprecode16to8$sse2(dst, dstsize, src, r);
memset(vz, 0, 16);
while (r.ax + 8 < dstsize) {
memcpy(v1, src + r.dx, 16);
pcmpgtw(v2, v1, vz);
pcmpgtw(v3, v1, kDel16);
pandn((void *)v2, (void *)v3, (void *)v2);
if (pmovmskb((void *)v2) != 0xFFFF) break;
packsswb((void *)v1, v1, v1);
memcpy(dst + r.ax, v1, 8);
r.ax += 8;
r.dx += 8;
}
} }
if (!(x = src[r.dx++])) break; if (!(x = src[r.dx++])) break;
if (IsUtf16Cont(x)) continue; if (IsUtf16Cont(x)) continue;

View file

@ -24,6 +24,25 @@
#include "libc/str/thompike.h" #include "libc/str/thompike.h"
#include "libc/str/utf16.h" #include "libc/str/utf16.h"
/* 34x speedup for ascii */
static noasan axdx_t tprecode8to16$sse2(char16_t *dst, size_t dstsize,
const char *src, axdx_t r) {
uint8_t v1[16], v2[16], vz[16];
memset(vz, 0, 16);
while (r.ax + 16 < dstsize) {
memcpy(v1, src + r.dx, 16);
pcmpgtb((int8_t *)v2, (int8_t *)v1, (int8_t *)vz);
if (pmovmskb(v2) != 0xFFFF) break;
punpcklbw(v2, v1, vz);
punpckhbw(v1, v1, vz);
memcpy(dst + r.ax + 0, v2, 16);
memcpy(dst + r.ax + 8, v1, 16);
r.ax += 16;
r.dx += 16;
}
return r;
}
/** /**
* Transcodes UTF-8 to UTF-16. * Transcodes UTF-8 to UTF-16.
* *
@ -38,24 +57,11 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) {
unsigned n; unsigned n;
uint64_t w; uint64_t w;
wint_t x, y; wint_t x, y;
uint8_t v1[16], v2[16], vz[16];
r.ax = 0; r.ax = 0;
r.dx = 0; r.dx = 0;
for (;;) { for (;;) {
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) { if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
/* 34x speedup for ascii */ tprecode8to16$sse2(dst, dstsize, src, r);
memset(vz, 0, 16);
while (r.ax + 16 < dstsize) {
memcpy(v1, src + r.dx, 16);
pcmpgtb((int8_t *)v2, (int8_t *)v1, (int8_t *)vz);
if (pmovmskb(v2) != 0xFFFF) break;
punpcklbw(v2, v1, vz);
punpckhbw(v1, v1, vz);
memcpy(dst + r.ax + 0, v2, 16);
memcpy(dst + r.ax + 8, v1, 16);
r.ax += 16;
r.dx += 16;
}
} }
x = src[r.dx++] & 0xff; x = src[r.dx++] & 0xff;
if (ThomPikeCont(x)) continue; if (ThomPikeCont(x)) continue;

View file

@ -69,8 +69,8 @@ static const struct DeflateConsts {
{{144, 8}, {112, 9}, {24, 7}, {8, 8}, {32, 5}, {0, 0}}, {{144, 8}, {112, 9}, {24, 7}, {8, 8}, {32, 5}, {0, 0}},
}; };
static uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree, static noasan uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree,
const uint8_t *lens, size_t symcount) { const uint8_t *lens, size_t symcount) {
size_t i, len; size_t i, len;
uint32_t code, slot; uint32_t code, slot;
uint16_t codes[16], first[16], counts[16]; uint16_t codes[16], first[16], counts[16];
@ -96,10 +96,10 @@ static uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree,
return first[15]; return first[15];
} }
static struct DeflateHold undeflatesymbol(struct DeflateHold hold, static noasan struct DeflateHold undeflatesymbol(struct DeflateHold hold,
const uint32_t *tree, const uint32_t *tree,
size_t treecount, size_t treecount,
uint32_t *out_symbol) { uint32_t *out_symbol) {
size_t left, right, m; size_t left, right, m;
uint32_t search, key; uint32_t search, key;
left = 0; left = 0;
@ -122,17 +122,20 @@ static struct DeflateHold undeflatesymbol(struct DeflateHold hold,
return hold; return hold;
} }
/* TODO(jart): Do we really need noasan? */
/** /**
* Decompresses raw DEFLATE data. * Decompresses raw DEFLATE data.
* *
* This is 10x smaller and 10x slower than chromium zlib. * This is 10x smaller and 10x slower than chromium zlib.
* *
* @param output should be followed by a single guard page, and have * @param output should be followed by a single guard page, and have
* 36kb of guard pages preceding it too * 36kb of guard pages preceding it too because buffer overflows
* are part of the design of this algorithm
* @note h/t Phil Katz, David Huffman, Claude Shannon * @note h/t Phil Katz, David Huffman, Claude Shannon
*/ */
ssize_t undeflate(void *output, size_t outputsize, void *input, noasan ssize_t undeflate(void *output, size_t outputsize, void *input,
size_t inputsize, struct DeflateState *ds) { size_t inputsize, struct DeflateState *ds) {
struct DeflateHold hold; struct DeflateHold hold;
bool isfinalblock; bool isfinalblock;
size_t i, nlit, ndist; size_t i, nlit, ndist;

View file

@ -77,10 +77,6 @@ __asan_load32:
ud2 ud2
.endfn __asan_load32,weak .endfn __asan_load32,weak
__asan_noreentry:
ud2
.endfn __asan_noreentry,weak
__asan_option_detect_stack_use_after_return: __asan_option_detect_stack_use_after_return:
ud2 ud2
.endfn __asan_option_detect_stack_use_after_return,weak .endfn __asan_option_detect_stack_use_after_return,weak

View file

@ -437,28 +437,28 @@ syscon utime UTIME_OMIT 0x3ffffffe 0x3ffffffe -2 -1 0x3ffffffe # polyf
# getauxval() keys # getauxval() keys
# #
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary # group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary
syscon auxv AT_EXECFD 2 0 2 0 0 syscon auxv AT_EXECFD 2 0 2 0 0 # file descriptor of program
syscon auxv AT_PHDR 3 0 3 0 0 syscon auxv AT_PHDR 3 0 3 0 0 # address of program headers of executable
syscon auxv AT_PHENT 4 0 4 0 0 syscon auxv AT_PHENT 4 0 4 0 0
syscon auxv AT_PHNUM 5 0 5 0 0 syscon auxv AT_PHNUM 5 0 5 0 0
syscon auxv AT_PAGESZ 6 0 6 0 0 syscon auxv AT_PAGESZ 6 0 6 0 0
syscon auxv AT_BASE 7 0 7 0 0 syscon auxv AT_BASE 7 0 7 0 0 # address of program interpreter
syscon auxv AT_ENTRY 9 0 9 0 0 syscon auxv AT_ENTRY 9 0 9 0 0 # entry address of executable
syscon auxv AT_NOTELF 10 0 10 0 0 syscon auxv AT_NOTELF 10 0 10 0 0
syscon auxv AT_OSRELDATE 0 0 18 0 0 syscon auxv AT_OSRELDATE 0 0 18 0 0
syscon auxv AT_UID 11 0 0 0 0 syscon auxv AT_UID 11 0 0 0 0
syscon auxv AT_EUID 12 0 0 0 0 syscon auxv AT_EUID 12 0 0 0 0
syscon auxv AT_GID 13 0 0 0 0 syscon auxv AT_GID 13 0 0 0 0
syscon auxv AT_EGID 14 0 0 0 0 syscon auxv AT_EGID 14 0 0 0 0
syscon auxv AT_PLATFORM 15 0 0 0 0 # RHEL5.0 limit syscon auxv AT_PLATFORM 15 0 0 0 0 # address of string with hardware platform for rpath interpretation [RHEL5.0 LIMIT]
syscon auxv AT_CLKTCK 17 0 0 0 0 syscon auxv AT_CLKTCK 17 0 0 0 0
syscon auxv AT_DCACHEBSIZE 19 0 0 0 0 syscon auxv AT_DCACHEBSIZE 19 0 0 0 0
syscon auxv AT_ICACHEBSIZE 20 0 0 0 0 syscon auxv AT_ICACHEBSIZE 20 0 0 0 0
syscon auxv AT_UCACHEBSIZE 21 0 0 0 0 syscon auxv AT_UCACHEBSIZE 21 0 0 0 0
syscon auxv AT_SECURE 23 0 0 0 0 syscon auxv AT_SECURE 23 0 0 0 0
syscon auxv AT_BASE_PLATFORM 24 0 0 0 0 syscon auxv AT_BASE_PLATFORM 24 0 0 0 0
syscon auxv AT_RANDOM 25 0 0 0 0 syscon auxv AT_RANDOM 25 0 0 0 0 # address of sixteen bytes of random data
syscon auxv AT_EXECFN 31 999 999 999 999 # faked on non-linux syscon auxv AT_EXECFN 31 999 999 999 999 # address of string containing first argument passed to execve() used when running program [faked on non-linux]
syscon auxv AT_SYSINFO_EHDR 33 0 0 0 0 syscon auxv AT_SYSINFO_EHDR 33 0 0 0 0
syscon auxv AT_NO_AUTOMOUNT 0x0800 0 0 0 0 syscon auxv AT_NO_AUTOMOUNT 0x0800 0 0 0 0

View file

@ -17,6 +17,9 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/map.h"
#include "libc/macros.h" #include "libc/macros.h"
.source __FILE__ .source __FILE__
@ -161,9 +164,10 @@ systemfive.xnu:
/ Initializes System Five system call support. / Initializes System Five system call support.
/ /
/ (1) Extracts parameters passed by kernel, / (1) Extracts parameters passed by kernel
/ (2) Detects O/S without issuing system calls, / (2) Detects OS without issuing system calls
/ (3) Unpacks numbers. / (3) Unpacks magnums from libc/sysv/consts.sh
/ (4) Replaces stack with one we control
/ /
/ @param %r15 is auxv / @param %r15 is auxv
/ @note OpenBSD devs: let us know if you start using auxv / @note OpenBSD devs: let us know if you start using auxv
@ -219,26 +223,17 @@ systemfive.init.os:
pop %rax pop %rax
add %rcx,%rax add %rcx,%rax
stosq # __systemfive stosq # __systemfive
/ 𝑠𝑙𝑖𝑑𝑒
systemfive.init.magnums:
push %rdi push %rdi
ezlea syscon.start,di ezlea syscon.start,di
ezlea syscon.end,bx ezlea syscon.end,bx
call systemfive.sleb128unpacker
pop %rdi
/ 𝑠𝑙𝑖𝑑𝑒
systemfive.init.done:
pop %rsi
pop %rbx
.init.end 300,_init_systemfive,globl,hidden
.text.startup
systemfive.sleb128unpacker:
.leafprologue
or $-1,%r9 or $-1,%r9
2: cmp %rbx,%rdi 2: cmp %rbx,%rdi
jnb 5f jnb 5f
xor %ecx,%ecx xor %ecx,%ecx
xor %edx,%edx xor %edx,%edx
3: lodsb 3: lodsb # decodes sleb128
mov %rax,%r8 mov %rax,%r8
and $127,%r8d and $127,%r8d
sal %cl,%r8 sal %cl,%r8
@ -252,14 +247,66 @@ systemfive.sleb128unpacker:
sal %cl,%rax sal %cl,%rax
or %rax,%rdx or %rax,%rdx
4: mov %rdx,%rax 4: mov %rdx,%rax
cmpq $0,(%rdi) # don't change consts already set cmpq $0,(%rdi) # dont change if set
cmovne (%rdi),%rax # @see WinMain() for example cmovne (%rdi),%rax # @see WinMain()
stosq stosq
jmp 2b jmp 2b
5: .leafepilogue 5: pop %rdi
.previous pop %rsi
pop %rbx
/ 𝑠𝑙𝑖𝑑𝑒
#ifndef TINY
systemfive.init.stack:
testb IsWindows() # already did this
jnz systemfive.init.done
testb IsOpenbsd() # todo fix openbsd
jnz systemfive.init.done
push %rdi
push %rsi
mov __NR_mmap,%eax
mov $0x700000000000-STACKSIZE,%rdi
mov $STACKSIZE,%esi
mov $PROT_READ|PROT_WRITE,%edx
mov $MAP_PRIVATE|MAP_FIXED,%r10d
or MAP_ANONYMOUS,%r10d
or MAP_GROWSDOWN,%r10d
or $-1,%r8
xor %r9d,%r9d
push %r9 # openbsd:pad
/ clc
syscall
pop %r9
jnc 2f
1: mov %eax,%edi
mov __NR_exit_group,%eax
syscall
2: test %rax,%rax
js 1b
.weak _mmi
ezlea _mmi,cx
test %rcx,%rcx
jz 3f
movb $1,(%rcx) # _mmi.i
movl $(0x700000000000-STACKSIZE)>>16,8(%rcx) # _mmi.p[0].x
movl $(0x700000000000-1)>>16,12(%rcx) # _mmi.p[0].y
mov %edx,20(%rcx) # _mmi.p[0].prot
mov %r10d,24(%rcx) # _mmi.p[0].flags
3: pop %rsi
pop %rdi
leave
pop %rcx
lea STACKSIZE(%rax),%rsp
push %rcx
xor %ebp,%ebp
push %rbp
mov %rsp,%rbp
/ 𝑠𝑙𝑖𝑑𝑒
#endif /* TINY */
systemfive.init.done:
nop
.init.end 300,_init_systemfive,globl,hidden
/ Sections for varint encoded numbers. / Sections for varint encoded magic numbers.
/ /
/ These sections are all ordered by (group_name, constant_name). / These sections are all ordered by (group_name, constant_name).
/ They're populated by modules simply referencing the symbols. / They're populated by modules simply referencing the symbols.

View file

@ -37,8 +37,8 @@ testonly void testlib_runfixtures(testfn_t *test_start, testfn_t *test_end,
unsigned i, count; unsigned i, count;
count = testlib_countfixtures(fixture_start, fixture_end); count = testlib_countfixtures(fixture_start, fixture_end);
for (i = 0; i < count && !g_testlib_failed; ++i) { for (i = 0; i < count && !g_testlib_failed; ++i) {
snprintf(g_fixturename, sizeof(g_fixturename), "%s_%s", (snprintf)(g_fixturename, sizeof(g_fixturename), "%s_%s",
fixture_start[i].group, fixture_start[i].name); fixture_start[i].group, fixture_start[i].name);
_piro(PROT_READ | PROT_WRITE); _piro(PROT_READ | PROT_WRITE);
fixture_start[i].fn(); fixture_start[i].fn();
_piro(PROT_READ); _piro(PROT_READ);

View file

@ -233,71 +233,6 @@ COSMOPOLITAN_C_START_
#define EXPECT_LGBL_LT(VAL, GOT) \ #define EXPECT_LGBL_LT(VAL, GOT) \
expectLongDoubleLessThan(VAL, GOT, #VAL " < " #GOT, false) expectLongDoubleLessThan(VAL, GOT, #VAL " < " #GOT, false)
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § testing library » hardware-accelerated memory safety
*/
typedef void (*testfn_t)(void);
struct TestFixture {
const char *group;
const char *name;
testfn_t fn;
};
struct TestAllocation {
void *mapaddr;
size_t mapsize;
void *useraddr;
size_t usersize;
};
struct TestMemoryStack {
size_t i;
size_t n;
struct TestAllocation *p;
};
extern struct TestMemoryStack g_testmem;
void tfree(void *) paramsnonnull();
void *tmalloc(size_t) returnsnonnull returnspointerwithnoaliases nodiscard
attributeallocsize((1)) returnsaligned((1));
void *tmemalign(size_t,
size_t) returnsnonnull returnspointerwithnoaliases nodiscard
attributeallocsize((2)) libcesque attributeallocalign((1));
char *tstrdup(const char *) returnsnonnull returnspointerwithnoaliases nodiscard
returnsaligned((1));
void *tunbing(const char16_t *)
paramsnonnull() returnsnonnull returnspointerwithnoaliases nodiscard
returnsaligned((1));
void *tunbinga(size_t, const char16_t *)
paramsnonnull() returnsnonnull returnspointerwithnoaliases nodiscard
attributeallocalign((1));
void testlib_clearxmmregisters(void);
#define tgc(TMEM) \
({ \
void *Res; \
/* volatile b/c testmem only lifo atm */ \
asm volatile("" ::: "memory"); \
Res = defer((tfree), (TMEM)); \
asm volatile("" ::: "memory"); \
Res; \
})
#define tfree(P) \
do { \
__tfree_check(P); \
(tfree)(P); \
} while (0)
#define __tfree_check(P) \
ASSERT_BETWEEN(g_testmem.p[g_testmem.i - 1].useraddr, \
((char *)g_testmem.p[g_testmem.i - 1].useraddr + \
g_testmem.p[g_testmem.i - 1].usersize), \
P)
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § testing library » implementation details cosmopolitan § testing library » implementation details
*/ */
@ -310,6 +245,14 @@ void testlib_clearxmmregisters(void);
const char *file; \ const char *file; \
int line int line
typedef void (*testfn_t)(void);
struct TestFixture {
const char *group;
const char *name;
testfn_t fn;
};
extern char g_fixturename[256]; extern char g_fixturename[256];
extern bool g_testlib_shoulddebugbreak; /* set by testmain */ extern bool g_testlib_shoulddebugbreak; /* set by testmain */
extern unsigned g_testlib_ran; /* set by wrappers */ extern unsigned g_testlib_ran; /* set by wrappers */
@ -374,6 +317,7 @@ void testlib_formatbinaryasglyphs(const char16_t *, const void *, size_t,
char **, char **); char **, char **);
bool testlib_almostequallongdouble(long double, long double); bool testlib_almostequallongdouble(long double, long double);
void testlib_incrementfailed(void); void testlib_incrementfailed(void);
void testlib_clearxmmregisters(void);
forceinline void testlib_ontest() { forceinline void testlib_ontest() {
YOINK(__testcase_start); YOINK(__testcase_start);

View file

@ -56,7 +56,6 @@ LIBC_TESTLIB_A_SRCS_C = \
libc/testlib/shoulddebugbreak.c \ libc/testlib/shoulddebugbreak.c \
libc/testlib/showerror.c \ libc/testlib/showerror.c \
libc/testlib/showerror_.c \ libc/testlib/showerror_.c \
libc/testlib/testmem.c \
libc/testlib/strequals.c \ libc/testlib/strequals.c \
libc/testlib/startswith.c \ libc/testlib/startswith.c \
libc/testlib/endswith.c \ libc/testlib/endswith.c \

View file

@ -1,179 +0,0 @@
/*-*- 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 2020 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/assert.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/fmt/bing.internal.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/sysconf.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/testlib.h"
/* TODO(jart): DELETE */
struct TestMemoryStack g_testmem;
struct TestMemoryStack g_testmem_trash;
static struct TestAllocation g_testmem_scratch[2][8];
static const char kMemZero[1];
static bool g_atstartofpage;
static struct TestAllocation testmem_push(struct TestMemoryStack *stack,
struct TestAllocation entry) {
if (stack->i == stack->n) {
if (!__grow(&stack->p, &stack->n, sizeof(struct TestAllocation), 0)) {
abort();
}
}
return (stack->p[stack->i++] = entry);
}
static struct TestAllocation testmem_pop(struct TestMemoryStack *stack) {
assert(stack->i > 0);
struct TestAllocation res = stack->p[--stack->i];
return res;
}
static void testmem_destroy(struct TestAllocation alloc) {
if (munmap(alloc.mapaddr, alloc.mapsize) == -1) perror("munmap"), __die();
}
static struct TestAllocation talloc(size_t n) {
struct TestAllocation alloc;
if (n) {
while (g_testmem_trash.i) {
struct TestAllocation trash = testmem_pop(&g_testmem_trash);
if (n <= trash.usersize) {
return trash;
} else {
testmem_destroy(trash);
}
}
alloc.mapsize = ROUNDUP(n + PAGESIZE * 2, FRAMESIZE);
CHECK_NE(MAP_FAILED, (alloc.mapaddr = mapanon(alloc.mapsize)));
CHECK_NE(-1, mprotect(alloc.mapaddr, PAGESIZE, PROT_NONE));
CHECK_NE(-1, mprotect((char *)alloc.mapaddr + alloc.mapsize - PAGESIZE,
PAGESIZE, PROT_NONE));
alloc.useraddr = (char *)alloc.mapaddr + PAGESIZE;
alloc.usersize = alloc.mapsize - PAGESIZE * 2;
CHECK_GE(alloc.usersize, n);
return alloc;
} else {
alloc.mapaddr = (/*unconst*/ void *)kMemZero;
alloc.mapsize = 0;
alloc.useraddr = (/*unconst*/ void *)kMemZero;
alloc.usersize = 0;
return alloc;
}
}
static textstartup void testmem_init(void) {
g_testmem.p = g_testmem_scratch[0];
g_testmem.n = ARRAYLEN(g_testmem_scratch[0]);
g_testmem_trash.p = g_testmem_scratch[1];
g_testmem_trash.n = ARRAYLEN(g_testmem_scratch[1]);
}
const void *const testmem_ctor[] initarray = {testmem_init};
FIXTURE(testmemory, triggerOffByOneArrayErrors) {
/* automate testing buffer overflows *and* underflows */
g_atstartofpage = true;
}
/**
* Allocates memory with properties useful for testing.
*
* This returns a pointer 𝑝 where reading or writing to either 𝑝[-1] or
* 𝑝[𝑛+𝟷] will immediately trigger a segmentation fault; and bytes are
* initialized to 10100101 (A5).
*
* Implementation Details: Accomplishing this entails two things. First,
* we grant each allocation a page granular memory mapping, with access
* to the two adjacent pages disabled. Second, since hardware memory
* protection isn't 1-byte granular, we add a fixture so each test runs
* a second time; the first call we return a pointer where the data is
* placed on the righthand side, and the second call we return the data
* on the lefthand side, thereby allowing both underflow/overflow
* off-by-one out-of-bounds accesses to be detected.
*/
void *tmalloc(size_t n) {
struct TestAllocation alloc = talloc(n);
memset(alloc.useraddr, 0xa5, alloc.usersize);
testmem_push(&g_testmem, alloc);
return (char *)alloc.useraddr + (g_atstartofpage ? 0 : alloc.usersize - n);
}
/**
* Same as tmalloc() but guarantees a specific alignment.
*
* Reading or writing to either 𝑝[-1] or 𝑝[roundup(𝑛+𝟷,𝑎)] will
* immediately trigger a segmentation fault.
*
* @param 𝑎 is alignment in bytes, e.g. 16
* @param 𝑛 is number of bytes
*/
void *tmemalign(size_t a, size_t n) {
/* TODO(jart): ASAN detect 𝑝[𝑛+𝟷] */
return tmalloc(ROUNDUP(n, a));
}
/**
* Same as tunbing() w/ alignment guarantee.
*/
void *tunbinga(size_t a, const char16_t *binglyphs) {
size_t size;
EXPECT_NE(0, (size = strlen16(binglyphs)));
return unbingbuf(tmemalign(a, size), size, binglyphs, -1);
}
/**
* Decodes CP437 glyphs to bounds-checked binary buffer, e.g.
*
* char *mem = tunbing(u" ☺☻♥♦");
* EXPECT_EQ(0, memcmp("\0\1\2\3\4", mem, 5));
* tfree(mem);
*
* @see tunbing(), unbingstr(), unbing()
*/
void *tunbing(const char16_t *binglyphs) {
return tunbinga(1, binglyphs);
}
/**
* Frees memory allocated with tmalloc().
* This needs to be called in LIFO order.
* @param
*/
void(tfree)(void *p) {
struct TestAllocation alloc;
__tfree_check(p);
alloc = testmem_pop(&g_testmem);
if (alloc.mapsize) testmem_push(&g_testmem_trash, alloc);
}
char *tstrdup(const char *s) {
return strcpy(tmalloc(strlen(s) + 1), s);
}

43
libc/x/tunbing.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- 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/fmt/bing.internal.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
/**
* Same as xunbing() w/ alignment guarantee.
*/
void *xunbinga(size_t a, const char16_t *binglyphs) {
size_t size;
size = strlen16(binglyphs);
return unbingbuf(xmemalign(a, size), size, binglyphs, -1);
}
/**
* Decodes CP437 glyphs to bounds-checked binary buffer, e.g.
*
* char *mem = xunbing(u" ☺☻♥♦");
* EXPECT_EQ(0, memcmp("\0\1\2\3\4", mem, 5));
* tfree(mem);
*
* @see xunbing(), unbingstr(), unbing()
*/
void *xunbing(const char16_t *binglyphs) {
return xunbinga(1, binglyphs);
}

View file

@ -46,6 +46,8 @@ char *xstrndup(const char *, size_t) _XPNN _XMAL;
char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL; char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL;
char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL; char *xstrmul(const char *, size_t) paramsnonnull((1)) _XMAL;
char *xinet_ntop(int, const void *) _XPNN _XMAL; char *xinet_ntop(int, const void *) _XPNN _XMAL;
void *xunbinga(size_t, const char16_t *) attributeallocalign((1)) _XMAL _XRET;
void *xunbing(const char16_t *) _XMAL _XRET;
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § eXtended apis » files cosmopolitan § eXtended apis » files

View file

@ -38,7 +38,7 @@ char *xslurp(const char *path, size_t *opt_out_size) {
struct stat st; struct stat st;
res = NULL; res = NULL;
if ((fd = open(path, O_RDONLY)) != -1) { if ((fd = open(path, O_RDONLY)) != -1) {
if (fstat(fd, &st) != -1 && (res = valloc(st.st_size))) { if (fstat(fd, &st) != -1 && (res = valloc(st.st_size + 1))) {
if (st.st_size > 2 * 1024 * 1024) { if (st.st_size > 2 * 1024 * 1024) {
fadvise(fd, 0, st.st_size, MADV_SEQUENTIAL); fadvise(fd, 0, st.st_size, MADV_SEQUENTIAL);
} }

View file

@ -31,7 +31,6 @@ NET_HTTP_A_DIRECTDEPS = \
LIBC_FMT \ LIBC_FMT \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_RUNTIME \ LIBC_RUNTIME \
LIBC_SOCK \ LIBC_SOCK \

View file

@ -17,16 +17,17 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "ape/lib/pc.h" #include "ape/lib/pc.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(smapsort, testEmpty_doesntOverrunBuffer) { TEST(smapsort, testEmpty_doesntOverrunBuffer) {
struct SmapEntry *smap = tmalloc(sizeof(struct SmapEntry)); struct SmapEntry *smap = malloc(sizeof(struct SmapEntry));
memset(smap, 0, sizeof(struct SmapEntry)); memset(smap, 0, sizeof(struct SmapEntry));
smapsort(smap); smapsort(smap);
EXPECT_EQ(0, smap[0].addr); EXPECT_EQ(0, smap[0].addr);
EXPECT_EQ(0, smap[0].size); EXPECT_EQ(0, smap[0].size);
tfree(smap); free(smap);
} }
/* TEST(smapsort, testSorted_doesNothing) { */ /* TEST(smapsort, testSorted_doesNothing) { */

View file

@ -26,6 +26,7 @@ TEST_APE_LIB_DIRECTDEPS = \
APE_LIB \ APE_LIB \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_RUNTIME \ LIBC_RUNTIME \
LIBC_STR \ LIBC_STR \

View file

@ -46,10 +46,6 @@ o/$(MODE)/test/dsp/core/%.com.dbg: \
$(APE) $(APE)
@$(APELINK) @$(APELINK)
# $(TEST_DSP_CORE_OBJS): \
# OVERRIDE_CFLAGS += \
# -fsanitize=address
.PHONY: o/$(MODE)/test/dsp/core .PHONY: o/$(MODE)/test/dsp/core
o/$(MODE)/test/dsp/core: \ o/$(MODE)/test/dsp/core: \
$(TEST_DSP_CORE_BINS) \ $(TEST_DSP_CORE_BINS) \

View file

@ -25,6 +25,7 @@
#include "libc/math.h" #include "libc/math.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/gc.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -40,9 +41,9 @@ TEST(magikarp, testConvolve) {
u"λλ  λλ  λλ  λλ  " u"λλ  λλ  λλ  λλ  "
u"λλ  λλ  λλ  λλ  ", u"λλ  λλ  λλ  λλ  ",
cDecimate2xUint8x8(32 * 3, cDecimate2xUint8x8(32 * 3,
tgc(tunbing(u"λλλλ    λλλλ    λλλλ    λλλλ    " gc(xunbing(u"λλλλ    λλλλ    λλλλ    λλλλ    "
u"λλλλ    λλλλ    λλλλ    λλλλ    " u"λλλλ    λλλλ    λλλλ    λλλλ    "
u"λλλλ    λλλλ    λλλλ    λλλλ    ")), u"λλλλ    λλλλ    λλλλ    λλλλ    ")),
K)); K));
} }
@ -53,9 +54,9 @@ TEST(magikarp, testConvolveMin1) {
u"λλ  λλ  λλ  λλ  " u"λλ  λλ  λλ  λλ  "
u"λλ  λλ  λλ  λλ  ", u"λλ  λλ  λλ  λλ  ",
cDecimate2xUint8x8(32 * 3 - 1, cDecimate2xUint8x8(32 * 3 - 1,
tgc(tunbing(u"λλλλ    λλλλ    λλλλ    λλλλ    " gc(xunbing(u"λλλλ    λλλλ    λλλλ    λλλλ    "
u"λλλλ    λλλλ    λλλλ    λλλλ    " u"λλλλ    λλλλ    λλλλ    λλλλ    "
u"λλλλ    λλλλ    λλλλ    λλλλ   ")), u"λλλλ    λλλλ    λλλλ    λλλλ   ")),
K)); K));
} }
@ -71,14 +72,14 @@ TEST(magikarp, testConvolve2) {
u"┴├┼╟╔╦═╧╤╙╒╫┘█▌▀" u"┴├┼╟╔╦═╧╤╙╒╫┘█▌▀"
u"ßπστΘδφ∩±≤⌡≈∙√²λ", u"ßπστΘδφ∩±≤⌡≈∙√²λ",
cDecimate2xUint8x8(256, cDecimate2xUint8x8(256,
tgc(tunbing(u" ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼" gc(xunbing(u" ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼"
u" !“#$%&()*+,-./0123456789:;<=>⁇" u" !“#$%&()*+,-./0123456789:;<=>⁇"
u"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" u"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
u"`abcdefghijklmnopqrstuvwxyz{|}~⌂" u"`abcdefghijklmnopqrstuvwxyz{|}~⌂"
u"ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥€ƒ" u"ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥€ƒ"
u"áíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐" u"áíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
u"└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀" u"└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
u"αßΓπΣσμτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■λ")), u"αßΓπΣσμτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙×√ⁿ²■λ")),
K)); K));
} }
@ -147,8 +148,8 @@ noopopppqqqqqqqqqqqqqqqppoooonn",
BENCH(magikarp, bench) { /* 30ms */ BENCH(magikarp, bench) { /* 30ms */
unsigned char kMagkern[16] = {4, 12, 12, 4}; unsigned char kMagkern[16] = {4, 12, 12, 4};
signed char kMagikarp[16] = {-1, -3, 3, 17, 17, 3, -3, -1}; signed char kMagikarp[16] = {-1, -3, 3, 17, 17, 3, -3, -1};
unsigned char(*Me)[HDY][HDX] = tgc(tmalloc((HDX + 1) * (HDY + 1))); unsigned char(*Me)[HDY][HDX] = gc(malloc((HDX + 1) * (HDY + 1)));
unsigned char(*Mo)[HDY][HDX] = tgc(tmalloc((HDX + 1) * (HDY + 1))); unsigned char(*Mo)[HDY][HDX] = gc(malloc((HDX + 1) * (HDY + 1)));
if (X86_HAVE(AVX)) { if (X86_HAVE(AVX)) {
EZBENCH2("Decimate2xUint8x8", donothing, EZBENCH2("Decimate2xUint8x8", donothing,
cDecimate2xUint8x8((HDX * HDY - 16 - 8) / 2 / 8 * 8, (void *)Me, cDecimate2xUint8x8((HDX * HDY - 16 - 8) / 2 / 8 * 8, (void *)Me,

View file

@ -21,7 +21,9 @@
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/limits.h" #include "libc/limits.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/x86feature.h" #include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/gc.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/ezbench.h" #include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -54,12 +56,12 @@ const short kW3[64] forcealign(32) = {
1730, 2041, 7707, 5096, 6876, 1324, 1242, 7, 0x7fff, 1730, 2041, 7707, 5096, 6876, 1324, 1242, 7, 0x7fff,
}; };
#define TestIt(impl, index, value, n, array) \ #define TestIt(impl, index, value, n, array) \
({ \ ({ \
short *a = memcpy(tgc(tmemalign(32, n * 2)), array, n * 2); \ short *a = memcpy(gc(memalign(32, n * 2)), array, n * 2); \
unsigned res = impl(array, n); \ unsigned res = impl(array, n); \
ASSERT_EQ(index, res); \ ASSERT_EQ(index, res); \
ASSERT_EQ(value, a[res]); \ ASSERT_EQ(value, a[res]); \
}) })
TEST(windex, testRealWorldPicks) { TEST(windex, testRealWorldPicks) {

View file

@ -18,12 +18,13 @@
*/ */
#include "libc/alg/alg.h" #include "libc/alg/alg.h"
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(comparator, testByteCompare) { TEST(comparator, testByteCompare) {
char *b1 = tmalloc(1); char *b1 = malloc(1);
char *b2 = tmalloc(1); char *b2 = malloc(1);
/* sign doesn't matter */ /* sign doesn't matter */
EXPECT_EQ(cmpsb(memcpy(b1, "a", 1), memcpy(b2, "a", 1)), 0); EXPECT_EQ(cmpsb(memcpy(b1, "a", 1), memcpy(b2, "a", 1)), 0);
EXPECT_LT(cmpsb(memcpy(b1, "a", 1), memcpy(b2, "z", 1)), 0); EXPECT_LT(cmpsb(memcpy(b1, "a", 1), memcpy(b2, "z", 1)), 0);
@ -41,26 +42,26 @@ TEST(comparator, testByteCompare) {
/* two's complement bane */ /* two's complement bane */
EXPECT_GT(cmpsb(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0); EXPECT_GT(cmpsb(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0);
EXPECT_LT(cmpub(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0); EXPECT_LT(cmpub(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0);
tfree(b2); free(b2);
tfree(b1); free(b1);
} }
TEST(comparator, testWordCompare) { TEST(comparator, testWordCompare) {
char *b1 = tmalloc(2); char *b1 = malloc(2);
char *b2 = tmalloc(2); char *b2 = malloc(2);
EXPECT_EQ(cmpsw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x80", 2)), 0); EXPECT_EQ(cmpsw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x80", 2)), 0);
EXPECT_GT(cmpsw(memcpy(b1, "\x00\x7f", 2), memcpy(b2, "\x00\x80", 2)), 0); EXPECT_GT(cmpsw(memcpy(b1, "\x00\x7f", 2), memcpy(b2, "\x00\x80", 2)), 0);
EXPECT_LT(cmpsw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x7f", 2)), 0); EXPECT_LT(cmpsw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x7f", 2)), 0);
EXPECT_EQ(cmpuw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x80", 2)), 0); EXPECT_EQ(cmpuw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x80", 2)), 0);
EXPECT_LT(cmpuw(memcpy(b1, "\x00\x7f", 2), memcpy(b2, "\x00\x80", 2)), 0); EXPECT_LT(cmpuw(memcpy(b1, "\x00\x7f", 2), memcpy(b2, "\x00\x80", 2)), 0);
EXPECT_GT(cmpuw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x7f", 2)), 0); EXPECT_GT(cmpuw(memcpy(b1, "\x00\x80", 2), memcpy(b2, "\x00\x7f", 2)), 0);
tfree(b2); free(b2);
tfree(b1); free(b1);
} }
TEST(comparator, testDoublewordCompare) { TEST(comparator, testDoublewordCompare) {
char *b1 = tmalloc(4); char *b1 = malloc(4);
char *b2 = tmalloc(4); char *b2 = malloc(4);
EXPECT_EQ(cmpsl(memcpy(b1, "\x00\x00\x00\x80", 4), EXPECT_EQ(cmpsl(memcpy(b1, "\x00\x00\x00\x80", 4),
memcpy(b2, "\x00\x00\x00\x80", 4)), memcpy(b2, "\x00\x00\x00\x80", 4)),
0); 0);
@ -79,13 +80,13 @@ TEST(comparator, testDoublewordCompare) {
EXPECT_GT(cmpul(memcpy(b1, "\x00\x00\x00\x80", 4), EXPECT_GT(cmpul(memcpy(b1, "\x00\x00\x00\x80", 4),
memcpy(b2, "\x00\x00\x00\x7f", 4)), memcpy(b2, "\x00\x00\x00\x7f", 4)),
0); 0);
tfree(b2); free(b2);
tfree(b1); free(b1);
} }
TEST(comparator, testQuadwordCompare) { TEST(comparator, testQuadwordCompare) {
char *b1 = tmalloc(8); char *b1 = malloc(8);
char *b2 = tmalloc(8); char *b2 = malloc(8);
EXPECT_EQ(cmpsq(memcpy(b1, "\x00\x00\x00\x00\x00\x00\x00\x80", 8), EXPECT_EQ(cmpsq(memcpy(b1, "\x00\x00\x00\x00\x00\x00\x00\x80", 8),
memcpy(b2, "\x00\x00\x00\x00\x00\x00\x00\x80", 8)), memcpy(b2, "\x00\x00\x00\x00\x00\x00\x00\x80", 8)),
0); 0);
@ -104,6 +105,6 @@ TEST(comparator, testQuadwordCompare) {
EXPECT_GT(cmpuq(memcpy(b1, "\x00\x00\x00\x00\x00\x00\x00\x80", 8), EXPECT_GT(cmpuq(memcpy(b1, "\x00\x00\x00\x00\x00\x00\x00\x80", 8),
memcpy(b2, "\x00\x00\x00\x00\x00\x00\x00\x7f", 8)), memcpy(b2, "\x00\x00\x00\x00\x00\x00\x00\x7f", 8)),
0); 0);
tfree(b2); free(b2);
tfree(b1); free(b1);
} }

View file

@ -38,9 +38,9 @@ int32_t *a, *b, *c;
TEST(djbsort, test4) { TEST(djbsort, test4) {
static const int kA[] = {4, 3, 2, 1}; static const int kA[] = {4, 3, 2, 1};
n = ARRAYLEN(kA); n = ARRAYLEN(kA);
a = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); a = memcpy(gc(malloc(n * 4)), kA, n * 4);
b = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); b = memcpy(gc(malloc(n * 4)), kA, n * 4);
c = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); c = memcpy(gc(malloc(n * 4)), kA, n * 4);
insertionsort(a, n); insertionsort(a, n);
djbsort$avx2(b, n); djbsort$avx2(b, n);
djbsort(c, n); djbsort(c, n);
@ -65,9 +65,9 @@ TEST(djbsort, test64) {
-1323943608, -1219421355, -582289873, 1062699814, -1323943608, -1219421355, -582289873, 1062699814,
}; };
n = ARRAYLEN(kA); n = ARRAYLEN(kA);
a = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); a = memcpy(gc(malloc(n * 4)), kA, n * 4);
b = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); b = memcpy(gc(malloc(n * 4)), kA, n * 4);
c = memcpy(tgc(tmalloc(n * 4)), kA, n * 4); c = memcpy(gc(malloc(n * 4)), kA, n * 4);
insertionsort(a, n); insertionsort(a, n);
djbsort(c, n); djbsort(c, n);
ASSERT_EQ(0, memcmp(a, c, n * 4)); ASSERT_EQ(0, memcmp(a, c, n * 4));

View file

@ -19,6 +19,7 @@
#include "libc/alg/alg.h" #include "libc/alg/alg.h"
#include "libc/bits/bits.h" #include "libc/bits/bits.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -29,9 +30,9 @@ TEST(qsort, test) {
const int32_t B[][2] = {{-31, 'd'}, {0, 'e'}, {1, 'j'}, {2, 'c'}, const int32_t B[][2] = {{-31, 'd'}, {0, 'e'}, {1, 'j'}, {2, 'c'},
{2, 'g'}, {4, 'a'}, {65, 'b'}, {83, 'h'}, {2, 'g'}, {4, 'a'}, {65, 'b'}, {83, 'h'},
{99, 'f'}, {782, 'i'}}; {99, 'f'}, {782, 'i'}};
int32_t(*M)[2] = tmalloc(sizeof(A)); int32_t(*M)[2] = malloc(sizeof(A));
memcpy(M, B, sizeof(A)); memcpy(M, B, sizeof(A));
qsort(M, ARRAYLEN(A), sizeof(*M), cmpsl); qsort(M, ARRAYLEN(A), sizeof(*M), cmpsl);
EXPECT_EQ(0, memcmp(M, B, sizeof(B))); EXPECT_EQ(0, memcmp(M, B, sizeof(B)));
tfree(M); free(M);
} }

View file

@ -18,6 +18,7 @@
*/ */
#include "libc/dns/dns.h" #include "libc/dns/dns.h"
#include "libc/dns/dnsheader.h" #include "libc/dns/dnsheader.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -28,20 +29,20 @@ TEST(serializednsheader, test) {
header.id = 255; header.id = 255;
header.bf1 = true; header.bf1 = true;
header.qdcount = 1; header.qdcount = 1;
uint8_t *buf = tmalloc(12); uint8_t *buf = malloc(12);
ASSERT_EQ(12, serializednsheader(buf, 12, header)); ASSERT_EQ(12, serializednsheader(buf, 12, header));
EXPECT_BINEQ(u" λ☺  ☺      ", buf); EXPECT_BINEQ(u" λ☺  ☺      ", buf);
tfree(buf); free(buf);
} }
TEST(serializednsheader, fuzzSymmetry) { TEST(serializednsheader, fuzzSymmetry) {
uint8_t *buf = tmalloc(12); uint8_t *buf = malloc(12);
struct DnsHeader *in = tmalloc(sizeof(struct DnsHeader)); struct DnsHeader *in = malloc(sizeof(struct DnsHeader));
struct DnsHeader *out = tmalloc(sizeof(struct DnsHeader)); struct DnsHeader *out = malloc(sizeof(struct DnsHeader));
ASSERT_EQ(12, serializednsheader(buf, 12, *in)); ASSERT_EQ(12, serializednsheader(buf, 12, *in));
ASSERT_EQ(12, deserializednsheader(out, buf, 12)); ASSERT_EQ(12, deserializednsheader(out, buf, 12));
ASSERT_EQ(0, memcmp(in, out, 12)); ASSERT_EQ(0, memcmp(in, out, 12));
tfree(out); free(out);
tfree(in); free(in);
tfree(buf); free(buf);
} }

View file

@ -17,21 +17,22 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/dns/dns.h" #include "libc/dns/dns.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(dnsnamecmp, testEmpty) { TEST(dnsnamecmp, testEmpty) {
char *A = strcpy(tmalloc(1), ""); char *A = strcpy(malloc(1), "");
char *B = strcpy(tmalloc(1), ""); char *B = strcpy(malloc(1), "");
EXPECT_EQ(dnsnamecmp(A, B), 0); EXPECT_EQ(dnsnamecmp(A, B), 0);
EXPECT_EQ(dnsnamecmp(A, A), 0); EXPECT_EQ(dnsnamecmp(A, A), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testDotless_caseInsensitiveBehavior) { TEST(dnsnamecmp, testDotless_caseInsensitiveBehavior) {
char *A = tmalloc(2); char *A = malloc(2);
char *B = tmalloc(2); char *B = malloc(2);
EXPECT_EQ(dnsnamecmp(strcpy(A, "a"), strcpy(B, "a")), 0); EXPECT_EQ(dnsnamecmp(strcpy(A, "a"), strcpy(B, "a")), 0);
EXPECT_EQ(dnsnamecmp(A, A), 0); EXPECT_EQ(dnsnamecmp(A, A), 0);
EXPECT_EQ(dnsnamecmp(strcpy(A, "a"), strcpy(B, "A")), 0); EXPECT_EQ(dnsnamecmp(strcpy(A, "a"), strcpy(B, "A")), 0);
@ -39,13 +40,13 @@ TEST(dnsnamecmp, testDotless_caseInsensitiveBehavior) {
EXPECT_LT(dnsnamecmp(strcpy(A, "a"), strcpy(B, "b")), 0); EXPECT_LT(dnsnamecmp(strcpy(A, "a"), strcpy(B, "b")), 0);
EXPECT_LT(dnsnamecmp(strcpy(A, "a"), strcpy(B, "B")), 0); EXPECT_LT(dnsnamecmp(strcpy(A, "a"), strcpy(B, "B")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "d"), strcpy(B, "a")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "d"), strcpy(B, "a")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testMultiLabel_lexiReverse) { TEST(dnsnamecmp, testMultiLabel_lexiReverse) {
char *A = tmalloc(16); char *A = malloc(16);
char *B = tmalloc(16); char *B = malloc(16);
EXPECT_EQ(dnsnamecmp(strcpy(A, "a.example"), strcpy(B, "a.example")), 0); EXPECT_EQ(dnsnamecmp(strcpy(A, "a.example"), strcpy(B, "a.example")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "b.example"), strcpy(B, "a.example")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "b.example"), strcpy(B, "a.example")), 0);
EXPECT_LT(dnsnamecmp(strcpy(A, "b.example"), strcpy(B, "a.examplz")), 0); EXPECT_LT(dnsnamecmp(strcpy(A, "b.example"), strcpy(B, "a.examplz")), 0);
@ -53,48 +54,48 @@ TEST(dnsnamecmp, testMultiLabel_lexiReverse) {
EXPECT_EQ(dnsnamecmp(strcpy(A, "c.a.example"), strcpy(B, "c.a.example")), 0); EXPECT_EQ(dnsnamecmp(strcpy(A, "c.a.example"), strcpy(B, "c.a.example")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "d.a.example"), strcpy(B, "c.a.example")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "d.a.example"), strcpy(B, "c.a.example")), 0);
EXPECT_LT(dnsnamecmp(strcpy(A, "cat.example"), strcpy(B, "lol.example")), 0); EXPECT_LT(dnsnamecmp(strcpy(A, "cat.example"), strcpy(B, "lol.example")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testTldDotQualifier_canBeEqualToDottedNames) { TEST(dnsnamecmp, testTldDotQualifier_canBeEqualToDottedNames) {
char *A = tmalloc(16); char *A = malloc(16);
char *B = tmalloc(16); char *B = malloc(16);
EXPECT_EQ(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "aaa.example")), 0); EXPECT_EQ(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "aaa.example")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testFullyQualified_alwaysComesFirst) { TEST(dnsnamecmp, testFullyQualified_alwaysComesFirst) {
char *A = tmalloc(16); char *A = malloc(16);
char *B = tmalloc(16); char *B = malloc(16);
EXPECT_LT(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "zzz")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "zzz")), 0);
EXPECT_LT(dnsnamecmp(strcpy(B, "zzz.example."), strcpy(A, "aaa")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "zzz.example."), strcpy(A, "aaa")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "zzz"), strcpy(B, "aaa.example.")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "zzz"), strcpy(B, "aaa.example.")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "aaa"), strcpy(B, "zzz.example.")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "aaa"), strcpy(B, "zzz.example.")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testLikelySld_alwaysComesBeforeLocalName) { TEST(dnsnamecmp, testLikelySld_alwaysComesBeforeLocalName) {
char *A = tmalloc(16); char *A = malloc(16);
char *B = tmalloc(16); char *B = malloc(16);
EXPECT_LT(dnsnamecmp(strcpy(B, "z.e"), strcpy(A, "a")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "z.e"), strcpy(A, "a")), 0);
EXPECT_LT(dnsnamecmp(strcpy(B, "aaa.example"), strcpy(A, "zzz")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "aaa.example"), strcpy(A, "zzz")), 0);
EXPECT_LT(dnsnamecmp(strcpy(B, "zzz.example"), strcpy(A, "aaa")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "zzz.example"), strcpy(A, "aaa")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "zzz"), strcpy(B, "aaa.example")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "zzz"), strcpy(B, "aaa.example")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "aaa"), strcpy(B, "zzz.example")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "aaa"), strcpy(B, "zzz.example")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }
TEST(dnsnamecmp, testLikelySubdomain_alwaysComesAfterSld) { TEST(dnsnamecmp, testLikelySubdomain_alwaysComesAfterSld) {
char *A = tmalloc(16); char *A = malloc(16);
char *B = tmalloc(16); char *B = malloc(16);
EXPECT_LT(dnsnamecmp(strcpy(B, "a.e"), strcpy(A, "z.a.e")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "a.e"), strcpy(A, "z.a.e")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "z.a.e"), strcpy(B, "a.e")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "z.a.e"), strcpy(B, "a.e")), 0);
EXPECT_LT(dnsnamecmp(strcpy(B, "b.e"), strcpy(A, "a.b.e")), 0); EXPECT_LT(dnsnamecmp(strcpy(B, "b.e"), strcpy(A, "a.b.e")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "a.b.e"), strcpy(B, "b.e")), 0); EXPECT_GT(dnsnamecmp(strcpy(A, "a.b.e"), strcpy(B, "b.e")), 0);
tfree(B); free(B);
tfree(A); free(A);
} }

View file

@ -19,11 +19,12 @@
#include "libc/dns/dns.h" #include "libc/dns/dns.h"
#include "libc/dns/dnsquestion.h" #include "libc/dns/dnsquestion.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/mem/mem.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(serializednsquestion, test) { TEST(serializednsquestion, test) {
uint8_t *buf = tmalloc(1 + 3 + 1 + 3 + 1 + 4); uint8_t *buf = malloc(1 + 3 + 1 + 3 + 1 + 4);
char *name = tstrdup("foo.bar"); char *name = strdup("foo.bar");
struct DnsQuestion dq; struct DnsQuestion dq;
dq.qname = name; dq.qname = name;
dq.qtype = 0x0201; dq.qtype = 0x0201;
@ -31,19 +32,19 @@ TEST(serializednsquestion, test) {
EXPECT_EQ(1 + 3 + 1 + 3 + 1 + 4, EXPECT_EQ(1 + 3 + 1 + 3 + 1 + 4,
serializednsquestion(buf, 1 + 3 + 1 + 3 + 1 + 4, dq)); serializednsquestion(buf, 1 + 3 + 1 + 3 + 1 + 4, dq));
EXPECT_BINEQ(u"♥foo♥bar ☻☺☺☻", buf); EXPECT_BINEQ(u"♥foo♥bar ☻☺☺☻", buf);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(serializednsquestion, testNoSpace) { TEST(serializednsquestion, testNoSpace) {
uint8_t *buf = tmalloc(1 + 3 + 1 + 3 + 1 + 3); uint8_t *buf = malloc(1 + 3 + 1 + 3 + 1 + 3);
char *name = tstrdup("foo.bar"); char *name = strdup("foo.bar");
struct DnsQuestion dq; struct DnsQuestion dq;
dq.qname = name; dq.qname = name;
dq.qtype = 0x0201; dq.qtype = 0x0201;
dq.qclass = 0x0102; dq.qclass = 0x0102;
EXPECT_EQ(-1, serializednsquestion(buf, 1 + 3 + 1 + 3 + 1 + 3, dq)); EXPECT_EQ(-1, serializednsquestion(buf, 1 + 3 + 1 + 3 + 1 + 3, dq));
EXPECT_EQ(ENOSPC, errno); EXPECT_EQ(ENOSPC, errno);
tfree(name); free(name);
tfree(buf); free(buf);
} }

View file

@ -18,61 +18,62 @@
*/ */
#include "libc/dns/dns.h" #include "libc/dns/dns.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
TEST(pascalifydnsname, testEmpty) { TEST(pascalifydnsname, testEmpty) {
uint8_t *buf = tmalloc(1); uint8_t *buf = malloc(1);
char *name = tstrdup(""); char *name = strdup("");
EXPECT_EQ(0, pascalifydnsname(buf, 1, name)); EXPECT_EQ(0, pascalifydnsname(buf, 1, name));
EXPECT_BINEQ(u" ", buf); EXPECT_BINEQ(u" ", buf);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(pascalifydnsname, testOneLabel) { TEST(pascalifydnsname, testOneLabel) {
uint8_t *buf = tmalloc(1 + 3 + 1); uint8_t *buf = malloc(1 + 3 + 1);
char *name = tstrdup("foo"); char *name = strdup("foo");
EXPECT_EQ(1 + 3, pascalifydnsname(buf, 1 + 3 + 1, name)); EXPECT_EQ(1 + 3, pascalifydnsname(buf, 1 + 3 + 1, name));
EXPECT_BINEQ(u"♥foo ", buf); EXPECT_BINEQ(u"♥foo ", buf);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(pascalifydnsname, testTwoLabels) { TEST(pascalifydnsname, testTwoLabels) {
uint8_t *buf = tmalloc(1 + 3 + 1 + 3 + 1); uint8_t *buf = malloc(1 + 3 + 1 + 3 + 1);
char *name = tstrdup("foo.bar"); char *name = strdup("foo.bar");
EXPECT_EQ(1 + 3 + 1 + 3, pascalifydnsname(buf, 1 + 3 + 1 + 3 + 1, name)); EXPECT_EQ(1 + 3 + 1 + 3, pascalifydnsname(buf, 1 + 3 + 1 + 3 + 1, name));
EXPECT_BINEQ(u"♥foo♥bar ", buf); EXPECT_BINEQ(u"♥foo♥bar ", buf);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(pascalifydnsname, testFqdnDot_isntIncluded) { TEST(pascalifydnsname, testFqdnDot_isntIncluded) {
uint8_t *buf = tmalloc(1 + 3 + 1 + 3 + 1); uint8_t *buf = malloc(1 + 3 + 1 + 3 + 1);
char *name = tstrdup("foo.bar."); char *name = strdup("foo.bar.");
EXPECT_EQ(1 + 3 + 1 + 3, pascalifydnsname(buf, 1 + 3 + 1 + 3 + 1, name)); EXPECT_EQ(1 + 3 + 1 + 3, pascalifydnsname(buf, 1 + 3 + 1 + 3 + 1, name));
EXPECT_BINEQ(u"♥foo♥bar ", buf); EXPECT_BINEQ(u"♥foo♥bar ", buf);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(pascalifydnsname, testTooLong) { TEST(pascalifydnsname, testTooLong) {
uint8_t *buf = tmalloc(1); uint8_t *buf = malloc(1);
char *name = tmalloc(1000); char *name = malloc(1000);
memset(name, '.', 999); memset(name, '.', 999);
name[999] = '\0'; name[999] = '\0';
EXPECT_EQ(-1, pascalifydnsname(buf, 1, name)); EXPECT_EQ(-1, pascalifydnsname(buf, 1, name));
EXPECT_EQ(ENAMETOOLONG, errno); EXPECT_EQ(ENAMETOOLONG, errno);
tfree(name); free(name);
tfree(buf); free(buf);
} }
TEST(pascalifydnsname, testNoSpace) { TEST(pascalifydnsname, testNoSpace) {
uint8_t *buf = tmalloc(1); uint8_t *buf = malloc(1);
char *name = tstrdup("foo"); char *name = strdup("foo");
EXPECT_EQ(-1, pascalifydnsname(buf, 1, name)); EXPECT_EQ(-1, pascalifydnsname(buf, 1, name));
EXPECT_EQ(ENOSPC, errno); EXPECT_EQ(ENOSPC, errno);
tfree(name); free(name);
tfree(buf); free(buf);
} }

View file

@ -626,17 +626,17 @@ TEST(xasprintf, twosBane) {
TEST(snprintf, testFixedWidthString_wontOverrunInput) { TEST(snprintf, testFixedWidthString_wontOverrunInput) {
const int N = 2; const int N = 2;
char *buf = tmalloc(N + 1); char *buf = malloc(N + 1);
char *inp = memcpy(tmalloc(N), "hi", N); char *inp = memcpy(malloc(N), "hi", N);
EXPECT_EQ(2, snprintf(buf, N + 1, "%.*s", N, inp)); EXPECT_EQ(2, snprintf(buf, N + 1, "%.*s", N, inp));
EXPECT_BINEQ(u"hi ", buf); EXPECT_BINEQ(u"hi ", buf);
tfree(inp); free(inp);
tfree(buf); free(buf);
} }
TEST(snprintf, testFixedWidthStringIsNull_wontOverrunBuffer) { TEST(snprintf, testFixedWidthStringIsNull_wontOverrunBuffer) {
int N = 3; int N = 3;
char *buf = tmalloc(N + 1); char *buf = malloc(N + 1);
EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL))); EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL)));
EXPECT_BINEQ(u"(nu ", buf); EXPECT_BINEQ(u"(nu ", buf);
EXPECT_EQ(6, snprintf(buf, N + 1, "%#.*s", pushpop(N), pushpop(NULL))); EXPECT_EQ(6, snprintf(buf, N + 1, "%#.*s", pushpop(N), pushpop(NULL)));
@ -645,12 +645,12 @@ TEST(snprintf, testFixedWidthStringIsNull_wontOverrunBuffer) {
EXPECT_BINEQ(u"NUL ", buf); EXPECT_BINEQ(u"NUL ", buf);
EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL))); EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL)));
EXPECT_BINEQ(u"NUL ", buf); EXPECT_BINEQ(u"NUL ", buf);
tfree(buf); free(buf);
} }
TEST(snprintf, testFixedWidthStringIsNull_wontLeakMemory) { TEST(snprintf, testFixedWidthStringIsNull_wontLeakMemory) {
int N = 16; int N = 16;
char *buf = tmalloc(N + 1); char *buf = malloc(N + 1);
memset(buf, 0, N + 1); memset(buf, 0, N + 1);
EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL))); EXPECT_EQ(6, snprintf(buf, N + 1, "%.*s", pushpop(N), pushpop(NULL)));
EXPECT_BINEQ(u"(null)           ", buf); EXPECT_BINEQ(u"(null)           ", buf);
@ -663,7 +663,7 @@ TEST(snprintf, testFixedWidthStringIsNull_wontLeakMemory) {
memset(buf, 0, N + 1); memset(buf, 0, N + 1);
EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL))); EXPECT_EQ(4, snprintf(buf, N + 1, "%`#.*s", pushpop(N), pushpop(NULL)));
EXPECT_BINEQ(u"NULL             ", buf); EXPECT_BINEQ(u"NULL             ", buf);
tfree(buf); free(buf);
} }
TEST(snprintf, twosBaneWithTypePromotion) { TEST(snprintf, twosBaneWithTypePromotion) {

View file

@ -22,7 +22,6 @@ TEST_LIBC_FMT_DIRECTDEPS = \
LIBC_FMT \ LIBC_FMT \
LIBC_INTRIN \ LIBC_INTRIN \
LIBC_LOG \ LIBC_LOG \
LIBC_LOG_ASAN \
LIBC_MEM \ LIBC_MEM \
LIBC_NEXGEN32E \ LIBC_NEXGEN32E \
LIBC_RUNTIME \ LIBC_RUNTIME \

View file

@ -18,6 +18,7 @@
*/ */
#include "dsp/core/q.h" #include "dsp/core/q.h"
#include "libc/intrin/pmulhrsw.h" #include "libc/intrin/pmulhrsw.h"
#include "libc/log/check.h"
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
@ -108,6 +109,15 @@ TEST(pmulhrsw, testFakeFloat) {
} /* clang-format on */; } /* clang-format on */;
FOR88(QD[y][x] = F2Q(15, D[y][x])); FOR88(QD[y][x] = F2Q(15, D[y][x]));
FOR88(QM[y][x] = F2Q(15, M[y][x])); FOR88(QM[y][x] = F2Q(15, M[y][x]));
/* for (y = 0; y < 8; ++y) { */
/* for (x = 0; x < 8; ++x) { */
/* CHECK_NE(8, x); */
/* CHECK_NE(8, y); */
/* QM[y][x] = F2Q(15, M[y][x]); */
/* CHECK_NE(8, x); */
/* CHECK_NE(8, y); */
/* } */
/* } */
FOR8(pmulhrsw(QQ[y], QD[y], QM[y])); FOR8(pmulhrsw(QQ[y], QD[y], QM[y]));
FOR88(Q[y][x] = Q2F(15, QQ[y][x])); FOR88(Q[y][x] = Q2F(15, QQ[y][x]));
FOR88(R[y][x] = D[y][x] * M[y][x]); FOR88(R[y][x] = D[y][x] * M[y][x]);

View file

@ -52,10 +52,6 @@ o/$(MODE)/test/libc/intrin/%.com.dbg: \
$(APE) $(APE)
@$(APELINK) @$(APELINK)
# $(TEST_LIBC_INTRIN_OBJS): \
# OVERRIDE_CFLAGS += \
# -fsanitize=address
.PHONY: o/$(MODE)/test/libc/intrin .PHONY: o/$(MODE)/test/libc/intrin
o/$(MODE)/test/libc/intrin: \ o/$(MODE)/test/libc/intrin: \
$(TEST_LIBC_INTRIN_BINS) \ $(TEST_LIBC_INTRIN_BINS) \

View file

@ -19,6 +19,7 @@
#include "libc/bits/safemacros.h" #include "libc/bits/safemacros.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/kompressor.h" #include "libc/nexgen32e/kompressor.h"
#include "libc/nexgen32e/lz4.h" #include "libc/nexgen32e/lz4.h"
#include "libc/runtime/ezmap.internal.h" #include "libc/runtime/ezmap.internal.h"
@ -30,13 +31,13 @@ TEST(lz4, decompress_emptyStringWithoutChecksum) {
/* lz4 -9 --content-size --no-frame-crc /tmp/empty - | hexdump -C */ /* lz4 -9 --content-size --no-frame-crc /tmp/empty - | hexdump -C */
static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x60, 0x40, static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x60, 0x40,
0x82, 0x00, 0x00, 0x00, 0x00}; 0x82, 0x00, 0x00, 0x00, 0x00};
char *src = memcpy(tmalloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data)); char *src = memcpy(malloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = tmalloc(1); char *dst = malloc(1);
*dst = 'z'; *dst = 'z';
ASSERT_EQ(dst, lz4decode(dst, src)); ASSERT_EQ(dst, lz4decode(dst, src));
ASSERT_EQ('z', *dst); ASSERT_EQ('z', *dst);
tfree(dst); free(dst);
tfree(src); free(src);
} }
TEST(lz4, decompress_oneLetterWithoutChecksum) { TEST(lz4, decompress_oneLetterWithoutChecksum) {
@ -45,12 +46,12 @@ TEST(lz4, decompress_oneLetterWithoutChecksum) {
static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x01, 0x00, static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01,
0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x00}; 0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x00};
char *src = memcpy(tmalloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data)); char *src = memcpy(malloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = tmalloc(1); char *dst = malloc(1);
ASSERT_EQ(dst + 1, lz4decode(dst, src)); ASSERT_EQ(dst + 1, lz4decode(dst, src));
ASSERT_EQ('a', *dst); ASSERT_EQ('a', *dst);
tfree(dst); free(dst);
tfree(src); free(src);
} }
TEST(lz4, decompress_runLengthDecode) { TEST(lz4, decompress_runLengthDecode) {
@ -60,13 +61,13 @@ TEST(lz4, decompress_runLengthDecode) {
0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x61, 0x01, 0x00, 0x07, 0x00, 0x00, 0x30, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x61, 0x01, 0x00, 0x07,
0x50, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00, 0x00, 0x00, 0x00}; 0x50, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00, 0x00, 0x00, 0x00};
char *src = memcpy(tmalloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data)); char *src = memcpy(malloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
const char *want = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; const char *want = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
char *dst = tmalloc(strlen(want)); char *dst = malloc(strlen(want));
ASSERT_EQ(dst + strlen(want), lz4decode(dst, src)); ASSERT_EQ(dst + strlen(want), lz4decode(dst, src));
ASSERT_STREQN(want, dst, strlen(want)); ASSERT_STREQN(want, dst, strlen(want));
tfree(dst); free(dst);
tfree(src); free(src);
} }
TEST(lz4, zoneFileGmt) { TEST(lz4, zoneFileGmt) {

Some files were not shown because too many files have changed in this diff Show more