Bug Summary

File:libinterp/corefcn/zfstream.cc
Location:line 194, column 3
Description:String copy function overflows destination buffer

Annotated Source Code

1/*
2
3Copyright (C) 2005-2013 Ludwig Schwardt, Kevin Ruland
4
5
6This file is part of Octave.
7
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
21
22*/
23
24/*
25
26 This file is adapted from the zlib 1.2.2 contrib/iostream3 code,
27 written by
28
29 Ludwig Schwardt <schwardt@sun.ac.za>
30 original version by Kevin Ruland <kevin@rodin.wustl.edu>
31
32*/
33
34#ifdef HAVE_CONFIG_H1
35#include <config.h>
36#endif
37
38#include <iostream>
39
40#include "zfstream.h"
41
42#ifdef HAVE_ZLIB1
43
44#include <cstring> // for strcpy, strcat, strlen (mode strings)
45#include <cstdio> // for BUFSIZ
46
47// Internal buffer sizes (default and "unbuffered" versions)
48#define STASHED_CHARACTERS16 16
49#define BIGBUFSIZE(256 * 1024 + 16) (256 * 1024 + STASHED_CHARACTERS16)
50#define SMALLBUFSIZE1 1
51
52/*****************************************************************************/
53
54// Default constructor
55gzfilebuf::gzfilebuf ()
56 : file(0), io_mode(std::ios_base::openmode(0)), own_fd(false),
57 buffer(0), buffer_size(BIGBUFSIZE(256 * 1024 + 16)), own_buffer(true)
58{
59 // No buffers to start with
60 this->disable_buffer ();
61}
62
63// Destructor
64gzfilebuf::~gzfilebuf ()
65{
66 // Sync output buffer and close only if responsible for file
67 // (i.e. attached streams should be left open at this stage)
68 this->sync ();
69 if (own_fd)
70 this->close ();
71 // Make sure internal buffer is deallocated
72 this->disable_buffer ();
73}
74
75// Set compression level and strategy
76int
77gzfilebuf::setcompression (int comp_level, int comp_strategy)
78{
79 return gzsetparams (file, comp_level, comp_strategy);
80}
81
82// Open gzipped file
83gzfilebuf*
84gzfilebuf::open (const char *name, std::ios_base::openmode mode)
85{
86 // Fail if file already open
87 if (this->is_open ())
88 return 0;
89 // Don't support simultaneous read/write access (yet)
90 if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
91 return 0;
92
93 // Build mode string for gzopen and check it [27.8.1.3.2]
94 char char_mode[6] = "\0\0\0\0\0";
95 if (! this->open_mode (mode, char_mode))
96 return 0;
97
98 // Attempt to open file
99 if ((file = gzopen (name, char_mode)) == 0)
100 return 0;
101
102 // On success, allocate internal buffer and set flags
103 this->enable_buffer ();
104 io_mode = mode;
105 own_fd = true;
106 return this;
107}
108
109// Attach to gzipped file
110gzfilebuf*
111gzfilebuf::attach (int fd, std::ios_base::openmode mode)
112{
113 // Fail if file already open
114 if (this->is_open ())
2
Taking false branch
115 return 0;
116 // Don't support simultaneous read/write access (yet)
117 if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
118 return 0;
119
120 // Build mode string for gzdopen and check it [27.8.1.3.2]
121 char char_mode[6] = "\0\0\0\0\0";
122 if (! this->open_mode (mode, char_mode))
3
Calling 'gzfilebuf::open_mode'
123 return 0;
124
125 // Attempt to attach to file
126 if ((file = gzdopen (fd, char_mode)) == 0)
127 return 0;
128
129 // On success, allocate internal buffer and set flags
130 this->enable_buffer ();
131 io_mode = mode;
132 own_fd = false;
133 return this;
134}
135
136// Close gzipped file
137gzfilebuf*
138gzfilebuf::close ()
139{
140 // Fail immediately if no file is open
141 if (! this->is_open ())
142 return 0;
143 // Assume success
144 gzfilebuf* retval = this;
145 // Attempt to sync and close gzipped file
146 if (this->sync () == -1)
147 retval = 0;
148 if (gzclose (file) < 0)
149 retval = 0;
150 // File is now gone anyway (postcondition [27.8.1.3.8])
151 file = 0;
152 own_fd = false;
153 // Destroy internal buffer if it exists
154 this->disable_buffer ();
155 return retval;
156}
157
158/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
159
160// Convert int open mode to mode string
161bool
162gzfilebuf::open_mode (std::ios_base::openmode mode, char* c_mode) const
163{
164 // FIXME: do we need testb?
165 // bool testb = mode & std::ios_base::binary;
166 bool testi = mode & std::ios_base::in;
167 bool testo = mode & std::ios_base::out;
168 bool testt = mode & std::ios_base::trunc;
169 bool testa = mode & std::ios_base::app;
170
171 // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
172 // Original zfstream hardcoded the compression level to maximum here...
173 // Double the time for less than 1% size improvement seems
174 // excessive though - keeping it at the default level
175 // To change back, just append "9" to the next three mode strings
176 if (!testi && testo && !testt && !testa)
177 strcpy (c_mode, "w");
178 if (!testi && testo && !testt && testa)
179 strcpy (c_mode, "a");
180 if (!testi && testo && testt && !testa)
181 strcpy (c_mode, "w");
182 if (testi && !testo && !testt && !testa)
183 strcpy (c_mode, "r");
184 // No read/write mode yet
185 // if (testi && testo && !testt && !testa)
186 // strcpy(c_mode, "r+");
187 // if (testi && testo && testt && !testa)
188 // strcpy(c_mode, "w+");
189
190 // Mode string should be empty for invalid combination of flags
191 if (strlen (c_mode) == 0)
4
Taking false branch
192 return false;
193
194 strcat (c_mode, "b");
5
String copy function overflows destination buffer
195
196 return true;
197}
198
199// Determine number of characters in internal get buffer
200std::streamsize
201gzfilebuf::showmanyc ()
202{
203 // Calls to underflow will fail if file not opened for reading
204 if (! this->is_open () || !(io_mode & std::ios_base::in))
205 return -1;
206 // Make sure get area is in use
207 if (this->gptr () && (this->gptr () < this->egptr ()))
208 return std::streamsize (this->egptr () - this->gptr ());
209 else
210 return 0;
211}
212
213// Puts back a character to the stream in two cases. Firstly, when there
214// is no putback position available, and secondly when the character putback
215// differs from the one in the file. We can only support the first case
216// with gzipped files.
217gzfilebuf::int_type
218gzfilebuf::pbackfail (gzfilebuf::int_type c)
219{
220 if (this->is_open ())
221 {
222 if (gzseek (file, this->gptr () - this->egptr () - 1, SEEK_CUR1) < 0)
223 return traits_type::eof ();
224
225 // Invalidates contents of the buffer
226 enable_buffer ();
227
228 // Attempt to fill internal buffer from gzipped file
229 // (buffer must be guaranteed to exist...)
230 int bytes_read = gzread (file, buffer, buffer_size);
231 // Indicates error or EOF
232 if (bytes_read <= 0)
233 {
234 // Reset get area
235 this->setg (buffer, buffer, buffer);
236 return traits_type::eof ();
237 }
238
239 // Make all bytes read from file available as get area
240 this->setg (buffer, buffer, buffer + bytes_read);
241
242 // If next character in get area differs from putback character
243 // flag a failure
244 gzfilebuf::int_type ret = traits_type::to_int_type (*(this->gptr ()));
245 if (ret != c)
246 return traits_type::eof ();
247 else
248 return ret;
249 }
250 else
251 return traits_type::eof ();
252}
253
254// Fill get area from gzipped file
255gzfilebuf::int_type
256gzfilebuf::underflow ()
257{
258 // If something is left in the get area by chance, return it
259 // (this shouldn't normally happen, as underflow is only supposed
260 // to be called when gptr >= egptr, but it serves as error check)
261 if (this->gptr () && (this->gptr () < this->egptr ()))
262 return traits_type::to_int_type (*(this->gptr ()));
263
264 // If the file hasn't been opened for reading, produce error
265 if (! this->is_open () || !(io_mode & std::ios_base::in))
266 return traits_type::eof ();
267
268 // Copy the final characters to the front of the buffer
269 int stash = 0;
270 if (this->eback () && buffer && buffer_size > STASHED_CHARACTERS16)
271 {
272 char_type *ptr1 = buffer;
273 char_type *ptr2 = this->egptr () - STASHED_CHARACTERS16 + 1;
274 if (ptr2 > this->eback ())
275 while (stash++ <= STASHED_CHARACTERS16)
276 *ptr1++ = *ptr2++;
277 }
278
279 // Attempt to fill internal buffer from gzipped file
280 // (buffer must be guaranteed to exist...)
281 int bytes_read = gzread (file, buffer + stash, buffer_size - stash);
282
283 // Indicates error or EOF
284 if (bytes_read <= 0)
285 {
286 // Reset get area
287 this->setg (buffer, buffer, buffer);
288 return traits_type::eof ();
289 }
290 // Make all bytes read from file plus the stash available as get area
291 this->setg (buffer, buffer + stash, buffer + bytes_read + stash);
292
293 // Return next character in get area
294 return traits_type::to_int_type (*(this->gptr ()));
295}
296
297// Write put area to gzipped file
298gzfilebuf::int_type
299gzfilebuf::overflow (int_type c)
300{
301 // Determine whether put area is in use
302 if (this->pbase ())
303 {
304 // Double-check pointer range
305 if (this->pptr () > this->epptr () || this->pptr () < this->pbase ())
306 return traits_type::eof ();
307 // Add extra character to buffer if not EOF
308 if (! traits_type::eq_int_type (c, traits_type::eof ()))
309 {
310 *(this->pptr ()) = traits_type::to_char_type (c);
311 this->pbump (1);
312 }
313 // Number of characters to write to file
314 int bytes_to_write = this->pptr () - this->pbase ();
315 // Overflow doesn't fail if nothing is to be written
316 if (bytes_to_write > 0)
317 {
318 // If the file hasn't been opened for writing, produce error
319 if (! this->is_open () || !(io_mode & std::ios_base::out))
320 return traits_type::eof ();
321 // If gzipped file won't accept all bytes written to it, fail
322 if (gzwrite (file, this->pbase (), bytes_to_write) != bytes_to_write)
323 return traits_type::eof ();
324 // Reset next pointer to point to pbase on success
325 this->pbump (-bytes_to_write);
326 }
327 }
328 // Write extra character to file if not EOF
329 else if (! traits_type::eq_int_type (c, traits_type::eof ()))
330 {
331 // If the file hasn't been opened for writing, produce error
332 if (! this->is_open () || !(io_mode & std::ios_base::out))
333 return traits_type::eof ();
334 // Impromptu char buffer (allows "unbuffered" output)
335 char_type last_char = traits_type::to_char_type (c);
336 // If gzipped file won't accept this character, fail
337 if (gzwrite (file, &last_char, 1) != 1)
338 return traits_type::eof ();
339 }
340
341 // If you got here, you have succeeded (even if c was EOF)
342 // The return value should therefore be non-EOF
343 if (traits_type::eq_int_type (c, traits_type::eof ()))
344 return traits_type::not_eof (c);
345 else
346 return c;
347}
348
349// Assign new buffer
350std::streambuf*
351gzfilebuf::setbuf (char_type* p, std::streamsize n)
352{
353 // First make sure stuff is sync'ed, for safety
354 if (this->sync () == -1)
355 return 0;
356 // If buffering is turned off on purpose via setbuf(0,0), still allocate one.
357 // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
358 // least a buffer of size 1 (very inefficient though, therefore make it
359 // bigger?). This follows from [27.5.2.4.3]/12 (gptr needs to point at
360 // something, it seems).
361 if (!p || !n)
362 {
363 // Replace existing buffer (if any) with small internal buffer
364 this->disable_buffer ();
365 buffer = 0;
366 buffer_size = 0;
367 own_buffer = true;
368 this->enable_buffer ();
369 }
370 else
371 {
372 // Replace existing buffer (if any) with external buffer
373 this->disable_buffer ();
374 buffer = p;
375 buffer_size = n;
376 own_buffer = false;
377 this->enable_buffer ();
378 }
379 return this;
380}
381
382// Write put area to gzipped file (i.e. ensures that put area is empty)
383int
384gzfilebuf::sync ()
385{
386 return traits_type::eq_int_type (this->overflow (),
387 traits_type::eof ()) ? -1 : 0;
388}
389
390/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
391
392// Allocate internal buffer
393void
394gzfilebuf::enable_buffer ()
395{
396 // If internal buffer required, allocate one
397 if (own_buffer && !buffer)
398 {
399 // Check for buffered vs. "unbuffered"
400 if (buffer_size > 0)
401 {
402 // Allocate internal buffer
403 buffer = new char_type [buffer_size];
404 // Get area starts empty and will be expanded by underflow as needed
405 this->setg (buffer, buffer, buffer);
406 // Setup entire internal buffer as put area.
407 // The one-past-end pointer actually points to the last element of
408 // the buffer, so that overflow(c) can safely add the extra character
409 // c to the sequence. These pointers remain in place for the
410 // duration of the buffer
411 this->setp (buffer, buffer + buffer_size - 1);
412 }
413 else
414 {
415 // Even in "unbuffered" case, (small?) get buffer is still required
416 buffer_size = SMALLBUFSIZE1;
417 buffer = new char_type [buffer_size];
418 this->setg (buffer, buffer, buffer);
419 // "Unbuffered" means no put buffer
420 this->setp (0, 0);
421 }
422 }
423 else
424 {
425 // If buffer already allocated, reset buffer pointers just to make sure no
426 // stale chars are lying around
427 this->setg (buffer, buffer, buffer);
428 this->setp (buffer, buffer + buffer_size - 1);
429 }
430}
431
432// Destroy internal buffer
433void
434gzfilebuf::disable_buffer ()
435{
436 // If internal buffer exists, deallocate it
437 if (own_buffer && buffer)
438 {
439 // Preserve unbuffered status by zeroing size
440 if (! this->pbase ())
441 buffer_size = 0;
442 delete[] buffer;
443 buffer = 0;
444 this->setg (0, 0, 0);
445 this->setp (0, 0);
446 }
447 else
448 {
449 // Reset buffer pointers to initial state if external buffer exists
450 this->setg (buffer, buffer, buffer);
451 if (buffer)
452 this->setp (buffer, buffer + buffer_size - 1);
453 else
454 this->setp (0, 0);
455 }
456}
457
458/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
459
460// Seek functions
461gzfilebuf::pos_type
462gzfilebuf::seekoff (off_type off, std::ios_base::seekdir way,
463 std::ios_base::openmode)
464{
465 pos_type ret = pos_type (off_type (-1));
466
467 if (this->is_open ())
468 {
469 off_type computed_off = off;
470
471 if ((io_mode & std::ios_base::in) && way == std::ios_base::cur)
472 computed_off += this->gptr () - this->egptr ();
473
474 // Handle tellg/tellp as a special case up front, no need to seek
475 // or invalidate get/put buffers
476 if (off == 0 && way == std::ios_base::cur)
477 return pos_type (gztell (file) + computed_off);
478
479 if (way == std::ios_base::beg)
480 ret = pos_type (gzseek (file, computed_off, SEEK_SET0));
481 else if (way == std::ios_base::cur)
482 ret = pos_type (gzseek (file, computed_off, SEEK_CUR1));
483 else
484 // Can't seek from end of a gzipped file, so this will give -1
485 ret = pos_type (gzseek (file, computed_off, SEEK_END2));
486
487 if (io_mode & std::ios_base::in)
488 // Invalidates contents of the buffer
489 enable_buffer ();
490 else
491 // flush contents of buffer to file
492 overflow ();
493 }
494
495 return ret;
496}
497
498gzfilebuf::pos_type
499gzfilebuf::seekpos (pos_type sp, std::ios_base::openmode)
500{
501 pos_type ret = pos_type (off_type (-1));
502
503 if (this->is_open ())
504 {
505 ret = pos_type (gzseek (file, sp, SEEK_SET0));
506
507 if (io_mode & std::ios_base::in)
508 // Invalidates contents of the buffer
509 enable_buffer ();
510 else
511 // flush contents of buffer to file
512 overflow ();
513 }
514
515 return ret;
516}
517
518/*****************************************************************************/
519
520// Default constructor initializes stream buffer
521gzifstream::gzifstream ()
522 : std::istream (0), sb ()
523{ this->init (&sb); }
524
525// Initialize stream buffer and open file
526gzifstream::gzifstream (const char* name, std::ios_base::openmode mode)
527 : std::istream (0), sb ()
528{
529 this->init (&sb);
530 this->open (name, mode);
531}
532
533// Initialize stream buffer and attach to file
534gzifstream::gzifstream (int fd, std::ios_base::openmode mode)
535 : std::istream (0), sb ()
536{
537 this->init (&sb);
538 this->attach (fd, mode);
539}
540
541// Open file and go into fail() state if unsuccessful
542void
543gzifstream::open (const char* name, std::ios_base::openmode mode)
544{
545 if (! sb.open (name, mode | std::ios_base::in))
546 this->setstate (std::ios_base::failbit);
547 else
548 this->clear ();
549}
550
551// Attach to file and go into fail() state if unsuccessful
552void
553gzifstream::attach (int fd, std::ios_base::openmode mode)
554{
555 if (! sb.attach (fd, mode | std::ios_base::in))
556 this->setstate (std::ios_base::failbit);
557 else
558 this->clear ();
559}
560
561// Close file
562void
563gzifstream::close ()
564{
565 if (! sb.close ())
566 this->setstate (std::ios_base::failbit);
567}
568
569/*****************************************************************************/
570
571// Default constructor initializes stream buffer
572gzofstream::gzofstream ()
573 : std::ostream (0), sb ()
574{ this->init (&sb); }
575
576// Initialize stream buffer and open file
577gzofstream::gzofstream (const char* name, std::ios_base::openmode mode)
578 : std::ostream (0), sb ()
579{
580 this->init (&sb);
581 this->open (name, mode);
582}
583
584// Initialize stream buffer and attach to file
585gzofstream::gzofstream (int fd, std::ios_base::openmode mode)
586 : std::ostream (0), sb ()
587{
588 this->init (&sb);
589 this->attach (fd, mode);
590}
591
592// Open file and go into fail() state if unsuccessful
593void
594gzofstream::open (const char* name, std::ios_base::openmode mode)
595{
596 if (! sb.open (name, mode | std::ios_base::out))
597 this->setstate (std::ios_base::failbit);
598 else
599 this->clear ();
600}
601
602// Attach to file and go into fail() state if unsuccessful
603void
604gzofstream::attach (int fd, std::ios_base::openmode mode)
605{
606 if (! sb.attach (fd, mode | std::ios_base::out))
1
Calling 'gzfilebuf::attach'
607 this->setstate (std::ios_base::failbit);
608 else
609 this->clear ();
610}
611
612// Close file
613void
614gzofstream::close ()
615{
616 if (! sb.close ())
617 this->setstate (std::ios_base::failbit);
618}
619
620#endif // HAVE_ZLIB