Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian....@packages.debian.org
Usertags: pu

[ Reason ]
cyrus-imapd before 3.2.8 allows remote attackers to cause a denial of
service (multiple-minute daemon hang) via input that is mishandled
during hash-table interaction. Because there are many insertions into
a single bucket, strcmp becomes slow. This is fixed in 3.4.2, 3.2.8,
and 3.0.16.

[ Impact ]
Medium vulnerability

[ Tests ]
The new cunit/strhash.testc passed.

[ Risks ]
Low risk, patch is easy to read

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
New string hashing algorithm and test.

Cheers,
Yadd
diff --git a/debian/changelog b/debian/changelog
index c8259297..bd11af8d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+cyrus-imapd (3.2.6-2+deb11u1) bullseye; urgency=medium
+
+  * Replace string hashing algorithm (Closes: #993433, CVE-2021-33582)
+
+ -- Yadd <y...@debian.org>  Wed, 01 Sep 2021 07:58:38 +0200
+
 cyrus-imapd (3.2.6-2) unstable; urgency=medium
 
   * Update gbp.conf for Bullseye branch
diff --git a/debian/control b/debian/control
index 3a4556b0..9b31670e 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Maintainer: Debian Cyrus Team <team+cy...@tracker.debian.org>
 Uploaders: Henrique de Moraes Holschuh <h...@debian.org>,
            Ondřej Surý <ond...@debian.org>,
            Anthony Prades <toony.deb...@chezouam.net>,
-           Xavier Guimard <y...@debian.org>
+           Yadd <y...@debian.org>
 Section: mail
 Priority: optional
 Build-Depends: bison,
diff --git a/debian/patches/CVE-2021-33582.patch 
b/debian/patches/CVE-2021-33582.patch
new file mode 100644
index 00000000..af48b338
--- /dev/null
+++ b/debian/patches/CVE-2021-33582.patch
@@ -0,0 +1,632 @@
+Description: Fixed CVE-2021-33582
+ Certain user inputs are used as hash table keys during processing. A
+ poorly chosen string hashing algorithm meant that the user could control
+ which bucket their data was stored in, allowing a malicious user to direct
+ many inputs to a single bucket. Each subsequent insertion to the same bucket
+ requires a strcmp of every other entry in it. At tens of thousands of
+ entries, each new insertion could keep the CPU busy in a strcmp loop for
+ minutes.
+ .
+ The string hashing algorithm has been replaced with a better one, and now
+ also uses a random seed per hash table, so malicious inputs cannot be
+ precomputed.
+ .
+ Discovered by Matthew Horsfall, Fastmail
+Author: ellie timoney <el...@fastmail.com>
+Origin: upstream, 
https://github.com/cyrusimap/cyrus-imapd/compare/cyrus-imapd-3.2.7...cyrus-imapd-3.2.8
+Bug: https://security-tracker.debian.org/tracker/CVE-2021-33582
+Forwarded: not-needed
+Reviewed-By: Yadd <y...@debian.org>
+Last-Update: 2021-09-01
+
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -677,6 +677,7 @@
+       cunit/squat.testc \
+       cunit/strarray.testc \
+       cunit/strconcat.testc \
++      cunit/strhash.testc \
+       cunit/times.testc \
+       cunit/tok.testc \
+       cunit/vparse.testc
+--- a/configure.ac
++++ b/configure.ac
+@@ -191,7 +191,7 @@
+ 
+ AC_CHECK_HEADERS(unistd.h sys/select.h sys/param.h stdarg.h)
+ AC_REPLACE_FUNCS(memmove strcasecmp ftruncate strerror posix_fadvise strsep 
memmem memrchr)
+-AC_CHECK_FUNCS(strlcat strlcpy strnchr getgrouplist fmemopen pselect futimens 
futimes)
++AC_CHECK_FUNCS(strlcat strlcpy strnchr getgrouplist fmemopen pselect futimens 
futimes getline)
+ AC_HEADER_DIRENT
+ 
+ dnl check whether to use getpassphrase or getpass
+--- a/cunit/hash.testc
++++ b/cunit/hash.testc
+@@ -117,6 +117,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(0, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(0, hash_numrecords(&ht));
++
+     /* free the hash table */
+     free_hash_table(&ht, NULL);
+ }
+@@ -146,6 +149,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(1, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(1, hash_numrecords(&ht));
++
+     /* re-insert into the hash table */
+     d = hash_insert(KEY0, VALUE1, &ht);
+     /* get the old value back */
+@@ -160,6 +166,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(1, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(1, hash_numrecords(&ht));
++
+     /* delete from the hash table */
+     d = hash_del(KEY0, &ht);
+     CU_ASSERT_PTR_EQUAL(VALUE1, d);
+@@ -173,6 +182,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(0, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(0, hash_numrecords(&ht));
++
+     /* free the hash table */
+     free_hash_table(&ht, NULL);
+ }
+@@ -239,6 +251,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(N, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(N, hash_numrecords(&ht));
++
+     /* delete from the hash table */
+     for (i = 0 ; i < N ; i++) {
+         d = hash_del(key(i), &ht);
+@@ -256,6 +271,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(0, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(0, hash_numrecords(&ht));
++
+     /* free the hash table */
+     freed_count = 0;
+     free_hash_table(&ht, lincoln);
+@@ -286,6 +304,9 @@
+     hash_enumerate(&ht, count_cb, &count);
+     CU_ASSERT_EQUAL(N, count);
+ 
++    /* check hash_numrecords */
++    CU_ASSERT_EQUAL(N, hash_numrecords(&ht));
++
+     /* free the hash table */
+     freed_count = 0;
+     free_hash_table(&ht, lincoln);
+--- /dev/null
++++ b/cunit/strhash.testc
+@@ -0,0 +1,230 @@
++#include <config.h>
++
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#include <errno.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include "cunit/cyrunit.h"
++#include "lib/util.h"
++#include "lib/strhash.h"
++
++extern int verbose;
++
++static const char *repeat(int c, unsigned n)
++{
++    static char buf[1024];
++    char *p = buf;
++
++    /* always leave room for a \0 */
++    if (n >= sizeof(buf))
++        n = sizeof(buf) - 1;
++
++    memset(buf, 0, sizeof(buf));
++    while (n--) {
++        *p++ = c;
++    }
++
++    return buf;
++}
++
++static void test_repeated(void)
++{
++    /* repeated chars on the end should not obliterate earlier input */
++    unsigned suffix_lengths[] = { 15, 31, 63, 127, 255, 511, 1023 };
++    unsigned i;
++
++    for (i = 0; i < sizeof(suffix_lengths) / sizeof(suffix_lengths[0]); i++) {
++        char *cat = strconcat("cat", repeat('a', suffix_lengths[i]), NULL);
++        char *dog = strconcat("dog", repeat('a', suffix_lengths[i]), NULL);
++        char *mouse = strconcat("mouse", repeat('a', suffix_lengths[i]), 
NULL);
++
++        unsigned xcat = strhash(cat);
++        unsigned xdog = strhash(dog);
++        unsigned xmouse = strhash(mouse);
++
++        CU_ASSERT_NOT_EQUAL(xcat, xdog);
++        CU_ASSERT_NOT_EQUAL(xdog, xmouse);
++        CU_ASSERT_NOT_EQUAL(xmouse, xcat);
++
++        free(cat);
++        free(dog);
++        free(mouse);
++    }
++}
++
++static void test_seeded(void)
++{
++    const char *const words[] = { "lorem", "ipsum", "dolor", "sit", "amet" };
++    const size_t n_words = sizeof(words) / sizeof(words[0]);
++    unsigned hashes[n_words];
++    unsigned i, j;
++
++    memset(hashes, 0, sizeof(hashes));
++
++    /* with no seed, same input should produce same hash */
++    for (i = 0; i < n_words; i++) {
++        unsigned h1 = strhash(words[i]);
++        unsigned h2 = strhash(words[i]);
++        CU_ASSERT_EQUAL(h1, h2);
++    }
++
++    /* with explicit zero seed, same input should produce same hash */
++    for (i = 0; i < n_words; i++) {
++        unsigned h1 = strhash(words[i]);
++        unsigned h2 = strhash_seeded(0, words[i]);
++        unsigned h3 = strhash_seeded(0, words[i]);
++        CU_ASSERT_EQUAL(h1, h2);
++        CU_ASSERT_EQUAL(h2, h3);
++        CU_ASSERT_EQUAL(h3, h1);
++    }
++
++    /* with some seed, same input should produce same hash */
++    for (j = 0; j < 5; j++) {
++        uint32_t seed;
++        do {
++            seed = rand();
++        } while (seed == 0);
++
++        for (i = 0; i < n_words; i++) {
++            unsigned h1 = strhash_seeded(seed, words[i]);
++            unsigned h2 = strhash_seeded(seed, words[i]);
++            CU_ASSERT_EQUAL(h1, h2);
++        }
++    }
++
++    /* with different seed, same input should produce different hash */
++    for (i = 0; i < n_words; i++) {
++        uint32_t seed1, seed2;
++        do {
++            seed1 = rand();
++            seed2 = rand();
++        } while (seed1 == 0 || seed2 == 0 || seed1 == seed2);
++
++        unsigned h1 = strhash_seeded(seed1, words[i]);
++        unsigned h2 = strhash_seeded(seed2, words[i]);
++
++        CU_ASSERT_NOT_EQUAL(h1, h2);
++    }
++}
++
++/* We can't define-out an entire test function when a feature is missing
++ * (in this case getline), because it confuses cunit.pl. So instead we
++ * make sure it will at least compile, but then return early without doing
++ * anything if the feature we wanted was missing.
++ */
++#ifndef HAVE_GETLINE
++#define getline(a,b,c) (((void)(b)), -1)
++#endif
++
++#define NBUCKETS (0x10000)
++static void test_quality(void)
++{
++    const char *wordsfile = "/usr/share/dict/words";
++    unsigned buckets[NBUCKETS] = {0};
++    FILE *stream;
++    char *line = NULL;
++    size_t len = 0;
++    ssize_t nread;
++    unsigned i;
++    unsigned inputs = 0;
++    unsigned contains_none = 0;
++    unsigned contains_one = 0;
++    unsigned contains_many = 0;
++    unsigned contains_many_sum = 0;
++    unsigned highest_count = 0;
++    unsigned highest_count_freq = 0;
++    unsigned max_acceptable_count;
++    double load;
++
++#ifndef HAVE_GETLINE
++    /* can't do anything anyway */
++    return;
++#endif
++
++    stream = fopen(wordsfile, "r");
++    if (!stream) {
++        if (verbose)
++            fprintf(stderr, "%s: %s (skipping) ", wordsfile, strerror(errno));
++        return;
++    }
++
++    while ((nread = getline(&line, &len, stream)) != -1) {
++        /* chomp */
++        if (line[nread - 1] == '\n')
++            line[nread - 1] = '\0';
++
++        unsigned hash = strhash_seeded_djb2(0, line) % NBUCKETS;
++
++        buckets[hash]++;
++        inputs++;
++    }
++    free(line);
++
++    /* arbitrary declaration of quality: no buckets should have more
++     * than ten times the expected load
++     */
++    load = inputs * 1.0 / NBUCKETS;
++    max_acceptable_count = load * 10;
++
++    unsigned bucket_counts[max_acceptable_count + 2];
++    memset(bucket_counts, 0, sizeof(bucket_counts));
++
++    for (i = 0; i < NBUCKETS; i++) {
++        switch (buckets[i]) {
++        case 0:
++            contains_none++;
++            break;
++        case 1:
++            contains_one++;
++            break;
++        default:
++            contains_many++;
++            contains_many_sum += buckets[i];
++            break;
++        }
++
++        if (buckets[i] > max_acceptable_count) {
++            bucket_counts[max_acceptable_count+1]++;
++        }
++        else {
++            bucket_counts[buckets[i]]++;
++        }
++
++        if (buckets[i] > highest_count) {
++            highest_count = buckets[i];
++            highest_count_freq = 1;
++        }
++        else if (buckets[i] == highest_count) {
++            highest_count_freq++;
++        }
++    }
++
++    if (verbose) {
++        putc('\n', stderr);
++        fprintf(stderr, "buckets: %u inputs: %u load: %g\n",
++                        NBUCKETS, inputs, load);
++        fprintf(stderr, "empty: %u unique: %u busy: %u\n",
++                        contains_none, contains_one, contains_many);
++        fprintf(stderr, "avg count in busy buckets: %g\n",
++                        contains_many_sum * 1.0 / contains_many);
++        fprintf(stderr, "busiest %u buckets contain %u each\n",
++                        highest_count_freq, highest_count);
++        fprintf(stderr, "max acceptable count: %u\n", max_acceptable_count);
++        fprintf(stderr, "\nbucket count histogram:\ncount frequency\n");
++        for (i = 0; i <= max_acceptable_count; i++) {
++            fprintf(stderr, "%4u  %u\n", i, bucket_counts[i]);
++        }
++        fprintf(stderr, "%4u+ %u\n", max_acceptable_count + 1,
++                                     bucket_counts[max_acceptable_count + 1]);
++    }
++
++    CU_ASSERT_EQUAL(bucket_counts[max_acceptable_count + 1], 0);
++}
++#undef NBUCKETS
++
++/* vim: set ft=c: */
+--- a/imap/http_dav.c
++++ b/imap/http_dav.c
+@@ -2678,13 +2678,10 @@
+ 
+     if (!propstat) {
+         /* Prescreen "property" request */
+-        if (fctx->req_tgt->collection ||
+-            (fctx->req_tgt->userid && fctx->depth >= 1) || fctx->depth >= 2) {
+-            /* Add namespaces for possible privileges */
+-            ensure_ns(fctx->ns, NS_CYRUS, fctx->root, XML_NS_CYRUS, "CY");
+-            if (fctx->req_tgt->namespace->id == URL_NS_CALENDAR) {
+-                ensure_ns(fctx->ns, NS_CALDAV, fctx->root, XML_NS_CALDAV, 
"C");
+-            }
++        /* Add namespaces for possible privileges */
++        ensure_ns(fctx->ns, NS_CYRUS, fctx->root, XML_NS_CYRUS, "CY");
++        if (fctx->req_tgt->namespace->id == URL_NS_CALENDAR) {
++            ensure_ns(fctx->ns, NS_CALDAV, fctx->root, XML_NS_CALDAV, "C");
+         }
+ 
+         return 0;
+@@ -6047,7 +6044,7 @@
+     xmlDocPtr indoc = NULL, outdoc = NULL;
+     xmlNodePtr root, cur = NULL, props = NULL;
+     xmlNsPtr ns[NUM_NAMESPACE];
+-    struct hash_table ns_table = { 0, NULL, NULL };
++    struct hash_table ns_table = HASH_TABLE_INITIALIZER;
+     struct propfind_ctx fctx;
+ 
+     memset(&fctx, 0, sizeof(struct propfind_ctx));
+@@ -8011,7 +8008,7 @@
+     xmlNodePtr inroot = NULL, outroot = NULL, cur, prop = NULL, props = NULL;
+     const struct report_type_t *report = NULL;
+     xmlNsPtr ns[NUM_NAMESPACE];
+-    struct hash_table ns_table = { 0, NULL, NULL };
++    struct hash_table ns_table = HASH_TABLE_INITIALIZER;
+     struct propfind_ctx fctx;
+ 
+     memset(&fctx, 0, sizeof(struct propfind_ctx));
+--- a/imap/jmap_mail.c
++++ b/imap/jmap_mail.c
+@@ -4044,7 +4044,7 @@
+     memset(&touched_ids, 0, sizeof(hash_table));
+     construct_hash_table(&touched_ids, mdcount + 1, 0);
+ 
+-    hashu64_table touched_cids = HASH_TABLE_INITIALIZER;
++    hashu64_table touched_cids = HASHU64_TABLE_INITIALIZER;
+     memset(&touched_cids, 0, sizeof(hashu64_table));
+     construct_hashu64_table(&touched_cids, mdcount + 1, 0);
+ 
+--- a/lib/hash.c
++++ b/lib/hash.c
+@@ -43,10 +43,12 @@
+       assert(table);
+       assert(size);
+ 
+-      table->size  = size;
++      table->size = size;
++      table->count = 0;
++      table->seed = rand(); /* might be zero, that's okay */
+ 
+       /* Allocate the table -- different for using memory pools and not */
+-      if(use_mpool) {
++      if (use_mpool) {
+           /* Allocate an initial memory pool for 32 byte keys + the hash table
+            * + the buckets themselves */
+           table->pool =
+@@ -72,7 +74,7 @@
+ 
+ EXPORTED void *hash_insert(const char *key, void *data, hash_table *table)
+ {
+-      unsigned val = strhash(key) % table->size;
++      unsigned val = strhash_seeded(table->seed, key) % table->size;
+       bucket *ptr, *newptr;
+       bucket **prev;
+ 
+@@ -93,12 +95,13 @@
+           }
+           (table->table)[val] -> next = NULL;
+           (table->table)[val] -> data = data;
++          table->count++;
+           return (table->table)[val] -> data;
+       }
+ 
+       /*
+       ** This spot in the table is already in use.  See if the current string
+-      ** has already been inserted, and if so, increment its count.
++      ** has already been inserted, and if so, replace its data
+       */
+       for (prev = &((table->table)[val]), ptr=(table->table)[val];
+            ptr;
+@@ -124,6 +127,7 @@
+               newptr->data = data;
+               newptr->next = ptr;
+               *prev = newptr;
++              table->count++;
+               return data;
+           }
+       }
+@@ -142,10 +146,10 @@
+       newptr->data = data;
+       newptr->next = NULL;
+       *prev = newptr;
++      table->count++;
+       return data;
+ }
+ 
+-
+ /*
+ ** Look up a key and return the associated data.  Returns NULL if
+ ** the key is not in the table.
+@@ -159,7 +163,7 @@
+       if (!table->size)
+           return NULL;
+ 
+-      val = strhash(key) % table->size;
++      val = strhash_seeded(table->seed, key) % table->size;
+ 
+       if (!(table->table)[val])
+             return NULL;
+@@ -183,8 +187,7 @@
+  * since it will leak memory until you get rid of the entire hash table */
+ EXPORTED void *hash_del(const char *key, hash_table *table)
+ {
+-      unsigned val = strhash(key) % table->size;
+-      void *data;
++      unsigned val = strhash_seeded(table->seed, key) % table->size;
+       bucket *ptr, *last = NULL;
+ 
+       if (!(table->table)[val])
+@@ -205,15 +208,10 @@
+           int cmpresult = strcmp(key, ptr->key);
+           if (!cmpresult)
+           {
++              void *data = ptr->data;
+               if (last != NULL )
+               {
+-                  data = ptr -> data;
+                   last -> next = ptr -> next;
+-                  if(!table->pool) {
+-                      free(ptr->key);
+-                      free(ptr);
+-                  }
+-                  return data;
+               }
+ 
+               /*
+@@ -226,15 +224,16 @@
+ 
+               else
+               {
+-                  data = ptr->data;
+                   (table->table)[val] = ptr->next;
+-                  if(!table->pool) {
+-                      free(ptr->key);
+-                      free(ptr);
+-                  }
+-                  return data;
+               }
+-          } else if (cmpresult < 0) {
++              if(!table->pool) {
++                  free(ptr->key);
++                  free(ptr);
++              }
++              table->count--;
++              return data;
++          }
++          if (cmpresult < 0) {
+               /* its not here! */
+               return NULL;
+           }
+@@ -292,6 +291,7 @@
+       }
+       table->table = NULL;
+       table->size = 0;
++      table->count = 0;
+ }
+ 
+ /*
+@@ -340,19 +340,8 @@
+ 
+ EXPORTED int hash_numrecords(hash_table *table)
+ {
+-    unsigned i;
+-    bucket *temp;
+-    int count = 0;
+-
+-    for (i = 0; i < table->size; i++) {
+-        temp = (table->table)[i];
+-        while (temp) {
+-            count++;
+-            temp = temp->next;
+-        }
+-    }
+-
+-    return count;
++    /* XXX macro or inline this if we keep the count field long term */
++    return table->count;
+ }
+ 
+ EXPORTED void hash_enumerate_sorted(hash_table *table, void (*func)(const 
char *, void *, void *),
+--- a/lib/hash.h
++++ b/lib/hash.h
+@@ -3,10 +3,11 @@
+ #define HASH__H
+ 
+ #include <stddef.h>           /* For size_t     */
++#include <stdint.h>
+ #include "mpool.h"
+ #include "strarray.h"
+ 
+-#define HASH_TABLE_INITIALIZER {0, NULL, NULL}
++#define HASH_TABLE_INITIALIZER {0, 0, 0, NULL, NULL}
+ 
+ /*
+ ** A hash table consists of an array of these buckets.  Each bucket
+@@ -32,6 +33,8 @@
+ 
+ typedef struct hash_table {
+     size_t size;
++    size_t count;
++    uint32_t seed;
+     bucket **table;
+     struct mpool *pool;
+ } hash_table;
+--- a/lib/strhash.c
++++ b/lib/strhash.c
+@@ -42,17 +42,32 @@
+ 
+ #include "config.h"
+ 
+-EXPORTED unsigned strhash(const char *string)
++#include "lib/strhash.h"
++
++/* The well-known djb2 algorithm (e.g. http://www.cse.yorku.ca/~oz/hash.html),
++ * with the addition of an optional seed to limit predictability.
++ *
++ * XXX return type 'unsigned' for back-compat to previous version, but
++ * XXX ought to be 'uint32_t'
++ */
++EXPORTED unsigned strhash_seeded_djb2(uint32_t seed, const char *string)
+ {
+-      unsigned ret_val = 0;
+-      int i;
++    const unsigned char *ustr = (const unsigned char *) string;
++    unsigned hash = 5381;
++    int c;
++
++    if (seed) {
++        /* treat the bytes of the seed as a prefix to the string */
++        unsigned i;
++        for (i = 0; i < sizeof seed; i++) {
++            c = seed & 0xff;
++            hash = ((hash << 5) + hash) ^ c;
++            seed >>= 8;
++        }
++    }
++
++    while ((c = *ustr++))
++        hash = ((hash << 5) + hash) ^ c;
+ 
+-      while (*string)
+-      {
+-            i = (int) *string;
+-            ret_val ^= i;
+-            ret_val <<= 1;
+-            string ++;
+-      }
+-      return ret_val;
++    return hash;
+ }
+--- a/lib/strhash.h
++++ b/lib/strhash.h
+@@ -41,7 +41,11 @@
+  */
+ 
+ #ifndef _STRHASH_H_
++#include <stdint.h>
+ 
+-unsigned strhash(const char *string);
++unsigned strhash_seeded_djb2(uint32_t seed, const char *string);
++
++#define strhash(in)             strhash_seeded_djb2((0),  (in))
++#define strhash_seeded(sd, in)  strhash_seeded_djb2((sd), (in))
+ 
+ #endif /* _STRHASH_H_ */
diff --git a/debian/patches/series b/debian/patches/series
index 27fc0ec9..1577d428 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -8,3 +8,4 @@
 0012-Use-UnicodeData.txt-from-system.patch
 0018-increase-test-timeout.patch
 CVE-2021-32056.patch
+CVE-2021-33582.patch

Reply via email to