]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
7553b45944e04269a73b6dfbb1be0ea48853829b
[libucw.git] / lib / lizard-safe.c
1 /*
2  *      LiZaRd -- Fast compression method based on Lempel-Ziv 77
3  *
4  *      (c) 2004, Robert Spalek <robert@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "lib/threads.h"
12 #include "lib/lizard.h"
13
14 #include <sys/mman.h>
15 #include <sys/user.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <setjmp.h>
19 #include <errno.h>
20
21 struct lizard_buffer {
22   uns len;
23   void *ptr;
24 };
25
26 struct lizard_buffer *
27 lizard_alloc(void)
28 {
29   struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
30   buf->len = 0;
31   buf->ptr = NULL;
32   handle_signal(SIGSEGV);
33   return buf;
34 }
35
36 void
37 lizard_free(struct lizard_buffer *buf)
38 {
39   unhandle_signal(SIGSEGV);
40   if (buf->ptr)
41     munmap(buf->ptr, buf->len + PAGE_SIZE);
42   xfree(buf);
43 }
44
45 static void
46 lizard_realloc(struct lizard_buffer *buf, uns max_len)
47   /* max_len needs to be aligned to PAGE_SIZE */
48 {
49   if (max_len <= buf->len)
50     return;
51   if (max_len < 2*buf->len)                             // to ensure logarithmic cost
52     max_len = 2*buf->len;
53
54   if (buf->ptr)
55     munmap(buf->ptr, buf->len + PAGE_SIZE);
56   buf->len = max_len;
57   buf->ptr = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
58   if (buf->ptr == MAP_FAILED)
59     die("mmap(anonymous, %d bytes): %m", (uns)(buf->len + PAGE_SIZE));
60   if (mprotect(buf->ptr + buf->len, PAGE_SIZE, PROT_NONE) < 0)
61     die("mprotect: %m");
62 }
63
64 static jmp_buf safe_decompress_jump;
65 static int
66 sigsegv_handler(int signal UNUSED)
67 {
68   longjmp(safe_decompress_jump, 1);
69   return 1;
70 }
71
72 byte *
73 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
74   /* Decompresses in into buf, sets *ptr to the data, and returns the
75    * uncompressed length.  If an error has occured, -1 is returned and errno is
76    * set.  The buffer buf is automatically reallocated.  SIGSEGV is caught in
77    * case of buffer-overflow.  The function is not re-entrant because of a
78    * static longjmp handler.  */
79 {
80   uns lock_offset = ALIGN_TO(expected_length + 3, PAGE_SIZE);   // +3 due to the unaligned access
81   if (lock_offset > buf->len)
82     lizard_realloc(buf, lock_offset);
83   volatile sh_sighandler_t old_handler = set_signal_handler(SIGSEGV, sigsegv_handler);
84   byte *ptr;
85   if (!setjmp(safe_decompress_jump))
86   {
87     ptr = buf->ptr + buf->len - lock_offset;
88     int len = lizard_decompress(in, ptr);
89     if (len != (int) expected_length)
90     {
91       ptr = NULL;
92       errno = EINVAL;
93     }
94   }
95   else
96   {
97     log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
98     ptr = NULL;
99     errno = EFAULT;
100   }
101   set_signal_handler(SIGSEGV, old_handler);
102   return ptr;
103 }