Emmanuel Bouillon reported a memory corruption flaw in CUPS daemon. A specially-crafted IPP request can cause daemon to crash or, possibly, execute arbitrary code. Acknowledgements: Red Hat would like to thank Emmanuel Bouillon of NATO C3 Agency for reporting this issue.
The problem is a mismatch between different memory allocators. In the IPP protocol, an attribute can have multiple values, and each value is typed. In the CUPS data model for this, all values for a given attribute must have the same (or a compatible) type. String types have values allocated with the StrAlloc allocator, a reference-counting string pool. 'Unknown' types use malloc. By giving the first value for an attribute a value tag of 56, which does not correspond to any particular value type and so is 'unknown', but which also is accepted as 'compatible' with string types due to the value tag range check, CUPS does not reject the request. The 'unknown' value is allocated using malloc; the remaining values are allocated using StrAlloc. When the in-memory request is freed, free() is used for all values including those allocated using StrAlloc. The solution is to use exactly the same conditions when deciding how to allocate the value as when freeing it. The initial value is allocated using malloc(), as it does not match any of the value tag cases. Successive values are allocated using StrAlloc because they are string types. Here is the compatibility check just before the successive values are allocated (cups/ipp.c); here, value_tag is the value tag given to the first value, and tag is the value tag given to the current (successive) value: /* * Make sure we aren't adding a new value of a different * type... */ if (value_tag == IPP_TAG_ZERO) { /* * Setting the value of a collection member... */ attr->value_tag = tag; } else if ((value_tag >= IPP_TAG_TEXTLANG && value_tag <= IPP_TAG_MIMETYPE)) { /* * String values can sometimes come across in different * forms; accept sets of differing values... */ /* * String values can sometimes come across in different * forms; accept sets of differing values... */ if ((tag < IPP_TAG_TEXTLANG || tag > IPP_TAG_MIMETYPE) && tag != IPP_TAG_NOVALUE) { DEBUG_printf(("1ippReadIO: 1setOf value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag))); ipp_buffer_release(buffer); return (IPP_ERROR); } } else if (value_tag != tag) { DEBUG_printf(("1ippReadIO: value tag %x(%s) != %x(%s)", value_tag, ippTagString(value_tag), tag, ippTagString(tag))); ipp_buffer_release(buffer); return (IPP_ERROR); } Now, IPP_TAG_TEXTLANG < 56 < IPP_TAG_MIMETYPE, so '56' is allowed to be compatible with string types -- even though it uses a different allocator. Here is how the data is freed: default : if (!((int)attr->value_tag & IPP_TAG_COPY)) { for (i = 0, value = attr->values; i < attr->num_values; i ++, value ++) if (value->unknown.data) free(value->unknown.data); } break; (because all the values are collected together under the '56' value tag). As far as I have been able to tell from source code inspection, all versions of CUPS are vulnerable to this denial of service attack.
Created attachment 438968 [details] cups-value-tag-mismatch.patch Patch attached (for RHEL-6) to fix the compatibility check to use exactly the same checking as in _ippAttrFree().
RHEL-3 and RHEL-4 are not vulnerable to this because they do not use the string pool/StrAlloc allocator, only malloc (in fact strdup).
(In reply to comment #1) > As far as I have been able to tell from source code inspection, all versions > of CUPS are vulnerable to this denial of service attack. There are differences in the impact between 1.3 and 1.4 CUPS versions due to the differences in the way _cups_sp_item_t is organized. In 1.4, _cups_sp_item_t is: 71 typedef struct _cups_sp_item_s /**** String Pool Item ****/ 72 { 73 # ifdef DEBUG_GUARDS 74 unsigned int guard; /* Guard word */ 75 # endif /* DEBUG_GUARDS */ 76 unsigned int ref_count; /* Reference count */ 77 char str[1]; /* String */ 78 } _cups_sp_item_t; _cupsStrAlloc() allocates new item as: calloc(1, sizeof(_cups_sp_item_t) + strlen(s)) and returns str pointer. Therefore, _cupsStrFree() calls free() on a value point to the middle of the malloced chunk, triggering glibc malloc checks failure and abort. In 1.3, _cups_sp_item_t is: 69 typedef struct _cups_sp_item_s /**** String Pool Item ****/ 70 { 71 char *str; /* String */ 72 unsigned int ref_count; /* Reference count */ 73 } _cups_sp_item_t; and str is strdup()-allocated independently of the structure. Hence value passed to free() from _cupsStrFree() points to valid malloc()ed chunk that can be freed, but string pool item is not properly ref-counted and removed from string pool, that may lead to use-after-free with possible additional memory corruptions.
Status update: we still don't have any embargo date proposal from Apple security team. No changes in the upstream bug.
Created attachment 442221 [details] cups-CVE-2010-2941-EL5.patch Same patch back-ported to Red Hat Enterprise Linux 5.
Created attachment 442263 [details] Alternate fix from Mike Sweet, upstream CUPS author and maintainer
Created attachment 442372 [details] Mike's patch back-ported to RHEL5
Update from Apple Product Security: We're currently planning on including this fix in our next Mac OS X update. At this time, we don't have a date for when the fix might be available.
6 weeks now. Any news regarding the disclosure date?
No update from Apple on disclosure date so far.
New upstream release is expected in about 1 month, based on what we've been told.
Apple is planning to release late this week.
The new upstream release is not available yet, but we have been given the go-ahead to observe the original embargo (today) and release.
This issue has been addressed in following products: Red Hat Enterprise Linux 5 Via RHSA-2010:0811 https://rhn.redhat.com/errata/RHSA-2010-0811.html
This issue has been addressed in following products: Red Hat Enterprise Linux 6 Via RHSA-2010:0866 https://rhn.redhat.com/errata/RHSA-2010-0866.html
Created cups tracking bugs for this issue Affects: fedora-all [bug 652161]