<html>
    <head>
      <base href="https://bugs.freedesktop.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - cairo_image_surface_create_from_png() returns PNG errors as CAIRO_STATUS_NO_MEMORY"
   href="https://bugs.freedesktop.org/show_bug.cgi?id=102142">102142</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>cairo_image_surface_create_from_png() returns PNG errors as CAIRO_STATUS_NO_MEMORY
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>cairo
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>unspecified
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>Other
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>All
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>medium
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>png functions
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>cworth@cworth.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>federico@gnome.org
          </td>
        </tr>

        <tr>
          <th>QA Contact</th>
          <td>cairo-bugs@cairographics.org
          </td>
        </tr></table>
      <p>
        <div>
        <pre>I was writing some tests for cairo-rs (the Rust binding to Cairo) and tried
feeding it an invalid PNG.

We get to cairo-png.c:read_png() where it does

    if (setjmp (png_jmpbuf (png))) {
        surface = _cairo_surface_create_in_error (status);
        goto BAIL;
    }

There, the status it gets was set by png_simple_error_callback():

static void
png_simple_error_callback (png_structp png,
                           png_const_charp error_msg)
{
    cairo_status_t *error = png_get_error_ptr (png);

    if (*error == CAIRO_STATUS_SUCCESS)
        *error = _cairo_error (CAIRO_STATUS_PNG_ERROR);

    longjmp (png_jmpbuf (png), 1);
}

I thought that I would get an error surface with CAIRO_STATUS_PNG_ERROR out of
the call to cairo_image_surface_get_from_png(), but read_png()'s call to
_cairo_surface_create_in_error() has this:

cairo_surface_t *
_cairo_surface_create_in_error (cairo_status_t status)
{
    switch (status) {
    ...
    case CAIRO_STATUS_PNG_ERROR:
    ... other fall throughs ...
    default:
        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
        return (cairo_surface_t *) &_cairo_surface_nil;
    }
}

And _cairo_surface_nil is the one with CAIRO_STATUS_NO_MEMORY.

I *think* we could meaningfully distinguish "out of memory in libpng" from
"libpng reports an error in the data" from "I/O error in the reader functions"
by a combination of things.

1. Use png_create_read_struct_2() instead of png_create_read_struct().  That
would let us pass our own malloc() replacement, detect OOM, and store a flag to
that effect in Cairo's own png_read_closure_t.

2. Add a new error surface for PNG_ERROR (why are all those fall-throughs to
_cairo_surface_nil?).

Comments appreciated.</pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are the QA Contact for the bug.</li>
      </ul>
    </body>
</html>