329 lines
11 KiB
Diff
329 lines
11 KiB
Diff
Origin: https://sources.debian.org/data/main/e/espeak-ng/1.51%2Bdfsg-12/debian/patches/CVE
|
||
|
||
commit 58f1e0b6a4e6aa55621c6f01118994d01fd6f68c
|
||
Merge: f983e445 e7bcd3cc
|
||
Author: Alexander Epaneshnikov <aarnaarn2@gmail.com>
|
||
Date: Sun Dec 17 15:29:30 2023 +0300
|
||
|
||
tests: fix CVE crashes (#1846)
|
||
|
||
Fixes: #1823, #1824, #1825, #1826, #1827
|
||
|
||
- Add crash test and vectors provided by @SEU-SSL
|
||
- Disallow dummy/null voice load (that causes incorrect translator
|
||
initialization)
|
||
- Fix empty `phondata` file load (that causes unitialized memory access)
|
||
- Limit max word length for RemoveEnding (causes buffer overflow)
|
||
- Limit punctlist initialization from embedded commands (buffer
|
||
overflow)
|
||
- Fix unitialized pitch in wavegen (DBZ and indexing problems)
|
||
- Properly zeroize stack variables before use in TranslateClause and
|
||
SetWordStress
|
||
|
||
TODO (in nextup PR): add & fix more vectors from fuzzer.
|
||
|
||
commit 9decedb8c229e1a4219baceaab7a3d656e889e31
|
||
Author: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||
Date: Thu Jun 30 00:50:18 2022 +0200
|
||
|
||
Fix missing checks for EOF
|
||
|
||
commit c4c05820c4a47369d5a81e4a506fe7abb2fa7ed6
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 19:24:51 2023 +0300
|
||
|
||
tests: add CVE crash vectors
|
||
|
||
commit e79405772cecf47053116aeaad10e64606292b14
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 23:55:03 2023 +0300
|
||
|
||
voices: disallow dummy voice when not compiling
|
||
|
||
commit 7d4ad3c2ae063cb08bfd606021bc323dfbadaba9
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 21:50:07 2023 +0300
|
||
|
||
synthdata: fix empty file load
|
||
|
||
commit b99f332c576eb49839613a55cfd3e0e1b5487191
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 22:45:15 2023 +0300
|
||
|
||
dictionary: limit word length
|
||
|
||
commit 1a7ecfc2f202438b17e742368f910e6099ce02b7
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 22:50:01 2023 +0300
|
||
|
||
readclause: limit embedded punctlist length
|
||
|
||
commit a5eb246debb51ba328ef399350dfcd5d87782245
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 23:03:16 2023 +0300
|
||
|
||
wavegen: fix unitialized pitch
|
||
|
||
commit 5f7db763e2eff1d8174d2b65a4bbe4b2a85c8a0c
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 23:17:45 2023 +0300
|
||
|
||
translate: fix number_buf initialization
|
||
|
||
commit e7bcd3cc1599ebb531bb62fc3007d3ce1dade167
|
||
Author: Yury Popov <git@phoenix.dj>
|
||
Date: Sat Dec 16 23:26:07 2023 +0300
|
||
|
||
dictionary: fix stack initialization
|
||
|
||
---
|
||
src/libespeak-ng/dictionary.c | 4 ++++
|
||
src/libespeak-ng/readclause.c | 12 ++++++------
|
||
src/libespeak-ng/synthdata.c | 18 ++++++++++++++----
|
||
src/libespeak-ng/translate.c | 1 +
|
||
src/libespeak-ng/voices.c | 20 ++++++++++++--------
|
||
src/libespeak-ng/wavegen.c | 9 ++++++---
|
||
tests/crash.test | 17 +++++++++++++++++
|
||
tests/crash_vectors/cve-2023-49990.txt | 1 +
|
||
tests/crash_vectors/cve-2023-49991.txt | 1 +
|
||
tests/crash_vectors/cve-2023-49994.txt | 1 +
|
||
10 files changed, 63 insertions(+), 21 deletions(-)
|
||
|
||
--- a/src/libespeak-ng/readclause.c
|
||
+++ b/src/libespeak-ng/readclause.c
|
||
@@ -335,7 +335,7 @@ static int AnnouncePunctuation(Translato
|
||
|
||
if ((*bufix == 0) || (end_clause == 0) || (tr->langopts.param[LOPT_ANNOUNCE_PUNCT] & 2)) {
|
||
punct_count = 1;
|
||
- while ((c2 == c1) && (c1 != '<')) { // don't eat extra '<', it can miss XML tags
|
||
+ while (!Eof() && (c2 == c1) && (c1 != '<')) { // don't eat extra '<', it can miss XML tags
|
||
punct_count++;
|
||
c2 = GetC();
|
||
}
|
||
@@ -647,7 +647,7 @@ int ReadClause(Translator *tr, char *buf
|
||
// an embedded command. If it's a voice change, end the clause
|
||
if (c2 == 'V') {
|
||
buf[ix++] = 0; // end the clause at this point
|
||
- while (!iswspace(c1 = GetC()) && !Eof() && (ix < (n_buf-1)))
|
||
+ while (!Eof() && !iswspace(c1 = GetC()) && (ix < (n_buf-1)))
|
||
buf[ix++] = c1; // add voice name to end of buffer, after the text
|
||
buf[ix++] = 0;
|
||
return CLAUSE_VOICE;
|
||
@@ -657,7 +657,7 @@ int ReadClause(Translator *tr, char *buf
|
||
strcpy(&buf[ix], " ");
|
||
ix += 3;
|
||
|
||
- if ((c2 = GetC()) == '0')
|
||
+ if (!Eof() && (c2 = GetC()) == '0')
|
||
option_punctuation = 0;
|
||
else {
|
||
option_punctuation = 1;
|
||
@@ -665,7 +665,7 @@ int ReadClause(Translator *tr, char *buf
|
||
if (c2 != '1') {
|
||
// a list of punctuation characters to be spoken, terminated by space
|
||
j = 0;
|
||
- while (!iswspace(c2) && !Eof()) {
|
||
+ while (!Eof() && !iswspace(c2) && (j < N_PUNCTLIST-1)) {
|
||
option_punctlist[j++] = c2;
|
||
c2 = GetC();
|
||
buf[ix++] = ' ';
|
||
@@ -791,7 +791,7 @@ int ReadClause(Translator *tr, char *buf
|
||
}
|
||
|
||
if ((c1 == '.') && (c2 == '.')) {
|
||
- while ((c_next = GetC()) == '.') {
|
||
+ while (!Eof() && (c_next = GetC()) == '.') {
|
||
// 3 or more dots, replace by elipsis
|
||
c1 = 0x2026;
|
||
c2 = ' ';
|
||
@@ -808,7 +808,7 @@ int ReadClause(Translator *tr, char *buf
|
||
// Handling of sequences of ? and ! like ??!?, !!??!, ?!! etc
|
||
// Use only first char as determinant
|
||
if(punct_data & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
|
||
- while(clause_type_from_codepoint(c2) & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
|
||
+ while(!Eof() && clause_type_from_codepoint(c2) & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
|
||
c_next = GetC();
|
||
c2 = c_next;
|
||
}
|
||
--- /dev/null
|
||
+++ b/tests/crash.test
|
||
@@ -0,0 +1,17 @@
|
||
+#!/bin/sh
|
||
+# include common script
|
||
+. "`dirname $0`/common"
|
||
+
|
||
+test_crash() {
|
||
+ TEST_NAME=$1
|
||
+
|
||
+ echo "testing CVE-${TEST_NAME}"
|
||
+ ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} \
|
||
+ $VALGRIND src/espeak-ng -f "$(dirname $0)/crash_vectors/${TEST_NAME}.txt" -w /dev/null || exit 1
|
||
+}
|
||
+
|
||
+test_crash cve-2023-49990
|
||
+test_crash cve-2023-49991
|
||
+test_crash cve-2023-49992
|
||
+test_crash cve-2023-49993
|
||
+test_crash cve-2023-49994
|
||
--- /dev/null
|
||
+++ b/tests/crash_vectors/cve-2023-49990.txt
|
||
@@ -0,0 +1 @@
|
||
+<2B><><EFBFBD><EFBFBD>V<><56><EFBFBD><EFBFBD>
|
||
<EFBFBD><EFBFBD>V
|
||
<EFBFBD><EFBFBD>V<><07><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>s<><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>s<EFBFBD><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>eeeeeeeeseee<65><65><EFBFBD><EFBFBD><EFBFBD>
|
||
\ No newline at end of file
|
||
--- /dev/null
|
||
+++ b/tests/crash_vectors/cve-2023-49991.txt
|
||
@@ -0,0 +1 @@
|
||
+<2B><>V<>
|
||
<EFBFBD><EFBFBD>V<><56>h<EFBFBD><68><EFBFBD><EFBFBD><EFBFBD>VD<56>Z<EFBFBD><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>컻<EFBFBD><ECBBBB>־<EFBFBD><D6BE><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD><08><>ֻ<EFBFBD><D6BB><EFBFBD>ժ<EFBFBD><D5AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>`v
|
||
\ No newline at end of file
|
||
--- /dev/null
|
||
+++ b/tests/crash_vectors/cve-2023-49994.txt
|
||
@@ -0,0 +1 @@
|
||
+"[[-#,- -1-2.
|
||
r--<2D>#--O)C--!<21>E-1<>@5-!-V-1--
|
||
\ No newline at end of file
|
||
--- a/src/libespeak-ng/voices.c
|
||
+++ b/src/libespeak-ng/voices.c
|
||
@@ -554,6 +554,10 @@ voice_t *LoadVoice(const char *vname, in
|
||
static char voice_name[40]; // voice name for current_voice_selected
|
||
static char voice_languages[100]; // list of languages and priorities for current_voice_selected
|
||
|
||
+ if ((vname == NULL || vname[0] == 0) && !(control & 8)) {
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
strncpy0(voicename, vname, sizeof(voicename));
|
||
if (control & 0x10) {
|
||
strcpy(buf, vname);
|
||
@@ -937,14 +941,14 @@ voice_t *LoadVoice(const char *vname, in
|
||
|
||
if (!tone_only) {
|
||
if (!!(control & 8/*compiling phonemes*/)) {
|
||
- /* Set by espeak_ng_CompilePhonemeDataPath when it
|
||
- * calls LoadVoice("", 8) to set up a dummy(?) voice.
|
||
- * As phontab may not yet exist this avoids the spurious
|
||
- * error message and guarantees consistent results by
|
||
- * not actually reading a potentially bogus phontab...
|
||
- */
|
||
- ix = 0;
|
||
- } else if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) {
|
||
+ /* Set by espeak_ng_CompilePhonemeDataPath when it
|
||
+ * calls LoadVoice("", 8) to set up a dummy(?) voice.
|
||
+ * As phontab may not yet exist this avoids the spurious
|
||
+ * error message and guarantees consistent results by
|
||
+ * not actually reading a potentially bogus phontab...
|
||
+ */
|
||
+ ix = 0;
|
||
+ } else if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) {
|
||
fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name);
|
||
ix = 0;
|
||
}
|
||
--- a/src/libespeak-ng/synthdata.c
|
||
+++ b/src/libespeak-ng/synthdata.c
|
||
@@ -75,8 +75,15 @@ static espeak_ng_STATUS ReadPhFile(void
|
||
if ((f_in = fopen(buf, "rb")) == NULL)
|
||
return create_file_error_context(context, errno, buf);
|
||
|
||
- if (*ptr != NULL)
|
||
+ if (*ptr != NULL) {
|
||
free(*ptr);
|
||
+ *ptr = NULL;
|
||
+ }
|
||
+
|
||
+ if (length == 0) {
|
||
+ *ptr = NULL;
|
||
+ return 0;
|
||
+ }
|
||
|
||
if ((*ptr = malloc(length)) == NULL) {
|
||
fclose(f_in);
|
||
@@ -86,6 +93,7 @@ static espeak_ng_STATUS ReadPhFile(void
|
||
int error = errno;
|
||
fclose(f_in);
|
||
free(*ptr);
|
||
+ *ptr = NULL;
|
||
return create_file_error_context(context, error, buf);
|
||
}
|
||
|
||
@@ -119,9 +127,11 @@ espeak_ng_STATUS LoadPhData(int *srate,
|
||
// read the version number and sample rate from the first 8 bytes of phondata
|
||
version = 0; // bytes 0-3, version number
|
||
rate = 0; // bytes 4-7, sample rate
|
||
- for (ix = 0; ix < 4; ix++) {
|
||
- version += (wavefile_data[ix] << (ix*8));
|
||
- rate += (wavefile_data[ix+4] << (ix*8));
|
||
+ if (wavefile_data) {
|
||
+ for (ix = 0; ix < 4; ix++) {
|
||
+ version += (wavefile_data[ix] << (ix*8));
|
||
+ rate += (wavefile_data[ix+4] << (ix*8));
|
||
+ }
|
||
}
|
||
|
||
if (version != version_phdata)
|
||
--- a/src/libespeak-ng/dictionary.c
|
||
+++ b/src/libespeak-ng/dictionary.c
|
||
@@ -1062,6 +1062,9 @@ void SetWordStress(Translator *tr, char
|
||
|
||
static char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
|
||
|
||
+ memset(syllable_weight, 0, sizeof(syllable_weight));
|
||
+ memset(vowel_length, 0, sizeof(vowel_length));
|
||
+
|
||
stressflags = tr->langopts.stress_flags;
|
||
|
||
if (dictionary_flags != NULL)
|
||
@@ -3070,6 +3073,7 @@ int RemoveEnding(Translator *tr, char *w
|
||
*word_end = 'e';
|
||
}
|
||
i = word_end - word;
|
||
+ if (i >= N_WORD_BYTES) i = N_WORD_BYTES-1;
|
||
|
||
if (word_copy != NULL) {
|
||
memcpy(word_copy, word, i);
|
||
--- a/src/libespeak-ng/wavegen.c
|
||
+++ b/src/libespeak-ng/wavegen.c
|
||
@@ -537,14 +537,14 @@ static void AdvanceParameters()
|
||
if (wvoice == NULL)
|
||
return;
|
||
|
||
- int x;
|
||
+ int x = 0;
|
||
int ix;
|
||
static int Flutter_ix = 0;
|
||
|
||
// advance the pitch
|
||
wdata.pitch_ix += wdata.pitch_inc;
|
||
if ((ix = wdata.pitch_ix>>8) > 127) ix = 127;
|
||
- x = wdata.pitch_env[ix] * wdata.pitch_range;
|
||
+ if (wdata.pitch_env) x = wdata.pitch_env[ix] * wdata.pitch_range;
|
||
wdata.pitch = (x>>8) + wdata.pitch_base;
|
||
|
||
|
||
@@ -560,7 +560,7 @@ static void AdvanceParameters()
|
||
|
||
if(const_f0)
|
||
wdata.pitch = (const_f0<<12);
|
||
-
|
||
+
|
||
if (wdata.pitch < 102400)
|
||
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
|
||
|
||
@@ -1268,6 +1268,9 @@ static int WavegenFill2()
|
||
static bool resume = false;
|
||
static int echo_complete = 0;
|
||
|
||
+ if (wdata.pitch < 102400)
|
||
+ wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
|
||
+
|
||
while (out_ptr < out_end) {
|
||
if (WcmdqUsed() <= 0) {
|
||
if (echo_complete > 0) {
|
||
--- a/src/libespeak-ng/translate.c
|
||
+++ b/src/libespeak-ng/translate.c
|
||
@@ -2630,6 +2630,7 @@ void TranslateClause(Translator *tr, int
|
||
if (dict_flags & FLAG_SPELLWORD) {
|
||
// redo the word, speaking single letters
|
||
for (pw = word; *pw != ' ';) {
|
||
+ memset(number_buf, 0, sizeof(number_buf));
|
||
memset(number_buf, ' ', 9);
|
||
nx = utf8_in(&c_temp, pw);
|
||
memcpy(&number_buf[2], pw, nx);
|