1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <algorithm>
6 #include "SeekableZStream.h"
7 #include "Logging.h"
8 
9 bool
Init(const void * buf,size_t length)10 SeekableZStream::Init(const void *buf, size_t length)
11 {
12   const SeekableZStreamHeader *header = SeekableZStreamHeader::validate(buf);
13   if (!header) {
14     ERROR("Not a seekable zstream");
15     return false;
16   }
17 
18   buffer = reinterpret_cast<const unsigned char *>(buf);
19   totalSize = header->totalSize;
20   chunkSize = header->chunkSize;
21   lastChunkSize = header->lastChunkSize;
22   windowBits = header->windowBits;
23   dictionary.Init(buffer + sizeof(SeekableZStreamHeader), header->dictSize);
24   offsetTable.Init(buffer + sizeof(SeekableZStreamHeader) + header->dictSize,
25                    header->nChunks);
26   filter = GetFilter(header->filter);
27 
28   /* Sanity check */
29   if ((chunkSize == 0) ||
30       (!IsPageAlignedSize(chunkSize)) ||
31       (chunkSize > 8 * PageSize()) ||
32       (offsetTable.numElements() < 1) ||
33       (lastChunkSize == 0) ||
34       (lastChunkSize > chunkSize) ||
35       (length < totalSize)) {
36     ERROR("Malformed or broken seekable zstream");
37     return false;
38   }
39 
40   return true;
41 }
42 
43 bool
Decompress(void * where,size_t chunk,size_t length)44 SeekableZStream::Decompress(void *where, size_t chunk, size_t length)
45 {
46   while (length) {
47     size_t len = std::min(length, static_cast<size_t>(chunkSize));
48     if (!DecompressChunk(where, chunk, len))
49       return false;
50     where = reinterpret_cast<unsigned char *>(where) + len;
51     length -= len;
52     chunk++;
53   }
54   return true;
55 }
56 
57 bool
DecompressChunk(void * where,size_t chunk,size_t length)58 SeekableZStream::DecompressChunk(void *where, size_t chunk, size_t length)
59 {
60   if (chunk >= offsetTable.numElements()) {
61     ERROR("DecompressChunk: chunk #%" PRIdSize " out of range [0-%" PRIdSize ")",
62         chunk, offsetTable.numElements());
63     return false;
64   }
65 
66   bool isLastChunk = (chunk == offsetTable.numElements() - 1);
67 
68   size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize;
69 
70   if (length == 0 || length > chunkLen)
71     length = chunkLen;
72 
73   DEBUG_LOG("DecompressChunk #%" PRIdSize " @%p (%" PRIdSize "/% " PRIdSize ")",
74         chunk, where, length, chunkLen);
75   zxx_stream zStream(&allocator);
76   zStream.avail_in = (isLastChunk ? totalSize : uint32_t(offsetTable[chunk + 1]))
77                      - uint32_t(offsetTable[chunk]);
78   zStream.next_in = const_cast<Bytef *>(buffer + uint32_t(offsetTable[chunk]));
79   zStream.avail_out = length;
80   zStream.next_out = reinterpret_cast<Bytef *>(where);
81 
82   /* Decompress chunk */
83   if (inflateInit2(&zStream, windowBits) != Z_OK) {
84     ERROR("inflateInit failed: %s", zStream.msg);
85     return false;
86   }
87   if (dictionary && inflateSetDictionary(&zStream, dictionary,
88                                          dictionary.numElements()) != Z_OK) {
89     ERROR("inflateSetDictionary failed: %s", zStream.msg);
90     return false;
91   }
92   if (inflate(&zStream, (length == chunkLen) ? Z_FINISH : Z_SYNC_FLUSH)
93       != (length == chunkLen) ? Z_STREAM_END : Z_OK) {
94     ERROR("inflate failed: %s", zStream.msg);
95     return false;
96   }
97   if (inflateEnd(&zStream) != Z_OK) {
98     ERROR("inflateEnd failed: %s", zStream.msg);
99     return false;
100   }
101   if (filter)
102     filter(chunk * chunkSize, UNFILTER, (unsigned char *)where, chunkLen);
103 
104   return true;
105 }
106 
107 /* Branch/Call/Jump conversion filter for Thumb, derived from xz-utils
108  * by Igor Pavlov and Lasse Collin, published in the public domain */
109 static void
BCJ_Thumb_filter(off_t offset,SeekableZStream::FilterDirection dir,unsigned char * buf,size_t size)110 BCJ_Thumb_filter(off_t offset, SeekableZStream::FilterDirection dir,
111                  unsigned char *buf, size_t size)
112 {
113   size_t i;
114   for (i = 0; i + 4 <= size; i += 2) {
115     if ((buf[i + 1] & 0xf8) == 0xf0 && (buf[i + 3] & 0xf8) == 0xf8) {
116       uint32_t src = (buf[i] << 11)
117                      | ((buf[i + 1] & 0x07) << 19)
118                      | buf[i + 2]
119                      | ((buf[i + 3] & 0x07) << 8);
120       src <<= 1;
121       uint32_t dest;
122       if (dir == SeekableZStream::FILTER)
123         dest = offset + (uint32_t)(i) + 4 + src;
124       else
125         dest = src - (offset + (uint32_t)(i) + 4);
126 
127       dest >>= 1;
128       buf[i] = dest >> 11;
129       buf[i + 1] = 0xf0 | ((dest >> 19) & 0x07);
130       buf[i + 2] = dest;
131       buf[i + 3] = 0xf8 | ((dest >> 8) & 0x07);
132       i += 2;
133     }
134   }
135 }
136 
137 /* Branch/Call/Jump conversion filter for ARM, derived from xz-utils
138  * by Igor Pavlov and Lasse Collin, published in the public domain */
139 static void
BCJ_ARM_filter(off_t offset,SeekableZStream::FilterDirection dir,unsigned char * buf,size_t size)140 BCJ_ARM_filter(off_t offset, SeekableZStream::FilterDirection dir,
141                unsigned char *buf, size_t size)
142 {
143   size_t i;
144   for (i = 0; i + 4 <= size; i += 4) {
145     if (buf[i + 3] == 0xeb) {
146       uint32_t src = buf[i]
147                      | (buf[i + 1] << 8)
148                      | (buf[i + 2] << 16);
149       src <<= 2;
150       uint32_t dest;
151       if (dir == SeekableZStream::FILTER)
152         dest = offset + (uint32_t)(i) + 8 + src;
153       else
154         dest = src - (offset + (uint32_t)(i) + 8);
155 
156       dest >>= 2;
157       buf[i] = dest;
158       buf[i + 1] = dest >> 8;
159       buf[i + 2] = dest >> 16;
160     }
161   }
162 }
163 
164 /* Branch/Call/Jump conversion filter for x86, derived from xz-utils
165  * by Igor Pavlov and Lasse Collin, published in the public domain */
166 
167 #define Test86MSByte(b) ((b) == 0 || (b) == 0xff)
168 
169 static void
BCJ_X86_filter(off_t offset,SeekableZStream::FilterDirection dir,unsigned char * buf,size_t size)170 BCJ_X86_filter(off_t offset, SeekableZStream::FilterDirection dir,
171                unsigned char *buf, size_t size)
172 {
173   static const bool MASK_TO_ALLOWED_STATUS[8] =
174     { true, true, true, false, true, false, false, false };
175 
176   static const uint32_t MASK_TO_BIT_NUMBER[8] =
177     { 0, 1, 2, 2, 3, 3, 3, 3 };
178 
179   uint32_t prev_mask = 0;
180   uint32_t prev_pos = 0;
181 
182   for (size_t i = 0; i + 5 <= size;) {
183     uint8_t b = buf[i];
184     if (b != 0xe8 && b != 0xe9) {
185       ++i;
186       continue;
187     }
188 
189     const uint32_t off = offset + (uint32_t)(i) - prev_pos;
190     prev_pos = offset + (uint32_t)(i);
191 
192     if (off > 5) {
193       prev_mask = 0;
194     } else {
195       for (uint32_t i = 0; i < off; ++i) {
196         prev_mask &= 0x77;
197         prev_mask <<= 1;
198       }
199     }
200 
201     b = buf[i + 4];
202 
203     if (Test86MSByte(b) && MASK_TO_ALLOWED_STATUS[(prev_mask >> 1) & 0x7]
204         && (prev_mask >> 1) < 0x10) {
205 
206       uint32_t src = ((uint32_t)(b) << 24)
207                      | ((uint32_t)(buf[i + 3]) << 16)
208                      | ((uint32_t)(buf[i + 2]) << 8)
209                      | (buf[i + 1]);
210 
211       uint32_t dest;
212       while (true) {
213         if (dir == SeekableZStream::FILTER)
214           dest = src + (offset + (uint32_t)(i) + 5);
215         else
216           dest = src - (offset + (uint32_t)(i) + 5);
217 
218         if (prev_mask == 0)
219           break;
220 
221         const uint32_t i = MASK_TO_BIT_NUMBER[prev_mask >> 1];
222 
223         b = (uint8_t)(dest >> (24 - i * 8));
224 
225         if (!Test86MSByte(b))
226           break;
227 
228         src = dest ^ ((1 << (32 - i * 8)) - 1);
229       }
230 
231       buf[i + 4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
232       buf[i + 3] = (uint8_t)(dest >> 16);
233       buf[i + 2] = (uint8_t)(dest >> 8);
234       buf[i + 1] = (uint8_t)(dest);
235       i += 5;
236       prev_mask = 0;
237 
238     } else {
239       ++i;
240       prev_mask |= 1;
241       if (Test86MSByte(b))
242         prev_mask |= 0x10;
243     }
244   }
245 }
246 
247 SeekableZStream::ZStreamFilter
GetFilter(SeekableZStream::FilterId id)248 SeekableZStream::GetFilter(SeekableZStream::FilterId id)
249 {
250   switch (id) {
251   case BCJ_THUMB:
252     return BCJ_Thumb_filter;
253   case BCJ_ARM:
254     return BCJ_ARM_filter;
255   case BCJ_X86:
256     return BCJ_X86_filter;
257   default:
258     return nullptr;
259   }
260   return nullptr;
261 }
262