After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 770986 - Code in gdk-pixbuf invokes undefined behaviour
Code in gdk-pixbuf invokes undefined behaviour
Status: RESOLVED OBSOLETE
Product: gdk-pixbuf
Classification: Platform
Component: loaders
git master
Other Linux
: Normal normal
: ---
Assigned To: gdk-pixbuf-maint
gdk-pixbuf-maint
Depends on:
Blocks:
 
 
Reported: 2016-09-07 05:42 UTC by Dhiru Kholia
Modified: 2017-11-30 01:33 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
patch to avoid invoking undefined behavior (824 bytes, patch)
2016-09-07 05:42 UTC, Dhiru Kholia
committed Details | Review
patch to avoid undefined behavior in io-tiff.c (987 bytes, patch)
2017-04-07 08:52 UTC, Dhiru Kholia
none Details | Review

Description Dhiru Kholia 2016-09-07 05:42:32 UTC
Created attachment 334957 [details] [review]
patch to avoid invoking undefined behavior

gdk-pixbuf (from git master, commit 94d739702ea0a7) has the following code in
gdk-pixbuf/gdk-pixbuf.c file.

GdkPixbuf *
gdk_pixbuf_new (GdkColorspace colorspace,
                gboolean      has_alpha,
                int           bits_per_sample,
                int           width,
                int           height)
{
        guchar *buf;
        int channels;
        int rowstride;

        channels = has_alpha ? 4 : 3;
        rowstride = width * channels;
        if (rowstride / channels != width || rowstride + 3 < 0) /* overflow */
                return NULL;
        ...
        buf = g_try_malloc_n (height, rowstride);
        if (!buf)
                return NULL;
}

When this code is compiled with GCC 6.1 or Clang 3.8 with "-O2" flag, the
overflow check (rowstride / channels != width) is eliminated completely by the
compiler. This is because signed integer overflow (rowstride = width *
channels) is undefined and the overflow check occurs after the undefined
behaviour. Large values of the "width" function argument lead to signed integer
overflow and this is not detected at runtime since the overflow check has been
eliminated (optimized away) at compile time. Such large width values result in
a small memory allocation (buf = g_try_malloc_n) and memory corruption problems
in OneLine32 function in io-bmp.c file later on.

See https://bugzilla.gnome.org/show_bug.cgi?id=768738 for details about
the memory corruption problems caused by a malformed BMP file.

We can fix this code by doing the multiplication (rowstride = width * channels) 
operation in unsigned type, instead of signed type. The patch to do so is
attached.

...

The following code pattern (in io-tiff.c) also invokes undefined behaviour and should be fixed,

static GdkPixbuf *
tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
{
        guchar *pixels = NULL;
        gint width, height, rowstride, bytes;
        ...
        rowstride = width * 4;
        if (rowstride / 4 != width) { /* overflow */
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("Dimensions of TIFF image too large"));
                return NULL;
        }

        bytes = height * rowstride;
        if (bytes / rowstride != height) { /* overflow */
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("Dimensions of TIFF image too large"));
                return NULL;
        }


Both these overflow checks are eliminated by the compiler. I do not have a patch for fixing these two overflow checks.
Comment 1 Dhiru Kholia 2017-04-07 08:52:41 UTC
Created attachment 349433 [details] [review]
patch to avoid undefined behavior in io-tiff.c
Comment 2 Tobias Mueller 2017-04-07 09:01:48 UTC
ah. I didn't see this bug.  I opened 780269. It also has a patch. It's a bit different though. If this patch makes it work, then I guess this one here is to be preferred. Semantically, it seems to make sense to make rowstride and bytes an unsigned int.
Comment 3 Dhiru Kholia 2017-04-07 09:46:02 UTC
Earlier, I had only checked that the compiler retained the overflow checks after my patch was applied. However, after checking with the reproducer from 780269, I found out that my patch was not enough.

The patch in 780269 works great and should be applied. Thanks Tobias for pointing out this other bug.
Comment 4 Bastien Nocera 2017-11-30 01:33:32 UTC
(In reply to Dhiru Kholia from comment #0)
> Created attachment 334957 [details] [review] [review]
> patch to avoid invoking undefined behavior
> 
> gdk-pixbuf (from git master, commit 94d739702ea0a7) has the following code in
> gdk-pixbuf/gdk-pixbuf.c file.
> 
> GdkPixbuf *
> gdk_pixbuf_new (GdkColorspace colorspace,
>                 gboolean      has_alpha,
>                 int           bits_per_sample,
>                 int           width,
>                 int           height)
> {
>         guchar *buf;
>         int channels;
>         int rowstride;
> 
>         channels = has_alpha ? 4 : 3;
>         rowstride = width * channels;
>         if (rowstride / channels != width || rowstride + 3 < 0) /* overflow
> */

This was fixed in bug 765094.

> The following code pattern (in io-tiff.c) also invokes undefined behaviour
> and should be fixed,
> 
> static GdkPixbuf *
> tiff_image_parse (TIFF *tiff, TiffContext *context, GError **error)
> {
>         guchar *pixels = NULL;
>         gint width, height, rowstride, bytes;
>         ...
>         rowstride = width * 4;
>         if (rowstride / 4 != width) { /* overflow */
>                 g_set_error_literal (error,
>                                      GDK_PIXBUF_ERROR,
>                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
>                                      _("Dimensions of TIFF image too
> large"));
>                 return NULL;
>         }
> 
>         bytes = height * rowstride;
>         if (bytes / rowstride != height) { /* overflow */
>                 g_set_error_literal (error,
>                                      GDK_PIXBUF_ERROR,
>                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
>                                      _("Dimensions of TIFF image too
> large"));
>                 return NULL;
>         }

And those in bug 780269.