]> mj.ucw.cz Git - libucw.git/blob - ucw/lizard-safe.c
tableprinter: code cleanup contd.
[libucw.git] / ucw / 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 <ucw/lib.h>
11 #include <ucw/threads.h>
12 #include <ucw/sighandler.h>
13 #include <ucw/lizard.h>
14
15 #include <sys/mman.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <setjmp.h>
19 #include <errno.h>
20
21 struct lizard_buffer {
22   uint 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 + CPU_PAGE_SIZE);
42   xfree(buf);
43 }
44
45 static void
46 lizard_realloc(struct lizard_buffer *buf, uint max_len)
47   /* max_len needs to be aligned to CPU_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 + CPU_PAGE_SIZE);
56   buf->len = max_len;
57   buf->ptr = mmap(NULL, buf->len + CPU_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", (uint)(buf->len + CPU_PAGE_SIZE));
60   if (mprotect(buf->ptr + buf->len, CPU_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(const byte *in, struct lizard_buffer *buf, uint expected_length)
74 {
75   uint lock_offset = ALIGN_TO(expected_length + 3, CPU_PAGE_SIZE);      // +3 due to the unaligned access
76   if (lock_offset > buf->len)
77     lizard_realloc(buf, lock_offset);
78   volatile ucw_sighandler_t old_handler = set_signal_handler(SIGSEGV, sigsegv_handler);
79   byte *ptr;
80   if (!setjmp(safe_decompress_jump))
81   {
82     ptr = buf->ptr + buf->len - lock_offset;
83     int len = lizard_decompress(in, ptr);
84     if (len != (int) expected_length)
85     {
86       ptr = NULL;
87       errno = EINVAL;
88     }
89   }
90   else
91   {
92     msg(L_ERROR, "SIGSEGV caught in lizard_decompress()");
93     ptr = NULL;
94     errno = EFAULT;
95   }
96   set_signal_handler(SIGSEGV, old_handler);
97   return ptr;
98 }