Make redbean StoreAsset() work better

- Better UBSAN error messages
- POSIX Advisory Locks polyfills
- Move redbean manual to /.help.txt
- System call memory safety in ASAN mode
- Character classification now does UNICODE
This commit is contained in:
Justine Tunney 2021-05-14 05:36:58 -07:00
parent 919b6fec10
commit 690be544da
228 changed files with 3653 additions and 3015 deletions

View file

@ -91,6 +91,9 @@ bool wantpg;
bool wantrecord;
bool wantubsan;
bool touchtarget;
bool no_sanitize_null;
bool no_sanitize_alignment;
bool no_sanitize_pointer_overflow;
char *cmd;
char *comdbg;
@ -400,6 +403,12 @@ int main(int argc, char *argv[]) {
} else if (!strcmp(argv[i], "-fno-sanitize=all")) {
wantasan = false;
wantubsan = false;
} else if (!strcmp(argv[i], "-fno-sanitize=null")) {
if (isgcc && ccversion >= 6) no_sanitize_null = true;
} else if (!strcmp(argv[i], "-fno-sanitize=alignment")) {
if (isgcc && ccversion >= 6) no_sanitize_alignment = true;
} else if (!strcmp(argv[i], "-fno-sanitize=pointer-overflow")) {
if (isgcc && ccversion >= 6) no_sanitize_pointer_overflow = true;
} else if (startswith(argv[i], "-fsanitize=implicit") &&
strstr(argv[i], "integer")) {
if (isgcc) AddArg(argv[i]);
@ -470,6 +479,15 @@ int main(int argc, char *argv[]) {
AddArg("-fsanitize=undefined");
AddArg("-fno-data-sections");
}
if (no_sanitize_null) {
AddArg("-fno-sanitize=null");
}
if (no_sanitize_alignment) {
AddArg("-fno-sanitize=alignment");
}
if (no_sanitize_pointer_overflow) {
AddArg("-fno-sanitize=pointer-overflow");
}
if (wantframe) {
AddArg("-fno-omit-frame-pointer");
}

View file

@ -21,14 +21,14 @@
.macro .errno local:req linux:req
.globl \local
.long \local-kLinuxErrnos
.long \linux
.byte \linux
.endm
// Lookup table translating errnos between systems.
//
// @see libc/sysv/systemfive.S
.rodata
.align 8
.align 4
kLinuxErrnos:
.errno EPERM,1
.errno ENOENT,2
@ -85,47 +85,18 @@ kLinuxErrnos:
.errno EAFNOSUPPORT,97
.errno EADDRINUSE,98
.errno EADDRNOTAVAIL,99
.errno ECHRNG,44
.errno EL2NSYNC,45
.errno EL3HLT,46
.errno EL3RST,47
.errno ELNRNG,48
.errno EUNATCH,49
.errno ENOCSI,50
.errno EL2HLT,51
.errno EBADE,52
.errno EBADR,53
.errno EXFULL,54
.errno ENOANO,55
.errno EBADRQC,56
.errno EBADSLT,57
.errno ENOSTR,60
.errno ENODATA,61
.errno ETIME,62
.errno ENOSR,63
.errno ENONET,64
.errno ENOPKG,65
.errno EREMOTE,66
.errno ENOLINK,67
.errno EADV,68
.errno ESRMNT,69
.errno ECOMM,70
.errno EPROTO,71
.errno EMULTIHOP,72
.errno EDOTDOT,73
.errno EBADMSG,74
.errno EOVERFLOW,75
.errno ENOTUNIQ,76
.errno EBADFD,77
.errno EREMCHG,78
.errno ELIBACC,79
.errno ELIBBAD,80
.errno ELIBSCN,81
.errno ELIBMAX,82
.errno ELIBEXEC,83
.errno EILSEQ,84
.errno ERESTART,85
.errno ESTRPIPE,86
.errno ENETDOWN,100
.errno ENETUNREACH,101
.errno ENETRESET,102
@ -143,23 +114,10 @@ kLinuxErrnos:
.errno EALREADY,114
.errno EINPROGRESS,115
.errno ESTALE,116
.errno EUCLEAN,117
.errno ENOTNAM,118
.errno ENAVAIL,119
.errno EISNAM,120
.errno EREMOTEIO,121
.errno EDQUOT,122
.errno ENOMEDIUM,123
.errno EMEDIUMTYPE,124
.errno ECANCELED,125
.errno ENOKEY,126
.errno EKEYEXPIRED,127
.errno EKEYREVOKED,128
.errno EKEYREJECTED,129
.errno EOWNERDEAD,130
.errno ENOTRECOVERABLE,131
.errno ERFKILL,132
.errno EHWPOISON,133
.long 0
.byte 0
.endobj kLinuxErrnos,globl
kLinuxErrnosLength = (.-kLinuxErrnos)/8
.globl kLinuxErrnosLength

View file

@ -21,42 +21,21 @@
#include "libc/runtime/carsort.h"
#include "tool/build/lib/xlaterrno.h"
struct LinuxErrno {
struct thatispacked LinuxErrno {
int32_t local;
int32_t linux;
uint8_t linux;
};
extern const char kLinuxErrnosLength[];
extern const struct LinuxErrno kLinuxErrnos[];
static struct LinuxErrno *errnos;
/**
* Turns local errno into Linux errno.
*/
int XlatErrno(int local) {
static bool once;
long i, n, m, l, r;
n = (uintptr_t)kLinuxErrnosLength;
if (!once) {
errnos = malloc(sizeof(struct LinuxErrno) * n);
for (i = 0; i < n; ++i) {
errnos[i].local =
*(int *)((intptr_t)kLinuxErrnos + kLinuxErrnos[i].local);
errnos[i].linux = kLinuxErrnos[i].linux;
}
carsort100(n, (void *)errnos);
once = true;
}
l = 0;
r = n - 1;
while (l <= r) {
m = (l + r) / 2;
if (errnos[m].local < local) {
l = m + 1;
} else if (errnos[m].local > local) {
r = m - 1;
} else {
return errnos[m].linux;
int i;
for (i = 0; kLinuxErrnos[i].local; ++i) {
if (local == *(int *)((intptr_t)kLinuxErrnos + kLinuxErrnos[i].local)) {
return kLinuxErrnos[i].linux;
}
}
return ENOSYS;

View file

@ -42,7 +42,6 @@
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/f.h"

View file

@ -19,7 +19,6 @@
#include "libc/dns/dns.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sock.h"
#include "tool/decode/lib/socknames.h"

View file

@ -390,6 +390,13 @@ void ShowCentralDirHeader64(uint8_t *cd) {
printf("0:");
disassemblehex(ZIP_CDIR64_COMMENT(cd), ZIP_CDIR64_COMMENTSIZE(cd), stdout);
printf("1:\n");
cd += ZIP_CDIR64_HDRSIZE(cd);
printf("\n/\t%s (%zu %s)\n", "zip64 end of central directory locator",
kZipCdir64LocatorSize, "bytes");
show(".ascii", format(b1, "%`'.*s", 4, cd), "magic");
show(".long", format(b1, "%d", READ32LE(cd + 4)), "startingdisk");
show(".quad", format(b1, "%lu", READ64LE(cd + 4 + 4)), "eocd64 offset");
show(".long", format(b1, "%d", READ32LE(cd + 4 + 4 + 8)), "totaldisks");
}
uint8_t *GetZipCdir32(const uint8_t *p, size_t n) {
@ -406,12 +413,13 @@ uint8_t *GetZipCdir32(const uint8_t *p, size_t n) {
}
uint8_t *GetZipCdir64(const uint8_t *p, size_t n) {
size_t i;
if (n >= kZipCdir64HdrMinSize) {
i = n - kZipCdir64HdrMinSize;
uint64_t i, j;
if (n >= kZipCdir64LocatorSize) {
i = n - kZipCdir64LocatorSize;
do {
if (READ32LE(p + i) == kZipCdir64HdrMagic && IsZipCdir64(p, n, i)) {
return (/*unconst*/ uint8_t *)(p + i);
if (READ32LE(p + i) == kZipCdir64LocatorMagic &&
(j = ZIP_LOCATE64_OFFSET(p + i)) + kZipCdir64HdrMinSize <= n) {
return p + j;
}
} while (i--);
}
@ -451,6 +459,7 @@ void DisassembleZip(const char *path, uint8_t *p, size_t n) {
ShowCentralFileHeader(cf);
pos = (cf - p) + ZIP_CFILE_HDRSIZE(cf);
}
/* TODO(jart): This is absurd. */
if (eocd32 && eocd64) {
if (eocd32 < eocd64) {
ShowCentralDirHeader32(eocd32);
@ -458,8 +467,12 @@ void DisassembleZip(const char *path, uint8_t *p, size_t n) {
ShowCentralDirHeader64(eocd64);
AdvancePosition(p, &pos, eocd64 - p);
} else {
/* pos = eocd64 - p + ZIP_CDIR_HDRSIZE(eocd64); */
/* AdvancePosition(p, &pos, n); */
ShowCentralDirHeader64(eocd64);
AdvancePosition(p, &pos, eocd64 - p);
/* pos = eocd32 - p + ZIP_CDIR_HDRSIZE(eocd32); */
/* AdvancePosition(p, &pos, n); */
ShowCentralDirHeader32(eocd32);
AdvancePosition(p, &pos, eocd32 - p);
}
@ -470,11 +483,7 @@ void DisassembleZip(const char *path, uint8_t *p, size_t n) {
ShowCentralDirHeader64(eocd64);
AdvancePosition(p, &pos, eocd64 - p);
}
if (!eocd64 || eocd32 > eocd64) {
pos = eocd32 - p + ZIP_CDIR_HDRSIZE(eocd32);
} else {
pos = eocd64 - p + ZIP_CDIR_HDRSIZE(eocd64);
}
pos = n;
AdvancePosition(p, &pos, n);
}

View file

@ -440,7 +440,10 @@
(interactive "P")
(setq arg (or arg 2))
;; -ffast-math -funsafe-math-optimizations -fsched2-use-superblocks -fjump-tables
(cond ((not (eq 0 (logand 8 arg)))
(cond ((eq arg 9)
(cosmo--assembly 1
"V=1 OVERRIDE_COPTS='-fverbose-asm -fsanitize=undefined -fno-sanitize=null -fno-sanitize=alignment -fno-sanitize=pointer-overflow'"))
((not (eq 0 (logand 8 arg)))
(cosmo--assembly (setq arg (logand (lognot 8)))
"V=1 OVERRIDE_COPTS='-fverbose-asm -fsanitize=address'"))
(t (cosmo--assembly arg "V=1 OVERRIDE_COPTS='' CPPFLAGS='-DSTACK_FRAME_UNLIMITED'"))))

158
tool/net/.help.txt Normal file
View file

@ -0,0 +1,158 @@
SYNOPSIS
redbean.com [-hvduzmbagf] [-p PORT] [-- SCRIPTARGS...]
DESCRIPTION
redbean - single-file distributable web server
FLAGS
-h help
-s increase silence [repeat]
-v increase verbosity [repeat]
-d daemonize
-u uniprocess
-z print port
-m log messages
-b log message body
-a log resource usage
-g log handler latency
-f log worker function calls
-H K:V sets http header globally [repeat]
-D DIR serve assets from local directory [repeat]
-t MS tunes read and write timeouts [default 30000]
-M INT tunes max message payload size [default 65536]
-c SEC configures static asset cache-control headers
-r /X=/Y redirect X to Y [repeat]
-R /X=/Y rewrites X to Y [repeat]
-l ADDR listen ip [default 0.0.0.0]
-p PORT listen port [default 8080]
-L PATH log file location
-P PATH pid file location
-U INT daemon set user id
-G INT daemon set group id
FEATURES
- Lua v5.4
- HTTP v0.9
- HTTP v1.0
- HTTP v1.1
- Pipelining
- Accounting
- Content-Encoding
- Range / Content-Range
- Last-Modified / If-Modified-Since
USAGE
This executable is also a ZIP file that contains static assets.
You can run redbean interactively in your terminal as follows:
./redbean.com -vvvmbag # starts server verbosely
open http://127.0.0.1:8080/ # shows zip listing page
CTRL-C # 1x: graceful shutdown
CTRL-C # 2x: forceful shutdown
You can override the default listing page by adding:
zip redbean.com index.lua # lua server pages take priority
zip redbean.com index.html # default page for directory
The listing page only applies to the root directory. However the
default index page applies to subdirectories too. In order for it
to work, there needs to be an empty directory entry in the zip.
That should already be the default practice of your zip editor.
wget \
--mirror \
--convert-links \
--adjust-extension \
--page-requisites \
--no-parent \
--no-if-modified-since \
http://a.example/index.html
zip -r redbean.com a.example/ # default page for directory
redbean normalizes the trailing slash for you automatically:
$ printf 'GET /a.example HTTP/1.0\n\n' | nc 127.0.0.1 8080
HTTP/1.0 307 Temporary Redirect
Location: /a.example/
Virtual hosting is accomplished this way too. The Host is simply
prepended to the path, and if it doesn't exist, it gets removed.
$ printf 'GET / HTTP/1.1\nHost:a.example\n\n' | nc 127.0.0.1 8080
HTTP/1.1 200 OK
Link: <http://127.0.0.1/a.example/index.html>; rel="canonical"
If you mirror a lot of websites within your redbean then you can
actually tell your browser that redbean is your proxy server, in
which redbean will act as your private version of the Internet.
$ printf 'GET http://a.example HTTP/1.0\n\n' | nc 127.0.0.1 8080
HTTP/1.0 200 OK
Link: <http://127.0.0.1/a.example/index.html>; rel="canonical"
If you use a reverse proxy, then redbean recognizes the following
provided that the proxy forwards requests over the local network:
X-Forwarded-For: 203.0.113.42:31337
X-Forwarded-Host: foo.example:80
There's a text/plain statistics page called /statusz that makes
it easy to track and monitor the health of your redbean:
printf 'GET /statusz\n\n' | nc 127.0.0.1 8080
redbean will display an error page using the /redbean.png logo
by default, embedded as a bas64 data uri. You can override the
custom page for various errors by adding files to the zip root.
zip redbean.com 404.html # custom not found page
Audio video content should not be compressed in your ZIP files.
Uncompressed assets enable browsers to send Range HTTP request.
On the other hand compressed assets are best for gzip encoding.
zip redbean.com index.html # adds file
zip -0 redbean.com video.mp4 # adds without compression
You can have redbean run as a daemon by doing the following:
redbean.com -vv -d -L redbean.log -P redbean.pid
kill -TERM $(cat redbean.pid) # 1x: graceful shutdown
kill -TERM $(cat redbean.pid) # 2x: forceful shutdown
redbean currently has a 32kb limit on request messages and 64kb
including the payload. redbean will grow to whatever the system
limits allow. Should fork() or accept() fail redbean will react
by going into "meltdown mode" which closes lingering workers.
You can trigger this at any time using:
kill -USR2 $(cat redbean.pid)
Another failure condition is running out of disk space in which
case redbean reacts by truncating the log file. Lastly, redbean
does the best job possible reporting on resource usage when the
logger is in debug mode noting that NetBSD is the best at this.
Your redbean is an actually portable executable, that's able to
run on six different operating systems. To do that, it needs to
overwrite its own MZ header at startup, with ELF or Mach-O, and
then puts the original back once the program loads. If you want
your redbean to follow the platform-local executable convention
then delete the /.ape file from zip.
redbean contains software licensed ISC, MIT, BSD-2, BSD-3, zlib
which makes it a permissively licensed gift to anyone who might
find it useful. The transitive closure of legalese can be found
inside the binary. redbean also respects your privacy and won't
phone home because your computer is its home.
SEE ALSO
https://justine.lol/redbean/index.html
https://news.ycombinator.com/item?id=26271117

View file

@ -164,7 +164,7 @@ local function main()
Write(EscapeHtml(VisualizeControlCodes(GetFragment())))
Write('\r\n')
end
Write('<dt>GetRemoteAddr() <small>(from Bekeley Sockets or X-Forwarded-For header)</small>\r\n')
Write('<dt>GetRemoteAddr() <small>(from Berkeley Sockets or X-Forwarded-For header)</small>\r\n')
Write('<dd>')
ip, port = GetRemoteAddr()
Write(string.format('%s, %d', FormatIp(ip), port))

View file

@ -23,7 +23,6 @@
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ai.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sock.h"
#include "tool/decode/lib/flagger.h"
@ -44,7 +43,8 @@ void lookup(const char *name) {
perror("getaddrinfo");
exit(1);
default:
fprintf(stderr, "getaddrinfo failed: %d (EAI_%s)\n", rc, gai_strerror(rc));
fprintf(stderr, "getaddrinfo failed: %d (EAI_%s)\n", rc,
gai_strerror(rc));
exit(1);
}
if (addrs) {

View file

@ -72,12 +72,13 @@ o/$(MODE)/tool/net/redbean.com.dbg: \
o/$(MODE)/tool/net/redbean.com: \
o/$(MODE)/tool/net/redbean.com.dbg \
tool/net/net.mk \
tool/net/.help.txt \
tool/net/.init.lua \
tool/net/favicon.ico \
tool/net/redbean.png
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.init.lua tool/net/favicon.ico tool/net/redbean.png
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.ape tool/net/.help.txt tool/net/.init.lua tool/net/favicon.ico tool/net/redbean.png
o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/redbean.com.dbg
@ -88,6 +89,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
tool/net/net.mk \
tool/net/favicon.ico \
tool/net/redbean.png \
tool/net/.help.txt \
tool/net/demo/.init.lua \
tool/net/demo/.reload.lua \
tool/net/demo/404.html \
@ -108,7 +110,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
@$(COMPILE) -AMKDIR -T$@ mkdir -p o/$(MODE)/tool/net/.redbean-demo
@$(COMPILE) -ADD -T$@ dd if=$@ of=o/$(MODE)/tool/net/.redbean-demo/.ape bs=64 count=11 conv=notrunc 2>/dev/null
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.redbean-demo/.ape tool/net/demo/.init.lua tool/net/demo/.reload.lua tool/net/demo/hello.lua
@$(COMPILE) -AZIP -T$@ zip -qj $@ o/$(MODE)/tool/net/.redbean-demo/.ape tool/net/.help.txt tool/net/demo/.init.lua tool/net/demo/.reload.lua tool/net/demo/hello.lua
@echo "&lt;-- check out this lua server page" | $(COMPILE) -AZIP -T$@ zip -cqj $@ tool/net/demo/redbean.lua
@$(COMPILE) -AZIP -T$@ zip -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ zip -cqj0 $@ tool/net/demo/seekable.txt

File diff suppressed because it is too large Load diff