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_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_DIRECTDEPS = LIBC_STR LIBC_NEXGEN32E LIBC_STUBS
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)

View file

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

View file

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

View file

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

View file

@ -2,16 +2,16 @@
#-*-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─────────────┘
if [ -x "o/$MODE/tool/build/mkdeps.com" ]; then
set -- "o/$MODE/tool/build/mkdeps.com" "$@"
else
#if [ -x "o/$MODE/tool/build/mkdeps.com" ]; then
# set -- "o/$MODE/tool/build/mkdeps.com" "$@"
#else
if [ ! -x o/build/bootstrap/mkdeps.com ]; then
mkdir -p o/build/bootstrap &&
cp -a build/bootstrap/mkdeps.com \
o/build/bootstrap/mkdeps.com || exit
fi
set -- o/build/bootstrap/mkdeps.com "$@"
fi
#fi
if [ "$SILENT" = "0" ]; then
printf "%s\n" "$*" >&2

View file

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

View file

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

View file

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

View file

@ -32,7 +32,7 @@ windex: .quad 0
ezlea windex$sse4,dx
testb X86_HAVE(AVX2)+kCpuids(%rip)
cmovz %rdx,%rax
#endif /* AVX */
#endif /* AVX2 */
#if !X86_NEED(SSE4_2)
ezlea windex$k8,dx
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"
"mov\t$0xffffffffffffffff,%rax\n\t"
"movd\t%rax,%xmm15\n\t"
"fldpi\n\t");
res = *(int *)(intptr_t)boo / boo;
"fldpi\n\t"
"xor\t%eax,%eax\n\t"
"div\t%eax\n\t");
return res;
}

View file

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

