No public description
PiperOrigin-RevId: 655660744
diff --git a/patches/01.korean_affix.patch b/patches/01.korean_affix.patch
new file mode 100644
index 0000000..272d6c1
--- /dev/null
+++ b/patches/01.korean_affix.patch
@@ -0,0 +1,35 @@
+Change 16091682 by jiho@jiho-earthsea-medley-review-fix_segfault-git5 on 2010/06/16 01:13:54
+
+ Fix buffer allocation problem of hunspell, expecially for Korean Affix which
+ doesn't use UTF-8 as its base encoding.
+
+ PRESUBMIT=passed
+ BUG=2661104
+ R=jayr
+ DELTA=40 (9 added, 25 deleted, 6 changed)
+ OCL=16077686
+
+Affected files ...
+
+... //depot//hunspell_1_2_8/README.google#2 edit
+... //depot//hunspell_1_2_8/src/hunspell/atypes.hxx#2 edit
+... //depot//hunspell_1_2_8/src/hunspell/hunspell.cxx#3 edit
+
+==== //depot//src/hunspell/atypes.hxx#1 - /google/src/files/16091682/depot//hunspell_1_2_8/src/hunspell/atypes.hxx ====
+--- /google/src/files/13856229/depot//src/hunspell/atypes.hxx 2009-12-08 16:54:13.000000000 -0500
++++ /google/src/files/16091682/depot//src/hunspell/atypes.hxx 2010-06-16 04:13:54.000000000 -0400
+@@ -19,7 +19,13 @@
+ #define SETSIZE 256
+ #define CONTSIZE 65536
+ #define MAXWORDLEN 100
+-#define MAXWORDUTF8LEN 256
++// Note(jiho@google.com): Korean Hunspell dictionary doesn't use UTF-8 directly.
++// It decomposes one single Korean character to three characters. So, one single
++// UTF-8 Korean character(3 bytes) will eventually take 9 bytes. At least
++// MAXWORDLEN * 3 bytes are required for handling Korean dictionary.
++// And the Korean dictionary has a word with 513 byte length. I've changed this
++// value to 550 to cover the word. If not, hunspell dies with segfault.
++#define MAXWORDUTF8LEN 550
+
+ // affentry options
+ #define aeXPRODUCT (1 << 0)
diff --git a/patches/02.buffer_overrun.patch b/patches/02.buffer_overrun.patch
new file mode 100644
index 0000000..da174ac
--- /dev/null
+++ b/patches/02.buffer_overrun.patch
@@ -0,0 +1,118 @@
+Change 18870443 by jiho@jiho-earthsea-medley-work-git5 on 2011/01/06 23:29:34
+
+ Fixes a buffer overrun problem of hunspell-1.2.12 when a Korean word longer
+ than MAXWORDUTF8LEN/3 bytes by changing input length checking code. Hunspell
+ uses MAXWORDUTF8LEN byte long internal buffers. But, a very long Korean word
+ can overrun those buffers because Korean dictionary converts a UTF-8 charater
+ to 3 UTF-8 charaters.
+
+ PRESUBMIT=passed
+ BUG=2962572
+ R=jayr,jaisunda
+ CC=caribou-backend-reviews
+ APPROVED=jaisunda
+ DELTA=55 (44 added, 0 deleted, 11 changed)
+ OCL=18854064
+
+Affected files ...
+
+... //depot/google3/caribou/spell/spellcheck_unittest.cc#43 edit
+... //depot//hunspell_1_2_12/README.google#2 edit
+... //depot//hunspell_1_2_12/src/hunspell/hunspell.cxx#2 edit
+... //depot//hunspell_1_2_12/src/hunspell/replist.cxx#2 edit
+... //depot//hunspell_1_2_12/src/hunspell/replist.hxx#2 edit
+
+==== //depot//src/hunspell/hunspell.cxx#1 - /google/src/files/18870443/depot//hunspell_1_2_12/src/hunspell/hunspell.cxx ====
+--- /google/src/files/17885847/depot//src/hunspell/hunspell.cxx 2010-10-27 21:25:42.000000000 -0400
++++ /google/src/files/18870443/depot//src/hunspell/hunspell.cxx 2011-01-07 02:29:34.000000000 -0500
+@@ -346,8 +346,8 @@
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+- if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+- else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
++ if (rl && rl->conv(word, wspace, sizeof(wspace))) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
++ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ int info2 = 0;
+ if (wl == 0 || maxdic == 0) return 1;
+@@ -685,8 +687,8 @@
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+- if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+- else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
++ if (rl && rl->conv(word, wspace, sizeof(wspace))) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
++ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ if (wl == 0) return 0;
+ int ns = 0;
+@@ -971,7 +975,7 @@
+ // output conversion
+ rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL;
+ for (int j = 0; rl && j < ns; j++) {
+- if (rl->conv((*slst)[j], wspace)) {
++ if (rl->conv((*slst)[j], wspace, sizeof(wspace))) {
+ free((*slst)[j]);
+ (*slst)[j] = mystrdup(wspace);
+ }
+@@ -1346,8 +1350,8 @@
+
+ // input conversion
+ RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+- if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+- else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
++ if (rl && rl->conv(word, wspace, sizeof(wspace))) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
++ else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
+ if (wl == 0) {
+ if (abbv) {
+==== //depot//src/hunspell/replist.cxx#1 - /google/src/files/18870443/depot//hunspell_1_2_12/src/hunspell/replist.cxx ====
+--- /google/src/files/17885847/depot//src/hunspell/replist.cxx 2010-10-27 21:25:42.000000000 -0400
++++ /google/src/files/18870443/depot//src/hunspell/replist.cxx 2011-01-07 02:29:34.000000000 -0500
+@@ -69,7 +69,10 @@
+ return 0;
+ }
+
+-int RepList::conv(const char * word, char * dest) {
++int RepList::conv(const char * word, char * dest, unsigned int dest_size) {
++ // NOTE: Changed to check the size of dest buffer.
++ // Korean dictionary converts one utf-8 character to 3 utf-8 characters, and
++ // it can lead to buffer overrun errors.
+ int stl = 0;
+ int change = 0;
+ for (size_t i = 0; i < strlen(word); i++) {
+@@ -77,11 +80,20 @@
+ int n = near(word + i);
+ int l = match(word + i, n);
+ if (l) {
++ int pattern_len = strlen(dat[n]->pattern2);
++ if ((stl + pattern_len) >= dest_size) {
++ break;
++ }
+ strcpy(dest + stl, dat[n]->pattern2);
+- stl += strlen(dat[n]->pattern2);
++ stl += pattern_len;
+ i += l - 1;
+ change = 1;
+- } else dest[stl++] = word[i];
++ } else {
++ if ((stl + 1) >= dest_size) {
++ break;
++ }
++ dest[stl++] = word[i];
++ }
+ }
+ dest[stl] = '\0';
+ return change;
+==== //depot//src/hunspell/replist.hxx#1 - /google/src/files/18870443/depot//hunspell_1_2_12/src/hunspell/replist.hxx ====
+--- /google/src/files/17885847/depot//src/hunspell/replist.hxx 2010-10-27 21:25:42.000000000 -0400
++++ /google/src/files/18870443/depot//src/hunspell/replist.hxx 2011-01-07 02:29:34.000000000 -0500
+@@ -22,6 +22,6 @@
+ replentry * item(int n);
+ int near(const char * word);
+ int match(const char * word, int n);
+- int conv(const char * word, char * dest);
++ int conv(const char * word, char * dest, unsigned int dest_size);
+ };
+ #endif
diff --git a/patches/03.initial_checkin_edits.patch b/patches/03.initial_checkin_edits.patch
new file mode 100644
index 0000000..d6e5cce
--- /dev/null
+++ b/patches/03.initial_checkin_edits.patch
@@ -0,0 +1,321 @@
+
+
+==== //depot//src/hunspell/license.hunspell
+--- /google/src/files/a/depot//src/hunspell/license.hunspell 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/license.hunspell 2013-10-01 01:04:03.000000000 -0400
+@@ -56,4 +56,6 @@
+ *
+ * ***** END LICENSE BLOCK ***** */
+
++/* This isn't needed to be compiled.
+ #include "config.h"
++*/
+==== //depot//src/hunspell/hunspell.cxx
+--- /google/src/files/a/depot//src/hunspell/hunspell.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/hunspell.cxx 2013-10-01 01:04:03.000000000 -0400
+@@ -8,9 +8,11 @@
+
+ #include "hunspell.hxx"
+ #include "hunspell.h"
++/* This is not required
+ #ifndef MOZILLA_CLIENT
+ # include "config.h"
+ #endif
++*/
+ #include "csutil.hxx"
+
+ Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
+==== //depot//src/hunspell/csutil.cxx
+--- /google/src/files/a/depot//src/hunspell/csutil.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/csutil.cxx 2013-10-01 01:04:03.000000000 -0400
+@@ -10,13 +10,6 @@
+ #include "atypes.hxx"
+ #include "langnum.hxx"
+
+-// Unicode character encoding information
+-struct unicode_info {
+- unsigned short c;
+- unsigned short cupper;
+- unsigned short clower;
+-};
+-
+ #ifdef OPENOFFICEORG
+ # include <unicode/uchar.h>
+ #else
+==== //depot//src/hunspell/csutil.hxx
+--- /google/src/files/a/depot//src/hunspell/csutil.hxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/csutil.hxx 2013-10-01 01:04:03.000000000 -0400
+@@ -116,6 +116,13 @@
+ unsigned char cupper;
+ };
+
++// Unicode character encoding information
++struct unicode_info {
++ unsigned short c;
++ unsigned short cupper;
++ unsigned short clower;
++};
++
+ LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl();
+ LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl();
+ LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c, int langnum);
+==== //depot//src/hunspell/suggestmgr.cxx
+--- /google/src/files/a/depot//src/hunspell/suggestmgr.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/suggestmgr.cxx 2013-10-01 01:04:03.000000000 -0400
+@@ -155,10 +155,13 @@
+
+ if (utf8) {
+ wl = u8_u16(word_utf, MAXSWL, word);
+- if (wl == -1) {
+- *slst = wlst;
+- return nsug;
+- }
++ // Added to handle wrong encoded UTF-8 strings - jiho@google.com
++ if (wl <= 0) {
++ if (!*slst) {
++ free(wlst);
++ }
++ return -1;
++ }
+ }
+
+ for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
+--- /google/src/files/a/depot//src/hunspell/affixmgr.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/b/depot//src/hunspell/affixmgr.cxx 2013-10-01 01:04:03.000000000 -0400
+@@ -8,6 +8,7 @@
+
+ #include <vector>
+
++#include "base/scoped_ptr.h"
+ #include "affixmgr.hxx"
+ #include "affentry.hxx"
+ #include "langnum.hxx"
+@@ -1364,9 +1365,14 @@
+ // check compound patterns
+ int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
+ {
+- signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
+- signed short btwp[MAXWORDLEN]; // word positions for metacharacters
+- int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
++ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // problem.
++ scoped_array<signed short> scoped_btpp(new signed short[MAXWORDLEN]);
++ signed short * btpp = scoped_btpp.get(); // metacharacter (*, ?) positions for backtracking
++ scoped_array<signed short> scoped_btwp(new signed short[MAXWORDLEN]);
++ signed short * btwp = scoped_btwp.get(); // word positions for metacharacters
++ scoped_array<int> scoped_btnum(new int[MAXWORDLEN]);
++ int * btnum = scoped_btnum.get(); // number of matched characters in metacharacter positions
+ short bt = 0;
+ int i, j;
+ int ok;
+@@ -1531,8 +1537,18 @@
+ short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
+ struct hentry * rv = NULL;
+ struct hentry * rv_first;
+- struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
+- char st [MAXWORDUTF8LEN + 4];
++
++ // NOTE: This can not be a scoped_array because
++ // "new hentry*[MAXWORDLEN]" is compiled to call malloc() which is not
++ // compatible with scoped_ptr.
++ // scoped_array<struct hentry*> rwords(new hentry*[MAXWORDLEN]);
++ struct hentry ** rwords = // buffer for COMPOUND pattern checking
++ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
++
++ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // problem.
++ scoped_array<char> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ char * st = scoped_st.get();
+ char ch = '\0';
+ int cmin;
+ int cmax;
+@@ -1557,7 +1573,10 @@
+ // go to end of the UTF-8 character
+ if (utf8) {
+ for (; (st[i] & 0xc0) == 0x80; i++);
+- if (i >= cmax) return NULL;
++ if (i >= cmax) {
++ free(rwords);
++ return NULL;
++ }
+ }
+
+ words = oldwords;
+@@ -1612,8 +1631,8 @@
+ (compoundmiddle && wordnum && !words && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
+ (numdefcpd && onlycpdrule &&
+- ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
+- (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) ||
++ ((!words && !wordnum && defcpd_check(&words, wnum, rv, rwords, 0)) ||
++ (words && defcpd_check(&words, wnum, rv, rwords, 0))))) ||
+ (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
+ !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
+ ) {
+@@ -1693,6 +1712,7 @@
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
++ free(rwords);
+ return NULL;
+ }
+
+@@ -1784,7 +1804,10 @@
+ if (rv && forceucase && (rv) &&
+ (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
+
+- if (rv && words && words[wnum + 1]) return rv_first;
++ if (rv && words && words[wnum + 1]) {
++ free(rwords);
++ return rv_first;
++ }
+
+ oldnumsyllable2 = numsyllable;
+ oldwordnum2 = wordnum;
+@@ -1805,7 +1828,10 @@
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+- (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
++ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
++ free(rwords);
++ return NULL;
++ }
+
+ // second word is acceptable, as a root?
+ // hungarian conventions: compounding is acceptable,
+@@ -1833,6 +1859,7 @@
+ TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
+ )
+ {
++ free(rwords);
+ // forbid compound word, if it is a non compound word with typical fault
+ if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
+ return rv_first;
+@@ -1853,7 +1880,10 @@
+
+ if (!rv && numdefcpd && words) {
+ rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
+- if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
++ if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
++ free(rwords);
++ return rv_first;
++ }
+ rv = NULL;
+ }
+
+@@ -1882,7 +1912,10 @@
+ // check forbiddenwords
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
+ TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
+- (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
++ (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
++ free(rwords);
++ return NULL;
++ }
+
+ // pfxappnd = prefix of word+i, or NULL
+ // calculate syllable number of prefix.
+@@ -1936,7 +1969,11 @@
+ (!checkcompounddup || (rv != rv_first))
+ )) {
+ // forbid compound word, if it is a non compound word with typical fault
+- if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
++ if (checkcompoundrep && cpdrep_check(word, len)) {
++ free(rwords);
++ return NULL;
++ }
++ free(rwords);
+ return rv_first;
+ }
+
+@@ -1958,7 +1995,10 @@
+ if (checkcompoundrep || forbiddenword) {
+ struct hentry * rv2 = NULL;
+
+- if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
++ if (checkcompoundrep && cpdrep_check(word, len)) {
++ free(rwords);
++ return NULL;
++ }
+
+ // check first part
+ if (strncmp(rv->word, word + i, rv->blen) == 0) {
+@@ -1975,12 +2015,14 @@
+ if (!rv2) rv2 = affix_check(word, len);
+ if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
+ (strncmp(rv2->word, st, i + rv->blen) == 0)) {
++ free(rwords);
+ return NULL;
+ }
+ }
+ *(st + i + rv->blen) = r;
+ }
+ }
++ free(rwords);
+ return rv_first;
+ }
+ } while (striple && !checkedstriple); // end of striple loop
+@@ -2019,6 +2061,7 @@
+
+ }
+
++ free(rwords);
+ return NULL;
+ }
+
+@@ -2034,8 +2077,13 @@
+
+ struct hentry * rv = NULL;
+ struct hentry * rv_first;
+- struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
+- char st [MAXWORDUTF8LEN + 4];
++ // Note(jiho@google.com): Changed to use malloc to avoid stack overflow.
++ struct hentry ** rwords = // buffer for COMPOUND pattern checking
++ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
++ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // problem.
++ scoped_array<char> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ char * st = scoped_st.get();
+ char ch;
+
+ int checked_prefix;
+@@ -2060,7 +2108,10 @@
+ // go to end of the UTF-8 character
+ if (utf8) {
+ for (; (st[i] & 0xc0) == 0x80; i++);
+- if (i >= cmax) return 0;
++ if (i >= cmax) {
++ free(rwords);
++ return NULL;
++ }
+ }
+
+ words = oldwords;
+@@ -2094,8 +2145,8 @@
+ (compoundmiddle && wordnum && !words && !onlycpdrule &&
+ TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
+ (numdefcpd && onlycpdrule &&
+- ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
+- (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
++ ((!words && !wordnum && defcpd_check(&words, wnum, rv, rwords, 0)) ||
++ (words && defcpd_check(&words, wnum, rv, rwords, 0))))
+ ))) {
+ rv = rv->next_homonym;
+ }
+@@ -2289,6 +2340,7 @@
+ }
+ mystrcat(*result, "\n", MAXLNLEN);
+ ok = 1;
++ free(rwords);
+ return 0;
+ }
+
+@@ -2488,6 +2540,7 @@
+ } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
+
+ }
++ free(rwords);
+ return 0;
+ }
+
diff --git a/patches/04.smart_ptr_cleanup_1.patch b/patches/04.smart_ptr_cleanup_1.patch
new file mode 100644
index 0000000..145b9be
--- /dev/null
+++ b/patches/04.smart_ptr_cleanup_1.patch
@@ -0,0 +1,67 @@
+Change 53528230 by hwright@hwright:rosie52106145-0178_Rosie:21884:citc on 2013/09/30 22:04:03
+
+ Convert uses of scoped_array<T> to scoped_ptr<T[]>. This is in preparation for removing scoped_ptr_array, and converting all uses of scoped_ptr to std::unique_ptr.
+ In rare cases, this change may convert directly to std::unique_ptr.
+
+ NO ACTION REQUIRES: titus@ will globally approve these CLs.
+
+ This is part of the scoped_ptr to std::unique_ptr transition, contact
+ unique_ptr_migration@ for more information.
+
+ LSC doc: https://docs.google.com/a/google.com/document/d/1uuy4GpDtJ6nKutse-DcNjYh3tIjN6RsLeyien1gbHA8/edit#
+
+ Tested:
+ TAP presubmits for global presubmit queue
+ http://test/OCL:53498688:BASE:53503319:1380572748538:ac61f116
+ All failing tests were failing before this change
+
+ PRESUBMIT=passed
+ R=jiho
+ CC=rosie-reviews
+ APPROVED=jiho
+ DELTA=5 (0 added, 0 deleted, 5 changed)
+ OCL=53498688
+ CLEANUP=manage_cl
+ APPROVALS_REQUIRED=True
+ MASTER_CL=52106145
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#3 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#2 - /google/src/files/53528230/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/49864191/depot//src/hunspell/affixmgr.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/53528230/depot//src/hunspell/affixmgr.cxx 2013-10-01 01:04:03.000000000 -0400
+@@ -1367,11 +1367,11 @@
+ {
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_array<signed short> scoped_btpp(new signed short[MAXWORDLEN]);
++ scoped_ptr<signed short[]> scoped_btpp(new signed short[MAXWORDLEN]);
+ signed short * btpp = scoped_btpp.get(); // metacharacter (*, ?) positions for backtracking
+- scoped_array<signed short> scoped_btwp(new signed short[MAXWORDLEN]);
++ scoped_ptr<signed short[]> scoped_btwp(new signed short[MAXWORDLEN]);
+ signed short * btwp = scoped_btwp.get(); // word positions for metacharacters
+- scoped_array<int> scoped_btnum(new int[MAXWORDLEN]);
++ scoped_ptr<int[]> scoped_btnum(new int[MAXWORDLEN]);
+ int * btnum = scoped_btnum.get(); // number of matched characters in metacharacter positions
+ short bt = 0;
+ int i, j;
+@@ -1547,7 +1547,7 @@
+
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_array<char> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ scoped_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+ char ch = '\0';
+ int cmin;
+@@ -2082,7 +2082,7 @@
+ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_array<char> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ scoped_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+ char ch;
+
diff --git a/patches/05.smart_ptr_cleanup_2.patch b/patches/05.smart_ptr_cleanup_2.patch
new file mode 100644
index 0000000..ebf56f7
--- /dev/null
+++ b/patches/05.smart_ptr_cleanup_2.patch
@@ -0,0 +1,73 @@
+Change 58004236 by hwright@hwright:rosie56980077-0446_Rosie:48864:citc on 2013/12/10 18:21:03
+
+ Cleanup: Convert from scoped_ptr<T> to std::unique_ptr<T>.
+
+ This is part of the unique_ptr migration; for more information see
+ go/unique_ptr-endgame. Contact unique-ptr-migration@ with questions or
+ concerns.
+
+ NO ACTION REQUIRED: Where possible, this change will be globally approved
+ by titus@.
+
+ Tested:
+ TAP presubmits for global presubmit queue
+ http://test/OCL:57894443:BASE:57895567:1386602360560:2599c420
+
+ PRESUBMIT=passed
+ R=titus,jiho
+ CC=rosie-reviews
+ APPROVED=jiho,titus-google3-approvals(titus)
+ DELTA=6 (1 added, 0 deleted, 5 changed)
+ OCL=57894443
+ CLEANUP=manage_cl
+ APPROVALS_REQUIRED=True
+ MASTER_CL=56980077
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#4 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#3 - /google/src/files/58004236/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/53528230/depot//src/hunspell/affixmgr.cxx 2013-10-01 01:04:03.000000000 -0400
++++ /google/src/files/58004236/depot//src/hunspell/affixmgr.cxx 2013-12-10 21:21:03.000000000 -0500
+@@ -6,6 +6,7 @@
+ #include <stdio.h>
+ #include <ctype.h>
+
++#include <memory>
+ #include <vector>
+
+ #include "base/scoped_ptr.h"
+@@ -1367,11 +1368,11 @@
+ {
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_ptr<signed short[]> scoped_btpp(new signed short[MAXWORDLEN]);
++ std::unique_ptr<signed short[]> scoped_btpp(new signed short[MAXWORDLEN]);
+ signed short * btpp = scoped_btpp.get(); // metacharacter (*, ?) positions for backtracking
+- scoped_ptr<signed short[]> scoped_btwp(new signed short[MAXWORDLEN]);
++ std::unique_ptr<signed short[]> scoped_btwp(new signed short[MAXWORDLEN]);
+ signed short * btwp = scoped_btwp.get(); // word positions for metacharacters
+- scoped_ptr<int[]> scoped_btnum(new int[MAXWORDLEN]);
++ std::unique_ptr<int[]> scoped_btnum(new int[MAXWORDLEN]);
+ int * btnum = scoped_btnum.get(); // number of matched characters in metacharacter positions
+ short bt = 0;
+ int i, j;
+@@ -1547,7 +1548,7 @@
+
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ std::unique_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+ char ch = '\0';
+ int cmin;
+@@ -2082,7 +2083,7 @@
+ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
+ // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+- scoped_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
++ std::unique_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+ char ch;
+
diff --git a/patches/06.smart_ptr_cleanup_3.patch b/patches/06.smart_ptr_cleanup_3.patch
new file mode 100644
index 0000000..6a0d1e7
--- /dev/null
+++ b/patches/06.smart_ptr_cleanup_3.patch
@@ -0,0 +1,39 @@
+Change 60515892 by hwright@hwright:rosie59388532-0099_Rosie:55887:citc on 2014/01/28 20:43:31
+
+ Cleanup: Remove inclusion of base/scoped_ptr.h in files which don't use
+ scoped_ptr. Testing ensures that these inclusions don't have transitive
+ dependencies.
+
+ This is part of the unique_ptr migration; for more information see
+ go/unique_ptr-endgame. Contact unique-ptr-migration@ with questions or
+ concerns.
+
+ Tested:
+ TAP presubmits for global presubmit queue
+ http://test/OCL:60505594:BASE:60509048:1390961871934:f8075081
+
+ PRESUBMIT=passed
+ R=jiho
+ CC=rosie-reviews
+ APPROVED=jiho
+ DELTA=1 (0 added, 1 deleted, 0 changed)
+ OCL=60505594
+ CLEANUP=manage_cl
+ APPROVALS_REQUIRED=True
+ MASTER_CL=59388532
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#5 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#4 - /google/src/files/60515892/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/58004236/depot//src/hunspell/affixmgr.cxx 2013-12-10 21:21:03.000000000 -0500
++++ /google/src/files/60515892/depot//src/hunspell/affixmgr.cxx 2014-01-28 23:43:31.000000000 -0500
+@@ -9,7 +9,6 @@
+ #include <memory>
+ #include <vector>
+
+-#include "base/scoped_ptr.h"
+ #include "affixmgr.hxx"
+ #include "affentry.hxx"
+ #include "langnum.hxx"
diff --git a/patches/07.long_input.patch b/patches/07.long_input.patch
new file mode 100644
index 0000000..3e4469a
--- /dev/null
+++ b/patches/07.long_input.patch
@@ -0,0 +1,119 @@
+Change 50722709 by junyangl@junyangl:latency:20:citc on 2013/08/12 11:06:20
+
+ Wrap the parser for Hunspell check.
+ Use a parameter/dynamic flag to control which parser to use.
+ Fix bug in hunspell TextParser to accept very long input.
+
+ PRESUBMIT=passed
+ R=lianglin,shine,jiho
+ CC=caribou-backend-reviews,caribou-prod-reviews
+ APPROVED=jiho,shine
+ DELTA=527 (420 added, 51 deleted, 56 changed)
+ OCL=50412080
+
+Affected files ...
+
+... //depot/google3/caribou/medley/lib/internal/medley_server.cc#239 edit
+... //depot/google3/caribou/spell/BUILD#46 edit
+... //depot/google3/caribou/spell/server_main.cc#7 edit
+... //depot/google3/caribou/spell/spell_server.cc#15 edit
+... //depot/google3/caribou/spell/spell_server.h#5 edit
+... //depot/google3/caribou/spell/spellcheck.cc#61 edit
+... //depot/google3/caribou/spell/spellcheck.h#24 edit
+... //depot/google3/caribou/spell/spellcheck_benchmark.cc#2 edit
+... //depot/google3/caribou/spell/spellcheck_parser.cc#1 add
+... //depot/google3/caribou/spell/spellcheck_parser.h#1 add
+... //depot/google3/caribou/spell/spellcheck_parser_test.cc#1 add
+... //depot/google3/caribou/spell/spellcheck_unittest.cc#56 edit
+... //depot/google3/caribou/spell/stats.cc#9 edit
+... //depot/google3/caribou/spell/stats.h#8 edit
+... //depot//README.google#4 edit
+... //depot//src/parsers/htmlparser.cxx#3 edit
+... //depot//src/parsers/htmlparser.hxx#2 edit
+... //depot//src/parsers/textparser.cxx#2 edit
+... //depot//src/parsers/textparser.hxx#2 edit
+
+==== //depot//src/parsers/htmlparser.cxx#2 - /google/src/files/50722709/depot//src/parsers/htmlparser.cxx ====
+--- /google/src/files/49864191/depot//src/parsers/htmlparser.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/50722709/depot//src/parsers/htmlparser.cxx 2013-08-12 14:06:20.000000000 -0400
+@@ -43,10 +43,8 @@
+ init(wordchars);
+ }
+
+-HTMLParser::HTMLParser(unsigned short * wordchars, int len)
+-{
+- init(wordchars, len);
+-}
++HTMLParser::HTMLParser(unsigned short *wordchars, int len, int text_len)
++ : TextParser(wordchars, len, text_len) {}
+
+ HTMLParser::~HTMLParser()
+ {
+==== //depot//src/parsers/htmlparser.hxx#1 - /google/src/files/50722709/depot//src/parsers/htmlparser.hxx ====
+--- /google/src/files/37375269/depot//src/parsers/htmlparser.hxx 2012-10-31 14:23:51.000000000 -0400
++++ /google/src/files/50722709/depot//src/parsers/htmlparser.hxx 2013-08-12 14:06:20.000000000 -0400
+@@ -23,7 +23,7 @@
+ public:
+
+ HTMLParser(const char * wc);
+- HTMLParser(unsigned short * wordchars, int len);
++ HTMLParser(unsigned short * wordchars, int len, int text_len);
+ virtual ~HTMLParser();
+
+ virtual char * next_token();
+==== //depot//src/parsers/textparser.cxx#1 - /google/src/files/50722709/depot//src/parsers/textparser.cxx ====
+--- /google/src/files/37375269/depot//src/parsers/textparser.cxx 2012-10-31 14:23:51.000000000 -0400
++++ /google/src/files/50722709/depot//src/parsers/textparser.cxx 2013-08-12 14:06:20.000000000 -0400
+@@ -55,13 +55,25 @@
+ init(wordchars);
+ }
+
+-TextParser::TextParser(unsigned short * wordchars, int len)
++TextParser::TextParser(unsigned short * wordchars, int len, int text_len)
+ {
+- init(wordchars, len);
++ // Note(junyangl@google.com): Dynamically allocate space according to text_len
++ // to avoid segfault for too long input.
++ for (int i = 0; i < MAXPREVLINE; i++) {
++ line[i] = (char*)malloc(text_len + 1);
++ }
++ urlline = (char*)malloc(text_len + 1);
++
++ init(wordchars, len);
+ }
+
+ TextParser::~TextParser()
+ {
++ // Note(junyangl@google.com): Free dynamically allocated space.
++ for (int i = 0; i < MAXPREVLINE; i++) {
++ free(line[i]);
++ }
++ free(urlline);
+ }
+
+ int TextParser::is_wordchar(char * w)
+==== //depot//src/parsers/textparser.hxx#1 - /google/src/files/50722709/depot//src/parsers/textparser.hxx ====
+--- /google/src/files/37375269/depot//src/parsers/textparser.hxx 2012-10-31 14:23:51.000000000 -0400
++++ /google/src/files/50722709/depot//src/parsers/textparser.hxx 2013-08-12 14:06:20.000000000 -0400
+@@ -29,8 +29,10 @@
+ void init(const char *);
+ void init(unsigned short * wordchars, int len);
+ int wordcharacters[256]; // for detection of the word boundaries
+- char line[MAXPREVLINE][MAXLNLEN]; // parsed and previous lines
+- char urlline[MAXLNLEN]; // mask for url detection
++ // Note(junyangl@google.com): Dynamically allocate space for line and urlline
++ // according to input length, to avoid segfault when the input is too long.
++ char * line[MAXPREVLINE]; // parsed and previous lines
++ char * urlline; // mask for url detection
+ int checkurl;
+ int actual; // actual line
+ int head; // head position
+@@ -44,7 +46,7 @@
+ public:
+
+ TextParser();
+- TextParser(unsigned short * wordchars, int len);
++ TextParser(unsigned short * wordchars, int len, int text_len);
+ TextParser(const char * wc);
+ virtual ~TextParser();
+
diff --git a/patches/08.stack_overflow.patch b/patches/08.stack_overflow.patch
new file mode 100644
index 0000000..f9ea0c3
--- /dev/null
+++ b/patches/08.stack_overflow.patch
@@ -0,0 +1,465 @@
+Change 140493454 by llwang@llwang:vanadiel-llwang-git-repository-hunspell-git5:1985:citc on 2016/11/29 10:12:47
+
+ Fix stack overflow problem in Hunspell.
+ * Replace the copt "-w" with "-Xclang-only=-Wno-empty-body", "-Xclang-only=-Wno-implicit-fallthrough", "-Xclang-only=-Wno-null-conversion", "-Xclang-only=-Wno-unused-variable" so that other more serious problems surface, including the problem that the stack frame exceeds the limit.
+ * Change the arrays of size BUFSIZE in hashmgr.cxx to size MAXWORDLEN or MAXWORDUTF8LEN as appropriate.
+ * Replace big stack-allocated arrays elsewhere with vectors.
+ * Fix the bug of using strncmp with the wrong length in AffixMgr::parse_convtable, also surfaced by removing -w.
+ * All tests that failed in cl/139982398 passed: http://sponge/8bd6fe9b-6d37-4f4d-8388-079ed82112a4
+ * Tested with TAP Global presubmit: http://test/OCL:140408149:BASE:140406276:1480375376511:67c3adaa
+
+ PRESUBMIT=passed
+ BUG=33099062
+ FIXED=33099062
+ R=jiho
+ CC=akomninos,handwriting-eng
+ APPROVED=jiho
+ REQUIRED_REVIEW=1
+ DELTA=101 (16 added, 0 deleted, 85 changed)
+ DELTA_BY_EXTENSION=cxx=89
+ OCL=140408149
+
+Affected files ...
+
+... //depot//BUILD#14 edit
+... //depot//METADATA#4 edit
+... //depot//src/hunspell/affixmgr.cxx#6 edit
+... //depot//src/hunspell/hashmgr.cxx#5 edit
+... //depot//src/hunspell/hunspell.cxx#3 edit
+... //depot//src/hunspell/hunzip.cxx#4 edit
+... //depot//src/hunspell/suggestmgr.cxx#3 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#5 - /google/src/files/140493454/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/60515892/depot//src/hunspell/affixmgr.cxx 2014-01-28 23:43:31.000000000 -0500
++++ /google/src/files/140493454/depot//src/hunspell/affixmgr.cxx 2016-11-29 13:12:47.000000000 -0500
+@@ -262,7 +262,7 @@
+ char ft; // affix type
+
+ // checking flag duplication
+- char dupflags[CONTSIZE];
++ std::vector<char> dupflags(CONTSIZE);
+ char dupflags_ini = 1;
+
+ // first line indicator for removing byte order mark
+@@ -693,10 +693,10 @@
+ if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
+ if (ft != ' ') {
+ if (dupflags_ini) {
+- memset(dupflags, 0, sizeof(dupflags));
++ dupflags.assign(dupflags.size(), '\0');
+ dupflags_ini = 0;
+ }
+- if (parse_affix(line, ft, afflst, dupflags)) {
++ if (parse_affix(line, ft, afflst, dupflags.data())) {
+ delete afflst;
+ process_pfx_tree_to_list();
+ process_sfx_tree_to_list();
+@@ -2710,9 +2710,9 @@
+ char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len,
+ int sfxopts, PfxEntry * ppfx, const FLAG needflag)
+ {
+- char result[MAXLNLEN];
+- char result2[MAXLNLEN];
+- char result3[MAXLNLEN];
++ std::vector<char> result(MAXLNLEN);
++ std::vector<char> result2(MAXLNLEN);
++ std::vector<char> result3(MAXLNLEN);
+
+ char * st;
+
+@@ -2729,17 +2729,17 @@
+ if (st) {
+ if (ppfx) {
+ if (ppfx->getMorph()) {
+- mystrcat(result, ppfx->getMorph(), MAXLNLEN);
+- mystrcat(result, " ", MAXLNLEN);
+- } else debugflag(result, ppfx->getFlag());
++ mystrcat(result.data(), ppfx->getMorph(), MAXLNLEN);
++ mystrcat(result.data(), " ", MAXLNLEN);
++ } else debugflag(result.data(), ppfx->getFlag());
+ }
+- mystrcat(result, st, MAXLNLEN);
++ mystrcat(result.data(), st, MAXLNLEN);
+ free(st);
+ if (se->getMorph()) {
+- mystrcat(result, " ", MAXLNLEN);
+- mystrcat(result, se->getMorph(), MAXLNLEN);
+- } else debugflag(result, se->getFlag());
+- mystrcat(result, "\n", MAXLNLEN);
++ mystrcat(result.data(), " ", MAXLNLEN);
++ mystrcat(result.data(), se->getMorph(), MAXLNLEN);
++ } else debugflag(result.data(), se->getFlag());
++ mystrcat(result.data(), "\n", MAXLNLEN);
+ }
+ }
+ se = se->getNext();
+@@ -2758,18 +2758,18 @@
+ if (st) {
+ sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
+ if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
+- strcpy(result2, st);
++ strcpy(result2.data(), st);
+ free(st);
+
+ result3[0] = '\0';
+
+ if (sptr->getMorph()) {
+- mystrcat(result3, " ", MAXLNLEN);
+- mystrcat(result3, sptr->getMorph(), MAXLNLEN);
+- } else debugflag(result3, sptr->getFlag());
+- strlinecat(result2, result3);
+- mystrcat(result2, "\n", MAXLNLEN);
+- mystrcat(result, result2, MAXLNLEN);
++ mystrcat(result3.data(), " ", MAXLNLEN);
++ mystrcat(result3.data(), sptr->getMorph(), MAXLNLEN);
++ } else debugflag(result3.data(), sptr->getFlag());
++ strlinecat(result2.data(), result3.data());
++ mystrcat(result2.data(), "\n", MAXLNLEN);
++ mystrcat(result.data(), result2.data(), MAXLNLEN);
+ }
+ }
+ sptr = sptr->getNextEQ();
+@@ -2777,7 +2777,7 @@
+ sptr = sptr->getNextNE();
+ }
+ }
+- if (*result) return mystrdup(result);
++ if (*result.data()) return mystrdup(result.data());
+ return NULL;
+ }
+
+@@ -3704,7 +3704,7 @@
+ if (*piece != '\0') {
+ switch(i) {
+ case 0: {
+- if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
++ if (strncmp(piece, keyword, strlen(keyword)) != 0) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ delete *rl;
+ *rl = NULL;
+==== //depot//src/hunspell/hashmgr.cxx#4 - /google/src/files/140493454/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/140001041/depot//src/hunspell/hashmgr.cxx 2016-11-23 03:35:19.000000000 -0500
++++ /google/src/files/140493454/depot//src/hunspell/hashmgr.cxx 2016-11-29 13:12:47.000000000 -0500
+@@ -223,12 +223,12 @@
+ if (al) memcpy(flags2, flags, al * sizeof(unsigned short));
+ flags2[al] = ONLYUPCASEFLAG;
+ if (utf8) {
+- char st[BUFSIZE];
+- w_char w[BUFSIZE];
+- int wlen = u8_u16(w, BUFSIZE, word);
++ char st[MAXWORDUTF8LEN];
++ w_char w[MAXWORDLEN];
++ int wlen = u8_u16(w, MAXWORDLEN, word);
+ mkallsmall_utf(w, wlen, langnum);
+ mkallcap_utf(w, 1, langnum);
+- u16_u8(st, BUFSIZE, w, wlen);
++ u16_u8(st, MAXWORDUTF8LEN, w, wlen);
+ return add_word(st,wbl,wcl,flags2,al+1,dp, true);
+ } else {
+ mkallsmall(word, csconv);
+@@ -243,8 +243,8 @@
+ int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) {
+ int len;
+ if (utf8) {
+- w_char dest_utf[BUFSIZE];
+- len = u8_u16(dest_utf, BUFSIZE, word);
++ w_char dest_utf[MAXWORDLEN];
++ len = u8_u16(dest_utf, MAXWORDLEN, word);
+ *captype = get_captype_utf8(dest_utf, len, langnum);
+ } else {
+ len = wbl;
+@@ -538,8 +538,8 @@
+ break;
+ }
+ case FLAG_UNI: { // UTF-8 characters
+- w_char w[BUFSIZE/2];
+- len = u8_u16(w, BUFSIZE/2, flags);
++ w_char w[MAXWORDLEN];
++ len = u8_u16(w, MAXWORDLEN, flags);
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ memcpy(*result, w, len * sizeof(short));
+==== //depot//src/hunspell/hunspell.cxx#2 - /google/src/files/140493454/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/49864191/depot//src/hunspell/hunspell.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/140493454/depot//src/hunspell/hunspell.cxx 2016-11-29 13:12:47.000000000 -0500
+@@ -4,6 +4,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>
++#include <vector>
+
+ #include "hunspell.hxx"
+ #include "hunspell.h"
+@@ -1133,20 +1134,20 @@
+
+ int Hunspell::stem(char*** slst, char ** desc, int n)
+ {
+- char result[MAXLNLEN];
+- char result2[MAXLNLEN];
++ std::vector<char> result(MAXLNLEN);
++ std::vector<char> result2(MAXLNLEN);
+ *slst = NULL;
+ if (n == 0) return 0;
+- *result2 = '\0';
++ result2[0] = '\0';
+ for (int i = 0; i < n; i++) {
+- *result = '\0';
++ result[0] = '\0';
+ // add compound word parts (except the last one)
+ char * s = (char *) desc[i];
+ char * part = strstr(s, MORPH_PART);
+ if (part) {
+ char * nextpart = strstr(part + 1, MORPH_PART);
+ while (nextpart) {
+- copy_field(result + strlen(result), part, MORPH_PART);
++ copy_field(result.data() + strlen(result.data()), part, MORPH_PART);
+ part = nextpart;
+ nextpart = strstr(part + 1, MORPH_PART);
+ }
+@@ -1154,14 +1155,14 @@
+ }
+
+ char **pl;
+- char tok[MAXLNLEN];
+- strcpy(tok, s);
+- char * alt = strstr(tok, " | ");
++ std::vector<char> tok(MAXLNLEN);
++ strcpy(tok.data(), s);
++ char * alt = strstr(tok.data(), " | ");
+ while (alt) {
+ alt[1] = MSEP_ALT;
+ alt = strstr(alt, " | ");
+ }
+- int pln = line_tok(tok, &pl, MSEP_ALT);
++ int pln = line_tok(tok.data(), &pl, MSEP_ALT);
+ for (int k = 0; k < pln; k++) {
+ // add derivational suffixes
+ if (strstr(pl[k], MORPH_DERI_SFX)) {
+@@ -1174,22 +1175,24 @@
+ int genl = line_tok(sg, &gen, MSEP_REC);
+ free(sg);
+ for (int j = 0; j < genl; j++) {
+- sprintf(result2 + strlen(result2), "%c%s%s",
+- MSEP_REC, result, gen[j]);
++ sprintf(result2.data() + strlen(result2.data()), "%c%s%s",
++ MSEP_REC, result.data(), gen[j]);
+ }
+ freelist(&gen, genl);
+ }
+ } else {
+- sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result);
++ sprintf(result2.data() + strlen(result2.data()), "%c%s", MSEP_REC,
++ result.data());
+ if (strstr(pl[k], MORPH_SURF_PFX)) {
+- copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX);
++ copy_field(result2.data() + strlen(result2.data()), pl[k],
++ MORPH_SURF_PFX);
+ }
+- copy_field(result2 + strlen(result2), pl[k], MORPH_STEM);
++ copy_field(result2.data() + strlen(result2.data()), pl[k], MORPH_STEM);
+ }
+ }
+ freelist(&pl, pln);
+ }
+- int sln = line_tok(result2, slst, MSEP_REC);
++ int sln = line_tok(result2.data(), slst, MSEP_REC);
+ return uniqlist(*slst, sln);
+
+ }
+==== //depot//src/hunspell/hunzip.cxx#3 - /google/src/files/140493454/depot//src/hunspell/hunzip.cxx ====
+--- /google/src/files/140001041/depot//src/hunspell/hunzip.cxx 2016-11-23 03:35:19.000000000 -0500
++++ /google/src/files/140493454/depot//src/hunspell/hunzip.cxx 2016-11-29 13:12:47.000000000 -0500
+@@ -1,6 +1,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>
++#include <vector>
+
+ #include "hunzip.hxx"
+
+@@ -152,7 +153,7 @@
+ }
+
+ const char * Hunzip::getline() {
+- char linebuf[BUFSIZE];
++ std::vector<char> linebuf(BUFSIZE);
+ int l = 0, eol = 0, left = 0, right = 0;
+ if (bufsiz == -1) return NULL;
+ while (l < bufsiz && !eol) {
+@@ -186,8 +187,8 @@
+ bufsiz = fin ? getbuf(): -1;
+ }
+ }
+- if (right) strcpy(linebuf + l - 1, line + strlen(line) - right - 1);
++ if (right) strcpy(linebuf.data() + l - 1, line + strlen(line) - right - 1);
+ else linebuf[l] = '\0';
+- strcpy(line + left, linebuf);
++ strcpy(line + left, linebuf.data());
+ return line;
+ }
+==== //depot//src/hunspell/suggestmgr.cxx#2 - /google/src/files/140493454/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/49864191/depot//src/hunspell/suggestmgr.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/140493454/depot//src/hunspell/suggestmgr.cxx 2016-11-29 13:12:47.000000000 -0500
+@@ -5,6 +5,7 @@
+ #include <string.h>
+ #include <stdio.h>
+ #include <ctype.h>
++#include <vector>
+
+ #include "suggestmgr.hxx"
+ #include "htypes.hxx"
+@@ -1586,8 +1587,8 @@
+ /* affixation */
+ char * SuggestMgr::suggest_hentry_gen(hentry * rv, char * pattern)
+ {
+- char result[MAXLNLEN];
+- *result = '\0';
++ std::vector<char> result(MAXLNLEN);
++ result[0] = '\0';
+ int sfxcount = get_sfxcount(pattern);
+
+ if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) return NULL;
+@@ -1596,8 +1597,8 @@
+ char * aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen,
+ HENTRY_DATA(rv), pattern, 0);
+ if (aff) {
+- mystrcat(result, aff, MAXLNLEN);
+- mystrcat(result, "\n", MAXLNLEN);
++ mystrcat(result.data(), aff, MAXLNLEN);
++ mystrcat(result.data(), "\n", MAXLNLEN);
+ free(aff);
+ }
+ }
+@@ -1622,8 +1623,8 @@
+ char * aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, rv2->alen,
+ HENTRY_DATA(rv2), pattern, 0);
+ if (aff) {
+- mystrcat(result, aff, MAXLNLEN);
+- mystrcat(result, "\n", MAXLNLEN);
++ mystrcat(result.data(), aff, MAXLNLEN);
++ mystrcat(result.data(), "\n", MAXLNLEN);
+ free(aff);
+ }
+ }
+@@ -1633,16 +1634,16 @@
+ p = strstr(p + plen, MORPH_ALLOMORPH);
+ }
+
+- return (*result) ? mystrdup(result) : NULL;
++ return (result[0]) ? mystrdup(result.data()) : NULL;
+ }
+
+ char * SuggestMgr::suggest_gen(char ** desc, int n, char * pattern) {
+- char result[MAXLNLEN];
+- char result2[MAXLNLEN];
+- char newpattern[MAXLNLEN];
+- *newpattern = '\0';
++ std::vector<char> result(MAXLNLEN);
++ std::vector<char> result2(MAXLNLEN);
++ std::vector<char> newpattern(MAXLNLEN);
++ newpattern[0] = '\0';
+ if (n == 0) return 0;
+- *result2 = '\0';
++ result2[0] = '\0';
+ struct hentry * rv = NULL;
+ if (!pAMgr) return NULL;
+
+@@ -1650,14 +1651,14 @@
+ while(1) {
+
+ for (int k = 0; k < n; k++) {
+- *result = '\0';
++ result[0] = '\0';
+ // add compound word parts (except the last one)
+ char * s = (char *) desc[k];
+ char * part = strstr(s, MORPH_PART);
+ if (part) {
+ char * nextpart = strstr(part + 1, MORPH_PART);
+ while (nextpart) {
+- copy_field(result + strlen(result), part, MORPH_PART);
++ copy_field(result.data() + strlen(result.data()), part, MORPH_PART);
+ part = nextpart;
+ nextpart = strstr(part + 1, MORPH_PART);
+ }
+@@ -1665,14 +1666,14 @@
+ }
+
+ char **pl;
+- char tok[MAXLNLEN];
+- strcpy(tok, s);
+- char * alt = strstr(tok, " | ");
++ std::vector<char> tok(MAXLNLEN);
++ strcpy(tok.data(), s);
++ char * alt = strstr(tok.data(), " | ");
+ while (alt) {
+ alt[1] = MSEP_ALT;
+ alt = strstr(alt, " | ");
+ }
+- int pln = line_tok(tok, &pl, MSEP_ALT);
++ int pln = line_tok(tok.data(), &pl, MSEP_ALT);
+ for (int i = 0; i < pln; i++) {
+ // remove inflectional and terminal suffixes
+ char * is = strstr(pl[i], MORPH_INFL_SFX);
+@@ -1684,13 +1685,13 @@
+ }
+ char * st = strstr(s, MORPH_STEM);
+ if (st) {
+- copy_field(tok, st, MORPH_STEM);
+- rv = pAMgr->lookup(tok);
++ copy_field(tok.data(), st, MORPH_STEM);
++ rv = pAMgr->lookup(tok.data());
+ while (rv) {
+- char newpat[MAXLNLEN];
+- strcpy(newpat, pl[i]);
+- strcat(newpat, pattern);
+- char * sg = suggest_hentry_gen(rv, newpat);
++ std::vector<char> newpat(MAXLNLEN);
++ strcpy(newpat.data(), pl[i]);
++ strcat(newpat.data(), pattern);
++ char * sg = suggest_hentry_gen(rv, newpat.data());
+ if (!sg) sg = suggest_hentry_gen(rv, pattern);
+ if (sg) {
+ char ** gen;
+@@ -1699,14 +1700,15 @@
+ sg = NULL;
+ for (int j = 0; j < genl; j++) {
+ if (strstr(pl[i], MORPH_SURF_PFX)) {
+- int r2l = strlen(result2);
++ int r2l = strlen(result2.data());
+ result2[r2l] = MSEP_REC;
+- strcpy(result2 + r2l + 1, result);
+- copy_field(result2 + strlen(result2), pl[i], MORPH_SURF_PFX);
+- mystrcat(result2, gen[j], MAXLNLEN);
++ strcpy(result2.data() + r2l + 1, result.data());
++ copy_field(result2.data() + strlen(result2.data()), pl[i],
++ MORPH_SURF_PFX);
++ mystrcat(result2.data(), gen[j], MAXLNLEN);
+ } else {
+- sprintf(result2 + strlen(result2), "%c%s%s",
+- MSEP_REC, result, gen[j]);
++ sprintf(result2.data() + strlen(result2.data()), "%c%s%s",
++ MSEP_REC, result.data(), gen[j]);
+ }
+ }
+ freelist(&gen, genl);
+@@ -1718,16 +1720,16 @@
+ freelist(&pl, pln);
+ }
+
+- if (*result2 || !strstr(pattern, MORPH_DERI_SFX)) break;
+- strcpy(newpattern, pattern);
+- pattern = newpattern;
++ if (result2[0] || !strstr(pattern, MORPH_DERI_SFX)) break;
++ strcpy(newpattern.data(), pattern);
++ pattern = newpattern.data();
+ char * ds = strstr(pattern, MORPH_DERI_SFX);
+ while (ds) {
+ strncpy(ds, MORPH_TERM_SFX, MORPH_TAG_LEN);
+ ds = strstr(pattern, MORPH_DERI_SFX);
+ }
+ }
+- return (*result2 ? mystrdup(result2) : NULL);
++ return (result2[0] ? mystrdup(result2.data()) : NULL);
+ }
+
+
diff --git a/patches/09.leak_and_oob_fixes.patch b/patches/09.leak_and_oob_fixes.patch
new file mode 100644
index 0000000..0cfeb5b
--- /dev/null
+++ b/patches/09.leak_and_oob_fixes.patch
@@ -0,0 +1,45 @@
+Change 151456944 by lbaudoin@lbaudoin:hunspell:5889:citc on 2017/03/28 09:13:02
+
+ Fix write at index -1 in a buffer and a memory leak with badly formatted UTF-8 inputs.
+
+ PRESUBMIT=passed
+ BUG=36581411
+ FIXED=36581411
+ R=jiho
+ CC=gmail-security+reviews
+ APPROVED=jiho
+ REQUIRED_REVIEW=1
+ DELTA_BY_EXTENSION=cxx=7
+ OCL=151161132
+
+Affected files ...
+
+... //depot//src/hunspell/hunspell.cxx#4 edit
+... //depot//src/hunspell/suggestmgr.cxx#4 edit
+... //depot//testdata/poc-b67f5296f69180725a8b9bbc95bccfb8be117a23d4ce14c5c5ce34421aebb5bf#1 add
+
+==== //depot//src/hunspell/hunspell.cxx#3 - /google/src/files/151456944/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/140493454/depot//src/hunspell/hunspell.cxx 2016-11-29 13:12:47.000000000 -0500
++++ /google/src/files/151456944/depot//src/hunspell/hunspell.cxx 2017-03-28 12:13:02.000000000 -0400
+@@ -861,7 +861,7 @@
+ // END OF LANG_hu section
+
+ // try ngram approach since found nothing or only compound words
+- if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) {
++ if (pAMgr && (ns == 0 || (ns >= 0 && onlycmpdsug)) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) {
+ switch(captype) {
+ case NOCAP: {
+ ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic);
+==== //depot//src/hunspell/suggestmgr.cxx#3 - /google/src/files/151456944/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/140493454/depot//src/hunspell/suggestmgr.cxx 2016-11-29 13:12:47.000000000 -0500
++++ /google/src/files/151456944/depot//src/hunspell/suggestmgr.cxx 2017-03-28 12:13:02.000000000 -0400
+@@ -158,6 +158,9 @@
+ wl = u8_u16(word_utf, MAXSWL, word);
+ // Added to handle wrong encoded UTF-8 strings - jiho@google.com
+ if (wl <= 0) {
++ for (int i = 0; i < maxSug; i++) {
++ if (wlst[i]) free(wlst[i]);
++ }
+ if (!*slst) {
+ free(wlst);
+ }
diff --git a/patches/10.mkinit_overflow.patch b/patches/10.mkinit_overflow.patch
new file mode 100644
index 0000000..f0d228f
--- /dev/null
+++ b/patches/10.mkinit_overflow.patch
@@ -0,0 +1,63 @@
+Change 160151083 by lbaudoin@lbaudoin:hunspell-fix:6013:citc on 2017/06/26 09:01:35
+
+ Fix a buffer overflow in hunspell case mkinit* functions.
+ The fix is flawed but:
+ - It prevents the buffer overflows
+ - I think that it works for the dictionaries we use with hunspell.
+ - The failure mode is getting a bad suggestion or no suggestions so I think we can live with it.
+
+ A real fix would be to use std::strings, allocate more than needed or allow re-allocation.
+ All of these require extensive changes to an outdated version of hunspell (checked in version is 1.3, current version is 1.6 and doesn't have these issues). Our plan is to deprecate all uses of hunspell instead of investing the time needed to either upgrade or fully fix.
+
+ PRESUBMIT=passed
+ BUG=28718593
+ R=bgoodman,shine
+ CC=gmail-security+reviews,sfen
+ APPROVED=shine
+ REQUIRED_REVIEW=1
+ DELTA=19 (14 added, 2 deleted, 3 changed)
+ DELTA_BY_EXTENSION=cxx=10
+ OCL=159994634
+
+Affected files ...
+
+... //depot//BUILD#16 edit
+... //depot//src/hunspell/hunspell.cxx#5 edit
+
+==== //depot//src/hunspell/hunspell.cxx#4 - /google/src/files/160151083/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/151456944/depot//src/hunspell/hunspell.cxx 2017-03-28 12:13:02.000000000 -0400
++++ /google/src/files/160151083/depot//src/hunspell/hunspell.cxx 2017-06-26 12:01:35.000000000 -0400
+@@ -1297,7 +1297,9 @@
+ unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+- u16_u8(p, MAXWORDUTF8LEN, u, len);
++ // TODO: p might not be long enough if the upper case character
++ // is longer in UTF-8 than the original one.
++ u16_u8(p, strlen(p), u, len);
+ }
+ }
+
+@@ -1309,7 +1311,9 @@
+ unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+- u16_u8(p, MAXWORDUTF8LEN, u, nc);
++ // TODO: p might not be long enough if the upper case character
++ // is longer in UTF-8 than the original one.
++ u16_u8(p, strlen(p), u, nc);
+ return strlen(p);
+ }
+ return nc;
+@@ -1323,7 +1327,10 @@
+ unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);
+ u[0].h = (unsigned char) (i >> 8);
+ u[0].l = (unsigned char) (i & 0x00FF);
+- u16_u8(p, MAXWORDUTF8LEN, u, nc);
++ auto len = strlen(p);
++ // TODO: p might not be long enough if the lower case character
++ // is longer in UTF-8 than the original one.
++ u16_u8(p, len, u, nc);
+ return strlen(p);
+ }
+ return nc;
diff --git a/patches/11.insert_sug.patch b/patches/11.insert_sug.patch
new file mode 100644
index 0000000..f81f0ba
--- /dev/null
+++ b/patches/11.insert_sug.patch
@@ -0,0 +1,35 @@
+Change 160419349 by lbaudoin@lbaudoin:hunspell-fix:6013:citc on 2017/06/28 09:50:38
+
+ Fix two crashes in hunspell.
+ The null check is borrowed from hunspell 1.3.4, checking for a negative index protects from the second type of crashes. The existing code had checks here and there (but missed spots) but it seems safer to check at the point of use.
+
+ PRESUBMIT=passed
+ BUG=28718593,62755495
+ FIXED=28718593,62755495
+ R=bgoodman,shine
+ CC=gmail-security+reviews
+ APPROVED=shine
+ REQUIRED_REVIEW=1
+ DELTA=10 (2 added, 6 deleted, 2 changed)
+ DELTA_BY_EXTENSION=cxx=3
+ OCL=160341892
+
+Affected files ...
+
+... //depot//BUILD#17 edit
+... //depot//src/hunspell/hunspell.cxx#6 edit
+
+==== //depot//src/hunspell/hunspell.cxx#5 - /google/src/files/160419349/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/160151083/depot//src/hunspell/hunspell.cxx 2017-06-26 12:01:35.000000000 -0400
++++ /google/src/files/160419349/depot//src/hunspell/hunspell.cxx 2017-06-28 12:50:38.000000000 -0400
+@@ -312,7 +312,9 @@
+
+ /* insert a word to the beginning of the suggestion array and return ns */
+ int Hunspell::insert_sug(char ***slst, char * word, int ns) {
+- char * dup = mystrdup(word);
++ if (!slst || !*slst || ns < 0)
++ return ns;
++ char * dup = mystrdup(word);
+ if (!dup) return ns;
+ if (ns == MAXSUGGESTION) {
+ ns--;
diff --git a/patches/12.buffer_overflow.patch b/patches/12.buffer_overflow.patch
new file mode 100644
index 0000000..a50ba02
--- /dev/null
+++ b/patches/12.buffer_overflow.patch
@@ -0,0 +1,41 @@
+Change 171343973 by lbaudoin@lbaudoin:hunspell-crash:6404:citc on 2017/10/06 14:25:31
+
+ Fix a buffer overflow in hunspell.
+ Also tested manually against the hunspell 1.3.4 test suite.
+
+ PRESUBMIT=passed
+ BUG=67454984
+ FIXED=67454984
+ R=lianglin
+ CC=asergeev,gmail-security+reviews
+ APPROVED=lianglin
+ REQUIRED_REVIEW=1
+ DELTA_BY_EXTENSION=cxx=3
+ OCL=171341547
+
+Affected files ...
+
+... //depot//src/hunspell/affentry.cxx#3 edit
+
+==== //depot//src/hunspell/affentry.cxx#2 - /google/src/files/171343973/depot//src/hunspell/affentry.cxx ====
+--- /google/src/files/49864191/depot//src/hunspell/affentry.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/171343973/depot//src/hunspell/affentry.cxx 2017-10-06 17:25:31.000000000 -0400
+@@ -586,7 +586,8 @@
+ // it checked in test_condition()
+
+ if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+- (tmpl + stripl >= numconds)) {
++ (tmpl + stripl >= numconds) &&
++ (len < sizeof(tmpword))) {
+
+ // generate new root word by removing suffix and adding
+ // back any characters that would have been stripped or
+@@ -594,7 +595,7 @@
+
+ strcpy (tmpword, word);
+ cp = (unsigned char *)(tmpword + tmpl);
+- if (stripl) {
++ if (stripl && len + stripl < sizeof(tmpword)) {
+ strcpy ((char *)cp, strip);
+ tmpl += stripl;
+ cp = (unsigned char *)(tmpword + tmpl);
diff --git a/patches/13.init_pointers.patch b/patches/13.init_pointers.patch
new file mode 100644
index 0000000..5c115eb
--- /dev/null
+++ b/patches/13.init_pointers.patch
@@ -0,0 +1,43 @@
+Change 180819946 by lbaudoin@lbaudoin:hunspell-fix:6844:citc on 2018/01/04 11:03:04
+
+ Initialize all the pointers in the SfxEntry constructor to prevent hunspell from dereferencing uninitialized memory when following the pointer chain.
+
+ PRESUBMIT=passed
+ BUG=67842908,68294846
+ R=samsonov,shine
+ APPROVED=samsonov,shine
+ REQUIRED_REVIEW=1
+ DELTA_BY_EXTENSION=hxx=7
+ OCL=180816035
+
+Affected files ...
+
+... //depot//src/hunspell/affentry.hxx#2 edit
+... //depot//testdata/poc-02d52382ead7347b24ada7d25e413a836129438617f849f44e08dd5019915024#1 add
+... //depot//testdata/poc-42e27028fdb17169fab11fbf2ed2c898e61f6105af09462653f2cd271e197f4f#1 add
+
+==== //depot//src/hunspell/affentry.hxx#1 - /google/src/files/180819946/depot//src/hunspell/affentry.hxx ====
+--- /google/src/files/37375269/depot//src/hunspell/affentry.hxx 2012-10-31 14:23:51.000000000 -0400
++++ /google/src/files/180819946/depot//src/hunspell/affentry.hxx 2018-01-04 14:03:04.000000000 -0500
+@@ -70,14 +70,14 @@
+ AffixMgr* pmyMgr;
+ char * rappnd;
+
+- SfxEntry * next;
+- SfxEntry * nexteq;
+- SfxEntry * nextne;
+- SfxEntry * flgnxt;
++ SfxEntry * next = nullptr;
++ SfxEntry * nexteq = nullptr ;
++ SfxEntry * nextne = nullptr;
++ SfxEntry * flgnxt = nullptr;
+
+- SfxEntry * l_morph;
+- SfxEntry * r_morph;
+- SfxEntry * eq_morph;
++ SfxEntry * l_morph = nullptr;
++ SfxEntry * r_morph = nullptr;
++ SfxEntry * eq_morph = nullptr;
+
+ public:
+
diff --git a/patches/14.buffer_overflow.patch b/patches/14.buffer_overflow.patch
new file mode 100644
index 0000000..3815e12
--- /dev/null
+++ b/patches/14.buffer_overflow.patch
@@ -0,0 +1,53 @@
+Change 205077565 by lbaudoin@lbaudoin:hunspell3:7918:citc on 2018/07/18 07:48:28
+
+ Fix a buffer overflow in hunspell.
+
+ PRESUBMIT=passed
+ BUG=62755495
+ FIXED=62755495
+ R=shine
+ CC=gmail-security+reviews,jduart,weihaw
+ APPROVED=shine
+ REQUIRED_REVIEW=1
+ DELTA_BY_EXTENSION=cxx=12
+ OCL=204916935
+
+Affected files ...
+
+... //depot//src/hunspell/suggestmgr.cxx#5 edit
+... //depot//testdata/poc-e70b6035dbc764fd241582c5f013edd783749a6ef4e8f653501e2a687c118cb0-180711063041634658#1 add
+
+==== //depot//src/hunspell/suggestmgr.cxx#4 - /google/src/files/205077565/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/151456944/depot//src/hunspell/suggestmgr.cxx 2017-03-28 12:13:02.000000000 -0400
++++ /google/src/files/205077565/depot//src/hunspell/suggestmgr.cxx 2018-07-18 10:48:28.000000000 -0400
+@@ -712,16 +712,21 @@
+ w_char * p;
+ clock_t timelimit = clock();
+ int timer = MINTIMER;
++ if (wl > MAXSWL || wl < 0) {
++ return -1;
++ }
+ // try inserting a tryme character at the end of the word and before every letter
+ for (int i = 0; i < ctryl; i++) {
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+- for (p = candidate_utf + wl; p >= candidate_utf; p--) {
+- *(p + 1) = *p;
+- *p = ctry_utf[i];
+- u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
+- ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
+- if (ns == -1) return -1;
+- if (!timer) return ns;
++ for (p = candidate_utf + wl;
++ p >= candidate_utf && p < candidate_utf + MAXSWL - 1; p--) {
++ *(p + 1) = *p;
++ *p = ctry_utf[i];
++ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
++ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer,
++ &timelimit);
++ if (ns == -1) return -1;
++ if (!timer) return ns;
+ }
+ }
+ return ns;
+==== //depot//testdata/poc-e70b6035dbc764fd241582c5f013edd783749a6ef4e8f653501e2a687c118cb0-180711063041634658 - /google/src/files/205077565/depot//testdata/poc-e70b6035dbc764fd241582c5f013edd783749a6ef4e8f653501e2a687c118cb0-180711063041634658 ==== (binary)
+(... files differ ...)
diff --git a/patches/15.overflow_check.patch b/patches/15.overflow_check.patch
new file mode 100644
index 0000000..81c1a06
--- /dev/null
+++ b/patches/15.overflow_check.patch
@@ -0,0 +1,47 @@
+Change 205133714 by lbaudoin@lbaudoin:fuzzer-38f43:7919:citc on 2018/07/18 14:06:49
+
+ Fix yet another buffer overflow in hunspell.
+
+ PRESUBMIT=passed
+ BUG=67454984
+ FIXED=67454984
+ R=sfen,shine
+ CC=gmail-security+reviews,jduart,weihaw
+ APPROVED=sfen,shine
+ REQUIRED_REVIEW=1
+ DELTA_BY_EXTENSION=cxx=11
+ OCL=204925722
+
+Affected files ...
+
+... //depot//src/hunspell/hunspell.cxx#7 edit
+... //depot//testdata/poc-38f43e7d002ec9c5711cea63753787e9c56533866247c1cf42d7e59eca991f8c#1 add
+
+==== //depot//src/hunspell/hunspell.cxx#6 - /google/src/files/205133714/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/160419349/depot//src/hunspell/hunspell.cxx 2017-06-28 12:50:38.000000000 -0400
++++ /google/src/files/205133714/depot//src/hunspell/hunspell.cxx 2018-07-18 17:06:49.000000000 -0400
+@@ -913,13 +913,17 @@
+ if (!spell(ppos)) {
+ nn = suggest(&nlst, ppos);
+ for (int j = nn - 1; j >= 0; j--) {
+- strncpy(wspace, cw, ppos - cw);
+- strcpy(wspace + (ppos - cw), nlst[j]);
+- if (!last) {
+- strcat(wspace, "-");
+- strcat(wspace, pos + 1);
+- }
+- ns = insert_sug(slst, wspace, ns);
++ int suggestion_size =
++ ppos - cw + strlen(nlst[j]) + (last ? 0 : strlen(pos + 1) + 1);
++ if (suggestion_size < sizeof(wspace)) {
++ strncpy(wspace, cw, ppos - cw);
++ strcpy(wspace + (ppos - cw), nlst[j]);
++ if (!last) {
++ strcat(wspace, "-");
++ strcat(wspace, pos + 1);
++ }
++ ns = insert_sug(slst, wspace, ns);
++ }
+ free(nlst[j]);
+ }
+ if (nlst != NULL) free(nlst);
diff --git a/patches/16.oss_fuzz_import.patch b/patches/16.oss_fuzz_import.patch
new file mode 100644
index 0000000..01df663
--- /dev/null
+++ b/patches/16.oss_fuzz_import.patch
@@ -0,0 +1,104 @@
+Change 409297624 by jvoisin@jvoisin:CS-wordlist2hunspell-2021-11-11_155813:9670:citc on 2021/11/11 19:00:20
+
+ Import hunspell's fuzzer from OSS-Fuzz
+
+ PRESUBMIT=passed
+ BUG=202533043
+ R=jiho
+ APPROVED=jiho
+ REQUIRED_REVIEW=1
+ DELTA=99 (99 added, 0 deleted, 0 changed)
+ DELTA_BY_EXTENSION=cxx=81
+ OCL=409141596
+
+Affected files ...
+
+... //depot//BUILD#25 edit
+... //depot//src/tools/fuzzer.cxx#1 add
+
+==== //depot//src/tools/fuzzer.cxx - /google/src/files/409297624/depot//src/tools/fuzzer.cxx ====
+--- /dev/null 2024-07-09 14:08:07.516000169 -0400
++++ /google/src/files/409297624/depot//src/tools/fuzzer.cxx 2021-11-11 22:00:20.000000000 -0500
+@@ -0,0 +1,81 @@
++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#include <../hunspell/hunspell.hxx>
++#include <sys/types.h>
++#include <dirent.h>
++#include <string.h>
++#include <libgen.h>
++#include <memory>
++#include <vector>
++#include <string>
++
++std::vector<std::unique_ptr<Hunspell>> dictionaries;
++
++bool endswith(const std::string &str, const std::string &suffix)
++{
++ return str.size() >= suffix.size() &&
++ str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
++}
++
++extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv)
++{
++ char* exe_path = (*argv)[0];
++ // dirname() can modify its argument.
++ char* exe_path_copy = strdup(exe_path);
++ char* dir = dirname(exe_path_copy);
++ DIR* d = opendir(dir);
++ struct dirent *direntry;
++ while ((direntry = readdir(d)) != NULL)
++ {
++ std::string entry(direntry->d_name);
++ if (endswith(entry, ".aff"))
++ {
++ std::string dic = entry.substr(0, entry.size() - 4) + ".dic";
++ dictionaries.emplace_back(new Hunspell(entry.c_str(), dic.c_str()));
++ }
++ }
++ closedir(d);
++ free(exe_path_copy);
++
++ return 0;
++}
++
++extern "C" int LLVMFuzzerTestOneInput(const char* data, size_t size)
++{
++ std::string word(data, size);
++ char **slist;
++ for (auto& dict : dictionaries)
++ {
++ if (!dict->spell(word.c_str()))
++ dict->suggest( &slist, word.c_str());
++ }
++ return 0;
++}
++
++/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+\ No newline at end of file
diff --git a/patches/17.leakfix.patch b/patches/17.leakfix.patch
new file mode 100644
index 0000000..8b1c242
--- /dev/null
+++ b/patches/17.leakfix.patch
@@ -0,0 +1,36 @@
+/google/src/cloud/gfalcon/hunspell_copybara//patches/05.leakfix.patch - empty, assuming text.
+Change 501585639 by mghiware@mghiware:fuzzer-5701496409030656:5968:citc on 2023/01/12 09:24:36
+
+ Release memory allocated during parsing on early returns due to errors
+
+ PRESUBMIT=passed
+ BUG=259670020
+ FIXED=259670020
+ R=bkuang
+ CC=jiho
+ REQUIRED_REVIEW=1
+ DELTA=7 (7 added, 0 deleted, 0 changed)
+ DELTA_BY_EXTENSION=cxx=7
+ OCL=501403365
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#8 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#7 - /google/src/files/501585639/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/501584981/depot//src/hunspell/affixmgr.cxx 2023-01-12 12:22:06.000000000 -0500
++++ /google/src/files/501585639/depot//src/hunspell/affixmgr.cxx 2023-01-12 12:24:36.000000000 -0500
+@@ -3644,6 +3644,13 @@
+ }
+ if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
++ // Free up memory allocated during parsing
++ for (int k=0; k < j; k++) {
++ free(reptable[k].pattern);
++ free(reptable[k].pattern2);
++ }
++ if (reptable[j].pattern) free(reptable[j].pattern);
++ if (reptable[j].pattern2) free(reptable[j].pattern2);
+ numrep = 0;
+ return 1;
+ }
diff --git a/patches/18.parser_bug.patch b/patches/18.parser_bug.patch
new file mode 100644
index 0000000..c443df3
--- /dev/null
+++ b/patches/18.parser_bug.patch
@@ -0,0 +1,34 @@
+Change 522447888 by alexsav@alexsav:fig-export-hunspellfix-1100-change-1:1102:citc on 2023/04/06 15:08:14
+
+ Fixes a string parsing bug causing a negative vector resize
+
+ https://clusterfuzz.corp.google.com/testcase-detail/5537541822218240
+
+ PRESUBMIT=passed
+ BUG=277153931
+ FIXED=277153931
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=522420750
+ FIG_CHANGESET=9667c6bc96e44f12112356b2f6e36cdda0684929
+ FIG_WORKSPACE=alexsav/1100:hunspellfix
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#10 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#9 - /google/src/files/522447888/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/501621097/depot//src/hunspell/affixmgr.cxx 2023-01-12 14:29:52.000000000 -0500
++++ /google/src/files/522447888/depot//src/hunspell/affixmgr.cxx 2023-04-06 18:08:14.000000000 -0400
+@@ -4281,7 +4281,7 @@
+ case 3: {
+ np++;
+ numents = atoi(piece);
+- if (numents == 0) {
++ if (numents <= 0) {
+ char * err = pHMgr->encode_flag(aflag);
+ if (err) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
diff --git a/patches/19.alloc_fix.patch b/patches/19.alloc_fix.patch
new file mode 100644
index 0000000..8eadf02
--- /dev/null
+++ b/patches/19.alloc_fix.patch
@@ -0,0 +1,146 @@
+Change 524965870 by sungyc@sungyc:fig-export-icing-153-change-311:4880:citc on 2023/04/17 15:28:39
+
+ Fixes memory allocate when unable to parse test case.
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5559624331558912' > /tmp/testcase-5559624331558912 && blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_env=ASAN_OPTIONS="fast_unwind_on_fatal=0" --test_arg=-runs=100 --test_arg=/tmp/testcase-5559624331558912 //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ This bug is caused by bad input.
+ - In `HashMgr::decode_flags`:
+ - We call `u8_u16` to decode utf-8/16 characters.
+ - `u8_u16` returns the length of decoded string, but -1 if unable to decode.
+ - **Therefore, we have to check the returned length before calling malloc.**
+ - Be aware of casting int to unsigned short.
+ - -1 will be casted to 65535.
+ - sizeof() returns an unsigned integer.
+ - So even if `len` is -1, calling `malloc(len * sizeof(...))` still attempts to allocate positive amount of memory (and may fail due to the # is too large).
+ - **Let's check `len` is NOT -1 before calling `malloc` to avoid this.**
+ - Also we may fail to parse the input, or the input contains < `numaliasf`/`numaliasm` lines.
+ - For example, [this test case](https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5559624331558912):
+ - Specifies 32764 af lines, but there is only one more line after this config.
+ - And the only line cannot be parsed by `u8_u16`.
+ - Which means, not all `aliasf[j]`s have been assigned a valid allocated memory address.
+ - **Thus, when freeing the memory, we should not assume all `aliasf[j]` is valid. Instead, we should:**
+ - **`memset()` all of them to 0 (NULL) when allocating.**
+ - **In destructor, only `free(aliasf[j])` if not NULL.**
+ - Same for `aliasm[j]`
+
+ PRESUBMIT=passed
+ BUG=273278397
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=33 (25 added, 0 deleted, 8 changed)
+ DELTA_BY_EXTENSION=cxx=33
+ OCL=524956186
+ FIG_CHANGESET=3fff249fb23560092527bbc6ed5eb02b6c7dac2b
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#6 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#5 - /google/src/files/524965870/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/140493454/depot//src/hunspell/hashmgr.cxx 2016-11-29 13:12:47.000000000 -0500
++++ /google/src/files/524965870/depot//src/hunspell/hashmgr.cxx 2023-04-17 18:28:39.000000000 -0400
+@@ -65,7 +65,11 @@
+ tablesize = 0;
+
+ if (aliasf) {
+- for (int j = 0; j < (numaliasf); j++) free(aliasf[j]);
++ for (int j = 0; j < (numaliasf); j++) {
++ if (aliasf[j]) {
++ free(aliasf[j]);
++ }
++ }
+ free(aliasf);
+ aliasf = NULL;
+ if (aliasflen) {
+@@ -74,7 +78,11 @@
+ }
+ }
+ if (aliasm) {
+- for (int j = 0; j < (numaliasm); j++) free(aliasm[j]);
++ for (int j = 0; j < (numaliasm); j++) {
++ if (aliasm[j]) {
++ free(aliasm[j]);
++ }
++ }
+ free(aliasm);
+ aliasm = NULL;
+ }
+@@ -540,6 +548,8 @@
+ case FLAG_UNI: { // UTF-8 characters
+ w_char w[MAXWORDLEN];
+ len = u8_u16(w, MAXWORDLEN, flags);
++ // u8_u16 returns -1 when unable to decode.
++ if (len == -1) return -1;
+ *result = (unsigned short *) malloc(len * sizeof(short));
+ if (!*result) return -1;
+ memcpy(*result, w, len * sizeof(short));
+@@ -734,6 +744,9 @@
+ aliasflen = NULL;
+ return 1;
+ }
++ // Initialize all aliasf to NULL.
++ memset(aliasf, 0, numaliasf * sizeof(unsigned short));
++ memset(aliasflen, 0, numaliasf * sizeof(short));
+ np++;
+ break;
+ }
+@@ -745,8 +758,8 @@
+ }
+ if (np != 2) {
+ numaliasf = 0;
+- free(aliasf);
+- free(aliasflen);
++ if (aliasf) free(aliasf);
++ if (aliasflen) free(aliasflen);
+ aliasf = NULL;
+ aliasflen = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+@@ -779,9 +792,19 @@
+ break;
+ }
+ case 1: {
+- aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af);
+- flag_qsort(aliasf[j], 0, aliasflen[j]);
+- break;
++ int result_aflen =
++ decode_flags(&(aliasf[j]), piece, af);
++ if (result_aflen == -1) {
++ HUNSPELL_WARNING(
++ stderr,
++ "error: line %d: unable to decode flag or "
++ "allocate memory\n",
++ af->getlinenum());
++ return 1;
++ }
++ aliasflen[j] = (unsigned short)result_aflen;
++ flag_qsort(aliasf[j], 0, aliasflen[j]);
++ break;
+ }
+ default: break;
+ }
+@@ -843,6 +866,8 @@
+ numaliasm = 0;
+ return 1;
+ }
++ // Initialize all aliasm to NULL.
++ memset(aliasm, 0, numaliasm * sizeof(char *));
+ np++;
+ break;
+ }
+@@ -854,7 +879,7 @@
+ }
+ if (np != 2) {
+ numaliasm = 0;
+- free(aliasm);
++ if (aliasm) free(aliasm);
+ aliasm = NULL;
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
diff --git a/patches/20.index_check.patch b/patches/20.index_check.patch
new file mode 100644
index 0000000..6d400c3
--- /dev/null
+++ b/patches/20.index_check.patch
@@ -0,0 +1,55 @@
+Change 537395995 by sungyc@sungyc:fig-export-icing-153-change-348:5470:citc on 2023/06/02 13:30:25
+
+ [hunspell] Check index before accessing when advancing
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5157202356469760' > /tmp/testcase-5157202356469760 && blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_env=ASAN_OPTIONS="fast_unwind_on_fatal=0" --test_arg=-runs=100 --test_arg=/tmp/testcase-5157202356469760 //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ When advancing `i` for utf-8, we have to make sure `i < cmax` before accessing `st[i]` since it is possible that `(st[i] & 0xc0) == 0x80` still holds after `i` is out of bound. See below printed debug message:
+
+
+ ```
+ cmin: 3, cmax: 6
+ i = 4
+ i = 5
+ i = 6
+ i = 7
+ i = 8
+ i = 9
+ i = 10
+ i = 11
+ ...
+ ```
+
+ PRESUBMIT=passed
+ BUG=280418190
+ R=mghiware
+ CC=adorokhine
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=537368756
+ FIG_CHANGESET=640c9939f4f2ae8a3b8c92921c2eff1488d668df
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#11 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#10 - /google/src/files/537395995/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/522447888/depot//src/hunspell/affixmgr.cxx 2023-04-06 18:08:14.000000000 -0400
++++ /google/src/files/537395995/depot//src/hunspell/affixmgr.cxx 2023-06-02 16:30:25.000000000 -0400
+@@ -1572,7 +1572,7 @@
+ for (i = cmin; i < cmax; i++) {
+ // go to end of the UTF-8 character
+ if (utf8) {
+- for (; (st[i] & 0xc0) == 0x80; i++);
++ for (; i < cmax && ((st[i] & 0xc0) == 0x80); i++);
+ if (i >= cmax) {
+ free(rwords);
+ return NULL;
diff --git a/patches/21.overflow_check.patch b/patches/21.overflow_check.patch
new file mode 100644
index 0000000..6fa3a68
--- /dev/null
+++ b/patches/21.overflow_check.patch
@@ -0,0 +1,71 @@
+Change 537406616 by sungyc@sungyc:fig-export-icing-153-change-349:5478:citc on 2023/06/02 14:14:02
+
+ [hunspell] Safe integer check for tablesize to prevent overflow
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=4972064410566656' > /tmp/testcase-4972064410566656 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-4972064410566656 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ - The input table size is `2147483647`.
+ - We add `5 + USERWORD` to it, which causes integer overflow.
+
+ Change `tablesize` check in L390 to prevent overflow.
+
+ PRESUBMIT=passed
+ BUG=280277605
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=15 (7 added, 4 deleted, 4 changed)
+ DELTA_BY_EXTENSION=cxx=11
+ OCL=537399369
+ FIG_CHANGESET=4fe6f3569fee5591c986f4905e54c74a8cea8192
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#7 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#6 - /google/src/files/537406616/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/524965870/depot//src/hunspell/hashmgr.cxx 2023-04-17 18:28:39.000000000 -0400
++++ /google/src/files/537406616/depot//src/hunspell/hashmgr.cxx 2023-06-02 17:14:02.000000000 -0400
+@@ -1,14 +1,16 @@
+-#include "license.hunspell"
+-#include "license.myspell"
++#include "hashmgr.hxx"
+
+-#include <stdlib.h>
+-#include <string.h>
+-#include <stdio.h>
+ #include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <limits>
+
+-#include "hashmgr.hxx"
+-#include "csutil.hxx"
+ #include "atypes.hxx"
++#include "csutil.hxx"
++#include "license.hunspell"
++#include "license.myspell"
+
+ // build a hash table from a munched word list
+
+@@ -385,7 +387,8 @@
+ }
+
+ tablesize = atoi(ts);
+- if (tablesize == 0) {
++ if (tablesize <= 0 ||
++ tablesize > std::numeric_limits<int>::max() - 5 - USERWORD) {
+ HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n");
+ delete dict;
+ return 4;
diff --git a/patches/22.index_check.patch b/patches/22.index_check.patch
new file mode 100644
index 0000000..8254ece
--- /dev/null
+++ b/patches/22.index_check.patch
@@ -0,0 +1,77 @@
+Change 537915601 by sungyc@sungyc:fig-export-icing-153-change-350:5488:citc on 2023/06/05 10:29:15
+
+ [hunspell] Safe integer check for phone->num to prevent overflow
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5179437376995328' > /tmp/testcase-5179437376995328 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-5179437376995328 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ - We malloc memory with size = `2 * (phone->num + 1) * sizeof(char *)` for `phone->rules`.
+ - If `phone->num` is too large, then it will cause integer overflow.
+
+ Change `phone->num` check in L3769 to prevent overflow. Also free `phone` before returning error to prevent memory leak.
+
+ PRESUBMIT=passed
+ BUG=280278127
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=15 (7 added, 4 deleted, 4 changed)
+ DELTA_BY_EXTENSION=cxx=11
+ OCL=537406342
+ FIG_CHANGESET=b89f41211e07c7f574813ec124d9743a7c505e10
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#12 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#11 - /google/src/files/537915601/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/537395995/depot//src/hunspell/affixmgr.cxx 2023-06-02 16:30:25.000000000 -0400
++++ /google/src/files/537915601/depot//src/hunspell/affixmgr.cxx 2023-06-05 13:29:15.000000000 -0400
+@@ -1,19 +1,19 @@
+-#include "license.hunspell"
+-#include "license.myspell"
++#include "affixmgr.hxx"
+
++#include <ctype.h>
++#include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+-#include <stdio.h>
+-#include <ctype.h>
+
++#include <limits>
+ #include <memory>
+ #include <vector>
+
+-#include "affixmgr.hxx"
+ #include "affentry.hxx"
+-#include "langnum.hxx"
+-
+ #include "csutil.hxx"
++#include "langnum.hxx"
++#include "license.hunspell"
++#include "license.myspell"
+
+ AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key)
+ {
+@@ -3766,8 +3766,11 @@
+ phone->num = atoi(piece);
+ phone->rules = NULL;
+ phone->utf8 = (char) utf8;
+- if (phone->num < 1) {
++ if (phone->num < 1 ||
++ phone->num > std::numeric_limits<int>::max() / (2 * sizeof(char *)) - 1) {
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
++ free(phone);
++ phone = NULL;
+ return 1;
+ }
+ phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
diff --git a/patches/23.pointer_serialize.patch b/patches/23.pointer_serialize.patch
new file mode 100644
index 0000000..3756af6
--- /dev/null
+++ b/patches/23.pointer_serialize.patch
@@ -0,0 +1,89 @@
+Change 537916729 by sungyc@sungyc:fig-export-icing-153-change-352:5498:citc on 2023/06/05 10:32:42
+
+ [hunspell] Fix pointer serialize error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=6280357192007680' > /tmp/testcase-6280357192007680 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-6280357192007680 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ - We `aliasm[atoi(desc) - 1]`'s content (a memory address) into `hpw + wbl + 1`.
+ - If `atoi(desc)` is out of bound (< 0 or >= numaliasm), then `get_aliasm(atoi(desc))` returns `NULL`.
+ - This bug is caused by incorrect `desc` content (note: `desc` is not NULL, but it contains only '\0') that causes `get_aliasm` returning NULL. We serialize NULL and later `strstr(HENTRY_DATA(hp), MORPH_PHON)` uses NULL, which causes memory error.
+
+ This CL:
+ - Adds NULL check before serializing.
+ - Also changes the memory allocate size calculation.
+
+ PRESUBMIT=passed
+ BUG=280420276
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=9 (5 added, 0 deleted, 4 changed)
+ DELTA_BY_EXTENSION=cxx=8,hxx=1
+ OCL=537433767
+ FIG_CHANGESET=87b3c783ed831422e442613718935eda7870b28a
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/csutil.cxx#3 edit
+... //depot//src/hunspell/csutil.hxx#3 edit
+... //depot//src/hunspell/hashmgr.cxx#8 edit
+
+==== //depot//src/hunspell/csutil.cxx#2 - /google/src/files/537916729/depot//src/hunspell/csutil.cxx ====
+--- /google/src/files/49864191/depot//src/hunspell/csutil.cxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/537916729/depot//src/hunspell/csutil.cxx 2023-06-05 13:32:42.000000000 -0400
+@@ -665,7 +665,7 @@
+ }
+
+ // conversion function for protected memory
+- void store_pointer(char * dest, char * source)
++ void store_pointer(char * dest, const char * source)
+ {
+ memcpy(dest, &source, sizeof(char *));
+ }
+==== //depot//src/hunspell/csutil.hxx#2 - /google/src/files/537916729/depot//src/hunspell/csutil.hxx ====
+--- /google/src/files/49864191/depot//src/hunspell/csutil.hxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/537916729/depot//src/hunspell/csutil.hxx 2023-06-05 13:32:42.000000000 -0400
+@@ -186,7 +186,7 @@
+ LIBHUNSPELL_DLL_EXPORTED int get_sfxcount(const char * morph);
+
+ // conversion function for protected memory
+-LIBHUNSPELL_DLL_EXPORTED void store_pointer(char * dest, char * source);
++LIBHUNSPELL_DLL_EXPORTED void store_pointer(char * dest, const char * source);
+
+ // conversion function for protected memory
+ LIBHUNSPELL_DLL_EXPORTED char * get_stored_pointer(const char * s);
+==== //depot//src/hunspell/hashmgr.cxx#7 - /google/src/files/537916729/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/537406616/depot//src/hunspell/hashmgr.cxx 2023-06-02 17:14:02.000000000 -0400
++++ /google/src/files/537916729/depot//src/hunspell/hashmgr.cxx 2023-06-05 13:32:42.000000000 -0400
+@@ -126,7 +126,7 @@
+ int al, const char * desc, bool onlyupcase)
+ {
+ bool upcasehomonym = false;
+- int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0;
++ int descl = desc ? (aliasm ? sizeof(char *) : strlen(desc) + 1) : 0;
+ // variable-length hash record with word and optional fields
+ struct hentry* hp =
+ (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl);
+@@ -158,7 +158,12 @@
+ hp->var = H_OPT;
+ if (aliasm) {
+ hp->var += H_OPT_ALIASM;
+- store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc)));
++ const char* aliasm_desc = get_aliasm(atoi(desc));
++ if (!aliasm_desc) {
++ free(hp);
++ return 1;
++ }
++ store_pointer(hpw + wbl + 1, aliasm_desc);
+ } else {
+ strcpy(hpw + wbl + 1, desc);
+ if (complexprefixes) {
diff --git a/patches/24.aliasf_leak.patch b/patches/24.aliasf_leak.patch
new file mode 100644
index 0000000..08e75ad
--- /dev/null
+++ b/patches/24.aliasf_leak.patch
@@ -0,0 +1,201 @@
+Change 541956399 by sungyc@sungyc:fig-export-icing-153-change-355:5516:citc on 2023/06/20 09:47:05
+
+ [hunspell] Fix aliasf memory leak error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5175740794273792' > /tmp/testcase-5175740794273792 && \
+ blaze --blazerc=/dev/null test --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-rss_limit_mb=2560 --test_arg=-timeout=90 --test_arg=-runs=100 --test_arg=/tmp/testcase-5175740794273792 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ The memory leak is caused by:
+ - Some previous round allocated `aliasf[j]` successfully.
+ - If later fails, then we free the entire `aliasf`/`aliasflen` and early return. But the original free memory code didn't correctly handle 2nd level of memory (`aliasf[j]`) allocated previously.
+
+ This CL creates a new private method `free_aliasf` to handle all these cases.
+
+ PRESUBMIT=passed
+ BUG=280296690
+ R=tjbarron
+ CC=mghiware
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=74 (32 added, 31 deleted, 11 changed)
+ DELTA_BY_EXTENSION=cxx=42,hxx=1
+ OCL=538018307
+ FIG_CHANGESET=dcae75cc8f1f8f5fb7ea69ec7f25b3e731f7b6df
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#9 edit
+... //depot//src/hunspell/hashmgr.hxx#2 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#8 - /google/src/files/541956399/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/537916729/depot//src/hunspell/hashmgr.cxx 2023-06-05 13:32:42.000000000 -0400
++++ /google/src/files/541956399/depot//src/hunspell/hashmgr.cxx 2023-06-20 12:47:05.000000000 -0400
+@@ -30,6 +30,7 @@
+ ignorechars_utf16_len = 0;
+ numaliasf = 0;
+ aliasf = NULL;
++ aliasflen = NULL;
+ numaliasm = 0;
+ aliasm = NULL;
+ forbiddenword = FORBIDDENWORD; // forbidden word signing flag
+@@ -66,19 +67,7 @@
+ }
+ tablesize = 0;
+
+- if (aliasf) {
+- for (int j = 0; j < (numaliasf); j++) {
+- if (aliasf[j]) {
+- free(aliasf[j]);
+- }
+- }
+- free(aliasf);
+- aliasf = NULL;
+- if (aliasflen) {
+- free(aliasflen);
+- aliasflen = NULL;
+- }
+- }
++ free_aliasf();
+ if (aliasm) {
+ for (int j = 0; j < (numaliasm); j++) {
+ if (aliasm[j]) {
+@@ -736,25 +725,27 @@
+ case 1: {
+ numaliasf = atoi(piece);
+ if (numaliasf < 1) {
+- numaliasf = 0;
+- aliasf = NULL;
+- aliasflen = NULL;
++ free_aliasf();
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
++
++ // Allocate and initialize aliasf
+ aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *));
+- aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short));
+- if (!aliasf || !aliasflen) {
+- numaliasf = 0;
+- if (aliasf) free(aliasf);
+- if (aliasflen) free(aliasflen);
+- aliasf = NULL;
+- aliasflen = NULL;
++ if (!aliasf) {
++ free_aliasf();
+ return 1;
+ }
+- // Initialize all aliasf to NULL.
+- memset(aliasf, 0, numaliasf * sizeof(unsigned short));
+- memset(aliasflen, 0, numaliasf * sizeof(short));
++ memset(aliasf, 0, numaliasf * sizeof(unsigned short *));
++
++ // Allocate and initialize aliasflen
++ aliasflen = (unsigned short *) malloc(numaliasf * sizeof(unsigned short));
++ if (!aliasflen) {
++ free_aliasf();
++ return 1;
++ }
++ memset(aliasflen, 0, numaliasf * sizeof(unsigned short));
++
+ np++;
+ break;
+ }
+@@ -765,11 +756,7 @@
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+- numaliasf = 0;
+- if (aliasf) free(aliasf);
+- if (aliasflen) free(aliasflen);
+- aliasf = NULL;
+- aliasflen = NULL;
++ free_aliasf();
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+@@ -777,7 +764,10 @@
+ /* now parse the numaliasf lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numaliasf; j++) {
+- if (!(nl = af->getline())) return 1;
++ if (!(nl = af->getline())) {
++ free_aliasf();
++ return 1;
++ }
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+@@ -789,11 +779,7 @@
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"AF",2) != 0) {
+- numaliasf = 0;
+- free(aliasf);
+- free(aliasflen);
+- aliasf = NULL;
+- aliasflen = NULL;
++ free_aliasf();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+@@ -803,6 +789,7 @@
+ int result_aflen =
+ decode_flags(&(aliasf[j]), piece, af);
+ if (result_aflen == -1) {
++ free_aliasf();
+ HUNSPELL_WARNING(
+ stderr,
+ "error: line %d: unable to decode flag or "
+@@ -821,11 +808,7 @@
+ piece = mystrsep(&tp, 0);
+ }
+ if (!aliasf[j]) {
+- free(aliasf);
+- free(aliasflen);
+- aliasf = NULL;
+- aliasflen = NULL;
+- numaliasf = 0;
++ free_aliasf();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+@@ -959,3 +942,20 @@
+ HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
+ return NULL;
+ }
++
++void HashMgr::free_aliasf() {
++ if (aliasf) {
++ for (int i = 0; i < numaliasf; i++) {
++ if (aliasf[i]) {
++ free(aliasf[i]);
++ }
++ }
++ free(aliasf);
++ aliasf = NULL;
++ }
++ if (aliasflen) {
++ free(aliasflen);
++ aliasflen = NULL;
++ }
++ numaliasf = 0;
++}
+==== //depot//src/hunspell/hashmgr.hxx#1 - /google/src/files/541956399/depot//src/hunspell/hashmgr.hxx ====
+--- /google/src/files/37375269/depot//src/hunspell/hashmgr.hxx 2012-10-31 14:23:51.000000000 -0400
++++ /google/src/files/541956399/depot//src/hunspell/hashmgr.hxx 2023-06-20 12:47:05.000000000 -0400
+@@ -64,6 +64,7 @@
+ int parse_aliasm(char * line, FileMgr * af);
+ int remove_forbidden_flag(const char * word);
+
++ void free_aliasf();
+ };
+
+ #endif
diff --git a/patches/25.aliasm_leak.patch b/patches/25.aliasm_leak.patch
new file mode 100644
index 0000000..edadcee
--- /dev/null
+++ b/patches/25.aliasm_leak.patch
@@ -0,0 +1,158 @@
+Change 541956676 by sungyc@sungyc:fig-export-icing-153-change-357:5526:citc on 2023/06/20 09:47:53
+
+ [hunspell] Fix aliasm memory leak error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=6277726054776832' > /tmp/testcase-6277726054776832 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-6277726054776832 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ The memory leak is caused by:
+ - Some previous round allocated `aliasm[j]` successfully.
+ - If later fails, then we free the entire `aliasm` and early return. But the original free memory code didn't correctly handle 2nd level of memory (`aliasm[j]`) allocated previously.
+
+ This CL creates a new private method `free_aliasm` to handle all these cases.
+
+ PRESUBMIT=passed
+ BUG=280280659
+ R=mghiware
+ CC=tjbarron
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=42 (19 added, 17 deleted, 6 changed)
+ DELTA_BY_EXTENSION=cxx=24,hxx=1
+ OCL=538271164
+ DIFFBASE=538018307
+ FIG_CHANGESET=9414dae38fc69132c785e6029fb78b64f1c57847
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#10 edit
+... //depot//src/hunspell/hashmgr.hxx#3 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#9 - /google/src/files/541956676/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/541956399/depot//src/hunspell/hashmgr.cxx 2023-06-20 12:47:05.000000000 -0400
++++ /google/src/files/541956676/depot//src/hunspell/hashmgr.cxx 2023-06-20 12:47:53.000000000 -0400
+@@ -68,15 +68,7 @@
+ tablesize = 0;
+
+ free_aliasf();
+- if (aliasm) {
+- for (int j = 0; j < (numaliasm); j++) {
+- if (aliasm[j]) {
+- free(aliasm[j]);
+- }
+- }
+- free(aliasm);
+- aliasm = NULL;
+- }
++ free_aliasm();
+
+ #ifndef OPENOFFICEORG
+ #ifndef MOZILLA_CLIENT
+@@ -849,12 +841,13 @@
+ case 1: {
+ numaliasm = atoi(piece);
+ if (numaliasm < 1) {
++ free_aliasm();
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ aliasm = (char **) malloc(numaliasm * sizeof(char *));
+ if (!aliasm) {
+- numaliasm = 0;
++ free_aliasm();
+ return 1;
+ }
+ // Initialize all aliasm to NULL.
+@@ -869,9 +862,7 @@
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
+- numaliasm = 0;
+- if (aliasm) free(aliasm);
+- aliasm = NULL;
++ free_aliasm();
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+@@ -879,7 +870,10 @@
+ /* now parse the numaliasm lines to read in the remainder of the table */
+ char * nl = line;
+ for (int j=0; j < numaliasm; j++) {
+- if (!(nl = af->getline())) return 1;
++ if (!(nl = af->getline())) {
++ free_aliasm();
++ return 1;
++ }
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+@@ -890,10 +884,8 @@
+ switch(i) {
+ case 0: {
+ if (strncmp(piece,"AM",2) != 0) {
++ free_aliasm();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+- numaliasm = 0;
+- free(aliasm);
+- aliasm = NULL;
+ return 1;
+ }
+ break;
+@@ -910,9 +902,7 @@
+ }
+ aliasm[j] = mystrdup(piece);
+ if (!aliasm[j]) {
+- numaliasm = 0;
+- free(aliasm);
+- aliasm = NULL;
++ free_aliasm();
+ return 1;
+ }
+ break; }
+@@ -923,9 +913,7 @@
+ piece = mystrsep(&tp, ' ');
+ }
+ if (!aliasm[j]) {
+- numaliasm = 0;
+- free(aliasm);
+- aliasm = NULL;
++ free_aliasm();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+ return 1;
+ }
+@@ -959,3 +947,16 @@
+ }
+ numaliasf = 0;
+ }
++
++void HashMgr::free_aliasm() {
++ if (aliasm) {
++ for (int i = 0; i < (numaliasm); i++) {
++ if (aliasm[i]) {
++ free(aliasm[i]);
++ }
++ }
++ free(aliasm);
++ aliasm = NULL;
++ }
++ numaliasm = 0;
++}
+==== //depot//src/hunspell/hashmgr.hxx#2 - /google/src/files/541956676/depot//src/hunspell/hashmgr.hxx ====
+--- /google/src/files/541956399/depot//src/hunspell/hashmgr.hxx 2023-06-20 12:47:05.000000000 -0400
++++ /google/src/files/541956676/depot//src/hunspell/hashmgr.hxx 2023-06-20 12:47:53.000000000 -0400
+@@ -65,6 +65,7 @@
+ int remove_forbidden_flag(const char * word);
+
+ void free_aliasf();
++ void free_aliasm();
+ };
+
+ #endif
diff --git a/patches/26.defcpdtable_leak.patch b/patches/26.defcpdtable_leak.patch
new file mode 100644
index 0000000..68885f9
--- /dev/null
+++ b/patches/26.defcpdtable_leak.patch
@@ -0,0 +1,143 @@
+Change 542666043 by sungyc@sungyc:fig-export-icing-153-change-361:5566:citc on 2023/06/22 14:19:55
+
+ [hunspell] Fix defcpdtable memory leak error in AffixMgr
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=4667620284301312' > /tmp/testcase-4667620284301312 && \
+ blaze --blazerc=/dev/null test --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-rss_limit_mb=2560 --test_arg=-timeout=90 --test_arg=-runs=100 --test_arg=/tmp/testcase-4667620284301312 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ The memory leak is caused by:
+ - Some previous round allocated `defcpdtable[j]` successfully.
+ - If later fails, then we free the entire `defcpdtable` and early return. But the original free memory code didn't correctly handle 2nd level of memory (`defcpdtable[j]`) allocated previously.
+
+ This CL creates a new private method `free_defcpdtable` to handle all these cases.
+
+ PRESUBMIT=passed
+ BUG=280277178
+ R=tjbarron
+ CC=mghiware
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=37 (25 added, 8 deleted, 4 changed)
+ DELTA_BY_EXTENSION=cxx=27,hxx=2
+ OCL=540728508
+ FIG_CHANGESET=a345f0b0f502b10b471cfd2c7a008bb06724aad8
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#13 edit
+... //depot//src/hunspell/affixmgr.hxx#3 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#12 - /google/src/files/542666043/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/537915601/depot//src/hunspell/affixmgr.cxx 2023-06-05 13:29:15.000000000 -0400
++++ /google/src/files/542666043/depot//src/hunspell/affixmgr.cxx 2023-06-22 17:19:55.000000000 -0400
+@@ -197,14 +197,8 @@
+ phone = NULL;
+ }
+
+- if (defcpdtable) {
+- for (int j=0; j < numdefcpd; j++) {
+- free(defcpdtable[j].def);
+- defcpdtable[j].def = NULL;
+- }
+- free(defcpdtable);
+- defcpdtable = NULL;
+- }
++ free_defcpdtable();
++
+ numrep = 0;
+ if (checkcpdtable) {
+ for (int j=0; j < numcheckcpd; j++) {
+@@ -3948,11 +3942,15 @@
+ case 1: {
+ numdefcpd = atoi(piece);
+ if (numdefcpd < 1) {
++ free_defcpdtable();
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+ defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
+- if (!defcpdtable) return 1;
++ if (!defcpdtable) {
++ free_defcpdtable();
++ return 1;
++ }
+ np++;
+ break;
+ }
+@@ -3963,6 +3961,7 @@
+ piece = mystrsep(&tp, 0);
+ }
+ if (np != 2) {
++ free_defcpdtable();
+ HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+ return 1;
+ }
+@@ -3970,7 +3969,10 @@
+ /* now parse the numdefcpd lines to read in the remainder of the table */
+ char * nl;
+ for (int j=0; j < numdefcpd; j++) {
+- if (!(nl = af->getline())) return 1;
++ if (!(nl = af->getline())) {
++ free_defcpdtable();
++ return 1;
++ }
+ mychomp(nl);
+ tp = nl;
+ i = 0;
+@@ -3981,8 +3983,8 @@
+ switch(i) {
+ case 0: {
+ if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
++ free_defcpdtable();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+- numdefcpd = 0;
+ return 1;
+ }
+ break;
+@@ -4019,8 +4021,8 @@
+ piece = mystrsep(&tp, 0);
+ }
+ if (!defcpdtable[j].len) {
++ free_defcpdtable();
+ HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+- numdefcpd = 0;
+ return 1;
+ }
+ }
+@@ -4582,3 +4584,16 @@
+ }
+ return 0;
+ }
++
++void AffixMgr::free_defcpdtable() {
++ if (defcpdtable) {
++ for (int j=0; j < numdefcpd; j++) {
++ free(defcpdtable[j].def);
++ defcpdtable[j].def = NULL;
++ }
++ free(defcpdtable);
++ defcpdtable = NULL;
++ }
++ numdefcpd = 0;
++}
++
+==== //depot//src/hunspell/affixmgr.hxx#2 - /google/src/files/542666043/depot//src/hunspell/affixmgr.hxx ====
+--- /google/src/files/49864191/depot//src/hunspell/affixmgr.hxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/542666043/depot//src/hunspell/affixmgr.hxx 2023-06-22 17:19:55.000000000 -0400
+@@ -244,6 +244,8 @@
+ int process_sfx_tree_to_list();
+ int redundant_condition(char, char * strip, int stripl,
+ const char * cond, int);
++
++ void free_defcpdtable();
+ };
+
+ #endif
diff --git a/patches/27.numaliasm_oom.patch b/patches/27.numaliasm_oom.patch
new file mode 100644
index 0000000..8dfad7d
--- /dev/null
+++ b/patches/27.numaliasm_oom.patch
@@ -0,0 +1,75 @@
+Change 542907937 by sungyc@sungyc:fig-export-icing-153-change-362:5570:citc on 2023/06/23 11:09:48
+
+ [hunspell] Fix bad numaliasm which causes out of memory error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5985909065252864' > /tmp/testcase-5985909065252864 && \
+ blaze --blazerc=/dev/null test --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-rss_limit_mb=2560 --test_arg=-timeout=90 --test_arg=-runs=100 --test_arg=/tmp/testcase-5985909065252864 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ This problem is triggered by bad `numaliasm` (the test data has 328420866).
+ - We succeeded to allocate `328420866 * sizeof(char *)` in the first round of test, but due to some issues (probably free() delay?), the allocation in the second round fails.
+ - Since:
+ - It is unlikely to have such large input.
+ - We've limited the input byte size in the test (see [codeptr](https://source.corp.google.com/piper///depot//fuzzers/dict_fuzzer.cc;l=9-11)) to be at most `2 << 13` (it is 2^14, 16384 actually).
+ - Let's add `kMaxNumAliasm` and enforce the check. Use 65536 as the limit.
+
+ PRESUBMIT=passed
+ BUG=280375760
+ R=tjbarron
+ CC=mghiware
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=8 (6 added, 0 deleted, 2 changed)
+ DELTA_BY_EXTENSION=cxx=6,hxx=2
+ OCL=540730895
+ FIG_CHANGESET=553a88a54a5f92d4fc948f5c616238bb0cd91ee7
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#11 edit
+... //depot//src/hunspell/hashmgr.hxx#4 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#10 - /google/src/files/542907937/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/541956676/depot//src/hunspell/hashmgr.cxx 2023-06-20 12:47:53.000000000 -0400
++++ /google/src/files/542907937/depot//src/hunspell/hashmgr.cxx 2023-06-23 14:09:48.000000000 -0400
+@@ -34,7 +34,11 @@
+ numaliasm = 0;
+ aliasm = NULL;
+ forbiddenword = FORBIDDENWORD; // forbidden word signing flag
+- load_config(apath, key);
++ if (load_config(apath, key)) {
++ // Early return if load config fails.
++ return;
++ }
++
+ int ec = load_tables(tpath, key);
+ if (ec) {
+ /* error condition - what should we do here */
+@@ -840,7 +844,7 @@
+ case 0: { np++; break; }
+ case 1: {
+ numaliasm = atoi(piece);
+- if (numaliasm < 1) {
++ if (numaliasm < 1 || numaliasm > kMaxNumAliasm) {
+ free_aliasm();
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+==== //depot//src/hunspell/hashmgr.hxx#3 - /google/src/files/542907937/depot//src/hunspell/hashmgr.hxx ====
+--- /google/src/files/541956676/depot//src/hunspell/hashmgr.hxx 2023-06-20 12:47:53.000000000 -0400
++++ /google/src/files/542907937/depot//src/hunspell/hashmgr.hxx 2023-06-23 14:09:48.000000000 -0400
+@@ -53,6 +53,8 @@
+ char * get_aliasm(int index);
+
+ private:
++ static constexpr int kMaxNumAliasm = 1 << 16; // 65536
++
+ int get_clen_and_captype(const char * word, int wbl, int * captype);
+ int load_tables(const char * tpath, const char * key);
+ int add_word(const char * word, int wbl, int wcl, unsigned short * ap,
diff --git a/patches/28.get_xml_list_leak.patch b/patches/28.get_xml_list_leak.patch
new file mode 100644
index 0000000..760f734
--- /dev/null
+++ b/patches/28.get_xml_list_leak.patch
@@ -0,0 +1,93 @@
+Change 550677556 by sungyc@sungyc:fig-export-icing-153-change-391:5978:citc on 2023/07/24 14:40:58
+
+ [hunspell] Fix get_xml_list memory leak
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5174280127905792' > /tmp/testcase-5174280127905792 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-5174280127905792 \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ It is a special case that causes memory leak. See code comments for explanation.
+
+ PRESUBMIT=passed
+ BUG=280374372
+ R=mghiware
+ CC=adorokhine
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=35 (30 added, 0 deleted, 5 changed)
+ DELTA_BY_EXTENSION=cxx=35
+ OCL=550638291
+ FIG_CHANGESET=fd93b8de505b51c42e24a7a4711af801f1827adc
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hunspell.cxx#8 edit
+
+==== //depot//src/hunspell/hunspell.cxx#7 - /google/src/files/550677556/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/205133714/depot//src/hunspell/hunspell.cxx 2018-07-18 17:06:49.000000000 -0400
++++ /google/src/files/550677556/depot//src/hunspell/hunspell.cxx 2023-07-24 17:40:58.000000000 -0400
+@@ -1709,22 +1709,52 @@
+ }
+
+ int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) {
+- int n = 0;
++ // - Allocates char* array for *slst with length = (# of tags in list).
++ // - Returns n where n = (# of successfully allocated and parsed xml char
++ // arrays).
++ // - Only (*slst)[0] to (*slst)[n - 1] have valid parsed xml char arrays and
++ // (*slst)[n] to (*slst)[slst_len - 1] are NULL.
++ *slst = NULL;
++
++ int slst_len = 0;
+ char * p;
+ if (!list) return 0;
+- for (p = list; (p = strstr(p, tag)); p++) n++;
+- if (n == 0) return 0;
+- *slst = (char **) malloc(sizeof(char *) * n);
++ for (p = list; (p = strstr(p, tag)); p++) slst_len++;
++ if (slst_len == 0) return 0;
++ *slst = (char **) malloc(sizeof(char *) * slst_len);
+ if (!*slst) return 0;
++ memset(*slst, 0, sizeof(char *) * slst_len);
++
++ int n;
+ for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) {
+ int l = strlen(p);
+ (*slst)[n] = (char *) malloc(l + 1);
+- if (!(*slst)[n]) return n;
++ if (!(*slst)[n]) {
++ break;
++ }
+ if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) {
+ free((*slst)[n]);
++ (*slst)[n] = NULL;
+ break;
+ }
+ }
++ // Returns n here so the caller will only use (*slst)[0] to (*slst)[n - 1].
++ // But we have to handle a special case when n == 0 and slst_len > 0:
++ // - The caller eventually calls the util method `freelist()` with the
++ // returned n, to free the inner char arrays from 0 to n - 1 and the
++ // entire char* array.
++ // - However, `freelist()` will not free anything when n is 0 and cause
++ // memory leak for outer char* array (i.e. *slst).
++ // - We could have returned slst_len instead to make `freelist()` work, but
++ // it changes the original logic because the caller may consequently
++ // access (*slst)[n] to (*slst)[slst_len - 1] incorrectly.
++ // - Also when n == 0, it means there is no inner parsed xml char array for
++ // caller to use, so it is correct to make *slst NULL.
++ // - Therefore, let's free *slst properly here.
++ if (n == 0) {
++ free(*slst);
++ *slst = NULL;
++ }
+ return n;
+ }
+
diff --git a/patches/29.parse_cpdsyllable_leak.patch b/patches/29.parse_cpdsyllable_leak.patch
new file mode 100644
index 0000000..416fce2
--- /dev/null
+++ b/patches/29.parse_cpdsyllable_leak.patch
@@ -0,0 +1,52 @@
+Change 553210266 by sungyc@sungyc:fig-export-icing-153-change-392:5984:citc on 2023/08/02 11:42:00
+
+ [hunspell] Fix memory leak for AffixMgr::parse_cpdsyllable
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=4652897513897984' > /tmp/testcase-4652897513897984 && \
+ blaze --blazerc=/dev/null test --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-rss_limit_mb=2560 --test_arg=-timeout=90 --test_arg=-runs=100 --test_arg=/tmp/testcase-4652897513897984 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ Some weird inputs will cause `parse_cpdsyllable` being called more than once, and the previous allocated `cpdvowels`/`cpdvowels_utf16` won't be freed correctly.
+
+ This CL adds check: if they're not null, then free the memory before taking any actions.
+
+ PRESUBMIT=passed
+ BUG=288966266
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=9 (9 added, 0 deleted, 0 changed)
+ DELTA_BY_EXTENSION=cxx=9
+ OCL=550684629
+ FIG_CHANGESET=e46312c43117a1d181a34248d38a05c3f87b55ce
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#14 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#13 - /google/src/files/553210266/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/542666043/depot//src/hunspell/affixmgr.cxx 2023-06-22 17:19:55.000000000 -0400
++++ /google/src/files/553210266/depot//src/hunspell/affixmgr.cxx 2023-08-02 14:42:00.000000000 -0400
+@@ -3519,6 +3519,15 @@
+ /* parse in the max syllablecount of compound words and */
+ int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
+ {
++ if (cpdvowels) {
++ free(cpdvowels);
++ cpdvowels = NULL;
++ }
++ if (cpdvowels_utf16) {
++ free(cpdvowels_utf16);
++ cpdvowels_utf16 = NULL;
++ cpdvowels_utf16_len = 0;
++ }
+ char * tp = line;
+ char * piece;
+ int i = 0;
diff --git a/patches/30.non_printables.patch b/patches/30.non_printables.patch
new file mode 100644
index 0000000..831d4ad
--- /dev/null
+++ b/patches/30.non_printables.patch
@@ -0,0 +1,40 @@
+Change 560733739 by alexsav@alexsav:fig-export-hunspell-fix-2-1266-change-1:1270:citc on 2023/08/28 09:31:02
+
+ Checks for non-print characters before xml attributes
+
+ PRESUBMIT=passed
+ BUG=297514503
+ FIXED=297514503
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=2 (1 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=2
+ OCL=560162596
+ FIG_CHANGESET=87faae99d65cea0369b2cff4ec0cfb6f77e44c70
+ FIG_WORKSPACE=alexsav/1266:hunspell-fix-2
+
+Affected files ...
+
+... //depot//src/hunspell/hunspell.cxx#9 edit
+
+==== //depot//src/hunspell/hunspell.cxx#8 - /google/src/files/560733739/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/550677556/depot//src/hunspell/hunspell.cxx 2023-07-24 17:40:58.000000000 -0400
++++ /google/src/files/560733739/depot//src/hunspell/hunspell.cxx 2023-08-28 12:31:02.000000000 -0400
+@@ -4,6 +4,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ #include <stdio.h>
++#include <cctype>
+ #include <vector>
+
+ #include "hunspell.hxx"
+@@ -1697,7 +1698,7 @@
+ do {
+ p = strstr(p, attr);
+ if (!p || p >= end) return 0;
+- } while (*(p-1) != ' ' && *(p-1) != '\n');
++ } while (*(p-1) != ' ' && *(p-1) != '\n' && isprint(*(p-1)));
+ return p + strlen(attr);
+ }
+
diff --git a/patches/31.overflow.patch b/patches/31.overflow.patch
new file mode 100644
index 0000000..90ed084
--- /dev/null
+++ b/patches/31.overflow.patch
@@ -0,0 +1,62 @@
+Change 567444076 by sungyc@sungyc:fig-export-icing-153-change-421:6204:citc on 2023/09/21 15:58:36
+
+ [hunspell][vulnerability] Fix buffer overflow vulnerability
+
+ ## Test Plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=4833834816307200' > /tmp/testcase-4833834816307200 && \
+ blaze --blazerc=/dev/null test -c opt --config=msan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-rss_limit_mb=2560 --test_arg=-timeout=90 --test_arg=-runs=100 --test_arg=/tmp/testcase-4833834816307200 \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ This security bug is caused by inconsistent length after converting back and forth between utf8 and utf16. The length of `st` may be greater than the original `wbl`, and it causes `add_word` allocating insufficient buffer to store `st`.
+
+ Recalculating the length of the C string after conversion will fix the issue.
+
+ PRESUBMIT=passed
+ BUG=293794756
+ R=tjbarron
+ CC=mghiware
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=11 (9 added, 0 deleted, 2 changed)
+ DELTA_BY_EXTENSION=cxx=11
+ OCL=567441268
+ FIG_CHANGESET=004a2f7bb331b9564a411dec60145f5f92379061
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#12 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#11 - /google/src/files/567444076/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/542907937/depot//src/hunspell/hashmgr.cxx 2023-06-23 14:09:48.000000000 -0400
++++ /google/src/files/567444076/depot//src/hunspell/hashmgr.cxx 2023-09-21 18:58:36.000000000 -0400
+@@ -223,13 +223,22 @@
+ if (al) memcpy(flags2, flags, al * sizeof(unsigned short));
+ flags2[al] = ONLYUPCASEFLAG;
+ if (utf8) {
+- char st[MAXWORDUTF8LEN];
++ // reserve one additional space for '\0', due to u16_u8() may set
++ // '\0' at position = MAXWORDUTF8LEN.
++ char st[MAXWORDUTF8LEN + 1];
+ w_char w[MAXWORDLEN];
+ int wlen = u8_u16(w, MAXWORDLEN, word);
+ mkallsmall_utf(w, wlen, langnum);
+ mkallcap_utf(w, 1, langnum);
+ u16_u8(st, MAXWORDUTF8LEN, w, wlen);
+- return add_word(st,wbl,wcl,flags2,al+1,dp, true);
++
++ // The length may be different after converting back and forth
++ // between utf8 and utf16, so we have to recalculate the length of
++ // st.
++ int st_captype;
++ int st_wbl = strlen(st);
++ int st_wcl = get_clen_and_captype(st, st_wbl, &st_captype);
++ return add_word(st,st_wbl,st_wcl,flags2,al+1,dp, true);
+ } else {
+ mkallsmall(word, csconv);
+ mkinitcap(word, csconv);
diff --git a/patches/32.u16_u8_xff.patch b/patches/32.u16_u8_xff.patch
new file mode 100644
index 0000000..8a743d0
--- /dev/null
+++ b/patches/32.u16_u8_xff.patch
@@ -0,0 +1,39 @@
+Change 579272908 by alexsav@alexsav:fig-export-clean-1452-change-1:1454:citc on 2023/11/03 12:42:34
+
+ Adds check for u16_u8 xFF
+
+ PRESUBMIT=passed
+ BUG=308240685
+ FIXED=308240685
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=8 (7 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=8
+ OCL=578306755
+ FIG_CHANGESET=ea4fd90ff399df9c012782dcd2c45439c6e9fcaf
+ FIG_WORKSPACE=alexsav/1452:clean
+
+Affected files ...
+
+... //depot//src/hunspell/csutil.cxx#4 edit
+
+==== //depot//src/hunspell/csutil.cxx#3 - /google/src/files/579272908/depot//src/hunspell/csutil.cxx ====
+--- /google/src/files/537916729/depot//src/hunspell/csutil.cxx 2023-06-05 13:32:42.000000000 -0400
++++ /google/src/files/579272908/depot//src/hunspell/csutil.cxx 2023-11-03 15:42:34.000000000 -0400
+@@ -48,7 +48,14 @@
+ while ((u2 < u2_max) && (u8 < u8_max)) {
+ if (u2->h) { // > 0xFF
+ // XXX 4-byte haven't implemented yet.
+- if (u2->h >= 0x08) { // >= 0x800 (3-byte UTF-8 character)
++ if (u2->h == 0xFF) {
++ // Error case. Handling this with the below else case will insert
++ // \U0000FFFD, shifting the string and pushing it into other
++ // memory locations, which might be later read as garbage and
++ // cause memory errors.
++ *u8 = 0xFF;
++ u8++;
++ } else if (u2->h >= 0x08) { // >= 0x800 (3-byte UTF-8 character)
+ *u8 = 0xe0 + (u2->h >> 4);
+ u8++;
+ if (u8 < u8_max) {
diff --git a/patches/33.compund_check.patch b/patches/33.compund_check.patch
new file mode 100644
index 0000000..4bc7f62
--- /dev/null
+++ b/patches/33.compund_check.patch
@@ -0,0 +1,118 @@
+Change 583480615 by sungyc@sungyc:fig-export-icing-153-change-459:7032:citc on 2023/11/17 13:59:58
+
+ [hunspell][vulnerability fix] Fix heap buffer overflow in AffixMgr::compound_check
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5877288434466816' > /tmp/testcase-5877288434466816 && \
+ blaze --blazerc=/dev/null test -c opt --config=asan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-5877288434466816 \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ - Variable `i`, `cmin`, `cmax` are changed in every iteration.
+ - Originally buffer `word` has length `len`.
+ - It is possible that `i >= len` and makes all `word + i` or `word[i]` invalid. This is the heap buffer overflow reason.
+ - So we must check if `i < len` before using `word + i` (or any possibility that accesses `word + i` or later.
+ - Also `len` is changed at L1604, so we should copy it out (as `word_len`) in the beginning of the function.
+
+ Also double check the [latest hunspell AffixMgr](https://github.com/hunspell/hunspell/blob/master/src/hunspell/affixmgr.cxx#L1570), verifying this change is similar to the current solution.
+
+ PRESUBMIT=passed
+ BUG=298635289
+ R=adorokhine,mghiware
+ APPROVED=adorokhine,mghiware
+ REQUIRED_REVIEW=1
+ DELTA=18 (10 added, 0 deleted, 8 changed)
+ DELTA_BY_EXTENSION=cxx=18
+ OCL=582895680
+ FIG_CHANGESET=4ab7f2818e7d826b55a4701f0d97153534ff41fd
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#15 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#14 - /google/src/files/583480615/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/553210266/depot//src/hunspell/affixmgr.cxx 2023-08-02 14:42:00.000000000 -0400
++++ /google/src/files/583480615/depot//src/hunspell/affixmgr.cxx 2023-11-17 16:59:58.000000000 -0500
+@@ -1527,6 +1527,10 @@
+ short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
+ char hu_mov_rule = 0, char is_sug = 0, int * info = NULL)
+ {
++ // Since len will be modified later, let's copy it out to preserve the
++ // word's len info.
++ int word_len = len;
++
+ int i;
+ short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
+ struct hentry * rv = NULL;
+@@ -1586,7 +1590,7 @@
+ do { // simplified checkcompoundpattern loop
+
+ if (scpd > 0) {
+- for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
++ for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 || i > word_len ||
+ strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
+
+ if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
+@@ -1865,15 +1869,19 @@
+ // perhaps second word has prefix or/and suffix
+ sfx = NULL;
+ sfxflag = FLAG_NULL;
+- rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
++ rv = (compoundflag && !onlycpdrule && i < word_len) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
+ if (!rv && compoundend && !onlycpdrule) {
+ sfx = NULL;
+ pfx = NULL;
++ if (i < word_len) {
+ rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
+ }
++ }
+
+ if (!rv && numdefcpd && words) {
++ if (i < word_len) {
+ rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
++ }
+ if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
+ free(rwords);
+ return rv_first;
+@@ -1886,7 +1894,7 @@
+ TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
+
+ // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
+- if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
++ if (rv && numcheckcpd && scpd == 0 && i < word_len && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
+
+ // check non_compound flag in suffix and prefix
+ if ((rv) &&
+@@ -1918,7 +1926,9 @@
+
+ if (langnum == LANG_hu) {
+ // calculate syllable number of the word
++ if (i < word_len) {
+ numsyllable += get_syllable(word + i, strlen(word + i));
++ }
+
+ // - affix syllable num.
+ // XXX only second suffix (inflections, not derivations)
+@@ -1979,7 +1989,7 @@
+ rv = compound_check((st+i),strlen(st+i), wordnum+1,
+ numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
+
+- if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
++ if (rv && numcheckcpd && i < word_len && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
+ (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
+ } else {
+ rv=NULL;
+@@ -1995,7 +2005,7 @@
+ }
+
+ // check first part
+- if (strncmp(rv->word, word + i, rv->blen) == 0) {
++ if (i < word_len && strncmp(rv->word, word + i, rv->blen) == 0) {
+ char r = *(st + i + rv->blen);
+ *(st + i + rv->blen) = '\0';
+
diff --git a/patches/34.commoncharacterpositions.patch b/patches/34.commoncharacterpositions.patch
new file mode 100644
index 0000000..b5cdd6d
--- /dev/null
+++ b/patches/34.commoncharacterpositions.patch
@@ -0,0 +1,48 @@
+Change 583481110 by sungyc@sungyc:fig-export-icing-153-change-460:7042:citc on 2023/11/17 14:01:57
+
+ [hunspell][vulnerability fix] Fix uninitialize error in SuggestMgr::commoncharacterpositions
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=6294633356263424' > /tmp/testcase-6294633356263424 && \
+ blaze --blazerc=/dev/null test --config=msan-fuzzer --test_strategy=local --test_sharding_strategy=disabled \
+ --test_env=ENABLE_BLAZE_TEST_FUZZING=1 --test_arg=-runs=100 --test_arg=/tmp/testcase-6294633356263424 \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ - Some uninitialized elements in `su2` will be used in `mkallsmall_utf` because we never check the return value of `l2`.
+ - If `l2 <= 0`, then we shouldn't proceed `mkallsmall_utf` with `su2`.
+ - Also double check the [latest hunspell SuggestMgr](https://github.com/hunspell/hunspell/blob/master/src/hunspell/suggestmgr.cxx#L2166-L2167). They also check `l1`, so let's do both.
+
+ PRESUBMIT=passed
+ BUG=309591156
+ R=adorokhine,mghiware
+ APPROVED=adorokhine,mghiware
+ REQUIRED_REVIEW=1
+ DELTA=5 (5 added, 0 deleted, 0 changed)
+ DELTA_BY_EXTENSION=cxx=5
+ OCL=583189793
+ FIG_CHANGESET=cbaaf79fbccd41c4cc9986332b3446a7d8896b41
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/suggestmgr.cxx#6 edit
+
+==== //depot//src/hunspell/suggestmgr.cxx#5 - /google/src/files/583481110/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/205077565/depot//src/hunspell/suggestmgr.cxx 2018-07-18 10:48:28.000000000 -0400
++++ /google/src/files/583481110/depot//src/hunspell/suggestmgr.cxx 2023-11-17 17:01:57.000000000 -0500
+@@ -1868,6 +1868,11 @@
+ w_char su2[MAXSWL];
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
++
++ if (l1 <= 0 || l2 <= 0) {
++ return 0;
++ }
++
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ mkallsmall_utf(su2+l2-1, 1, langnum);
diff --git a/patches/35.index_boundary.patch b/patches/35.index_boundary.patch
new file mode 100644
index 0000000..75fbf90
--- /dev/null
+++ b/patches/35.index_boundary.patch
@@ -0,0 +1,52 @@
+Change 613323179 by sungyc@sungyc:fig-export-icing-153-change-472:7338:citc on 2024/03/06 13:29:53
+
+ [hunspell][vulnerability fix] Add index boundary check to avoid buffer overflow or use-of-uninitialized-value error
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5674120459649024' > /tmp/testcase-5674120459649024 && \
+ blaze --blazerc=/dev/null test --config=fuzztest-msan \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5674120459649024 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ When setting `cmin` and `cmax`, it is possible that they go out of bound (not in range `[0, len - 1]`), which may potentially cause buffer overflow error, or using uninitialized value of `word[*cmin]`/`word[*cmax]`.
+
+ PRESUBMIT=passed
+ BUG=326368180
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=4 (0 added, 0 deleted, 4 changed)
+ DELTA_BY_EXTENSION=cxx=4
+ OCL=612513824
+ FIG_CHANGESET=318a0cff97e309dceb0197b75cce97b385d4a968
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#16 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#15 - /google/src/files/613323179/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/583480615/depot//src/hunspell/affixmgr.cxx 2023-11-17 16:59:58.000000000 -0500
++++ /google/src/files/613323179/depot//src/hunspell/affixmgr.cxx 2024-03-06 16:29:53.000000000 -0500
+@@ -1508,11 +1508,11 @@
+ void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
+ if (utf8) {
+ int i;
+- for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
+- for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
++ for (*cmin = 0, i = 0; (i < cpdmin) && *cmin < len && word[*cmin]; i++) {
++ for ((*cmin)++; *cmin < len && (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
+ }
+- for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
+- for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
++ for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax > 0; i++) {
++ for ((*cmax)--; *cmax > 0 && (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
+ }
+ } else {
+ *cmin = cpdmin;
diff --git a/patches/36.calloc.patch b/patches/36.calloc.patch
new file mode 100644
index 0000000..52064b0
--- /dev/null
+++ b/patches/36.calloc.patch
@@ -0,0 +1,50 @@
+Change 613323525 by sungyc@sungyc:fig-export-icing-153-change-473:7348:citc on 2024/03/06 13:30:53
+
+ [hunspell][vulnerability fix] Use calloc to initialize all allocated bytes to 0
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5038681210028032' > /tmp/testcase-5038681210028032 && \
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5038681210028032 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ - The element in `defcpdtable` contains a pointer to another `malloc` buffer.
+ - `defcpdtable` is not initialized (or partially initialized) after `malloc`. The pointer may still contain an invalid non-null value, so `free_defcpdtable` calls `free` with an invalid address and causes error.
+
+ By switching to `calloc`, all bytes are initialized to 0, so the pointer is initialized as `NULL`. Then later `free_defcpdtable` will skip NULL pointer freeing correctly.
+
+ PRESUBMIT=passed
+ BUG=325538748
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=612537516
+ DIFFBASE=612513824
+ FIG_CHANGESET=0f4c956db8114c22bca3683d2087fd101d3ddc8f
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#17 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#16 - /google/src/files/613323525/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/613323179/depot//src/hunspell/affixmgr.cxx 2024-03-06 16:29:53.000000000 -0500
++++ /google/src/files/613323525/depot//src/hunspell/affixmgr.cxx 2024-03-06 16:30:53.000000000 -0500
+@@ -3965,7 +3965,7 @@
+ HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
+ return 1;
+ }
+- defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
++ defcpdtable = (flagentry *) calloc(numdefcpd, sizeof(flagentry));
+ if (!defcpdtable) {
+ free_defcpdtable();
+ return 1;
diff --git a/patches/37.double_free.patch b/patches/37.double_free.patch
new file mode 100644
index 0000000..3b7292c
--- /dev/null
+++ b/patches/37.double_free.patch
@@ -0,0 +1,49 @@
+Change 614065610 by sungyc@sungyc:fig-export-icing-153-change-474:7360:citc on 2024/03/08 15:43:37
+
+ [hunspell][vulnerability fix] Set buffer to NULL to avoid double free
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5492326057705472' > /tmp/testcase-5492326057705472 && \
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5492326057705472 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ It is possible that `wlst[i]` is allocated from another place (`*slst`). When freeing `wlst[i]`, we have to set the value to NULL, otherwise the original `*slst` will be freed again.
+
+ PRESUBMIT=passed
+ BUG=325540032
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=4 (3 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=4
+ OCL=613985492
+ FIG_CHANGESET=e5882c8089a4dacab8b1ef3e281163b25362f1b9
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/suggestmgr.cxx#7 edit
+
+==== //depot//src/hunspell/suggestmgr.cxx#6 - /google/src/files/614065610/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/583481110/depot//src/hunspell/suggestmgr.cxx 2023-11-17 17:01:57.000000000 -0500
++++ /google/src/files/614065610/depot//src/hunspell/suggestmgr.cxx 2024-03-08 18:43:37.000000000 -0500
+@@ -159,7 +159,10 @@
+ // Added to handle wrong encoded UTF-8 strings - jiho@google.com
+ if (wl <= 0) {
+ for (int i = 0; i < maxSug; i++) {
+- if (wlst[i]) free(wlst[i]);
++ if (wlst[i]) {
++ free(wlst[i]);
++ wlst[i] = NULL;
++ }
+ }
+ if (!*slst) {
+ free(wlst);
diff --git a/patches/38.trivial_comment_fixes.patch b/patches/38.trivial_comment_fixes.patch
new file mode 100644
index 0000000..c5661fb
--- /dev/null
+++ b/patches/38.trivial_comment_fixes.patch
@@ -0,0 +1,137 @@
+Change 615029530 by aprotopa@aprotopa:sardine:1943:citc on 2024/03/12 07:13:27
+
+ Mirror Hunspell source to a public read-only repo on Google third-party-mirror.
+
+ Google Ads Editor is a native app that ships with the Hunspell library.
+ Following best practice for complying with the project's reciprocal license, we
+ will mirror the source including Google customizations/bug fixes to the public
+ mirror to be located at https://third-party-mirror.googlesource.com/hunspell
+
+ This excludes the spellcheck dictionaries which are licensed separately. Editor
+ uses the dictionaries from the Chromium repo so they aren't relevant here.
+
+ Also some trivial changes so that comments comply with formatting expections
+ of the rule which scrubs employee LDAPs from the source.
+
+ Validation:
+ copybara validate copy.bara.sky
+
+ Dry run:
+ copybara copy.bara.sky presubmit_piper_to_git <patch CL#> \
+ --init-history \
+ --squash \
+ --ignore-noop \
+ --force \
+ --git-destination-path <path to dump local git repo> \
+ --dry-run
+
+ PRESUBMIT=passed
+ BUG=307550303
+ R=itandetnik,mghiware,tjbarron
+ CC=ape-eng,bkuang
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=47 (38 added, 0 deleted, 9 changed)
+ DELTA_BY_EXTENSION=cxx=7,hxx=2,sky=38
+ OCL=609044675
+
+Affected files ...
+
+... //depot//copy.bara.sky#1 add
+... //depot//src/hunspell/affixmgr.cxx#18 edit
+... //depot//src/hunspell/atypes.hxx#3 edit
+... //depot//src/hunspell/suggestmgr.cxx#8 edit
+... //depot//src/parsers/textparser.cxx#3 edit
+... //depot//src/parsers/textparser.hxx#3 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#17 - /google/src/files/615029530/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/613323525/depot//src/hunspell/affixmgr.cxx 2024-03-06 16:30:53.000000000 -0500
++++ /google/src/files/615029530/depot//src/hunspell/affixmgr.cxx 2024-03-12 10:13:27.000000000 -0400
+@@ -1359,7 +1359,7 @@
+ // check compound patterns
+ int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
+ {
+- // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // NOTE: Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+ std::unique_ptr<signed short[]> scoped_btpp(new signed short[MAXWORDLEN]);
+ signed short * btpp = scoped_btpp.get(); // metacharacter (*, ?) positions for backtracking
+@@ -1543,7 +1543,7 @@
+ struct hentry ** rwords = // buffer for COMPOUND pattern checking
+ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
+
+- // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // NOTE: Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+ std::unique_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+@@ -2081,10 +2081,10 @@
+
+ struct hentry * rv = NULL;
+ struct hentry * rv_first;
+- // Note(jiho@google.com): Changed to use malloc to avoid stack overflow.
++ // NOTE: Changed to use malloc to avoid stack overflow.
+ struct hentry ** rwords = // buffer for COMPOUND pattern checking
+ (struct hentry**) malloc(sizeof(struct hentry*) * (MAXWORDLEN));
+- // Note(jiho@google.com): Changed to use scoped_ptr to avoid 64KB stack size
++ // NOTE: Changed to use scoped_ptr to avoid 64KB stack size
+ // problem.
+ std::unique_ptr<char[]> scoped_st(new char[MAXWORDUTF8LEN + 4]);
+ char * st = scoped_st.get();
+==== //depot//src/hunspell/atypes.hxx#2 - /google/src/files/615029530/depot//src/hunspell/atypes.hxx ====
+--- /google/src/files/49864191/depot//src/hunspell/atypes.hxx 2013-07-24 23:12:12.000000000 -0400
++++ /google/src/files/615029530/depot//src/hunspell/atypes.hxx 2024-03-12 10:13:27.000000000 -0400
+@@ -20,7 +20,7 @@
+ #define SETSIZE 256
+ #define CONTSIZE 65536
+ #define MAXWORDLEN 100
+-// Note(jiho@google.com): Korean Hunspell dictionary doesn't use UTF-8 directly.
++// NOTE: Korean Hunspell dictionary doesn't use UTF-8 directly.
+ // It decomposes one single Korean character to three characters. So, one single
+ // UTF-8 Korean character(3 bytes) will eventually take 9 bytes. At least
+ // MAXWORDLEN * 3 bytes are required for handling Korean dictionary.
+==== //depot//src/hunspell/suggestmgr.cxx#7 - /google/src/files/615029530/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/614065610/depot//src/hunspell/suggestmgr.cxx 2024-03-08 18:43:37.000000000 -0500
++++ /google/src/files/615029530/depot//src/hunspell/suggestmgr.cxx 2024-03-12 10:13:27.000000000 -0400
+@@ -156,7 +156,7 @@
+
+ if (utf8) {
+ wl = u8_u16(word_utf, MAXSWL, word);
+- // Added to handle wrong encoded UTF-8 strings - jiho@google.com
++ // NOTE: Added to handle wrong encoded UTF-8 strings
+ if (wl <= 0) {
+ for (int i = 0; i < maxSug; i++) {
+ if (wlst[i]) {
+==== //depot//src/parsers/textparser.cxx#2 - /google/src/files/615029530/depot//src/parsers/textparser.cxx ====
+--- /google/src/files/50722709/depot//src/parsers/textparser.cxx 2013-08-12 14:06:20.000000000 -0400
++++ /google/src/files/615029530/depot//src/parsers/textparser.cxx 2024-03-12 10:13:27.000000000 -0400
+@@ -57,7 +57,7 @@
+
+ TextParser::TextParser(unsigned short * wordchars, int len, int text_len)
+ {
+- // Note(junyangl@google.com): Dynamically allocate space according to text_len
++ // NOTE: Dynamically allocate space according to text_len
+ // to avoid segfault for too long input.
+ for (int i = 0; i < MAXPREVLINE; i++) {
+ line[i] = (char*)malloc(text_len + 1);
+@@ -69,7 +69,7 @@
+
+ TextParser::~TextParser()
+ {
+- // Note(junyangl@google.com): Free dynamically allocated space.
++ // NOTE: Free dynamically allocated space.
+ for (int i = 0; i < MAXPREVLINE; i++) {
+ free(line[i]);
+ }
+==== //depot//src/parsers/textparser.hxx#2 - /google/src/files/615029530/depot//src/parsers/textparser.hxx ====
+--- /google/src/files/50722709/depot//src/parsers/textparser.hxx 2013-08-12 14:06:20.000000000 -0400
++++ /google/src/files/615029530/depot//src/parsers/textparser.hxx 2024-03-12 10:13:27.000000000 -0400
+@@ -29,7 +29,7 @@
+ void init(const char *);
+ void init(unsigned short * wordchars, int len);
+ int wordcharacters[256]; // for detection of the word boundaries
+- // Note(junyangl@google.com): Dynamically allocate space for line and urlline
++ // NOTE: Dynamically allocate space for line and urlline
+ // according to input length, to avoid segfault when the input is too long.
+ char * line[MAXPREVLINE]; // parsed and previous lines
+ char * urlline; // mask for url detection
diff --git a/patches/39.free_uninit_ptr.patch b/patches/39.free_uninit_ptr.patch
new file mode 100644
index 0000000..d42569b
--- /dev/null
+++ b/patches/39.free_uninit_ptr.patch
@@ -0,0 +1,77 @@
+Change 621067117 by sungyc@sungyc:fig-export-icing-153-change-479:7398:citc on 2024/04/01 23:29:44
+
+ [hunspell][vulnerability fix] Fix memory freeing on uninitialized address value
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5472705942454272' > /tmp/testcase-5472705942454272 && \
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest-msan --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5472705942454272 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ This bug is "use-of-uninitialized-value", caused by using (freeing) uninitialized address in `reptable[j].pattern`.
+ - Use calloc for `reptable` to initialize all members to 0, so `reptable[j].pattern` will have NULL value.
+ - Add NULL check before `free()`.
+ - Change `reptable[j].pattern` to NULL after `free()`, to avoid double free error (except for destructor).
+
+ PRESUBMIT=passed
+ BUG=325665488
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=11 (8 added, 0 deleted, 3 changed)
+ DELTA_BY_EXTENSION=cxx=11
+ OCL=621036222
+ FIG_CHANGESET=ed6bff931bde83fdddfff0d9a7e060af03209ee2
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#19 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#18 - /google/src/files/621067117/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/615029530/depot//src/hunspell/affixmgr.cxx 2024-03-12 10:13:27.000000000 -0400
++++ /google/src/files/621067117/depot//src/hunspell/affixmgr.cxx 2024-04-02 02:29:44.000000000 -0400
+@@ -179,9 +179,13 @@
+ numbreak = 0;
+ if (reptable) {
+ for (int j=0; j < numrep; j++) {
++ if (reptable[j].pattern) {
+ free(reptable[j].pattern);
++ }
++ if (reptable[j].pattern2) {
+ free(reptable[j].pattern2);
+ }
++ }
+ free(reptable);
+ reptable = NULL;
+ }
+@@ -3601,7 +3605,7 @@
+ HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
+ return 1;
+ }
+- reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
++ reptable = (replentry *) calloc(numrep, sizeof(replentry));
+ if (!reptable) return 1;
+ np++;
+ break;
+@@ -3661,9 +3665,13 @@
+ for (int k=0; k < j; k++) {
+ free(reptable[k].pattern);
+ free(reptable[k].pattern2);
++ reptable[k].pattern = NULL;
++ reptable[k].pattern2 = NULL;
+ }
+ if (reptable[j].pattern) free(reptable[j].pattern);
+ if (reptable[j].pattern2) free(reptable[j].pattern2);
++ reptable[j].pattern = NULL;
++ reptable[j].pattern2 = NULL;
+ numrep = 0;
+ return 1;
+ }
diff --git a/patches/40.oob_read.patch b/patches/40.oob_read.patch
new file mode 100644
index 0000000..8876e60
--- /dev/null
+++ b/patches/40.oob_read.patch
@@ -0,0 +1,51 @@
+Change 623201281 by sungyc@sungyc:fig-export-icing-153-change-480:7422:citc on 2024/04/09 10:03:04
+
+ [hunspell][vulnerability fix] Fix out-of-bound memory access
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=4514935680335872' > /tmp/testcase-4514935680335872 && \
+ blaze --blazerc=/dev/null test --config=fuzztest-msan \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-4514935680335872 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ b/326456405 reports an "use-of-uninitialized-value" bug. After investigation, it is caused by `reverse_condition()`.
+ - When doing `*(k+1) = *k`, if it is the first round, then `*(k+1)` is actually `'\0'` character in the end of the string, and it is overwritten by `*k`.
+ - Since `'\0'` is falsely overwritten, the next part of code using this char array will iterate over the original `'\0'` and potentially use some uninitialized value(s) in the rest part of the char array.
+
+ To fix this, we can just simply add a condition to make sure the `*(k+1)` setter won't be executed in the first round. This fix is also available in the latest version of open source Hunspell ([link](https://github.com/hunspell/hunspell/blob/master/src/hunspell/affixmgr.cxx#L4388)).
+
+ PRESUBMIT=passed
+ BUG=326456405
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=3 (1 added, 0 deleted, 2 changed)
+ DELTA_BY_EXTENSION=cxx=3
+ OCL=621820692
+ FIG_CHANGESET=736c3d9eb7b5d829c4e05a7055da6987f4f2065e
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#20 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#19 - /google/src/files/623201281/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/621067117/depot//src/hunspell/affixmgr.cxx 2024-04-02 02:29:44.000000000 -0400
++++ /google/src/files/623201281/depot//src/hunspell/affixmgr.cxx 2024-04-09 13:03:04.000000000 -0400
+@@ -4253,7 +4253,8 @@
+ break;
+ }
+ case '^': {
+- if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
++ if (*(k+1) == ']') neg = 1;
++ else if (neg) *(k+1) = *k;
+ break;
+ }
+ default: {
diff --git a/patches/41.heap_overflow.patch b/patches/41.heap_overflow.patch
new file mode 100644
index 0000000..7a107e2
--- /dev/null
+++ b/patches/41.heap_overflow.patch
@@ -0,0 +1,46 @@
+Change 623201869 by sungyc@sungyc:fig-export-icing-153-change-481:7428:citc on 2024/04/09 10:04:20
+
+ [hunspell][vulnerability fix] Fix heap buffer overflow error
+
+ ## Test plan
+ ```
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-4897268770078720 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ Add check for `s2` to make sure `s2` doesn't go out of bound.
+
+ PRESUBMIT=passed
+ BUG=326385709
+ R=mghiware
+ APPROVED=mghiware
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=621828139
+ DIFFBASE=621820692
+ FIG_CHANGESET=5c6280f36dbdf8e1365a5a5b3111be309251be25
+ FIG_WORKSPACE=sungyc/153:icing
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affixmgr.cxx#21 edit
+
+==== //depot//src/hunspell/affixmgr.cxx#20 - /google/src/files/623201869/depot//src/hunspell/affixmgr.cxx ====
+--- /google/src/files/623201281/depot//src/hunspell/affixmgr.cxx 2024-04-09 13:03:04.000000000 -0400
++++ /google/src/files/623201869/depot//src/hunspell/affixmgr.cxx 2024-04-09 13:04:20.000000000 -0400
+@@ -1088,7 +1088,7 @@
+ // return 1 if s1 is a leading subset of s2 (dots are for infixes)
+ inline int AffixMgr::isSubset(const char * s1, const char * s2)
+ {
+- while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
++ while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0') && (*s2 != '\0')) {
+ s1++;
+ s2++;
+ }
diff --git a/patches/42.index_oob.patch b/patches/42.index_oob.patch
new file mode 100644
index 0000000..443f3c5
--- /dev/null
+++ b/patches/42.index_oob.patch
@@ -0,0 +1,48 @@
+Change 638422913 by sungyc@sungyc:fig-export-hunspell-7504-change-1:7506:citc on 2024/05/29 14:58:51
+
+ [hunspell][vulnerability fix] Fix index out of bound error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=6366934184034304' > /tmp/testcase-6366934184034304 && \
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-6366934184034304 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ The error is caused by `unicw + wl2 + 1` when `wl2` is -1. In fact, this won't cause index error because it is +1 again, but the detector throws the error before +1.
+
+ Therefore, we add parentheses to make `(wl2+1)` evaluated first.
+
+ PRESUBMIT=passed
+ BUG=331844463
+ R=tjbarron
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=638394343
+ FIG_CHANGESET=99e275a304d53a94f8ec1902d4c753ca0ab282ee
+ FIG_WORKSPACE=sungyc/7504:hunspell
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hunspell.cxx#10 edit
+
+==== //depot//src/hunspell/hunspell.cxx#9 - /google/src/files/638422913/depot//src/hunspell/hunspell.cxx ====
+--- /google/src/files/560733739/depot//src/hunspell/hunspell.cxx 2023-08-28 12:31:02.000000000 -0400
++++ /google/src/files/638422913/depot//src/hunspell/hunspell.cxx 2024-05-29 17:58:51.000000000 -0400
+@@ -410,7 +410,7 @@
+ wl2 = u8_u16(tmpword, MAXWORDLEN, cw);
+ *apostrophe = '\'';
+ if (wl2 < nc) {
+- mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1);
++ mkinitcap2(apostrophe + 1, unicw + (wl2 + 1), nc - wl2 - 1);
+ rv = checkword(cw, info, root);
+ if (rv) break;
+ }
diff --git a/patches/43.oob_pointer.patch b/patches/43.oob_pointer.patch
new file mode 100644
index 0000000..92ce6ea
--- /dev/null
+++ b/patches/43.oob_pointer.patch
@@ -0,0 +1,51 @@
+Change 638449400 by sungyc@sungyc:fig-export-hunspell-7504-change-2:7510:citc on 2024/05/29 16:28:31
+
+ [hunspell][vulnerability fix] Fix pointer out of bound & use of uninitialized value error
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5101182575509504' > /tmp/testcase-5101182575509504 && \
+ blaze --blazerc=/dev/null test --config=fuzztest-msan \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5101182575509504 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:dict_fuzzer
+ ```
+
+ ## Description
+ - Normally `ap` points to a `'/'` character without escaping by `'\'`.
+ - We will use it as a separator, so assign `*ap = '\0'`.
+ - Handle the rest part of the string after this separating position, i.e. `ap + 1`.
+ - However, when `ap` points to `'\0'` (the end of `ts`), it means there is no remaining part, and `ap + 1` shouldn't be accessed.
+ - This vulnerability bug is caused by accessing `ap + 1` when `ap` is already pointing at `'\0'`, and therefore accidentally uses uninitialized part of a char array.
+ - So we just need to add an additional check for `*ap`.
+
+ PRESUBMIT=passed
+ BUG=332580178
+ R=tjbarron
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=638419480
+ FIG_CHANGESET=f465be914d7915ab0ad9635aa1fc79faed571048
+ FIG_WORKSPACE=sungyc/7504:hunspell
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/hashmgr.cxx#13 edit
+
+==== //depot//src/hunspell/hashmgr.cxx#12 - /google/src/files/638449400/depot//src/hunspell/hashmgr.cxx ====
+--- /google/src/files/567444076/depot//src/hunspell/hashmgr.cxx 2023-09-21 18:58:36.000000000 -0400
++++ /google/src/files/638449400/depot//src/hunspell/hashmgr.cxx 2024-05-29 19:28:31.000000000 -0400
+@@ -445,7 +445,7 @@
+ ap = strchr(ap,'/');
+ }
+
+- if (ap) {
++ if (ap && *ap != '\0') {
+ *ap = '\0';
+ if (aliasf) {
+ int index = atoi(ap + 1);
diff --git a/patches/44.init_char_array.patch b/patches/44.init_char_array.patch
new file mode 100644
index 0000000..bc3092e
--- /dev/null
+++ b/patches/44.init_char_array.patch
@@ -0,0 +1,43 @@
+Change 640269658 by sungyc@sungyc:fig-export-hunspell-7504-change-4:7520:citc on 2024/06/04 13:44:22
+
+ [hunspell][vulnerability fix] Initialize char array
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=5210985962471424' > /tmp/testcase-5210985962471424 && \
+ blaze --blazerc=/dev/null test -c opt --config=fuzztest-msan --copt=-DNDEBUG \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-5210985962471424 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ PRESUBMIT=passed
+ BUG=336674722
+ R=tjbarron
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=1 (0 added, 0 deleted, 1 changed)
+ DELTA_BY_EXTENSION=cxx=1
+ OCL=639945677
+ FIG_CHANGESET=97f4e76a9360b2e68df5e7668ad14270d543631a
+ FIG_WORKSPACE=sungyc/7504:hunspell
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/affentry.cxx#4 edit
+
+==== //depot//src/hunspell/affentry.cxx#3 - /google/src/files/640269658/depot//src/hunspell/affentry.cxx ====
+--- /google/src/files/171343973/depot//src/hunspell/affentry.cxx 2017-10-06 17:25:31.000000000 -0400
++++ /google/src/files/640269658/depot//src/hunspell/affentry.cxx 2024-06-04 16:44:22.000000000 -0400
+@@ -153,7 +153,7 @@
+ {
+ int tmpl; // length of tmpword
+ struct hentry * he; // hash entry of root word or NULL
+- char tmpword[MAXWORDUTF8LEN + 4];
++ char tmpword[MAXWORDUTF8LEN + 4] = {0};
+
+ // on entry prefix is 0 length or already matches the beginning of the word.
+ // So if the remaining root word has positive length
diff --git a/patches/45.lcs_string_length_fix.patch b/patches/45.lcs_string_length_fix.patch
new file mode 100644
index 0000000..f1f0946
--- /dev/null
+++ b/patches/45.lcs_string_length_fix.patch
@@ -0,0 +1,62 @@
+Change 640274267 by sungyc@sungyc:fig-export-hunspell-7504-change-3:7518:citc on 2024/06/04 13:58:38
+
+ [hunspell][vulnerability fix] Fix incorrect string length for lcs algorithm
+
+ ## Test plan
+ ```
+ sso_client -location 'https://clusterfuzz.corp.google.com/testcase-detail/download-testcase?id=6268223149899776' > /tmp/testcase-6268223149899776 && \
+ blaze --blazerc=/dev/null test --config=fuzztest-msan \
+ --test_strategy=local \
+ --test_sharding_strategy=disabled \
+ --test_env=FUZZTEST_REPLAY=/tmp/testcase-6268223149899776 \
+ --test_filter=LLVMFuzzer.TestOneInput \
+ //third_party/hunspell/fuzzers:suggestions_fuzzer
+ ```
+
+ ## Description
+ This bug is triggered when an error occurs in `u8_u16` and returns -1 for the length.
+ - We malloc `(m + 1) * (n + 1)` bytes in the following line. Since one of `m` or `n` is -1 now, we're calling `malloc(0)` here.
+ - According to the documentation, `malloc(0)` can return `NULL` or a valid non-null pointer (freeable). In our system, `malloc(0)` returns the latter.
+ - Later when returning to the caller, we do:
+ ```
+ i = m;
+ j = n;
+ while ((i != 0) && (j != 0)) {
+ if (result[i*(n+1) + j] == LCS_UPLEFT)
+ // ...
+ }
+ ```
+ and potentially causes not only invalid iterations (since i, j are decremented only and will underflow when starting with -1), but also invalid access for `result[...]`.
+
+ Therefore, we should do early return in `lcs` algorithm when getting length <= 0.
+
+ PRESUBMIT=passed
+ BUG=336187503
+ R=tjbarron
+ APPROVED=tjbarron
+ REQUIRED_REVIEW=1
+ DELTA=4 (4 added, 0 deleted, 0 changed)
+ DELTA_BY_EXTENSION=cxx=4
+ OCL=639874531
+ FIG_CHANGESET=4acb332f66fbf4c2c694f9af5bd370a928e27010
+ FIG_WORKSPACE=sungyc/7504:hunspell
+ MARKDOWN=true
+
+Affected files ...
+
+... //depot//src/hunspell/suggestmgr.cxx#9 edit
+
+==== //depot//src/hunspell/suggestmgr.cxx#8 - /google/src/files/640274267/depot//src/hunspell/suggestmgr.cxx ====
+--- /google/src/files/615029530/depot//src/hunspell/suggestmgr.cxx 2024-03-12 10:13:27.000000000 -0400
++++ /google/src/files/640274267/depot//src/hunspell/suggestmgr.cxx 2024-06-04 16:58:38.000000000 -0400
+@@ -1969,6 +1969,10 @@
+ m = strlen(s);
+ n = strlen(s2);
+ }
++ if (m <= 0 || n <= 0) {
++ *result = NULL;
++ return;
++ }
+ c = (char *) malloc((m + 1) * (n + 1));
+ b = (char *) malloc((m + 1) * (n + 1));
+ if (!c || !b) {
diff --git a/patches/series b/patches/series
new file mode 100644
index 0000000..f3b8f25
--- /dev/null
+++ b/patches/series
@@ -0,0 +1,45 @@
+01.korean_affix.patch
+02.buffer_overrun.patch
+03.initial_checkin_edits.patch
+04.smart_ptr_cleanup_1.patch
+05.smart_ptr_cleanup_2.patch
+06.smart_ptr_cleanup_3.patch
+07.long_input.patch
+08.stack_overflow.patch
+09.leak_and_oob_fixes.patch
+10.mkinit_overflow.patch
+11.insert_sug.patch
+12.buffer_overflow.patch
+13.init_pointers.patch
+14.buffer_overflow.patch
+15.overflow_check.patch
+16.oss_fuzz_import.patch
+17.leakfix.patch
+18.parser_bug.patch
+19.alloc_fix.patch
+20.index_check.patch
+21.overflow_check.patch
+22.index_check.patch
+23.pointer_serialize.patch
+24.aliasf_leak.patch
+25.aliasm_leak.patch
+26.defcpdtable_leak.patch
+27.numaliasm_oom.patch
+28.get_xml_list_leak.patch
+29.parse_cpdsyllable_leak.patch
+30.non_printables.patch
+31.overflow.patch
+32.u16_u8_xff.patch
+33.compund_check.patch
+34.commoncharacterpositions.patch
+35.index_boundary.patch
+36.calloc.patch
+37.double_free.patch
+38.trivial_comment_fixes.patch
+39.free_uninit_ptr.patch
+40.oob_read.patch
+41.heap_overflow.patch
+42.index_oob.patch
+43.oob_pointer.patch
+44.init_char_array.patch
+45.lcs_string_length_fix.patch