[multiple] use new sys_setjmp_eval3() interface

wrap system os setjmp or compiler try/catch and access mmap inside
callback to protect against SIGBUS (or _WIN32 EXCEPTION_IN_PAGE_ERROR)
master
Glenn Strauss 10 months ago
parent a314b8d031
commit 6cd3b5f8af
  1. 49
      src/chunk.c
  2. 97
      src/mod_deflate.c
  3. 41
      src/network_write.c
  4. 17
      src/server.c
  5. 6
      src/sys-setjmp.c

@ -13,6 +13,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include "sys-mmap.h"
#include "sys-setjmp.h"
#include <stdlib.h>
#include <fcntl.h>
@ -1397,6 +1398,7 @@ chunkqueue_write_chunk_file (const int fd, chunk * const restrict c, log_error_s
#endif
#if defined(HAVE_MMAP) || defined(_WIN32) /*(see local sys-mmap.h)*/
/*(chunkqueue_write_chunk() caller must protect against SIGBUS, if needed)*/
const char * const data = chunkqueue_mmap_chunk_len(c, count);
if (NULL != data)
return chunkqueue_write_data(fd, data, count);
@ -1515,6 +1517,22 @@ chunkqueue_small_resp_optim (chunkqueue * const restrict cq)
}
#if 0
#if defined(_LP64) || defined(__LP64__) || defined(_WIN64)
#if defined(HAVE_MMAP) || defined(_WIN32) /*see local sys-mmap.h*/
__attribute_noinline__
static off_t
chunk_setjmp_memcpy_cb (void *dst, const void *src, off_t len)
{
/*(on 32-bit systems, caller should assert len <= SIZE_MAX)*/
memcpy(dst, src, (size_t)len);
return len;
}
#endif
#endif
#endif
int
chunkqueue_peek_data (chunkqueue * const cq,
char ** const data, uint32_t * const dlen,
@ -1532,6 +1550,8 @@ chunkqueue_peek_data (chunkqueue * const cq,
uint32_t have = buffer_clen(c->mem) - (uint32_t)c->offset;
if (have > space)
have = space;
if (__builtin_expect( (0 == have), 0))
break;
if (*dlen)
memcpy(data_in + *dlen, c->mem->ptr + c->offset, have);
else
@ -1546,17 +1566,17 @@ chunkqueue_peek_data (chunkqueue * const cq,
off_t len = c->file.length - c->offset;
if (len > (off_t)space)
len = (off_t)space;
if (0 == len)
if (__builtin_expect( (0 == len), 0))
break;
#if 0 /* XXX: might improve performance on some system workloads */
#if defined(_LP64) || defined(__LP64__) || defined(_WIN64)
#if defined(HAVE_MMAP) || defined(_WIN32) /*see local sys-mmap.h*/
/* mmap file to access data
* (Only consider temp files here since not catching SIGBUS)
* (For now, also limit to 64-bit to avoid address space issues)
* If temp file is used, data should be large enough that mmap
* is worthwhile. fd need not be kept open for the mmap once
* (chunkqueue_mmap_chunk_len() uses MAP_PRIVATE, even though we
* might use MAP_SHARED, if supported, and !c->file.is_temp)
* fd need not be kept open for the mmap once
* the mmap has been created, but is currently kept open for
* other pre-existing logic which checks fd and opens file,
* such as the condition for entering this code block above. */
@ -1565,19 +1585,24 @@ chunkqueue_peek_data (chunkqueue * const cq,
* of lots and lots of temp file read-only memory maps.
* pmap -X and exclude lighttpd temporary files to get a better
* view of memory use */
/* Data should be large enough that mmap is worthwhile.
* mmap if file chunk > 16k or if already mapped */
char *mdata;
if (c->file.is_temp
if ((c->file.mmap.start != MAP_FAILED
|| c->file.length - c->offset > 16384)
&& (mdata = chunkqueue_mmap_chunk_len(c, len))) {
if (*dlen) {
if (*data != data_in) {
memcpy(data_in, *data, *dlen);
*data = data_in;
if (!c->file.is_temp) {/*(might be changed to policy flag)*/
if (sys_setjmp_eval3(chunk_setjmp_memcpy_cb,
data_in+*dlen, mdata, len) < 0) {
log_error(errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
return -1;
}
memcpy(data_in+*dlen, mdata, (size_t)len);
}
else {
else if (*dlen)
memcpy(data_in+*dlen, mdata, (size_t)len);
else
*data = mdata;
}
*dlen += (uint32_t)len;
break;
}

@ -156,19 +156,8 @@
#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP
#define USE_MMAP
#include "sys-mmap.h"
#include <setjmp.h>
#include <signal.h>
static volatile int sigbus_jmp_valid;
static sigjmp_buf sigbus_jmp;
static void sigbus_handler(int sig) {
UNUSED(sig);
if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
ck_bt_abort(__FILE__, __LINE__, "SIGBUS");
}
#include "sys-setjmp.h"
#endif
/* request: accept-encoding */
@ -1547,26 +1536,23 @@ static int mod_deflate_using_libdeflate_sm (handler_ctx * const hctx, const plug
#ifdef USE_MMAP
#if defined(_LP64) || defined(__LP64__) || defined(_WIN64)
static void mod_deflate_using_libdeflate_compress (struct libdeflate_compressor * const compressor, handler_ctx * const hctx, void * const out, const size_t outsz)
struct mod_deflate_setjmp_params {
struct libdeflate_compressor *compressor;
void *out;
size_t outsz;
};
static off_t mod_deflate_using_libdeflate_setjmp_cb (void *dst, const void *src, off_t len)
{
signal(SIGBUS, sigbus_handler);
const struct mod_deflate_setjmp_params * const params = dst;
const handler_ctx * const hctx = src;
const chunk * const c = hctx->r->write_queue.first;
if (0 == sigsetjmp(sigbus_jmp, 1)) {
off_t moffset = c->offset - c->file.mmap.offset;
const char *in = c->file.mmap.start + moffset;
size_t in_nbytes = (size_t)hctx->bytes_in;
sigbus_jmp_valid = 1;
size_t sz = (hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP)
? libdeflate_gzip_compress(compressor, in, in_nbytes, out, outsz)
: libdeflate_deflate_compress(compressor, in, in_nbytes, out, outsz);
hctx->bytes_out = (off_t)sz;
sigbus_jmp_valid = 0;
}
else {
sigbus_jmp_valid = 0;
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
}
const char *in = c->file.mmap.start + c->offset - c->file.mmap.offset;
return (off_t)((hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP)
? libdeflate_gzip_compress(params->compressor, in, (size_t)len,
params->out, params->outsz)
: libdeflate_deflate_compress(params->compressor, in, (size_t)len,
params->out, params->outsz));
}
@ -1611,7 +1597,10 @@ static int mod_deflate_using_libdeflate (handler_ctx * const hctx, const plugin_
libdeflate_alloc_compressor(clevel > 0 ? clevel : 6);
/* Z_DEFAULT_COMPRESSION -1 not supported */
if (NULL != compressor) {
mod_deflate_using_libdeflate_compress(compressor, hctx, addr, sz);
struct mod_deflate_setjmp_params outparams = { compressor, addr, sz };
hctx->bytes_out =
sys_setjmp_eval3(mod_deflate_using_libdeflate_setjmp_cb,
&outparams, hctx, hctx->bytes_in);
libdeflate_free_compressor(compressor);
}
@ -1626,7 +1615,12 @@ static int mod_deflate_using_libdeflate (handler_ctx * const hctx, const plugin_
if (0 != munmap(addr, sz))
log_perror(hctx->r->conf.errh, __FILE__, __LINE__, "munmap");
if (0 != hctx->bytes_out && 0 != ftruncate(fd, hctx->bytes_out))
if (0 == hctx->bytes_out) {
const chunk * const c = hctx->r->write_queue.first;
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
}
else if (0 != ftruncate(fd, hctx->bytes_out))
hctx->bytes_out = 0;
if (0 == hctx->bytes_out)
@ -1673,33 +1667,34 @@ static off_t mod_deflate_file_chunk_no_mmap(request_st * const r, handler_ctx *
#ifdef USE_MMAP
static off_t mod_deflate_file_chunk_setjmp_cb (void *dst, const void *src, off_t len)
{
return mod_deflate_compress(dst, (const unsigned char *)src, len);
}
static off_t mod_deflate_file_chunk_mmap(request_st * const r, handler_ctx * const hctx, chunk * const c, off_t n)
{
if (!mod_deflate_file_chunk_remap(c, n))
return mod_deflate_file_chunk_no_mmap(r, hctx, c, n);
signal(SIGBUS, sigbus_handler);
if (0 == sigsetjmp(sigbus_jmp, 1)) {
off_t moffset = c->offset - c->file.mmap.offset;
char *p = c->file.mmap.start + moffset;
off_t len = c->file.mmap.length - moffset;
if (len > n) len = n;
sigbus_jmp_valid = 1;
int rc = mod_deflate_compress(hctx, (unsigned char *)p, len);
sigbus_jmp_valid = 0;
if (rc < 0) {
const off_t moffset = c->offset - c->file.mmap.offset;
char * const p = c->file.mmap.start + moffset;
off_t len = c->file.mmap.length - moffset;
if (len > n) len = n;
off_t rc = sys_setjmp_eval3(mod_deflate_file_chunk_setjmp_cb, hctx, p, len);
if (__builtin_expect( (rc < 0), 0)) {
if (errno == EFAULT)
log_error(r->conf.errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
else
log_error(r->conf.errh, __FILE__, __LINE__, "compress failed.");
len = -1;
}
return len;
}
else {
sigbus_jmp_valid = 0;
log_error(r->conf.errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
return -1;
len = -1; /*return -1;*/
}
return len;
}
#endif

@ -183,9 +183,7 @@ static int network_write_file_chunk_no_mmap(const int fd, chunkqueue * const cq,
#if defined(NETWORK_WRITE_USE_MMAP)
#include "sys-mmap.h"
#include <setjmp.h>
#include <signal.h>
#include "sys-setjmp.h"
#define MMAP_CHUNK_SIZE (512*1024)
@ -199,15 +197,6 @@ static off_t mmap_align_offset(off_t start) {
return start - (start % pagesize);
}
static volatile int sigbus_jmp_valid;
static sigjmp_buf sigbus_jmp;
static void sigbus_handler(int sig) {
UNUSED(sig);
if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
ck_bt_abort(__FILE__, __LINE__, "SIGBUS");
}
static int
network_write_file_chunk_remap(chunk * const c)
{
@ -272,6 +261,12 @@ network_write_file_chunk_remap(chunk * const c)
}
static off_t
network_write_setjmp_write_cb (void *fd, const void *data, off_t len)
{
return network_write_data_len((int)(uintptr_t)fd, data, len);
}
/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */
static int network_write_file_chunk_mmap(const int fd, chunkqueue * const cq, off_t * const p_max_bytes, log_error_st * const errh) {
chunk* const c = cq->first;
@ -288,26 +283,20 @@ static int network_write_file_chunk_mmap(const int fd, chunkqueue * const cq, of
off_t mmap_avail = (off_t)c->file.mmap.length - mmap_offset;
const char * const data = c->file.mmap.start + mmap_offset;
if (toSend > mmap_avail) toSend = mmap_avail;
/* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
if (0 == sigsetjmp(sigbus_jmp, 1)) {
signal(SIGBUS, sigbus_handler);
sigbus_jmp_valid = 1;
ssize_t wr = network_write_data_len(fd, data, toSend);
sigbus_jmp_valid = 0;
return network_write_accounting(fd, cq, p_max_bytes, errh, wr, toSend);
} else {
sigbus_jmp_valid = 0;
off_t wr = sys_setjmp_eval3(network_write_setjmp_write_cb,
(void *)(uintptr_t)fd, data, toSend);
#if 0
/*(future: might writev() with multiple maps,
* so will not know which chunk failed)*/
if (wr < 0 && errno == EFAULT) { /*(see sys-setjmp.c)*/
log_error(errh, __FILE__, __LINE__,
"SIGBUS in mmap: %s %d", c->mem->ptr, c->file.fd);
munmap(c->file.mmap.start, c->file.mmap.length);
c->file.mmap.start = MAP_FAILED;
return -1;
}
#endif
return network_write_accounting(fd,cq,p_max_bytes,errh,(ssize_t)wr,toSend);
}
#endif /* NETWORK_WRITE_USE_MMAP */

@ -30,6 +30,7 @@ static const buffer default_server_tag = { CONST_STR_LEN(PACKAGE_DESC)+1, 0 };
#include <sys/types.h>
#include <sys/stat.h>
#include "sys-setjmp.h"
#include "sys-time.h"
#include <string.h>
@ -1503,19 +1504,27 @@ static int server_main_setup (server * const srv, int argc, char **argv) {
#ifdef HAVE_SIGACTION
memset(&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL);
#ifndef _MSC_VER
act.sa_flags = SA_NODEFER;
act.sa_handler = sys_setjmp_sigbus;
sigaction(SIGBUS, &act, NULL);
act.sa_flags = 0;
#endif
# if defined(SA_SIGINFO)
last_sighup_info.si_uid = 0,
last_sighup_info.si_pid = 0;
last_sigterm_info.si_uid = 0,
last_sigterm_info.si_pid = 0;
act.sa_sigaction = sigaction_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
# else
act.sa_handler = signal_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
# endif
sigaction(SIGINT, &act, NULL);
@ -1527,7 +1536,6 @@ static int server_main_setup (server * const srv, int argc, char **argv) {
/* it should be safe to restart syscalls after SIGCHLD */
act.sa_flags |= SA_RESTART | SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, NULL);
#elif defined(HAVE_SIGNAL)
/* ignore the SIGPIPE from sendfile() */
signal(SIGPIPE, SIG_IGN);
@ -1537,6 +1545,9 @@ static int server_main_setup (server * const srv, int argc, char **argv) {
signal(SIGCHLD, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
#ifndef _MSC_VER
signal(SIGBUS, sys_setjmp_sigbus);
#endif
#endif

@ -51,9 +51,15 @@ void sys_setjmp_sigbus (int sig)
* something (elsewhere) is missing protection to catch SIGBUS.)
*/
/* Note: sigaction() config in server.c sets SA_NODEFER and empty signal mask
* so we avoid saving and restoring signal mask on systems with sigaction() */
#ifdef _WIN32
#define if_SYS_SETJMP_TRY() if ((sys_setjmp_sigbus_jmp_valid = \
!setjmp(sys_setjmp_sigbus_jmp_buf))) {
#elif defined(HAVE_SIGACTION)
#define if_SYS_SETJMP_TRY() if ((sys_setjmp_sigbus_jmp_valid = \
!sigsetjmp(sys_setjmp_sigbus_jmp_buf, 0))) {
#else
#define if_SYS_SETJMP_TRY() if ((sys_setjmp_sigbus_jmp_valid = \
!sigsetjmp(sys_setjmp_sigbus_jmp_buf, 1))) {

Loading…
Cancel
Save