*/
#include "lib/lib.h"
+#include "lib/threads.h"
#include "lib/lizard.h"
-#include <stdlib.h>
#include <sys/mman.h>
-#include <sys/user.h>
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
-static void
-lizard_alloc_internal(struct lizard_buffer *buf, uns max_len)
-{
- if (!max_len)
- {
- buf->len = 0;
- buf->start = NULL;
- return;
- }
- buf->len = ALIGN(max_len + 3, PAGE_SIZE); // +3 due to the unaligned access
- buf->start = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if (buf->start == MAP_FAILED)
- die("mmap(anonymous): %m");
- if (mprotect(buf->start + buf->len, PAGE_SIZE, PROT_NONE) < 0)
- die("mprotect: %m");
-}
+struct lizard_buffer {
+ uns len;
+ void *ptr;
+};
struct lizard_buffer *
-lizard_alloc(uns max_len)
+lizard_alloc(void)
{
struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
- lizard_alloc_internal(buf, max_len);
- buf->old_sigsegv_handler = xmalloc(sizeof(struct sigaction));
- handle_signal(SIGSEGV, buf->old_sigsegv_handler);
+ buf->len = 0;
+ buf->ptr = NULL;
+ handle_signal(SIGSEGV);
return buf;
}
void
lizard_free(struct lizard_buffer *buf)
{
- if (buf->start)
- munmap(buf->start, buf->len + PAGE_SIZE);
- unhandle_signal(SIGSEGV, buf->old_sigsegv_handler);
- xfree(buf->old_sigsegv_handler);
+ unhandle_signal(SIGSEGV);
+ if (buf->ptr)
+ munmap(buf->ptr, buf->len + CPU_PAGE_SIZE);
xfree(buf);
}
-void
+static void
lizard_realloc(struct lizard_buffer *buf, uns max_len)
+ /* max_len needs to be aligned to CPU_PAGE_SIZE */
{
- max_len += 3;
if (max_len <= buf->len)
return;
- if (max_len < 2*buf->len) // to ensure amortized logarithmic complexity
+ if (max_len < 2*buf->len) // to ensure logarithmic cost
max_len = 2*buf->len;
- if (buf->start)
- munmap(buf->start - 3, buf->len + PAGE_SIZE);
- lizard_alloc_internal(buf, max_len);
+
+ if (buf->ptr)
+ munmap(buf->ptr, buf->len + CPU_PAGE_SIZE);
+ buf->len = max_len;
+ buf->ptr = mmap(NULL, buf->len + CPU_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (buf->ptr == MAP_FAILED)
+ die("mmap(anonymous, %d bytes): %m", (uns)(buf->len + CPU_PAGE_SIZE));
+ if (mprotect(buf->ptr + buf->len, CPU_PAGE_SIZE, PROT_NONE) < 0)
+ die("mprotect: %m");
}
static jmp_buf safe_decompress_jump;
static int
sigsegv_handler(int signal UNUSED)
{
- log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
longjmp(safe_decompress_jump, 1);
return 1;
}
-int
+byte *
lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
- /* Decompresses into buf->ptr and returns the length of the uncompressed
- * file. If an error has occured, -1 is returned and errno is set. SIGSEGV
- * is caught in the case of buffer-overflow. The function is not re-entrant
- * because of a static longjmp handler. */
+ /* Decompresses in into buf, sets *ptr to the data, and returns the
+ * uncompressed length. If an error has occured, -1 is returned and errno is
+ * set. The buffer buf is automatically reallocated. SIGSEGV is caught in
+ * case of buffer-overflow. The function is not re-entrant because of a
+ * static longjmp handler. */
{
- uns lock_offset = ALIGN(expected_length + 3, PAGE_SIZE); // +3 due to the unaligned access
+ uns lock_offset = ALIGN_TO(expected_length + 3, CPU_PAGE_SIZE); // +3 due to the unaligned access
if (lock_offset > buf->len)
- {
- errno = EFBIG;
- return -1;
- }
- volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
- signal_handler[SIGSEGV] = sigsegv_handler;
- int len;
+ lizard_realloc(buf, lock_offset);
+ volatile sh_sighandler_t old_handler = set_signal_handler(SIGSEGV, sigsegv_handler);
+ byte *ptr;
if (!setjmp(safe_decompress_jump))
{
- buf->ptr = buf->start + buf->len - lock_offset;
- len = lizard_decompress(in, buf->ptr);
+ ptr = buf->ptr + buf->len - lock_offset;
+ int len = lizard_decompress(in, ptr);
+ if (len != (int) expected_length)
+ {
+ ptr = NULL;
+ errno = EINVAL;
+ }
}
else
{
- buf->ptr = NULL;
- len = -1;
+ log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
+ ptr = NULL;
errno = EFAULT;
}
- signal_handler[SIGSEGV] = old_handler;
- return len;
+ set_signal_handler(SIGSEGV, old_handler);
+ return ptr;
}