From ab83aeec841831b8b17b2efb9004f77c32027bc6 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Fri, 16 Jul 2021 07:32:45 -0400 Subject: [PATCH] Mirror IPP fuzzing fixes from OpenPrinting CUPS. --- cups/ipp.c | 169 +++++++++++++++++++++++------------------------------ 1 file changed, 74 insertions(+), 95 deletions(-) diff --git a/cups/ipp.c b/cups/ipp.c index 94a2264a6..42cf2fcaf 100644 --- a/cups/ipp.c +++ b/cups/ipp.c @@ -2868,7 +2868,7 @@ ippReadIO(void *src, /* I - Data source */ /* Small string buffer */ *bufptr, /* Pointer into buffer */ *bufend; /* End of buffer */ - ipp_attribute_t *attr; /* Current attribute */ + ipp_attribute_t *attr = NULL; /* Current attribute */ ipp_tag_t tag; /* Current tag */ ipp_tag_t value_tag; /* Current value tag */ _ipp_value_t *value; /* Current value */ @@ -2901,8 +2901,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, 8) < 8) { DEBUG_puts("1ippReadIO: Unable to read header."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } /* @@ -2940,8 +2939,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, 1) < 1) { DEBUG_puts("1ippReadIO: Callback returned EOF/error"); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } DEBUG_printf(("2ippReadIO: ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev)); @@ -2960,8 +2958,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, 4) < 4) { DEBUG_puts("1ippReadIO: Callback returned EOF/error"); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } tag = (ipp_tag_t)((((((buffer[0] << 8) | buffer[1]) << 8) | @@ -2975,8 +2972,7 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP extension tag larger than 0x7FFFFFFF."), 1); DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } } @@ -2995,8 +2991,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); DEBUG_printf(("1ippReadIO: bad tag 0x%02x.", tag)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } else if (tag < IPP_TAG_UNSUPPORTED_VALUE) { @@ -3004,13 +2999,20 @@ ippReadIO(void *src, /* I - Data source */ * Group tag... Set the current group and continue... */ - if (ipp->curtag == tag) + if (parent) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1); + DEBUG_printf(("1ippReadIO: bad tag 0x%02x.", tag)); + goto rollback; + } + else if (ipp->curtag == tag) ipp->prev = ippAddSeparator(ipp); else if (ipp->current) ipp->prev = ipp->current; ipp->curtag = tag; ipp->current = NULL; + attr = NULL; DEBUG_printf(("2ippReadIO: group tag=%x(%s), ipp->prev=%p", tag, ippTagString(tag), (void *)ipp->prev)); continue; } @@ -3025,8 +3027,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, 2) < 2) { DEBUG_puts("1ippReadIO: unable to read name length."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } n = (buffer[0] << 8) | buffer[1]; @@ -3035,8 +3036,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP name larger than 32767 bytes."), 1); DEBUG_printf(("1ippReadIO: bad name length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } DEBUG_printf(("2ippReadIO: name length=%d", n)); @@ -3045,7 +3045,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid named IPP attribute in collection."), 1); DEBUG_puts("1ippReadIO: bad attribute name in collection."); - return (IPP_STATE_ERROR); + goto rollback; } else if (n == 0 && tag != IPP_TAG_MEMBERNAME && tag != IPP_TAG_END_COLLECTION) { @@ -3057,8 +3057,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP attribute has no name."), 1); DEBUG_puts("1ippReadIO: Attribute without name and no current."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } attr = ipp->current; @@ -3097,8 +3096,7 @@ ippReadIO(void *src, /* I - Data source */ DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag))); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if (value_tag != tag) @@ -3124,8 +3122,7 @@ ippReadIO(void *src, /* I - Data source */ DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag))); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if (value_tag == IPP_TAG_INTEGER && tag == IPP_TAG_RANGE) @@ -3147,8 +3144,7 @@ ippReadIO(void *src, /* I - Data source */ DEBUG_printf(("1ippReadIO: value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag))); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } /* @@ -3156,10 +3152,7 @@ ippReadIO(void *src, /* I - Data source */ */ if ((value = ipp_set_value(ipp, &attr, attr->num_values)) == NULL) - { - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); - } + goto rollback; } else if (tag == IPP_TAG_MEMBERNAME) { @@ -3171,8 +3164,13 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member name is not empty."), 1); DEBUG_puts("1ippReadIO: member name not empty."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; + } + else if (!parent) + { + _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP member attribute outside of collection."), 1); + DEBUG_puts("1ippReadIO: member attribute outside of collection."); + goto rollback; } if (ipp->current) @@ -3183,8 +3181,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetHTTPError(HTTP_STATUS_ERROR); DEBUG_puts("1ippReadIO: unable to allocate attribute."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } DEBUG_printf(("2ippReadIO: membername, ipp->current=%p, ipp->prev=%p", (void *)ipp->current, (void *)ipp->prev)); @@ -3200,8 +3197,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, (size_t)n) < n) { DEBUG_puts("1ippReadIO: unable to read name."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } buffer[n] = '\0'; @@ -3214,8 +3210,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetHTTPError(HTTP_STATUS_ERROR); DEBUG_puts("1ippReadIO: unable to allocate attribute."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } DEBUG_printf(("2ippReadIO: name=\"%s\", ipp->current=%p, ipp->prev=%p", buffer, (void *)ipp->current, (void *)ipp->prev)); @@ -3231,8 +3226,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, 2) < 2) { DEBUG_puts("1ippReadIO: unable to read value length."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } n = (buffer[0] << 8) | buffer[1]; @@ -3243,8 +3237,7 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP value larger than 32767 bytes."), 1); DEBUG_printf(("1ippReadIO: bad value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } switch (tag) @@ -3260,15 +3253,13 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP enum value not 4 bytes."), 1); DEBUG_printf(("1ippReadIO: bad integer value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, buffer, 4) < 4) { DEBUG_puts("1ippReadIO: Unable to read integer value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } n = (((((buffer[0] << 8) | buffer[1]) << 8) | buffer[2]) << 8) | @@ -3286,15 +3277,13 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP boolean value not 1 byte."), 1); DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, buffer, 1) < 1) { DEBUG_puts("1ippReadIO: Unable to read boolean value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } value->boolean = (char)buffer[0]; @@ -3335,8 +3324,7 @@ ippReadIO(void *src, /* I - Data source */ if ((*cb)(src, buffer, (size_t)n) < n) { DEBUG_puts("1ippReadIO: unable to read string value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } } @@ -3350,15 +3338,13 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP date value not 11 bytes."), 1); DEBUG_printf(("1ippReadIO: bad date value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, value->date, 11) < 11) { DEBUG_puts("1ippReadIO: Unable to read date value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } break; @@ -3368,15 +3354,13 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP resolution value not 9 bytes."), 1); DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, buffer, 9) < 9) { DEBUG_puts("1ippReadIO: Unable to read resolution value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } value->resolution.xres = @@ -3396,15 +3380,13 @@ ippReadIO(void *src, /* I - Data source */ _("IPP rangeOfInteger value not 8 bytes."), 1); DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length " "%d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, buffer, 8) < 8) { DEBUG_puts("1ippReadIO: Unable to read range value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } value->range.lower = @@ -3429,22 +3411,19 @@ ippReadIO(void *src, /* I - Data source */ "minimum 4 bytes."), 1); DEBUG_printf(("1ippReadIO: bad stringWithLanguage value " "length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, buffer, (size_t)n) < n) { DEBUG_puts("1ippReadIO: Unable to read string w/language " "value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } bufptr = buffer; bufend = buffer + n; - /* * text-with-language and name-with-language are composite * values: @@ -3457,14 +3436,13 @@ ippReadIO(void *src, /* I - Data source */ n = (bufptr[0] << 8) | bufptr[1]; - if ((bufptr + 2 + n) >= bufend || n >= (int)sizeof(string)) + if ((bufptr + 2 + n + 2) > bufend || n >= (int)sizeof(string)) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP language length overflows value."), 1); DEBUG_printf(("1ippReadIO: bad language value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } else if (n >= IPP_MAX_LANGUAGE) { @@ -3472,8 +3450,7 @@ ippReadIO(void *src, /* I - Data source */ _("IPP language length too large."), 1); DEBUG_printf(("1ippReadIO: bad language value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } memcpy(string, bufptr + 2, (size_t)n); @@ -3481,7 +3458,7 @@ ippReadIO(void *src, /* I - Data source */ value->string.language = _cupsStrAlloc((char *)string); - bufptr += 2 + n; + bufptr += 2 + n; n = (bufptr[0] << 8) | bufptr[1]; if ((bufptr + 2 + n) > bufend) @@ -3489,8 +3466,7 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP string length overflows value."), 1); DEBUG_printf(("1ippReadIO: bad string value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } bufptr[2 + n] = '\0'; @@ -3510,30 +3486,28 @@ ippReadIO(void *src, /* I - Data source */ _("IPP begCollection value not 0 bytes."), 1); DEBUG_puts("1ippReadIO: begCollection tag with value length " "> 0."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if (ippReadIO(src, cb, 1, ipp, value->collection) == IPP_STATE_ERROR) { DEBUG_puts("1ippReadIO: Unable to read collection value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } break; case IPP_TAG_END_COLLECTION : - _cupsBufferRelease((char *)buffer); - if (n > 0) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP endCollection value not 0 bytes."), 1); DEBUG_puts("1ippReadIO: endCollection tag with value length " "> 0."); - return (IPP_STATE_ERROR); + goto rollback; } + _cupsBufferRelease((char *)buffer); + DEBUG_puts("1ippReadIO: endCollection tag..."); return (ipp->state = IPP_STATE_DATA); @@ -3548,22 +3522,19 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName with no attribute."), 1); DEBUG_puts("1ippReadIO: Member name without attribute."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } else if (n == 0) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("IPP memberName value is empty."), 1); DEBUG_puts("1ippReadIO: Empty member name value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } else if ((*cb)(src, buffer, (size_t)n) < n) { DEBUG_puts("1ippReadIO: Unable to read member name value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } buffer[n] = '\0'; @@ -3588,8 +3559,7 @@ ippReadIO(void *src, /* I - Data source */ _("IPP octetString length too large."), 1); DEBUG_printf(("1ippReadIO: bad octetString value length %d.", n)); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } value->unknown.length = n; @@ -3600,15 +3570,13 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetHTTPError(HTTP_STATUS_ERROR); DEBUG_puts("1ippReadIO: Unable to allocate value"); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } if ((*cb)(src, value->unknown.data, (size_t)n) < n) { DEBUG_puts("1ippReadIO: Unable to read unsupported value."); - _cupsBufferRelease((char *)buffer); - return (IPP_STATE_ERROR); + goto rollback; } } else @@ -3636,6 +3604,17 @@ ippReadIO(void *src, /* I - Data source */ _cupsBufferRelease((char *)buffer); return (ipp->state); + + // If we get here, there was an error that required us to roll back the last + // attribute read in order to keep the IPP message valid... + rollback: + + _cupsBufferRelease((char *)buffer); + + if (attr) + ippDeleteAttribute(ipp, attr); + + return (IPP_STATE_ERROR); } -- 2.39.2