]> mj.ucw.cz Git - libucw.git/blob - lib/lizard-safe.c
bugfix in image scaling (select the correct strategy)
[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 <sys/mman.h>
14 #include <sys/user.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   struct sigaction old_sigsegv_handler;
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, &buf->old_sigsegv_handler);
33   return buf;
34 }
35
36 void
37 lizard_free(struct lizard_buffer *buf)
38 {
39   if (buf->ptr)
40     munmap(buf->ptr, buf->len + PAGE_SIZE);
41   unhandle_signal(SIGSEGV, &buf->old_sigsegv_handler);
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): %m");
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   log(L_ERROR, "SIGSEGV caught in lizard_decompress()");
69   longjmp(safe_decompress_jump, 1);
70   return 1;
71 }
72
73 byte *
74 lizard_decompress_safe(byte *in, struct lizard_buffer *buf, uns expected_length)
75   /* Decompresses in into buf, sets *ptr to the data, and returns the
76    * uncompressed length.  If an error has occured, -1 is returned and errno is
77    * set.  The buffer buf is automatically reallocated.  SIGSEGV is caught in
78    * case of buffer-overflow.  The function is not re-entrant because of a
79    * static longjmp handler.  */
80 {
81   uns lock_offset = ALIGN_TO(expected_length + 3, PAGE_SIZE);   // +3 due to the unaligned access
82   if (lock_offset > buf->len)
83     lizard_realloc(buf, lock_offset);
84   volatile sh_sighandler_t old_handler = signal_handler[SIGSEGV];
85   signal_handler[SIGSEGV] = sigsegv_handler;
86   byte *ptr;
87   if (!setjmp(safe_decompress_jump))
88   {
89     ptr = buf->ptr + buf->len - lock_offset;
90     int len = lizard_decompress(in, ptr);
91     if (len != (int) expected_length)
92     {
93       ptr = NULL;
94       errno = EINVAL;
95     }
96   }
97   else
98   {
99     ptr = NULL;
100     errno = EFAULT;
101   }
102   signal_handler[SIGSEGV] = old_handler;
103   return ptr;
104 }