$OpenBSD: patch-src_libical_icalrecur_c,v 1.3 2016/04/26 10:24:46 ajacoutot Exp $

Add patch for possible use-after-free of icalrecurrencetype::rscale (Fedora).

Fix build on arm (Fedora).

--- src/libical/icalrecur.c.orig	Mon Dec 28 22:44:53 2015
+++ src/libical/icalrecur.c	Tue Apr 26 12:10:19 2016
@@ -134,6 +134,7 @@
 #endif
 
 #include "icalrecur.h"
+#include "icalarray.h"
 #include "icalerror.h"
 #include "icalmemory.h"
 #include "icaltimezone.h"
@@ -186,6 +187,139 @@
 
 #define LEAP_MONTH 0x1000
 
+#if defined(HAVE_PTHREAD)
+#include <pthread.h>
+static pthread_mutex_t rscale_texts_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static icalarray *rscale_texts = NULL;
+
+static void initialize_rscale_texts(void)
+{
+#if defined(HAVE_LIBICU)
+    UErrorCode status = U_ZERO_ERROR;
+    UEnumeration *en;
+    const char *cal;
+#endif
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_lock(&rscale_texts_mutex);
+#endif
+
+    if (rscale_texts != NULL) {
+    #if defined(HAVE_PTHREAD)
+        pthread_mutex_unlock(&rscale_texts_mutex);
+    #endif
+        return;
+    }
+
+    rscale_texts = icalarray_new(sizeof(char **), 20);
+
+#if defined(HAVE_LIBICU)
+    en = ucal_getKeywordValuesForLocale("calendar", NULL, FALSE, &status);
+    while ((cal = uenum_next(en, NULL, &status))) {
+        char *copy = icalmemory_strdup(cal);
+        icalarray_append(rscale_texts, &copy);
+    }
+    uenum_close(en);
+#endif
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_unlock(&rscale_texts_mutex);
+#endif
+}
+
+static const char *match_rscale_text(const char *text)
+{
+    size_t ii;
+    const char *res = NULL;
+
+    if(!text) {
+        return NULL;
+    }
+
+    initialize_rscale_texts();
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_lock(&rscale_texts_mutex);
+#endif
+
+    for(ii = 0; rscale_texts && ii < rscale_texts->num_elements; ii++) {
+        const char **stored, *p1, *p2;
+
+        stored = icalarray_element_at(rscale_texts, ii);
+        if(!stored || !*stored)
+            continue;
+
+        for(p1 = *stored, p2 = text; *p1 && *p2; p1++, p2++) {
+            if (tolower(*p1) != tolower(*p2))
+                break;
+        }
+
+        if(!*p1 && !*p2) {
+            res = *stored;
+            break;
+        }
+    }
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_unlock(&rscale_texts_mutex);
+#endif
+
+    return res;
+}
+
+static const char *match_or_add_rscale_text(const char *text)
+{
+    const char *res;
+
+    if(!text) {
+        return NULL;
+    }
+
+    res = match_rscale_text (text);
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_lock(&rscale_texts_mutex);
+#endif
+
+    if(!res && rscale_texts) {
+        res = icalmemory_strdup(text);
+        icalarray_append(rscale_texts, &res);
+    }
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_unlock(&rscale_texts_mutex);
+#endif
+
+    return res;
+}
+
+void icalrecur_free_rscale_texts(void)
+{
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_lock(&rscale_texts_mutex);
+#endif
+
+    if(rscale_texts) {
+        size_t ii;
+        for(ii = 0; rscale_texts && ii < rscale_texts->num_elements; ii++) {
+            char **stored;
+
+            stored = icalarray_element_at(rscale_texts, ii);
+            if(stored && *stored)
+                icalmemory_free_buffer(*stored);
+        }
+
+        icalarray_free(rscale_texts);
+        rscale_texts = NULL;
+    }
+
+#if defined(HAVE_PTHREAD)
+    pthread_mutex_unlock(&rscale_texts_mutex);
+#endif
+}
+
 int icalrecurrencetype_rscale_is_supported(void)
 {
     return RSCALE_IS_SUPPORTED;
@@ -491,8 +625,8 @@ static int icalrecur_add_bydayrules(struct icalrecur_p
 
     while (n != 0) {
         int sign = 1;
-        char weekno;  /* note: Novell/Groupwise sends BYDAY=255SU,
-                         so we fit in a signed char to get -1 SU for last Sun */
+        signed char weekno;  /* note: Novell/Groupwise sends BYDAY=255SU,
+                                so we fit in a signed char to get -1 SU for last Sun */
         icalrecurrencetype_weekday wd;
 
         if (i == ICAL_BY_DAY_SIZE) {
@@ -510,7 +644,7 @@ static int icalrecur_add_bydayrules(struct icalrecur_p
         }
 
         /* Get Optional weekno */
-        weekno = (char)strtol(t, &t, 10);
+        weekno = (signed char)strtol(t, &t, 10);
         if (weekno < 0) {
             weekno = -weekno;
             sign = -1;
@@ -585,7 +719,7 @@ struct icalrecurrencetype icalrecurrencetype_from_stri
             if (parser.rt.freq == ICAL_NO_RECURRENCE) r = -1;
         } else if (icalrecurrencetype_rscale_is_supported() &&
                    strcasecmp(name, "RSCALE") == 0) {
-            parser.rt.rscale = icalmemory_tmp_copy(value);
+            parser.rt.rscale = match_or_add_rscale_text(value);
         } else if (icalrecurrencetype_rscale_is_supported() &&
                    strcasecmp(name, "SKIP") == 0) {
             parser.rt.skip = icalrecur_string_to_skip(value);
@@ -1359,19 +1493,16 @@ static int initialize_iterator(icalrecur_iterator *imp
         impl->greg = NULL;
     } else {
         UEnumeration *en;
-        const char *cal;
-        char *r;
+        const char *cal, *rrscale;
 
-        /* Lowercase the specified calendar */
-        for (r = rule.rscale; *r; r++) {
-            *r = tolower((int)*r);
-        }
+        /* This can be a user-created string, thus not a one from the pool */
+        rrscale = match_or_add_rscale_text (rule.rscale);
 
         /* Check if specified calendar is supported */
         en = ucal_getKeywordValuesForLocale("calendar", NULL, FALSE, &status);
         while ((cal = uenum_next(en, NULL, &status))) {
-            if (!strcmp(cal, rule.rscale)) {
-                is_hebrew = !strcmp(rule.rscale, "hebrew");
+            if (rrscale == match_rscale_text(cal)) {
+                is_hebrew = rrscale == match_rscale_text("hebrew");
                 break;
             }
         }
