]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
dcc01d11f59a6a69528eac1fd75f86f867230d35
[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/lizard.h"
12
13 #include <stdlib.h>
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   struct sigaction *old_sigsegv_handler;
25 };
26
27 struct lizard_buffer *
28 lizard_alloc(void)
29 {
30   struct lizard_buffer *buf = xmalloc(sizeof(struct lizard_buffer));
31   buf->len = 0;
32   buf->ptr = NULL;
33   buf->old_sigsegv_handler = xmalloc(sizeof(struct sigaction));
34   handle_signal(SIGSEGV, buf->old_sigsegv_handler);
35   return buf;
36 }
37
38 void
39 lizard_free(struct lizard_buffer *buf)
40 {
41   if (buf->ptr)
42     munmap(buf->ptr, buf->len + PAGE_SIZE);
43   unhandle_signal(SIGSEGV, buf->old_sigsegv_handler);
44   xfree(buf->old_sigsegv_handler);
45   xfree(buf);
46 }
47
48 static void
49 lizard_realloc(struct lizard_buffer *buf, uns max_len)
50   /* max_len needs to be aligned to PAGE_SIZE */
51 {
52   if (max_len <= buf->len)
53     return;
54   if (max_len < 2*buf->len)                             // to ensure logarithmic cost
55     max_len = 2*buf->len;
56
57   if (buf->ptr)
58     munmap(buf->ptr, buf->len + PAGE_SIZE);
59   buf->len = max_len;
60   buf->ptr = mmap(NULL, buf->len + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
61   if (buf->ptr == MAP_FAILED)
62     die("mmap(anonymous): %m");
63   if (mprotect(buf->ptr + buf->len, PAGE_SIZE, PROT_NONE) < 0)
64     die("mprotect: %m");
65 }
66
67 static jmp_buf safe_decompress_jump;
68 static int
69 sigsegv_handler(int signal UNUSED)
70 {
71   log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
72   longjmp(safe_decompress_jump, 1);
73   return 1;
74 }
75
76 int
77 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length, byte **ptr)
78   /* Decompresses in into buf, sets *ptr to the data, and returns the
79    * uncompressed length.  If an error has occured, -1 is returned and errno is
80    * set.  The buffer buf is automatically reallocated.  SIGSEGV is caught in
81    * case of buffer-overflow.  The function is not re-entrant because of a
82    * static longjmp handler.  */
83 {
84   uns lock_offset = ALIGN(expected_length + 3, PAGE_SIZE);      // +3 due to the unaligned access
85   if (lock_offset > buf->len)
86     lizard_realloc(buf, lock_offset);
87   volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
88   signal_handler[SIGSEGV] = sigsegv_handler;
89   int len;
90   if (!setjmp(safe_decompress_jump))
91   {
92     *ptr = buf->ptr + buf->len - lock_offset;
93     len = lizard_decompress(in, *ptr);
94   }
95   else
96   {
97     *ptr = NULL;
98     len = -1;
99     errno = EFAULT;
100   }
101   signal_handler[SIGSEGV] = old_handler;
102   return len;
103 }