View file

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

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
/**
@ -35,5 +36,5 @@
* @asyncsignalsafe
*/
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
* @flags can have O_CLOEXEC
* @see dup(), dup2()
* @syscall
*/
int dup3(int oldfd, int newfd, int flags) {
if (oldfd == newfd) return einval();
if (!IsWindows()) {
return dup3$sysv(oldfd, newfd, flags);
} 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
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.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.

View file

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

View file

@ -53,7 +53,7 @@ _start: test %rdi,%rdi
repnz scasq
mov %rdi,%rcx # auxv
mov %ebx,%edi
call _executive
call cosmo
9: ud2
.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/str/str.h"
const char *geterrname(int code) {
extern const char kErrnoNames[];
STATIC_YOINK("E2BIG");
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;
size_t i, n;
e = &E2BIG;

View file

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

View file

@ -597,6 +597,13 @@ typedef uint64_t uintmax_t;
#define nocallersavedregisters "need modern compiler"
#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
#define unreachable __builtin_unreachable()
#endif

View file

@ -57,7 +57,7 @@
#endif
#define BIGPAGESIZE 0x200000
#define STACKSIZE 0x10000
#define STACKSIZE 0x100000
#define FRAMESIZE 0x10000 /* 8086 */
#define PAGESIZE 0x1000 /* i386+ */
#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): \
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_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS))

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.h"
.privileged
.source __FILE__
/ @fileoverview Address Sanitizer Thunks
@ -48,9 +47,12 @@ __asan_report_load16:
.endfn __asan_report_load16,globl
OnReportLoad:
pop %rsi
ezlea __asan_report_load_n,ax
jmp __asan_report_noreentry
/ 𝑠𝑙𝑖𝑑𝑒
.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:
push $1
@ -78,21 +80,24 @@ __asan_report_store32:
.endfn __asan_report_store32,globl
ReportStore:
pop %rsi
ezlea __asan_report_store_n,ax
/ 𝑠𝑙𝑖𝑑𝑒
.endfn ReportStore
__asan_report_store_n:
lea __asan_report_store_impl(%rip),%r11
/ 𝑠𝑙𝑖𝑑𝑒
.endfn __asan_report_store_n,globl
__asan_report_noreentry:
push %rbp
mov %rsp,%rbp
cmpb $0,noreentry(%rip)
xor %eax,%eax
mov $1,%r10b
cmpxchg %r10b,__asan_noreentry(%rip)
jnz 2f
incb noreentry(%rip)
call *%rax
decb noreentry(%rip)
pop %rbp
call *%r11
decb __asan_noreentry(%rip)
2: pop %rbp
ret
2: call abort
.endfn __asan_report_noreentry
__asan_stack_free_0:
@ -194,14 +199,27 @@ OnStackMalloc:
.endfn OnStackMalloc
__asan_handle_no_return:
push %rbp
mov %rsp,%rbp
lea 8(%rsp),%rdi
call __asan_handle_no_return_impl
pop %rbp
ret
.endfn __asan_handle_no_return,globl
__asan_before_dynamic_init:
push %rbp
mov %rsp,%rbp
ud2
pop %rbp
ret
.endfn __asan_before_dynamic_init,globl
__asan_after_dynamic_init:
push %rbp
mov %rsp,%rbp
ud2
pop %rbp
ret
.endfn __asan_after_dynamic_init,globl
@ -229,7 +247,7 @@ __asan_option_detect_stack_use_after_return:
.previous
.bss
noreentry:
__asan_noreentry:
.byte 0
.endobj noreentry
.endobj __asan_noreentry
.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/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/fmt/itoa.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/log.h"
#include "libc/nexgen32e/gc.internal.h"
@ -33,9 +35,10 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#define kBacktraceMaxFrames 128
#define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (16 + 1))
#define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1))
static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
ssize_t got;
@ -43,7 +46,9 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
size_t i, j, gi;
int ws, pid, pipefds[2];
struct Garbages *garbage;
sigset_t chldmask, savemask;
const struct StackFrame *frame;
struct sigaction ignore, saveint, savequit;
const char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
if (IsOpenbsd()) return -1;
@ -66,12 +71,25 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
--gi;
} while ((addr = garbage->p[gi].ret) == weakaddr("__gc"));
}
argv[i++] = &buf[j];
j += snprintf(&buf[j], 17, "%#x", addr - 1) + 1;
argv[i++] = buf + j;
buf[j++] = '0';
buf[j++] = 'x';
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
}
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);
if (!(pid = vfork())) {
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
dup2(pipefds[1], 1);
close(pipefds[0]);
close(pipefds[1]);
@ -106,6 +124,9 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
if (errno == EINTR) continue;
return -1;
}
sigaction(SIGINT, &saveint, NULL);
sigaction(SIGQUIT, &savequit, NULL);
sigprocmask(SIG_SETMASK, &savemask, NULL);
if (WIFEXITED(ws) && !WEXITSTATUS(ws)) {
return 0;
} else {

View file

@ -56,10 +56,10 @@ relegated void __check_fail(const char *suffix, const char *opstr,
gethostname(hostname, sizeof(hostname));
(dprintf)(STDERR_FILENO,
"check failed on %s pid %d\n"
"\tCHECK_%s(%s, %s);\n"
"\t\t → %#lx (%s)\n"
"\t\t%s %#lx (%s)\n",
"check failed on %s pid %d\r\n"
"\tCHECK_%s(%s, %s);\r\n"
"\t\t → %#lx (%s)\r\n"
"\t\t%s %#lx (%s)\r\n",
hostname, getpid(), sufbuf, wantstr, gotstr, want, wantstr, opstr,
got, gotstr);
@ -68,19 +68,19 @@ relegated void __check_fail(const char *suffix, const char *opstr,
va_start(va, fmt);
(vdprintf)(STDERR_FILENO, fmt, 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);
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 ? " \\" : "");
}
if (!IsTiny() && lasterr == ENOMEM) {
(dprintf)(STDERR_FILENO, "\n");
(dprintf)(STDERR_FILENO, "\r\n");
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_string(" (");
__print(bx, int64toarray_radix10(lasterr, bx));
__print_string(")\n");
__print_string(")\r\n");
}

View file

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

View file

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

View file

@ -22,4 +22,6 @@
/**
* 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.
*/
bool isterminalinarticulate(void) {
bool IsTerminalInarticulate(void) {
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 */
uint16_t getttycols(uint16_t);
int getttysize(int, struct winsize *) paramsnonnull();
bool isterminalinarticulate(void) nosideeffect;
bool IsTerminalInarticulate(void) nosideeffect;
char *commandvenv(const char *, const char *) nodiscard;
const char *GetAddr2linePath(void);
const char *GetGdbPath(void);
@ -47,7 +47,7 @@ const char *GetGdbPath(void);
void showcrashreports(void);
void callexitontermination(struct sigset *);
bool32 IsDebuggerPresent(bool);
bool isrunningundermake(void);
bool IsRunningUnderMake(void);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § liblog » logging

View file

@ -58,15 +58,21 @@ $(LIBC_LOG_A).pkg: \
$(LIBC_LOG_A_OBJS) \
$(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 += \
$(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
$(NO_MAGIC)
LIBC_LOG_LIBS = $(foreach x,$(LIBC_LOG_ARTIFACTS),$($(x)))
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);
for (i = 0; i < g_argc; ++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) {
@ -221,9 +222,9 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
rip = ctx ? ctx->uc_mcontext.rip : 0;
if ((gdbpid = IsDebuggerPresent(true))) {
DebugBreak();
} else if (isterminalinarticulate() || isrunningundermake()) {
} else if (IsTerminalInarticulate() || IsRunningUnderMake()) {
gdbpid = -1;
} else {
} else if (FindDebugBinary()) {
RestoreDefaultCrashSignalHandlers();
gdbpid =
attachdebugger(((sig == SIGTRAP || sig == SIGQUIT) &&

View file

@ -90,6 +90,6 @@ kCp437:
.short 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x03bc,0x03c4 #e0:αßΓπΣσμτ
.short 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229 #e8:ΦΘΩδφε
.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
.previous

View file

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

View file

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

View file

@ -25,22 +25,29 @@
.text.startup
.source __FILE__
/ Stack frame that owns process from spawn to exit.
/ Cosmopolitan runtime.
/
/ @param edi is argc
/ @param rsi is argv
/ @param rdx is environ
/ @param rcx is auxv
/ @noreturn
_executive:
push %rbp
cosmo: push %rbp
mov %rsp,%rbp
ezlea _base,bx
mov %edi,%r12d
mov %rsi,%r13
mov %rdx,%r14
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 %r13,%rsi
mov %r14,%rdx
@ -49,7 +56,7 @@ _executive:
call main
xchg %eax,%edi
call exit
.endfn _executive,weak,hidden
.endfn cosmo,weak,hidden
ud2
#ifdef __PG__

View file

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

View file

@ -19,7 +19,7 @@ extern hidden void *g_stacktop;
void _init(void) hidden;
void _piro(int) 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_local(void) wontreturn relegated hidden;
void _jmpstack(void *, void *, ...) hidden wontreturn;

View file

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

View file

@ -21,7 +21,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/log/asan.internal.h"
#include "libc/intrin/asan.internal.h"
#include "libc/macros.h"
#include "libc/rand/rand.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();
}
if (weaken(__asan_map_shadow)) {
weaken(__asan_map_shadow)(dm.addr, size);
weaken(__asan_map_shadow)((uintptr_t)dm.addr, size);
}
return dm.addr;
}

View file

@ -56,7 +56,23 @@ $(LIBC_RUNTIME_A).pkg: \
$(LIBC_RUNTIME_A_OBJS) \
$(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 += \
$(NO_MAGIC)

View file

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

View file

@ -45,7 +45,8 @@
*/
void *memccpy(void *d, const void *s, int c, size_t n) {
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);
} else {
memcpy(d, s, n);

View file

@ -20,26 +20,8 @@
#include "libc/intrin/pmovmskb.h"
#include "libc/str/str.h"
/**
* 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;
static noasan size_t stpcpy$sse2(char *d, const char *s, size_t i) {
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 (;;) {
memset(vz, 0, 16);
memcpy(v1, s + i, 16);
@ -51,6 +33,26 @@ char *stpcpy(char *d, const char *s) {
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 (;;) {
if (!(d[i] = s[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 strnlen(const char *, size_t) strlenesque;
size_t strnlen_s(const char *, size_t);
size_t strlen$pure(const char *) strlenesque;
char *strchr(const char *, int) strlenesque;
char *index(const char *, int) strlenesque;
void *memchr(const void *, int, size_t) strlenesque;
@ -360,6 +361,17 @@ char *strsignal(int) returnsnonnull libcesque;
})
#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__ */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

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

View file

@ -20,26 +20,8 @@
#include "libc/intrin/pmovmskb.h"
#include "libc/str/str.h"
/**
* 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;
static noasan size_t strcpy$sse2(char *d, const char *s, size_t i) {
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 (;;) {
memset(vz, 0, 16);
memcpy(v1, s + i, 16);
@ -51,6 +33,26 @@ char *strcpy(char *d, const char *s) {
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 (;;) {
if (!(d[i] = s[i])) {
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};
/* 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.
*
@ -40,24 +59,11 @@ axdx_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) {
axdx_t r;
uint64_t w;
wint_t x, y;
int16_t v1[8], v2[8], v3[8], vz[8];
r.ax = 0;
r.dx = 0;
for (;;) {
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
/* 10x speedup for ascii */
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;
}
r = tprecode16to8$sse2(dst, dstsize, src, r);
}
if (!(x = src[r.dx++])) break;
if (IsUtf16Cont(x)) continue;

View file

@ -24,6 +24,25 @@
#include "libc/str/thompike.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.
*
@ -38,24 +57,11 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) {
unsigned n;
uint64_t w;
wint_t x, y;
uint8_t v1[16], v2[16], vz[16];
r.ax = 0;
r.dx = 0;
for (;;) {
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
/* 34x speedup for ascii */
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;
}
tprecode8to16$sse2(dst, dstsize, src, r);
}
x = src[r.dx++] & 0xff;
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}},
};
static uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree,
const uint8_t *lens, size_t symcount) {
static noasan uint32_t undeflatetree(struct DeflateState *ds, uint32_t *tree,
const uint8_t *lens, size_t symcount) {
size_t i, len;
uint32_t code, slot;
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];
}
static struct DeflateHold undeflatesymbol(struct DeflateHold hold,
const uint32_t *tree,
size_t treecount,
uint32_t *out_symbol) {
static noasan struct DeflateHold undeflatesymbol(struct DeflateHold hold,
const uint32_t *tree,
size_t treecount,
uint32_t *out_symbol) {
size_t left, right, m;
uint32_t search, key;
left = 0;
@ -122,17 +122,20 @@ static struct DeflateHold undeflatesymbol(struct DeflateHold hold,
return hold;
}
/* TODO(jart): Do we really need noasan? */
/**
* Decompresses raw DEFLATE data.
*
* This is 10x smaller and 10x slower than chromium zlib.
*
* @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
*/
ssize_t undeflate(void *output, size_t outputsize, void *input,
size_t inputsize, struct DeflateState *ds) {
noasan ssize_t undeflate(void *output, size_t outputsize, void *input,
size_t inputsize, struct DeflateState *ds) {
struct DeflateHold hold;
bool isfinalblock;
size_t i, nlit, ndist;

View file

@ -77,10 +77,6 @@ __asan_load32:
ud2
.endfn __asan_load32,weak
__asan_noreentry:
ud2
.endfn __asan_noreentry,weak
__asan_option_detect_stack_use_after_return:
ud2
.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
#
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary
syscon auxv AT_EXECFD 2 0 2 0 0
syscon auxv AT_PHDR 3 0 3 0 0
syscon auxv AT_EXECFD 2 0 2 0 0 # file descriptor of program
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_PHNUM 5 0 5 0 0
syscon auxv AT_PAGESZ 6 0 6 0 0
syscon auxv AT_BASE 7 0 7 0 0
syscon auxv AT_ENTRY 9 0 9 0 0
syscon auxv AT_BASE 7 0 7 0 0 # address of program interpreter
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_OSRELDATE 0 0 18 0 0
syscon auxv AT_UID 11 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_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_DCACHEBSIZE 19 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_SECURE 23 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_EXECFN 31 999 999 999 999 # faked on non-linux
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 # 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_NO_AUTOMOUNT 0x0800 0 0 0 0

View file

@ -17,6 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#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"
.source __FILE__
@ -161,9 +164,10 @@ systemfive.xnu:
/ Initializes System Five system call support.
/
/ (1) Extracts parameters passed by kernel,
/ (2) Detects O/S without issuing system calls,
/ (3) Unpacks numbers.
/ (1) Extracts parameters passed by kernel
/ (2) Detects OS without issuing system calls
/ (3) Unpacks magnums from libc/sysv/consts.sh
/ (4) Replaces stack with one we control
/
/ @param %r15 is auxv
/ @note OpenBSD devs: let us know if you start using auxv
@ -219,26 +223,17 @@ systemfive.init.os:
pop %rax
add %rcx,%rax
stosq # __systemfive
/ 𝑠𝑙𝑖𝑑𝑒
systemfive.init.magnums:
push %rdi
ezlea syscon.start,di
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
2: cmp %rbx,%rdi
jnb 5f
xor %ecx,%ecx
xor %edx,%edx
3: lodsb
3: lodsb # decodes sleb128
mov %rax,%r8
and $127,%r8d
sal %cl,%r8
@ -252,14 +247,66 @@ systemfive.sleb128unpacker:
sal %cl,%rax
or %rax,%rdx
4: mov %rdx,%rax
cmpq $0,(%rdi) # don't change consts already set
cmovne (%rdi),%rax # @see WinMain() for example
cmpq $0,(%rdi) # dont change if set
cmovne (%rdi),%rax # @see WinMain()
stosq
jmp 2b
5: .leafepilogue
.previous
5: pop %rdi
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).
/ 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;
count = testlib_countfixtures(fixture_start, fixture_end);
for (i = 0; i < count && !g_testlib_failed; ++i) {
snprintf(g_fixturename, sizeof(g_fixturename), "%s_%s",
fixture_start[i].group, fixture_start[i].name);
(snprintf)(g_fixturename, sizeof(g_fixturename), "%s_%s",
fixture_start[i].group, fixture_start[i].name);
_piro(PROT_READ | PROT_WRITE);
fixture_start[i].fn();
_piro(PROT_READ);

View file

@ -233,71 +233,6 @@ COSMOPOLITAN_C_START_
#define EXPECT_LGBL_LT(VAL, GOT) \
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
*/
@ -310,6 +245,14 @@ void testlib_clearxmmregisters(void);
const char *file; \
int line
typedef void (*testfn_t)(void);
struct TestFixture {
const char *group;
const char *name;
testfn_t fn;
};
extern char g_fixturename[256];
extern bool g_testlib_shoulddebugbreak; /* set by testmain */
extern unsigned g_testlib_ran; /* set by wrappers */
@ -374,6 +317,7 @@ void testlib_formatbinaryasglyphs(const char16_t *, const void *, size_t,
char **, char **);
bool testlib_almostequallongdouble(long double, long double);
void testlib_incrementfailed(void);
void testlib_clearxmmregisters(void);
forceinline void testlib_ontest() {
YOINK(__testcase_start);

View file

@ -56,7 +56,6 @@ LIBC_TESTLIB_A_SRCS_C = \
libc/testlib/shoulddebugbreak.c \
libc/testlib/showerror.c \
libc/testlib/showerror_.c \
libc/testlib/testmem.c \
libc/testlib/strequals.c \
libc/testlib/startswith.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 *xstrmul(const char *, size_t) paramsnonnull((1)) _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

View file

@ -38,7 +38,7 @@ char *xslurp(const char *path, size_t *opt_out_size) {
struct stat st;
res = NULL;
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) {
fadvise(fd, 0, st.st_size, MADV_SEQUENTIAL);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,7 +21,9 @@
#include "libc/bits/bits.h"
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/gc.h"
#include "libc/str/str.h"
#include "libc/testlib/ezbench.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,
};
#define TestIt(impl, index, value, n, array) \
({ \
short *a = memcpy(tgc(tmemalign(32, n * 2)), array, n * 2); \
unsigned res = impl(array, n); \
ASSERT_EQ(index, res); \
ASSERT_EQ(value, a[res]); \
#define TestIt(impl, index, value, n, array) \
({ \
short *a = memcpy(gc(memalign(32, n * 2)), array, n * 2); \
unsigned res = impl(array, n); \
ASSERT_EQ(index, res); \
ASSERT_EQ(value, a[res]); \
})
TEST(windex, testRealWorldPicks) {

View file

@ -18,12 +18,13 @@
*/
#include "libc/alg/alg.h"
#include "libc/bits/bits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
TEST(comparator, testByteCompare) {
char *b1 = tmalloc(1);
char *b2 = tmalloc(1);
char *b1 = malloc(1);
char *b2 = malloc(1);
/* sign doesn't matter */
EXPECT_EQ(cmpsb(memcpy(b1, "a", 1), memcpy(b2, "a", 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 */
EXPECT_GT(cmpsb(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0);
EXPECT_LT(cmpub(memcpy(b1, "\x7f", 1), memcpy(b2, "\x80", 1)), 0);
tfree(b2);
tfree(b1);
free(b2);
free(b1);
}
TEST(comparator, testWordCompare) {
char *b1 = tmalloc(2);
char *b2 = tmalloc(2);
char *b1 = malloc(2);
char *b2 = malloc(2);
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_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_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);
tfree(b2);
tfree(b1);
free(b2);
free(b1);
}
TEST(comparator, testDoublewordCompare) {
char *b1 = tmalloc(4);
char *b2 = tmalloc(4);
char *b1 = malloc(4);
char *b2 = malloc(4);
EXPECT_EQ(cmpsl(memcpy(b1, "\x00\x00\x00\x80", 4),
memcpy(b2, "\x00\x00\x00\x80", 4)),
0);
@ -79,13 +80,13 @@ TEST(comparator, testDoublewordCompare) {
EXPECT_GT(cmpul(memcpy(b1, "\x00\x00\x00\x80", 4),
memcpy(b2, "\x00\x00\x00\x7f", 4)),
0);
tfree(b2);
tfree(b1);
free(b2);
free(b1);
}
TEST(comparator, testQuadwordCompare) {
char *b1 = tmalloc(8);
char *b2 = tmalloc(8);
char *b1 = malloc(8);
char *b2 = malloc(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)),
0);
@ -104,6 +105,6 @@ TEST(comparator, testQuadwordCompare) {
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)),
0);
tfree(b2);
tfree(b1);
free(b2);
free(b1);
}

View file

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

View file

@ -19,6 +19,7 @@
#include "libc/alg/alg.h"
#include "libc/bits/bits.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/str/str.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'},
{2, 'g'}, {4, 'a'}, {65, 'b'}, {83, 'h'},
{99, 'f'}, {782, 'i'}};
int32_t(*M)[2] = tmalloc(sizeof(A));
int32_t(*M)[2] = malloc(sizeof(A));
memcpy(M, B, sizeof(A));
qsort(M, ARRAYLEN(A), sizeof(*M), cmpsl);
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/dnsheader.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
@ -28,20 +29,20 @@ TEST(serializednsheader, test) {
header.id = 255;
header.bf1 = true;
header.qdcount = 1;
uint8_t *buf = tmalloc(12);
uint8_t *buf = malloc(12);
ASSERT_EQ(12, serializednsheader(buf, 12, header));
EXPECT_BINEQ(u" λ☺  ☺      ", buf);
tfree(buf);
free(buf);
}
TEST(serializednsheader, fuzzSymmetry) {
uint8_t *buf = tmalloc(12);
struct DnsHeader *in = tmalloc(sizeof(struct DnsHeader));
struct DnsHeader *out = tmalloc(sizeof(struct DnsHeader));
uint8_t *buf = malloc(12);
struct DnsHeader *in = malloc(sizeof(struct DnsHeader));
struct DnsHeader *out = malloc(sizeof(struct DnsHeader));
ASSERT_EQ(12, serializednsheader(buf, 12, *in));
ASSERT_EQ(12, deserializednsheader(out, buf, 12));
ASSERT_EQ(0, memcmp(in, out, 12));
tfree(out);
tfree(in);
tfree(buf);
free(out);
free(in);
free(buf);
}

View file

@ -17,21 +17,22 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dns/dns.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
TEST(dnsnamecmp, testEmpty) {
char *A = strcpy(tmalloc(1), "");
char *B = strcpy(tmalloc(1), "");
char *A = strcpy(malloc(1), "");
char *B = strcpy(malloc(1), "");
EXPECT_EQ(dnsnamecmp(A, B), 0);
EXPECT_EQ(dnsnamecmp(A, A), 0);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testDotless_caseInsensitiveBehavior) {
char *A = tmalloc(2);
char *B = tmalloc(2);
char *A = malloc(2);
char *B = malloc(2);
EXPECT_EQ(dnsnamecmp(strcpy(A, "a"), strcpy(B, "a")), 0);
EXPECT_EQ(dnsnamecmp(A, 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_GT(dnsnamecmp(strcpy(A, "d"), strcpy(B, "a")), 0);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testMultiLabel_lexiReverse) {
char *A = tmalloc(16);
char *B = tmalloc(16);
char *A = malloc(16);
char *B = malloc(16);
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_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_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);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testTldDotQualifier_canBeEqualToDottedNames) {
char *A = tmalloc(16);
char *B = tmalloc(16);
char *A = malloc(16);
char *B = malloc(16);
EXPECT_EQ(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "aaa.example")), 0);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testFullyQualified_alwaysComesFirst) {
char *A = tmalloc(16);
char *B = tmalloc(16);
char *A = malloc(16);
char *B = malloc(16);
EXPECT_LT(dnsnamecmp(strcpy(B, "aaa.example."), strcpy(A, "zzz")), 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, "aaa"), strcpy(B, "zzz.example.")), 0);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testLikelySld_alwaysComesBeforeLocalName) {
char *A = tmalloc(16);
char *B = tmalloc(16);
char *A = malloc(16);
char *B = malloc(16);
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, "zzz.example"), strcpy(A, "aaa")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "zzz"), strcpy(B, "aaa.example")), 0);
EXPECT_GT(dnsnamecmp(strcpy(A, "aaa"), strcpy(B, "zzz.example")), 0);
tfree(B);
tfree(A);
free(B);
free(A);
}
TEST(dnsnamecmp, testLikelySubdomain_alwaysComesAfterSld) {
char *A = tmalloc(16);
char *B = tmalloc(16);
char *A = malloc(16);
char *B = malloc(16);
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_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);
tfree(B);
tfree(A);
free(B);
free(A);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -18,6 +18,7 @@
*/
#include "dsp/core/q.h"
#include "libc/intrin/pmulhrsw.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
@ -108,6 +109,15 @@ TEST(pmulhrsw, testFakeFloat) {
} /* clang-format on */;
FOR88(QD[y][x] = F2Q(15, D[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]));
FOR88(Q[y][x] = Q2F(15, QQ[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)
@$(APELINK)
# $(TEST_LIBC_INTRIN_OBJS): \
# OVERRIDE_CFLAGS += \
# -fsanitize=address
.PHONY: o/$(MODE)/test/libc/intrin
o/$(MODE)/test/libc/intrin: \
$(TEST_LIBC_INTRIN_BINS) \

View file

@ -19,6 +19,7 @@
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/kompressor.h"
#include "libc/nexgen32e/lz4.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 */
static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x60, 0x40,
0x82, 0x00, 0x00, 0x00, 0x00};
char *src = memcpy(tmalloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = tmalloc(1);
char *src = memcpy(malloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = malloc(1);
*dst = 'z';
ASSERT_EQ(dst, lz4decode(dst, src));
ASSERT_EQ('z', *dst);
tfree(dst);
tfree(src);
free(dst);
free(src);
}
TEST(lz4, decompress_oneLetterWithoutChecksum) {
@ -45,12 +46,12 @@ TEST(lz4, decompress_oneLetterWithoutChecksum) {
static char kLz4Data[] = {0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x01,
0x00, 0x00, 0x80, 0x61, 0x00, 0x00, 0x00, 0x00};
char *src = memcpy(tmalloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = tmalloc(1);
char *src = memcpy(malloc(sizeof(kLz4Data)), kLz4Data, sizeof(kLz4Data));
char *dst = malloc(1);
ASSERT_EQ(dst + 1, lz4decode(dst, src));
ASSERT_EQ('a', *dst);
tfree(dst);
tfree(src);
free(dst);
free(src);
}
TEST(lz4, decompress_runLengthDecode) {
@ -60,13 +61,13 @@ TEST(lz4, decompress_runLengthDecode) {
0x04, 0x22, 0x4d, 0x18, 0x68, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x61, 0x01, 0x00, 0x07,
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";
char *dst = tmalloc(strlen(want));
char *dst = malloc(strlen(want));
ASSERT_EQ(dst + strlen(want), lz4decode(dst, src));
ASSERT_STREQN(want, dst, strlen(want));
tfree(dst);
tfree(src);
free(dst);
free(src);
}
TEST(lz4, zoneFileGmt) {

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