| CVE: CVE-2018-20030 |
| Upstream-Status: Backport |
| Signed-off-by: Ross Burton <ross.burton@intel.com> |
| |
| From 6aa11df549114ebda520dde4cdaea2f9357b2c89 Mon Sep 17 00:00:00 2001 |
| From: Dan Fandrich <dan@coneharvesters.com> |
| Date: Fri, 12 Oct 2018 16:01:45 +0200 |
| Subject: [PATCH] Improve deep recursion detection in |
| exif_data_load_data_content. |
| |
| The existing detection was still vulnerable to pathological cases |
| causing DoS by wasting CPU. The new algorithm takes the number of tags |
| into account to make it harder to abuse by cases using shallow recursion |
| but with a very large number of tags. This improves on commit 5d28011c |
| which wasn't sufficient to counter this kind of case. |
| |
| The limitation in the previous fix was discovered by Laurent Delosieres, |
| Secunia Research at Flexera (Secunia Advisory SA84652) and is assigned |
| the identifier CVE-2018-20030. |
| |
| diff --git a/libexif/exif-data.c b/libexif/exif-data.c |
| index 67df4db..8d9897e 100644 |
| --- a/libexif/exif-data.c |
| +++ b/libexif/exif-data.c |
| @@ -35,6 +35,7 @@ |
| #include <libexif/olympus/exif-mnote-data-olympus.h> |
| #include <libexif/pentax/exif-mnote-data-pentax.h> |
| |
| +#include <math.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| @@ -344,6 +345,20 @@ if (data->ifd[(i)]->count) { \ |
| break; \ |
| } |
| |
| +/*! Calculate the recursion cost added by one level of IFD loading. |
| + * |
| + * The work performed is related to the cost in the exponential relation |
| + * work=1.1**cost |
| + */ |
| +static unsigned int |
| +level_cost(unsigned int n) |
| +{ |
| + static const double log_1_1 = 0.09531017980432493; |
| + |
| + /* Adding 0.1 protects against the case where n==1 */ |
| + return ceil(log(n + 0.1)/log_1_1); |
| +} |
| + |
| /*! Load data for an IFD. |
| * |
| * \param[in,out] data #ExifData |
| @@ -351,13 +366,13 @@ if (data->ifd[(i)]->count) { \ |
| * \param[in] d pointer to buffer containing raw IFD data |
| * \param[in] ds size of raw data in buffer at \c d |
| * \param[in] offset offset into buffer at \c d at which IFD starts |
| - * \param[in] recursion_depth number of times this function has been |
| - * recursively called without returning |
| + * \param[in] recursion_cost factor indicating how expensive this recursive |
| + * call could be |
| */ |
| static void |
| exif_data_load_data_content (ExifData *data, ExifIfd ifd, |
| const unsigned char *d, |
| - unsigned int ds, unsigned int offset, unsigned int recursion_depth) |
| + unsigned int ds, unsigned int offset, unsigned int recursion_cost) |
| { |
| ExifLong o, thumbnail_offset = 0, thumbnail_length = 0; |
| ExifShort n; |
| @@ -372,9 +387,20 @@ exif_data_load_data_content (ExifData *data, ExifIfd ifd, |
| if ((((int)ifd) < 0) || ( ((int)ifd) >= EXIF_IFD_COUNT)) |
| return; |
| |
| - if (recursion_depth > 30) { |
| + if (recursion_cost > 170) { |
| + /* |
| + * recursion_cost is a logarithmic-scale indicator of how expensive this |
| + * recursive call might end up being. It is an indicator of the depth of |
| + * recursion as well as the potential for worst-case future recursive |
| + * calls. Since it's difficult to tell ahead of time how often recursion |
| + * will occur, this assumes the worst by assuming every tag could end up |
| + * causing recursion. |
| + * The value of 170 was chosen to limit typical EXIF structures to a |
| + * recursive depth of about 6, but pathological ones (those with very |
| + * many tags) to only 2. |
| + */ |
| exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", |
| - "Deep recursion detected!"); |
| + "Deep/expensive recursion detected!"); |
| return; |
| } |
| |
| @@ -416,15 +442,18 @@ exif_data_load_data_content (ExifData *data, ExifIfd ifd, |
| switch (tag) { |
| case EXIF_TAG_EXIF_IFD_POINTER: |
| CHECK_REC (EXIF_IFD_EXIF); |
| - exif_data_load_data_content (data, EXIF_IFD_EXIF, d, ds, o, recursion_depth + 1); |
| + exif_data_load_data_content (data, EXIF_IFD_EXIF, d, ds, o, |
| + recursion_cost + level_cost(n)); |
| break; |
| case EXIF_TAG_GPS_INFO_IFD_POINTER: |
| CHECK_REC (EXIF_IFD_GPS); |
| - exif_data_load_data_content (data, EXIF_IFD_GPS, d, ds, o, recursion_depth + 1); |
| + exif_data_load_data_content (data, EXIF_IFD_GPS, d, ds, o, |
| + recursion_cost + level_cost(n)); |
| break; |
| case EXIF_TAG_INTEROPERABILITY_IFD_POINTER: |
| CHECK_REC (EXIF_IFD_INTEROPERABILITY); |
| - exif_data_load_data_content (data, EXIF_IFD_INTEROPERABILITY, d, ds, o, recursion_depth + 1); |
| + exif_data_load_data_content (data, EXIF_IFD_INTEROPERABILITY, d, ds, o, |
| + recursion_cost + level_cost(n)); |
| break; |
| case EXIF_TAG_JPEG_INTERCHANGE_FORMAT: |
| thumbnail_offset = o; |