mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-28 07:18:30 +00:00
Add torture test for zipos file descriptors
This change hardens the code for opening /zip/ files using the system call interface. Thread safety and signal safety has been improved for file descriptors in general. We now document fixed addresses that are needed for low level allocations.
This commit is contained in:
parent
579080cd4c
commit
e466dd0553
44 changed files with 2981 additions and 307 deletions
|
@ -16,24 +16,26 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/relocations.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/bits/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/runtime/directmap.internal.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
@ -41,64 +43,164 @@
|
|||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/zip.h"
|
||||
#include "libc/zipos/zipos.internal.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
||||
static int __zipos_inflate(struct ZiposHandle *h, uint8_t *data, size_t size) {
|
||||
return !__inflate(h->freeme, h->size, data, size) ? 0 : eio();
|
||||
static volatile size_t maptotal;
|
||||
|
||||
static pureconst size_t __zipos_granularity(void) {
|
||||
return (IsWindows() ? FRAMESIZE : PAGESIZE) * (IsAsan() ? 8 : 1);
|
||||
}
|
||||
|
||||
static void *__zipos_mmap(size_t mapsize) {
|
||||
size_t offset;
|
||||
int prot, flags;
|
||||
struct DirectMap dm;
|
||||
uint64_t addr, addr2;
|
||||
do offset = maptotal;
|
||||
while (!_cmpxchg(&maptotal, offset, maptotal + mapsize));
|
||||
if (offset + mapsize <= kMemtrackZiposSize) {
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
|
||||
addr = kMemtrackZiposStart + offset;
|
||||
if ((dm = sys_mmap((void *)addr, mapsize, prot, flags, -1, 0)).addr !=
|
||||
MAP_FAILED) {
|
||||
TrackMemoryInterval(&_mmi, addr >> 16, (addr + mapsize - 1) >> 16,
|
||||
dm.maphandle, prot, flags, false, false, 0, mapsize);
|
||||
if (IsAsan()) {
|
||||
addr2 = (addr >> 3) + 0x7fff8000;
|
||||
dm = sys_mmap((void *)addr2, mapsize >> 3, prot, flags, -1, 0);
|
||||
TrackMemoryInterval(&_mmi, addr2 >> 16,
|
||||
(addr2 + (mapsize >> 3) - 1) >> 16, dm.maphandle,
|
||||
prot, flags, false, false, 0, mapsize >> 3);
|
||||
}
|
||||
return (void *)addr;
|
||||
}
|
||||
}
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
|
||||
size_t mapsize;
|
||||
struct ZiposHandle *h, **ph;
|
||||
__zipos_lock();
|
||||
mapsize = sizeof(struct ZiposHandle) + size;
|
||||
mapsize = ROUNDUP(mapsize, __zipos_granularity());
|
||||
StartOver:
|
||||
ph = &zipos->freelist;
|
||||
while ((h = *ph)) {
|
||||
if (h->mapsize >= mapsize) {
|
||||
if (!_cmpxchg(ph, h, h->next)) goto StartOver;
|
||||
h->next = 0;
|
||||
break;
|
||||
}
|
||||
ph = &h->next;
|
||||
}
|
||||
if (!h) {
|
||||
h = __zipos_mmap(mapsize);
|
||||
}
|
||||
__zipos_unlock();
|
||||
if (IsAsan()) {
|
||||
__asan_unpoison((char *)h, sizeof(struct ZiposHandle) + size);
|
||||
__asan_poison((char *)h + sizeof(struct ZiposHandle) + size,
|
||||
mapsize - (sizeof(struct ZiposHandle) + size),
|
||||
kAsanHeapOverrun);
|
||||
}
|
||||
if (h) {
|
||||
h->size = size;
|
||||
h->mapsize = mapsize;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static int __zipos_mkfd(int minfd) {
|
||||
int e, fd;
|
||||
static bool demodernize;
|
||||
if (!demodernize) {
|
||||
e = errno;
|
||||
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
|
||||
return fd;
|
||||
} else if (errno == EINVAL) {
|
||||
demodernize = true;
|
||||
errno = e;
|
||||
} else {
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
if ((fd = __sys_fcntl(2, F_DUPFD, minfd)) != -1) {
|
||||
__sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags,
|
||||
int mode) {
|
||||
_cmpxchg(&g_fds.f, fd, fd + 1);
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags = flags | O_CLOEXEC;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].extra = 0;
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
||||
int mode) {
|
||||
size_t lf;
|
||||
int rc, fd;
|
||||
size_t size;
|
||||
uint8_t *data;
|
||||
int rc, fd, minfd;
|
||||
struct ZiposHandle *h;
|
||||
lf = GetZipCfileOffset(zipos->map + cf);
|
||||
if (!IsTiny() &&
|
||||
(ZIP_LFILE_MAGIC(zipos->map + lf) != kZipLfileHdrMagic ||
|
||||
(ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) != kZipCompressionNone &&
|
||||
ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) !=
|
||||
kZipCompressionDeflate))) {
|
||||
return eio();
|
||||
}
|
||||
if (!(h = calloc(1, sizeof(*h)))) return -1;
|
||||
h->cfile = cf;
|
||||
h->size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
if ((h->freeme = malloc(h->size))) {
|
||||
data = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
size = GetZipLfileCompressedSize(zipos->map + lf);
|
||||
if ((rc = __zipos_inflate(h, data, size)) != -1) {
|
||||
h->mem = h->freeme;
|
||||
assert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
|
||||
size = GetZipLfileUncompressedSize(zipos->map + lf);
|
||||
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
|
||||
case kZipCompressionNone:
|
||||
if (!(h = __zipos_alloc(zipos, 0))) return -1;
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
case kZipCompressionDeflate:
|
||||
if (!(h = __zipos_alloc(zipos, size))) return -1;
|
||||
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
|
||||
GetZipLfileCompressedSize(zipos->map + lf))) {
|
||||
h->mem = h->data;
|
||||
} else {
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
|
||||
break;
|
||||
default:
|
||||
return eio();
|
||||
}
|
||||
h->pos = 0;
|
||||
h->cfile = cf;
|
||||
h->size = size;
|
||||
if (!IsTiny() && h->mem &&
|
||||
crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(zipos->map + lf)) {
|
||||
errno = EIO;
|
||||
h->mem = NULL;
|
||||
h->mem = 0;
|
||||
eio();
|
||||
}
|
||||
if (h->mem) {
|
||||
if ((fd = IsWindows() ? __reservefd(-1) : dup(2)) != -1) {
|
||||
if (__ensurefds(fd) != -1) {
|
||||
__fds_lock();
|
||||
h->handle = g_fds.p[fd].handle;
|
||||
g_fds.p[fd].kind = kFdZip;
|
||||
g_fds.p[fd].handle = (intptr_t)h;
|
||||
g_fds.p[fd].flags = flags | O_CLOEXEC;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].extra = 0;
|
||||
__fds_unlock();
|
||||
return fd;
|
||||
minfd = 3;
|
||||
__fds_lock();
|
||||
TryAgain:
|
||||
if (IsWindows()) {
|
||||
if ((fd = __reservefd_unlocked(-1)) != -1) {
|
||||
return __zipos_setfd(fd, h, flags, mode);
|
||||
}
|
||||
close(fd);
|
||||
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
|
||||
if (__ensurefds_unlocked(fd) != -1) {
|
||||
if (g_fds.p[fd].kind) {
|
||||
sys_close(fd);
|
||||
minfd = fd + 1;
|
||||
goto TryAgain;
|
||||
}
|
||||
return __zipos_setfd(fd, h, flags, mode);
|
||||
}
|
||||
sys_close(fd);
|
||||
}
|
||||
__fds_unlock();
|
||||
}
|
||||
free(h->freeme);
|
||||
free(h);
|
||||
__zipos_free(zipos, h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -106,12 +208,12 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
|
|||
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
|
||||
*
|
||||
* @param uri is obtained via __zipos_parseuri()
|
||||
* @note don't call open() from signal handlers
|
||||
* @asyncsignalsafe (todo)
|
||||
* @threadsafe
|
||||
*/
|
||||
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
|
||||
int rc;
|
||||
ssize_t cf;
|
||||
sigset_t oldmask;
|
||||
struct Zipos *zipos;
|
||||
if ((flags & O_ACCMODE) == O_RDONLY) {
|
||||
if ((zipos = __zipos_get())) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue