Browse Source

Fix "Fl_Shared_Image: use of unitialized data" (#216)

- fix issue as proposed
- fix more potential access to uninitialized data issues
- document Fl_Shared_Image::add_handler()
- document typedef Fl_Shared_Image::Fl_Shared_Handler()
pull/228/head
Albrecht Schlosser 4 years ago
parent
commit
f9e8ef0b7a
  1. 52
      FL/Fl_Shared_Image.H
  2. 28
      src/Fl_Shared_Image.cxx
  3. 58
      src/fl_images_core.cxx

52
FL/Fl_Shared_Image.H

@ -1,7 +1,7 @@
// //
// Shared image header file for the Fast Light Tool Kit (FLTK). // Shared image header file for the Fast Light Tool Kit (FLTK).
// //
// Copyright 1998-2017 by Bill Spitzak and others. // Copyright 1998-2021 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -23,11 +23,54 @@
# include "Fl_Image.H" # include "Fl_Image.H"
// Test function for adding new formats /** Test function (typedef) for adding new shared image formats.
typedef Fl_Image *(*Fl_Shared_Handler)(const char *name, uchar *header,
This defines the function type you can use to add a handler for unknown
image formats that can be opened and loaded as an Fl_Shared_Image.
fl_register_images() adds all image formats known to FLTK.
Call Fl_Shared_Image::add_handler() to add your own check function to
the list of known image formats.
Your function will be passed the filename (\p name), some \p header
bytes already read from the image file and the size \p headerlen of the
data read. The max value of size is implementation dependent. If your
handler function needs to check more bytes you must open the image file
yourself.
The provided buffer \p header must not be overwritten.
If your handler function can identify the file type you must open the
file and return a valid Fl_Image or derived type, otherwise you must
return \c NULL.
Example:
\code
static Fl_Image *check_my_image(const char *name,
uchar *header,
int headerlen) {
// (test image type using header and headerlen)
if (known) {
// (load image data from file \p name)
return new Fl_RGB_Image(data, ...);
} else
return 0;
}
// add your handler:
Fl_Shared_Image::add_handler(check_my_image);
\endcode
\param[in] name filename to be checked and opened if applicable
\param[in] header portion of the file that has already been read
\param[in] headerlen length of provided \p header data
\returns valid Fl_Image or \c NULL.
\see Fl_Shared_Image::add_handler()
*/
typedef Fl_Image *(*Fl_Shared_Handler)(const char *name,
uchar *header,
int headerlen); int headerlen);
// Shared images class.
/** /**
This class supports caching, loading, and drawing of image files. This class supports caching, loading, and drawing of image files.
@ -42,6 +85,7 @@ typedef Fl_Image *(*Fl_Shared_Handler)(const char *name, uchar *header,
A refcount is used to determine if a released image is to be destroyed A refcount is used to determine if a released image is to be destroyed
with delete. with delete.
\see fl_register_image()
\see Fl_Shared_Image::get() \see Fl_Shared_Image::get()
\see Fl_Shared_Image::find() \see Fl_Shared_Image::find()
\see Fl_Shared_Image::release() \see Fl_Shared_Image::release()

28
src/Fl_Shared_Image.cxx

@ -1,7 +1,7 @@
// //
// Shared image code for the Fast Light Tool Kit (FLTK). // Shared image code for the Fast Light Tool Kit (FLTK).
// //
// Copyright 1998-2017 by Bill Spitzak and others. // Copyright 1998-2021 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -249,6 +249,7 @@ void Fl_Shared_Image::release() {
void Fl_Shared_Image::reload() { void Fl_Shared_Image::reload() {
// Load image from disk... // Load image from disk...
int i; // Looping var int i; // Looping var
int count = 0; // number of bytes read from image header
FILE *fp; // File pointer FILE *fp; // File pointer
uchar header[64]; // Buffer for auto-detecting files uchar header[64]; // Buffer for auto-detecting files
Fl_Image *img; // New image Fl_Image *img; // New image
@ -256,22 +257,23 @@ void Fl_Shared_Image::reload() {
if (!name_) return; if (!name_) return;
if ((fp = fl_fopen(name_, "rb")) != NULL) { if ((fp = fl_fopen(name_, "rb")) != NULL) {
if (fread(header, 1, sizeof(header), fp)==0) { /* ignore */ } count = fread(header, 1, sizeof(header), fp);
fclose(fp); fclose(fp);
if (count == 0)
return;
} else { } else {
return; return;
} }
// Load the image as appropriate... // Load the image as appropriate...
if (memcmp(header, "#define", 7) == 0) // XBM file if (count >= 7 && memcmp(header, "#define", 7) == 0) // XBM file
img = new Fl_XBM_Image(name_); img = new Fl_XBM_Image(name_);
else if (memcmp(header, "/* XPM */", 9) == 0) // XPM file else if (count >= 9 && memcmp(header, "/* XPM */", 9) == 0) // XPM file
img = new Fl_XPM_Image(name_); img = new Fl_XPM_Image(name_);
else { else {
// Not a standard format; try an image handler... // Not a standard format; try an image handler...
for (i = 0, img = 0; i < num_handlers_; i ++) { for (i = 0, img = 0; i < num_handlers_; i ++) {
img = (handlers_[i])(name_, header, sizeof(header)); img = (handlers_[i])(name_, header, count);
if (img) break; if (img) break;
} }
} }
@ -495,7 +497,19 @@ Fl_Shared_Image *Fl_Shared_Image::get(Fl_RGB_Image *rgb, int own_it)
/** Adds a shared image handler, which is basically a test function /** Adds a shared image handler, which is basically a test function
for adding new formats. for adding new image formats.
This function will be called when an Fl_Shared_Image is to be loaded
(for instance with Fl_Shared_Image::get()) and the image type is not
known to FLTK.
All registered image handlers will be called in the order of registration.
You should always call fl_register_images() before adding your own
handlers - unless you need to override a known image file type which
should be rare.
\see Fl_Shared_Handler for more information of the function you need
to define.
*/ */
void Fl_Shared_Image::add_handler(Fl_Shared_Handler f) { void Fl_Shared_Image::add_handler(Fl_Shared_Handler f) {
int i; // Looping var... int i; // Looping var...

58
src/fl_images_core.cxx

@ -2,6 +2,7 @@
// FLTK images library core. // FLTK images library core.
// //
// Copyright 1997-2010 by Easy Software Products. // Copyright 1997-2010 by Easy Software Products.
// Copyright 2011-2021 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@ -47,11 +48,13 @@ static Fl_Image *fl_check_images(const char *name, uchar *header, int headerlen)
/** /**
\brief Register the image formats. \brief Register the known image formats.
This function is provided in the fltk_images library and This function is provided in the fltk_images library and
registers all of the "extra" image file formats that are not part registers all of the "extra" image file formats known to FLTK
of the core FLTK library. that are not part of the core FLTK library.
You may add your own image formats with Fl_Shared_Image::add_handler().
*/ */
void fl_register_images() { void fl_register_images() {
Fl_Shared_Image::add_handler(fl_check_images); Fl_Shared_Image::add_handler(fl_check_images);
@ -62,49 +65,86 @@ void fl_register_images() {
// //
// 'fl_check_images()' - Check for a supported image format. // 'fl_check_images()' - Check for a supported image format.
// //
// returns 0 (NULL) if <headerlen> is less than 6 because:
// (1) some of the comparisons would otherwise access undefined data
// (2) there's no valid image file with less than 6 bytes
//
// Note 1: The number 6 above may be changed if necessary as long as
// condition (2) holds.
//
// Note 2: The provided buffer <header> MUST NOT be overwritten by any
// check function because subsequently called check functions need
// the original image header data. <header> should be const!
Fl_Image * // O - Image, if found Fl_Image * // O - Image, if found
fl_check_images(const char *name, // I - Filename fl_check_images(const char *name, // I - Filename
uchar *header, // I - Header data from file uchar *header, // I - Header data from file
int headerlen) { // I - Amount of data int headerlen) { // I - Amount of data in header
if (headerlen < 6) // not a valid image
return 0;
// GIF
if (memcmp(header, "GIF87a", 6) == 0 || if (memcmp(header, "GIF87a", 6) == 0 ||
memcmp(header, "GIF89a", 6) == 0) // GIF file memcmp(header, "GIF89a", 6) == 0) // GIF file
return new Fl_GIF_Image(name); return new Fl_GIF_Image(name);
// BMP
if (memcmp(header, "BM", 2) == 0) // BMP file if (memcmp(header, "BM", 2) == 0) // BMP file
return new Fl_BMP_Image(name); return new Fl_BMP_Image(name);
// PNM
if (header[0] == 'P' && header[1] >= '1' && header[1] <= '7') if (header[0] == 'P' && header[1] >= '1' && header[1] <= '7')
// Portable anymap // Portable anymap
return new Fl_PNM_Image(name); return new Fl_PNM_Image(name);
// PNG
#ifdef HAVE_LIBPNG #ifdef HAVE_LIBPNG
if (memcmp(header, "\211PNG", 4) == 0)// PNG file if (memcmp(header, "\211PNG", 4) == 0)// PNG file
return new Fl_PNG_Image(name); return new Fl_PNG_Image(name);
#endif // HAVE_LIBPNG #endif // HAVE_LIBPNG
// JPEG
#ifdef HAVE_LIBJPEG #ifdef HAVE_LIBJPEG
if (memcmp(header, "\377\330\377", 3) == 0 && // Start-of-Image if (memcmp(header, "\377\330\377", 3) == 0 && // Start-of-Image
header[3] >= 0xc0 && header[3] <= 0xfe) // APPn .. comment for JPEG file header[3] >= 0xc0 && header[3] <= 0xfe) // APPn .. comment for JPEG file
return new Fl_JPEG_Image(name); return new Fl_JPEG_Image(name);
#endif // HAVE_LIBJPEG #endif // HAVE_LIBJPEG
// SVG or SVGZ (gzip'ed SVG)
#ifdef FLTK_USE_SVG #ifdef FLTK_USE_SVG
uchar header2[64]; // buffer for decompression
uchar *buf = header; // original header data
int count = headerlen; // original header data size
// Note: variables 'buf' and 'count' may be overwritten subsequently
// if the image data is gzip'ed *and* we can decompress the data
# if defined(HAVE_LIBZ) # if defined(HAVE_LIBZ)
if (header[0] == 0x1f && header[1] == 0x8b) { // denotes gzip'ed data if (header[0] == 0x1f && header[1] == 0x8b) { // gzip'ed data
int fd = fl_open_ext(name, 1, 0); int fd = fl_open_ext(name, 1, 0);
if (fd < 0) return NULL; if (fd < 0) return NULL;
gzFile gzf = gzdopen(fd, "r"); gzFile gzf = gzdopen(fd, "r");
if (gzf) { if (gzf) {
gzread(gzf, header, headerlen); count = gzread(gzf, header2, (int)sizeof(header2));
gzclose(gzf); gzclose(gzf);
buf = header2; // decompressed data
} }
} } // gzip'ed data
# endif // HAVE_LIBZ # endif // HAVE_LIBZ
if ( (headerlen > 5 && memcmp(header, "<?xml", 5) == 0) ||
memcmp(header, "<svg", 4) == 0) if ((count >= 5 && memcmp(buf, "<?xml", 5) == 0) ||
(count >= 4 && memcmp(buf, "<svg", 4) == 0))
return new Fl_SVG_Image(name); return new Fl_SVG_Image(name);
#endif // FLTK_USE_SVG #endif // FLTK_USE_SVG
// unknown image format
return 0; return 0;
} }

Loading…
Cancel
Save