1 /* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2    Copyright (C) 2009 Red Hat, Inc.
3    This file is part of elfutils.
4 
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7 
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11 
12    or
13 
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17 
18    or both in parallel, as here.
19 
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24 
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28 
29 #include "libdwflP.h"
30 #include "system.h"
31 
32 #include <unistd.h>
33 
34 #ifdef LZMA
35 # define USE_INFLATE	1
36 # include <lzma.h>
37 # define unzip		__libdw_unlzma
38 # define DWFL_E_ZLIB	DWFL_E_LZMA
39 # define MAGIC		"\xFD" "7zXZ\0" /* XZ file format.  */
40 # define MAGIC2		"\x5d\0"	/* Raw LZMA format.  */
41 # define Z(what)	LZMA_##what
42 # define LZMA_ERRNO	LZMA_PROG_ERROR
43 # define z_stream	lzma_stream
44 # define inflateInit(z)	lzma_auto_decoder (z, 1 << 30, 0)
45 # define do_inflate(z)	lzma_code (z, LZMA_RUN)
46 # define inflateEnd(z)	lzma_end (z)
47 #elif defined BZLIB
48 # define USE_INFLATE	1
49 # include <bzlib.h>
50 # define unzip		__libdw_bunzip2
51 # define DWFL_E_ZLIB	DWFL_E_BZLIB
52 # define MAGIC		"BZh"
53 # define Z(what)	BZ_##what
54 # define BZ_ERRNO	BZ_IO_ERROR
55 # define z_stream	bz_stream
56 # define inflateInit(z)	BZ2_bzDecompressInit (z, 0, 0)
57 # define do_inflate(z)	BZ2_bzDecompress (z)
58 # define inflateEnd(z)	BZ2_bzDecompressEnd (z)
59 #else
60 # define USE_INFLATE	0
61 # define crc32		loser_crc32
62 # include <zlib.h>
63 # define unzip		__libdw_gunzip
64 # define MAGIC		"\037\213"
65 # define Z(what)	Z_##what
66 #endif
67 
68 #define READ_SIZE		(1 << 20)
69 
70 struct unzip_state {
71 #if !USE_INFLATE
72   gzFile zf;
73 #endif
74   size_t mapped_size;
75   void **whole;
76   void *buffer;
77   size_t size;
78   void *input_buffer;
79   off_t input_pos;
80 };
81 
82 static inline bool
bigger_buffer(struct unzip_state * state,size_t start)83 bigger_buffer (struct unzip_state *state, size_t start)
84 {
85   size_t more = state->size ? state->size * 2 : start;
86   char *b = realloc (state->buffer, more);
87   while (unlikely (b == NULL) && more >= state->size + 1024)
88     b = realloc (state->buffer, more -= 1024);
89   if (unlikely (b == NULL))
90     return false;
91   state->buffer = b;
92   state->size = more;
93   return true;
94 }
95 
96 static inline void
smaller_buffer(struct unzip_state * state,size_t end)97 smaller_buffer (struct unzip_state *state, size_t end)
98 {
99   state->buffer =
100       realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
101   state->size = end;
102 }
103 
104 static inline Dwfl_Error
fail(struct unzip_state * state,Dwfl_Error failure)105 fail (struct unzip_state *state, Dwfl_Error failure)
106 {
107   if (state->input_pos == (off_t) state->mapped_size)
108     *state->whole = state->input_buffer;
109   else
110     {
111       free (state->input_buffer);
112       *state->whole = NULL;
113     }
114   free (state->buffer);
115   return failure;
116 }
117 
118 static inline Dwfl_Error
zlib_fail(struct unzip_state * state,int result)119 zlib_fail (struct unzip_state *state, int result)
120 {
121   switch (result)
122     {
123     case Z (MEM_ERROR):
124       return fail (state, DWFL_E_NOMEM);
125     case Z (ERRNO):
126       return fail (state, DWFL_E_ERRNO);
127     default:
128       return fail (state, DWFL_E_ZLIB);
129     }
130 }
131 
132 #if !USE_INFLATE
133 static Dwfl_Error
open_stream(int fd,off_t start_offset,struct unzip_state * state)134 open_stream (int fd, off_t start_offset, struct unzip_state *state)
135 {
136     int d = dup (fd);
137     if (unlikely (d < 0))
138       return DWFL_E_BADELF;
139     if (start_offset != 0)
140       {
141 	off_t off = lseek (d, start_offset, SEEK_SET);
142 	if (off != start_offset)
143 	  {
144 	    close (d);
145 	    return DWFL_E_BADELF;
146 	  }
147       }
148     state->zf = gzdopen (d, "r");
149     if (unlikely (state->zf == NULL))
150       {
151 	close (d);
152 	return zlib_fail (state, Z (MEM_ERROR));
153       }
154 
155     /* From here on, zlib will close D.  */
156 
157     return DWFL_E_NOERROR;
158 }
159 #endif
160 
161 /* If this is not a compressed image, return DWFL_E_BADELF.
162    If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
163    Otherwise return an error for bad compressed data or I/O failure.
164    If we return an error after reading the first part of the file,
165    leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
166    is not null on entry, we'll use it in lieu of repeating a read.  */
167 
168 Dwfl_Error internal_function
unzip(int fd,off_t start_offset,void * mapped,size_t _mapped_size,void ** _whole,size_t * whole_size)169 unzip (int fd, off_t start_offset,
170        void *mapped, size_t _mapped_size,
171        void **_whole, size_t *whole_size)
172 {
173   struct unzip_state state =
174     {
175 #if !USE_INFLATE
176       .zf = NULL,
177 #endif
178       .mapped_size = _mapped_size,
179       .whole = _whole,
180       .buffer = NULL,
181       .size = 0,
182       .input_buffer = NULL,
183       .input_pos = 0
184     };
185 
186   if (mapped == NULL)
187     {
188       if (*state.whole == NULL)
189 	{
190 	  state.input_buffer = malloc (READ_SIZE);
191 	  if (unlikely (state.input_buffer == NULL))
192 	    return DWFL_E_NOMEM;
193 
194 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
195 	  if (unlikely (n < 0))
196 	    return zlib_fail (&state, Z (ERRNO));
197 
198 	  state.input_pos = n;
199 	  mapped = state.input_buffer;
200 	  state.mapped_size = n;
201 	}
202       else
203 	{
204 	  state.input_buffer = *state.whole;
205 	  state.input_pos = state.mapped_size = *whole_size;
206 	}
207     }
208 
209 #define NOMAGIC(magic) \
210   (state.mapped_size <= sizeof magic || \
211    memcmp (mapped, magic, sizeof magic - 1))
212 
213   /* First, look at the header.  */
214   if (NOMAGIC (MAGIC)
215 #ifdef MAGIC2
216       && NOMAGIC (MAGIC2)
217 #endif
218       )
219     /* Not a compressed file.  */
220     return DWFL_E_BADELF;
221 
222 #if USE_INFLATE
223 
224   /* This style actually only works with bzlib and liblzma.
225      The stupid zlib interface has nothing to grok the
226      gzip file headers except the slow gzFile interface.  */
227 
228   z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
229   int result = inflateInit (&z);
230   if (result != Z (OK))
231     {
232       inflateEnd (&z);
233       return zlib_fail (&state, result);
234     }
235 
236   do
237     {
238       if (z.avail_in == 0 && state.input_buffer != NULL)
239 	{
240 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
241 				   start_offset + state.input_pos);
242 	  if (unlikely (n < 0))
243 	    {
244 	      inflateEnd (&z);
245 	      return zlib_fail (&state, Z (ERRNO));
246 	    }
247 	  z.next_in = state.input_buffer;
248 	  z.avail_in = n;
249 	  state.input_pos += n;
250 	}
251       if (z.avail_out == 0)
252 	{
253 	  ptrdiff_t pos = (void *) z.next_out - state.buffer;
254 	  if (!bigger_buffer (&state, z.avail_in))
255 	    {
256 	      result = Z (MEM_ERROR);
257 	      break;
258 	    }
259 	  z.next_out = state.buffer + pos;
260 	  z.avail_out = state.size - pos;
261 	}
262     }
263   while ((result = do_inflate (&z)) == Z (OK));
264 
265 #ifdef BZLIB
266   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
267 			| z.total_out_lo32);
268   smaller_buffer (&state, total_out);
269 #else
270   smaller_buffer (&state, z.total_out);
271 #endif
272 
273   inflateEnd (&z);
274 
275   if (result != Z (STREAM_END))
276     return zlib_fail (&state, result);
277 
278 #else  /* gzip only.  */
279 
280   /* Let the decompression library read the file directly.  */
281 
282   Dwfl_Error result = open_stream (fd, start_offset, &state);
283 
284   if (result == DWFL_E_NOERROR && gzdirect (state.zf))
285     {
286       gzclose (state.zf);
287       return fail (&state, DWFL_E_BADELF);
288     }
289 
290   if (result != DWFL_E_NOERROR)
291     return fail (&state, result);
292 
293   ptrdiff_t pos = 0;
294   while (1)
295     {
296       if (!bigger_buffer (&state, 1024))
297 	{
298 	  gzclose (state.zf);
299 	  return zlib_fail (&state, Z (MEM_ERROR));
300 	}
301       int n = gzread (state.zf, state.buffer + pos, state.size - pos);
302       if (n < 0)
303 	{
304 	  int code;
305 	  gzerror (state.zf, &code);
306 	  gzclose (state.zf);
307 	  return zlib_fail (&state, code);
308 	}
309       if (n == 0)
310 	break;
311       pos += n;
312     }
313 
314   gzclose (state.zf);
315   smaller_buffer (&state, pos);
316 #endif
317 
318   free (state.input_buffer);
319 
320   *state.whole = state.buffer;
321   *whole_size = state.size;
322 
323   return DWFL_E_NOERROR;
324 }
325