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