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.master
@@ -927,6 +927,9 @@ void SetWordStress(Translator *tr, char *output, unsigned int *dictionary_flags, | |||
static const 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) | |||
@@ -2897,6 +2900,7 @@ int RemoveEnding(Translator *tr, char *word, int end_type, char *word_copy) | |||
*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); |
@@ -664,7 +664,7 @@ int ReadClause(Translator *tr, char *buf, short *charix, int *charix_top, int n_ | |||
if (c2 != '1') { | |||
// a list of punctuation characters to be spoken, terminated by space | |||
j = 0; | |||
while (!Eof() && !iswspace(c2)) { | |||
while (!Eof() && !iswspace(c2) && (j < N_PUNCTLIST-1)) { | |||
option_punctlist[j++] = c2; | |||
c2 = GetC(); | |||
buf[ix++] = ' '; |
@@ -79,8 +79,15 @@ static espeak_ng_STATUS ReadPhFile(void **ptr, const char *fname, int *size, esp | |||
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); | |||
@@ -90,6 +97,7 @@ static espeak_ng_STATUS ReadPhFile(void **ptr, const char *fname, int *size, esp | |||
int error = errno; | |||
fclose(f_in); | |||
free(*ptr); | |||
*ptr = NULL; | |||
return create_file_error_context(context, error, buf); | |||
} | |||
@@ -122,9 +130,11 @@ espeak_ng_STATUS LoadPhData(int *srate, espeak_ng_ERROR_CONTEXT *context) | |||
// 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) |
@@ -1601,6 +1601,7 @@ void TranslateClause(Translator *tr, int *tone_out, char **voice_change) | |||
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); |
@@ -442,6 +442,10 @@ voice_t *LoadVoice(const char *vname, int control) | |||
MAKE_MEM_UNDEFINED(&voice_languages, sizeof(voice_languages)); | |||
} | |||
if ((vname == NULL || vname[0] == 0) && !(control & 8)) { | |||
return NULL; | |||
} | |||
strncpy0(voicename, vname, sizeof(voicename)); | |||
if (control & 0x10) { | |||
strcpy(buf, vname); | |||
@@ -702,14 +706,14 @@ voice_t *LoadVoice(const char *vname, int control) | |||
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; | |||
} |
@@ -540,14 +540,14 @@ static void AdvanceParameters(void) | |||
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; | |||
@@ -563,7 +563,7 @@ static void AdvanceParameters(void) | |||
if(const_f0) | |||
wdata.pitch = (const_f0<<12); | |||
if (wdata.pitch < 102400) | |||
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12) | |||
@@ -1265,6 +1265,9 @@ static int WavegenFill2(void) | |||
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) { |
@@ -61,6 +61,7 @@ shell_test(ssml) | |||
shell_test(translate) | |||
shell_test(variants) | |||
shell_test(voices) | |||
shell_test(crash) | |||
# shell_test(windows-data) | |||
# shell_test(windows-installer) |
@@ -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 |
@@ -0,0 +1 @@ | |||
ã¦à»Vñ€¦ñ €¦V €äVñ€ãÂà¦æsññâñþâññà¶æØØsññâñþâññeeeeeeeeseee€ññûñ |
@@ -0,0 +1 @@ | |||
€¦Vń €ńVđŐhńůâ˙ńVDíZ»»ŐöÖÖÖÖÖÖÖÖÖě»»ş»ÖľÖÖÖÖÖÖ´ÖÖÖ»ţţ÷ÜÖÖÖ»»ş»ŐŞ»»®î˙˙€ę`v |
@@ -0,0 +1 @@ | |||
"[[-#,- -1-2. r--Ş#--O)C--!˙E-1‹@5-!-V-1-- |