From cd33c4042a022a3e1dea32da4c5541d6ca568cf0 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 15 Mar 2022 03:39:12 +0100 Subject: [PATCH 1/5] MatchRule: Do not go back before the pre-ptr pre_ptr is already one byte before the current letter, so we do not want to subtract 1 again. Otherwise this would for instance underflow word_iz of addPluralSuffixes. --- src/libespeak-ng/dictionary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libespeak-ng/dictionary.c b/src/libespeak-ng/dictionary.c index f93be021..cc1cd33d 100644 --- a/src/libespeak-ng/dictionary.c +++ b/src/libespeak-ng/dictionary.c @@ -2049,7 +2049,7 @@ static void MatchRule(Translator *tr, char *word[], char *word_start, int group_ break; case RULE_NOVOWELS: { - char *p = pre_ptr - letter_xbytes - 1; + char *p = pre_ptr - letter_xbytes; while (letter_w != RULE_SPACE) { if (IsLetter(tr, letter_w, LETTERGP_VOWEL2)) { failed = 1; From 7b9c99cec7b0c517049d39cbbee610c3a8407fa4 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 15 Mar 2022 02:35:08 +0100 Subject: [PATCH 2/5] Add more heading margin in TranslateClause Valgrind reports: ==3642987== Conditional jump or move depends on uninitialised value(s) ==3642987== at 0x491F268: TranslateNumber_1 (numbers.c:1785) ==3642987== by 0x4923C35: TranslateNumber (numbers.c:2080) ==3642987== by 0x49556DC: TranslateWord3 (translate.c:644) ==3642987== by 0x4957FCE: TranslateWord (translate.c:1100) ==3642987== by 0x4959344: TranslateWord2 (translate.c:1361) ==3642987== by 0x496116E: TranslateClause (translate.c:2613) ==3642987== by 0x494FF7A: SpeakNextClause (synthesize.c:1569) ==3642987== by 0x4939B9D: Synthesize (speech.c:457) ==3642987== by 0x493AE6A: sync_espeak_Synth (speech.c:570) ==3642987== by 0x493B286: espeak_ng_Synthesize (speech.c:678) ==3642987== by 0x4916925: espeak_Synth (espeak_api.c:90) ==3642987== by 0x10CF5D: main (espeak-ng.c:691) ==3642987== Uninitialised value was created by a stack allocation ==3642987== at 0x495BD9F: TranslateClause (translate.c:1941) Indeed, TranslateNumber_1 looks back up to three bytes before, with IsDigit09(word[-3])), so we have to increase the heading margin to three spaces. --- src/libespeak-ng/translate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libespeak-ng/translate.c b/src/libespeak-ng/translate.c index 59e854ea..93186f25 100644 --- a/src/libespeak-ng/translate.c +++ b/src/libespeak-ng/translate.c @@ -2539,7 +2539,9 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change) if (n_digits > 4) { // word is entirely digits, insert commas and break into 3 digit "words" number_buf[0] = ' '; - pn = &number_buf[1]; + number_buf[1] = ' '; + number_buf[2] = ' '; + pn = &number_buf[3]; nx = n_digits; nw = 0; @@ -2583,7 +2585,7 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change) pn[16] = 0; nw = 0; - for (pw = &number_buf[1]; pw < pn;) { + for (pw = &number_buf[3]; pw < pn;) { // keep wflags for each part, for FLAG_HYPHEN_AFTER dict_flags = TranslateWord2(tr, pw, &num_wtab[nw++], words[ix].pre_pause); while (*pw++ != ' ') From 26a675543c63832e37ec780a6c30e49707df1db4 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 15 Mar 2022 02:02:21 +0100 Subject: [PATCH 3/5] Properly maintain margin in ph_list2 TranslateWord2 uses phonemes in ph_list2. Apart from the breakable loops, it may statically require up to 7 phonemes. Then TranslateClause always uses 2 phonemes. We thus have to keep these margins along the loops to avoid any overflow. Fixes #1073 --- src/libespeak-ng/translate.c | 33 +++++++++++++++++++++++++++++---- tests/translate.test | 4 ++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/libespeak-ng/translate.c b/src/libespeak-ng/translate.c index 93186f25..0914619a 100644 --- a/src/libespeak-ng/translate.c +++ b/src/libespeak-ng/translate.c @@ -1276,6 +1276,11 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa Word_EmbeddedCmd(); } + if (n_ph_list2 >= N_PHONEME_LIST-2) { + // No room, can't translate anything + return 0; + } + if ((word[0] == 0) || (word_flags & FLAG_DELETE_WORD)) { // nothing to translate. Add a dummy phoneme to carry any embedded commands if (embedded_flag) { @@ -1292,6 +1297,11 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa return 0; } + if (n_ph_list2 >= N_PHONEME_LIST-7-2) { + // We may require up to 7 phonemes, plus the 2 phonemes from the caller, can't translate safely + return 0; + } + // after a $pause word attribute, ignore a $pause attribute on the next two words if (tr->prepause_timeout > 0) tr->prepause_timeout--; @@ -1487,7 +1497,10 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa if ((flags & FLAG_FOUND) && !(flags & FLAG_TEXTMODE)) found_dict_flag = SFLAG_DICTIONARY; - while ((pre_pause > 0) && (n_ph_list2 < N_PHONEME_LIST-4)) { + // Each iteration may require up to 1 phoneme + // and after this loop we may require up to 7 phonemes + // and our caller requires 2 phonemes + while ((pre_pause > 0) && (n_ph_list2 < N_PHONEME_LIST-7-2)) { // add pause phonemes here. Either because of punctuation (brackets or quotes) in the // text, or because the word is marked in the dictionary lookup as a conjunction if (pre_pause > 1) { @@ -1502,7 +1515,9 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa tr->prev_dict_flags[1] = 0; } plist2 = &ph_list2[n_ph_list2]; + // From here we may require up to 4+1+3 phonemes + // This may require up to 4 phonemes if ((option_capitals == 1) && (word_flags & FLAG_FIRST_UPPER)) { SetPlist2(&ph_list2[n_ph_list2++], phonPAUSE_SHORT); SetPlist2(&ph_list2[n_ph_list2++], phonCAPITAL); @@ -1513,6 +1528,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa } } + // This may require up to 1 phoneme if (switch_phonemes >= 0) { if ((p[0] == phonPAUSE) && (p[1] == phonSWITCH)) { // the new word starts with a phoneme table switch, so there's no need to switch before it. @@ -1541,7 +1557,10 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa p[1] = 0; } - while (((ph_code = *p++) != 0) && (n_ph_list2 < N_PHONEME_LIST-4)) { + // Each iteration may require up to 1 phoneme + // and after this loop we may require up to 3 phonemes + // and our caller requires 2 phonemes + while (((ph_code = *p++) != 0) && (n_ph_list2 < N_PHONEME_LIST-3-2)) { if (ph_code == 255) continue; // unknown phoneme @@ -1633,7 +1652,9 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa first_phoneme = false; } } + // From here, we may require up to 3 phonemes + // This may require up to 1 phoneme if (word_flags & FLAG_COMMA_AFTER) SetPlist2(&ph_list2[n_ph_list2++], phonPAUSE_CLAUSE); @@ -1645,6 +1666,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa if ((stress >= 4) && (phoneme_tab[ph_list2[n_ph_list2-1].phcode]->type == phVOWEL)) tr->end_stressed_vowel = 1; // word ends with a stressed vowel + // This may require up to 1 phoneme if (switch_phonemes >= 0) { // this word uses a different phoneme table, now switch back strcpy(dictionary_name, old_dictionary_name); @@ -1654,6 +1676,7 @@ static int TranslateWord2(Translator *tr, char *word, WORD_TAB *wtab, int pre_pa } + // This may require up to 1 phoneme if (pitch_raised > 0) { embedded_list[embedded_ix++] = EMBED_P+0x60+0x80 + (pitch_raised << 8); // lower pitch SetPlist2(&ph_list2[n_ph_list2], phonPAUSE_SHORT); @@ -2060,7 +2083,7 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change) } words[0].length = k; - while (!finished && (ix < (int)sizeof(sbuf) - 1) && (n_ph_list2 < N_PHONEME_LIST-4)) { + while (!finished && (ix < (int)sizeof(sbuf) - 1)) { prev_out2 = prev_out; utf8_in2(&prev_out, &sbuf[ix-1], 1); @@ -2488,7 +2511,9 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change) } words[0].flags |= FLAG_FIRST_WORD; - for (ix = 0; ix < word_count; ix++) { + // Each TranslateWord2 may require up to 7 phonemes + // and after this loop we require 2 phonemes + for (ix = 0; ix < word_count && (n_ph_list2 < N_PHONEME_LIST-7-2); ix++) { int nx; int c_temp; char *pn; diff --git a/tests/translate.test b/tests/translate.test index c90e7279..056020ac 100755 --- a/tests/translate.test +++ b/tests/translate.test @@ -34,8 +34,8 @@ test_phon hi "dUk'a:n" "दुकान" test_phon hi "ka:n'u:n" "कानून" # bug: https://github.com/espeak-ng/espeak-ng/issues/824 -test_phon ru "(en)s'i:(ru) n'ojl t'otS;ka v'os;E2md;E2s;ats;'ejm m;,IlI;'onof_! p;,It;s'ot SE2z;d;d;Is;'jatd;'evI3t; t'ys;VtS;_! dv;'es;t;I p;Vd;d;Is;'jattR;'i p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka t@-r;'ittsat;S'Es;t; t'ys;VtS;_! vOs;Ims'ot s;Imn'AttsVt; p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka dv;'e t'ys;VdZ; d;E2v;Itn'AttsVt; p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka n'ojl tR;'iv'os;E#m_!s;'ejm d;'evI3t;S'Es;t;_!tR;'i p@-rats'Ent dv'A (en)s'i:(ru) ojd;'in t'otS;ka n'ojl tR;'iv'os;E#m_!s;'ejm d;'evI3t;S'Es;t;_!tR;'i p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka tS;It'yr;E2sta d;E2v;Itn'AttsVt; t'ys;VtS;_! S,E#s;t;s'ot t@-r;'ittsat;s;'ejm p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka n'ojldv'A_! tR;'is;'ejm_!p;'jat; p@-rats'Ent dv'AttsVt; (en)s'i:(ru) p@-rats'Ent dv;'es;t;I t'otS;ka t@-r;'ittsVt; t'ys;VtS;_! v'os;E2md;E2s;ats;'ejm p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka n'ojltS;It'yR;I_! p;'jat;Vjd;'in_!tS;It'yR;I p@-rats'Ent dv;'es;t;I t'otS;ka p;,It;s'ot v'os;E2md;E2s;atdv;'e t'ys;VtS;_! s;,Ims'ot t@-r;'ittsat;d;'evI3t; p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka dv;'es;t;I SE#stn'AttsVt; t'ys;VtS;_! dev;ats'ot s'o@-*Okdv'A p@-rats'Ent dv;'es;t;I t'otS;ka s;,Ims'ot p;Vd;d;Is;'jat t'ys;VtS;_! p;,It;s'ot d;E2v;In'ostOtR;'i p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka tS;It'yr;E2sta s;'emd;E2s;Vt t'ys;VtS;_! t@-r;'ista v'os;E2m p@-rats'Ent dv;'es;t;I t'otS;ka n'ojld;'evI3t;_! p;'jat;n'ojl_!dv'A p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka st'o s'o@-*Okdv;'e t'ys;VtS;_! p;,It;s'ot s;Imn'AttsVt; p@-rats'Ent -dv'A (en)s'i:(ru) v'os;E2m t'otS;ka vOs;Ims'ot dv'Attsat;d;'evI3t; t'ys;VtS;_! s;,Ims'ot SE2z;d;d;Is;'jatv'os;E2m p@-rats'Ent dv'AttsVt; (en)s'i:(ru) p@-rats'Ent dv;'es;t;I p@-rats'Ent dv'A (en)s'i:(ru) p;'jat; t'otS;ka S,E#s;t;s'ot SE2z;d;d;Is;'jatd;'evI3t; t'ys;VtS;_! s'o@-*Okojd;'in p@-rats'Ent dv'AttsVt; tS;It'yR;I t'otS;ka p;Vd;d;Is;'jatd;'evI3t; t'ys;VtS;_! S,E#s;t;s'ot d;E2v;In'ostOd;'evI3t; p@-rats'Ent dv'A (en)s'i:(ru) d;'es;It; t'otS;ka dv'A m;,IlI;'onof_! S,E#s;t;s'ot p;Vd;d;Is;'jatdv;'e t'ys;VtS;_! t@-r;'ista d;E2v;In'ostOojd;'in p@-rats'Ent dv'AttsVt; d;'es;It; t'otS;ka dv;'es;t;I SE2z;d;d;Is;'jatp;'jat; t'ys;VtS;_! dv;'es;t;I t@-r;'ittsat;v'os;E2m p@-rats'Ent dv'A (en)s'i:(ru) d;'es;It; t'otS;ka dv'A m;,IlI;'onof_! S,E#s;t;s'ot p;Vd;d;Is;'jatdv;'e t'ys;VtS;_! t@-r;'ista d;E2v;In'ostOojd;'in p@-rats'Ent dv'AttsVt; (en)z'Ed(ru) p@-rats'Ent dv'AttsVt; (en)'Em(ru) p@-rats'Ent dv;'e t'ys;VdZ; d;'es;It; t'otS;ka n'ojltR;'i_!s;'ejm v'os;E#mS'Es;t;_!p;'jat; p@-rats'Ent dv'A (en)s'i:(ru) d;E2v;Itn'AttsVt; t'otS;ka s;,Ims'ot d;E2v;In'ostOv'os;E2m t'ys;VtS;_! p;,It;s'ot t@-r;'ittsat;s;'ejm p@-rats'Ent dv'AttsVt; (en)v'i:(ru) p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka st'o s;'emd;E2s;VttS;It'yR;I t'ys;VtS;_! st'o v'os;E2md;E2s;atv'os;E2m p@-rats'Ent dv'AttsVt; (en)'eI(ru) p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka tS;It'yr;E2sta v'os;E2md;E2s;atv'os;E2m t'ys;VtS;_! p;,It;s'ot d;E2v;Itn'AttsVt; p@-rats'Ent dv'A (en)s'i:(ru) ojd;'in t'otS;ka tS;It'yr;E2sta v'os;E2md;E2s;atv'os;E2m t'ys;VtS;_! p;,It;s'ot d;E2v;Itn'AttsVt; p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka vOs;Ims'ot p;Vd;d;Is;'jats;'ejm t'ys;VtS;" "C0.87569253%200%200%201%200.36817%2C-0.2019%201.0387963%2C1.0387963%200%200%201%200.419637%2C-0.02375%20c%200.30087%2C0.04514%200.582739%2C0.216942%200.750593%2C0.470308%200.09502%2C0.142517%200.153603%2C0.308788%200.18844%2C0.478226%200.03484%2C0.168646%200.0475%2C0.340459%200.05701%2C0.513064%200.03167%2C0.601741%200.03167%2C1.205067%200.01426%2C1.808392%20-0.01426%2C0.526524%20-0.04355%2C1.0673%20-0.253366%2C1.549486%20-0.271575%2C0.619159%20-0.817101%2C1.08155%20-1.405383%2C1.414092%20a%205.5835296%2C5.5835296%200%200%201%20-1.257323%2C0.512272%20c%200.38163%2C1.219319%200.580363%2C2.56532%200.580363%2C3.93349%20a%2013.935071%2C13.935071%200%200%201%20-0.106901%2C1.682498%2010.264446%2C10.264446%200%200%200%205.054631%2C-8.829768%20c%200%2C-5.669041%20-4.59699%2C-10.2652391%20-10.265238%2C-10.2652391%20z%20M%2010.037865%2C19.798537%20v%201.174188%20a%201.488519%2C1.488519%200%200%201%200.857482%2C0.286619%201.3760882%2C1.3760882%200%200%201%200.440222%2C0.538402%20c%200.0966%2C0.213775%200.131432%2C0.456056%200.09184%2C0.687252%20a%201.1821057%2C1.1821057%200%200%201%20-0.262867%2C0.560568%201.3040376%2C1.3040376%200%200%201%20-0.502772%2C0.36263%201.3760882%2C1.3760882%200%200%201%20-0.623119%2C0.0966%20v%202.953287%20l%206.145683%2C-3.33175%20-6.145683%2C-3," "ru sum strings" +test_phon ru "(en)s'i:(ru) n'ojl t'otS;ka# v'os;E2md;E2s;ats;'ejm m;,IlI;'onof_! p;,It;s'ot SE2z;d;d;Is;'jatd;'evI3t; t'ys;VtS;_! dv;'es;t;I p;Vd;d;Is;'jattR;'i p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka# t@-r;'ittsat;S'Es;t; t'ys;VtS;_! vOs;Ims'ot s;Imn'AttsVt; p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# dv;'e t'ys;VdZ; d;E2v;Itn'AttsVt; p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka# n'ojl tR;'iv'os;E#m_!s;'ejm d;'evI3t;S'Es;t;_!tR;'i p@-rats'Ent dv'A (en)s'i:(ru) ojd;'in t'otS;ka# n'ojl tR;'iv'os;E#m_!s;'ejm d;'evI3t;S'Es;t;_!tR;'i p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka# tS;It'yr;E2sta# d;E2v;Itn'AttsVt; t'ys;VtS;_! S,E#s;t;s'ot t@-r;'ittsat;s;'ejm p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# n'ojldv'A_! tR;'is;'ejm_!p;'jat; p@-rats'Ent dv'AttsVt; (en)s'i:(ru) p@-rats'Ent dv;'es;t;I t'otS;ka# t@-r;'ittsVt; t'ys;VtS;_! v'os;E2md;E2s;ats;'ejm p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# n'ojltS;It'yR;I_! p;'jat;Vjd;'in_!tS;It'yR;I p@-rats'Ent dv;'es;t;I t'otS;ka# p;,It;s'ot v'os;E2md;E2s;atdv;'e t'ys;VtS;_! s;,Ims'ot t@-r;'ittsat;d;'evI3t; p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# dv;'es;t;I SE#stn'AttsVt; t'ys;VtS;_! dev;ats'ot s'o@-*Okdv'A p@-rats'Ent dv;'es;t;I t'otS;ka# s;,Ims'ot p;Vd;d;Is;'jat t'ys;VtS;_! p;,It;s'ot d;E2v;In'ostOtR;'i p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# tS;It'yr;E2sta# s;'emd;E2s;Vt t'ys;VtS;_! t@-r;'ista# v'os;E2m p@-rats'Ent dv;'es;t;I t'otS;ka# n'ojld;'evI3t;_! p;'jat;n'ojl_!dv'A p@-rats'Ent dv'A (en)s'i:(ru) n'ojl t'otS;ka# st'o s'o@-*Okdv;'e t'ys;VtS;_! p;,It;s'ot s;Imn'AttsVt; p@-rats'Ent +dv'A (en)s'i:(ru) v'os;E2m t'otS;ka# vOs;Ims'ot dv'Attsat;d;'evI3t; t'ys;VtS;_! s;,Ims'ot SE2z;d;d;Is;'jatv'os;E2m p@-rats'Ent dv'AttsVt; (en)s'i:(ru) p@-rats'Ent dv;'es;t;I p@-rats'Ent dv'A (en)s'i:(ru) p;'jat; t'otS;ka# S,E#s;t;s'ot SE2z;d;d;Is;'jatd;'evI3t; t'ys;VtS;_! s'o@-*Okojd;'in p@-rats'Ent dv'AttsVt; tS;It'yR;I t'otS;ka# p;Vd;d;Is;'jatd;'evI3t; t'ys;VtS;_! S,E#s;t;s'ot d;E2v;In'ostOd;'evI3t; p@-rats'Ent dv'A (en)s'i:(ru) d;'es;It; t'otS;ka# dv'A m;,IlI;'onof_! S,E#s;t;s'ot p;Vd;d;Is;'jatdv;'e t'ys;VtS;_! t@-r;'ista# d;E2v;In'ostOojd;'in p@-rats'Ent dv'AttsVt; d;'es;It; t'otS;ka# dv;'es;t;I SE2z;d;d;Is;'jatp;'jat; t'ys;VtS;_! dv;'es;t;I t@-r;'ittsat;v'os;E2m p@-rats'Ent dv'A (en)s'i:(ru) d;'es;It; t'otS;ka# dv'A m;,IlI;'onof_! S,E#s;t;s'ot p;Vd;d;Is;'jatdv;'e t'ys;VtS;_! t@-r;'ista# d;E2v;In'ostOojd;'in p@-rats'Ent dv'AttsVt; (en)z'Ed(ru) p@-rats'Ent dv'AttsVt; (en)'Em(ru) p@-rats'Ent dv;'e t'ys;VdZ; d;'es;It; t'otS;ka# n'ojltR;'i_!s;'ejm v'os;E#mS'Es;t;_!p;'jat; p@-rats'Ent dv'A (en)s'i:(ru) d;E2v;Itn'AttsVt; t'otS;ka# s;,Ims'ot d;E2v;In'ostOv'os;E2m t'ys;VtS;_! p;,It;s'ot t@-r;'ittsat;s;'ejm p@-rats'Ent dv'AttsVt; (en)v'i:(ru) p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka# st'o s;'emd;E2s;VttS;It'yR;I t'ys;VtS;_! st'o v'os;E2md;E2s;atv'os;E2m p@-rats'Ent dv'AttsVt; (en)'eI(ru) p@-rats'Ent dv;'es;t;I ojd;'in t'otS;ka# tS;It'yr;E2sta# v'os;E2md;E2s;atv'os;E2m t'ys;VtS;_! p;,It;s'ot d;E2v;Itn'AttsVt; p@-rats'Ent dv'A (en)s'i:(ru) ojd;'in t'otS;ka# tS;It'yr;E2sta# v'os;E2md;E2s;atv'os;E2m t'ys;VtS;_! p;,It;s'ot d;E2v;Itn'AttsVt; p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I p@-rats'Ent dv;'es;t;I ojd;'in p@-rats'Ent dv;'es;t;I t'otS;ka# vOs;Ims'ot p;Vd;d;Is;'jats;'ejm t'ys;VtS;" "C0.87569253%200%200%201%200.36817%2C-0.2019%201.0387963%2C1.0387963%200%200%201%200.419637%2C-0.02375%20c%200.30087%2C0.04514%200.582739%2C0.216942%200.750593%2C0.470308%200.09502%2C0.142517%200.153603%2C0.308788%200.18844%2C0.478226%200.03484%2C0.168646%200.0475%2C0.340459%200.05701%2C0.513064%200.03167%2C0.601741%200.03167%2C1.205067%200.01426%2C1.808392%20-0.01426%2C0.526524%20-0.04355%2C1.0673%20-0.253366%2C1.549486%20-0.271575%2C0.619159%20-0.817101%2C1.08155%20-1.405383%2C1.414092%20a%205.5835296%2C5.5835296%200%200%201%20-1.257323%2C0.512272%20c%200.38163%2C1.219319%200.580363%2C2.56532%200.580363%2C3.93349%20a%2013.935071%2C13.935071%200%200%201%20-0.106901%2C1.682498%2010.264446%2C10.264446%200%200%200%205.054631%2C-8.829768%20c%200%2C-5.669041%20-4.59699%2C-10.2652391%20-10.265238%2C-10.2652391%20z%20M%2010.037865%2C19.798537%20v%201.174188%20a%201.488519%2C1.488519%200%200%201%200.857482%2C0.286619%201.3760882%2C1.3760882%200%200%201%200.440222%2C0.538402%20c%200.0966%2C0.213775%200.131432%2C0.456056%200.09184%2C0.687252%20a%201.1821057%2C1.1821057%200%200%201%20-0.262867%2C0.560568%201.3040376%2C1.3040376%200%200%201%20-0.502772%2C0.36263%201.3760882%2C1.3760882%200%200%201%20-0.623119%2C0.0966%20v%202.953287%20l%206.145683%2C-3.33175%20-6.145683%2C-3," "ru sum strings" # A deleted phoneme at the start of a word should preserve the sourceix property. test_phon en-GB-x-gbcwmd "aI 'av" "I have" From 1ce5a1bb0b229084c7c5f5154d01c35795d69e3f Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 15 Mar 2022 18:17:42 +0100 Subject: [PATCH 4/5] IsLetterGroup: Do not blindly walk back in the word strlen(p) may be arbitrarily long, that would underflow the word, for instance: testing fr Latn ================================================================= ==3741805==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd733c1329 at pc 0x7ff5ffbad2de bp 0x7ffd733bf310 sp 0x7ffd733bf308 READ of size 1 at 0x7ffd733c1329 thread T0 #0 0x7ff5ffbad2dd in IsLetterGroup src/libespeak-ng/dictionary.c:714 #1 0x7ff5ffbbe425 in MatchRule src/libespeak-ng/dictionary.c:1979 #2 0x7ff5ffbc09e9 in TranslateRules src/libespeak-ng/dictionary.c:2301 #3 0x7ff5ffc26656 in TranslateWord3 src/libespeak-ng/translate.c:733 #4 0x7ff5ffc2a10b in TranslateWord src/libespeak-ng/translate.c:1100 #5 0x7ff5ffc2bef2 in TranslateWord2 src/libespeak-ng/translate.c:1361 #6 0x7ff5ffc374e2 in TranslateClause src/libespeak-ng/translate.c:2623 #7 0x7ff5ffc1d010 in SpeakNextClause src/libespeak-ng/synthesize.c:1569 #8 0x7ff5ffbfbd46 in Synthesize src/libespeak-ng/speech.c:492 #9 0x7ff5ffbfd52a in sync_espeak_Synth src/libespeak-ng/speech.c:570 #10 0x7ff5ffbfdd1f in espeak_ng_Synthesize src/libespeak-ng/speech.c:678 #11 0x7ff5ffbc72fd in espeak_Synth src/libespeak-ng/espeak_api.c:90 #12 0x5627511a3137 in main src/espeak-ng.c:691 #13 0x7ff5fee557fc in __libc_start_main ../csu/libc-start.c:332 #14 0x5627511a0569 in _start (/home/samy/ens/projet/1/speech/espeak-ng-git/src/.libs/espeak-ng+0x6569) Address 0x7ffd733c1329 is located in stack of thread T0 at offset 1177 in frame #0 0x7ff5ffc2f760 in TranslateClause src/libespeak-ng/translate.c:1941 This frame has 16 object(s): [48, 52) 'cc' (line 1944) [64, 68) 'source_index' (line 1945) [80, 84) 'prev_in' (line 1948) [96, 100) 'prev_out' (line 1949) [112, 116) 'next_in' (line 1952) [128, 132) 'char_inserted' (line 1954) [144, 148) 'word_flags' (line 1963) [160, 164) 'charix_top' (line 1975) [176, 180) 'tone' (line 1985) [192, 196) 'next2_in' (line 2294) [208, 212) 'c_temp' (line 2518) [224, 374) 'number_buf' (line 2522) [448, 1048) 'num_wtab' (line 2523) [1184, 1984) 'sbuf' (line 1982) <== Memory access at offset 1177 underflows this variable [2112, 3720) 'charix' (line 1977) [3856, 7456) 'words' (line 1978) sbuf is however properly '\0'-header, so we can make IsLetterGroup carefully walk back in the word and issue a mismatch if it walks back too much. Fixes #1108 --- src/libespeak-ng/dictionary.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libespeak-ng/dictionary.c b/src/libespeak-ng/dictionary.c index cc1cd33d..3ff8e52e 100644 --- a/src/libespeak-ng/dictionary.c +++ b/src/libespeak-ng/dictionary.c @@ -693,7 +693,7 @@ static int IsLetterGroup(Translator *tr, char *word, int group, int pre) */ char *p; // group counter char *w; // word counter - int len = 0; + int len = 0, i; p = tr->letterGroups[group]; if (p == NULL) @@ -702,7 +702,14 @@ static int IsLetterGroup(Translator *tr, char *word, int group, int pre) while (*p != RULE_GROUP_END) { if (pre) { len = strlen(p); - w = word - len + 1; + w = word; + for (i = 0; i < len-1; i++) + { + w--; + if (*w == 0) + // Not found + return -1; + } } else w = word; From c790af66d0f6326f378254e6f814e70cc3e18810 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Tue, 15 Mar 2022 04:07:50 +0100 Subject: [PATCH 5/5] Add asan CI run Now that all asan errors are fixed. Ref #220 --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5670eb1..aa44511e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,3 +23,20 @@ jobs: run: make - name: make check run: make check + + asan: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: dependencies + run: sudo apt-get update && sudo apt-get install libpcaudio-dev libsonic-dev ronn kramdown + - name: autoconf + run: ./autogen.sh ; chmod -x INSTALL m4/*.m4 + - name: configure + run: CXXFLAGS="-fsanitize=address -g -Og" CFLAGS="-fsanitize=address -g -Og" ./configure + - name: make + run: ASAN_OPTIONS="detect_leaks=0 new_delete_type_mismatch=0" make + - name: make check + run: ASAN_OPTIONS="detect_leaks=0 new_delete_type_mismatch=0" make check