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
static const char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; | 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; | stressflags = tr->langopts.stress_flags; | ||||
if (dictionary_flags != NULL) | if (dictionary_flags != NULL) | ||||
*word_end = 'e'; | *word_end = 'e'; | ||||
} | } | ||||
i = word_end - word; | i = word_end - word; | ||||
if (i >= N_WORD_BYTES) i = N_WORD_BYTES-1; | |||||
if (word_copy != NULL) { | if (word_copy != NULL) { | ||||
memcpy(word_copy, word, i); | memcpy(word_copy, word, i); |
if (c2 != '1') { | if (c2 != '1') { | ||||
// a list of punctuation characters to be spoken, terminated by space | // a list of punctuation characters to be spoken, terminated by space | ||||
j = 0; | j = 0; | ||||
while (!Eof() && !iswspace(c2)) { | |||||
while (!Eof() && !iswspace(c2) && (j < N_PUNCTLIST-1)) { | |||||
option_punctlist[j++] = c2; | option_punctlist[j++] = c2; | ||||
c2 = GetC(); | c2 = GetC(); | ||||
buf[ix++] = ' '; | buf[ix++] = ' '; |
if ((f_in = fopen(buf, "rb")) == NULL) | if ((f_in = fopen(buf, "rb")) == NULL) | ||||
return create_file_error_context(context, errno, buf); | return create_file_error_context(context, errno, buf); | ||||
if (*ptr != NULL) | |||||
if (*ptr != NULL) { | |||||
free(*ptr); | free(*ptr); | ||||
*ptr = NULL; | |||||
} | |||||
if (length == 0) { | |||||
*ptr = NULL; | |||||
return 0; | |||||
} | |||||
if ((*ptr = malloc(length)) == NULL) { | if ((*ptr = malloc(length)) == NULL) { | ||||
fclose(f_in); | fclose(f_in); | ||||
int error = errno; | int error = errno; | ||||
fclose(f_in); | fclose(f_in); | ||||
free(*ptr); | free(*ptr); | ||||
*ptr = NULL; | |||||
return create_file_error_context(context, error, buf); | return create_file_error_context(context, error, buf); | ||||
} | } | ||||
// read the version number and sample rate from the first 8 bytes of phondata | // read the version number and sample rate from the first 8 bytes of phondata | ||||
version = 0; // bytes 0-3, version number | version = 0; // bytes 0-3, version number | ||||
rate = 0; // bytes 4-7, sample rate | 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) | if (version != version_phdata) |
if (dict_flags & FLAG_SPELLWORD) { | if (dict_flags & FLAG_SPELLWORD) { | ||||
// redo the word, speaking single letters | // redo the word, speaking single letters | ||||
for (pw = word; *pw != ' ';) { | for (pw = word; *pw != ' ';) { | ||||
memset(number_buf, 0, sizeof(number_buf)); | |||||
memset(number_buf, ' ', 9); | memset(number_buf, ' ', 9); | ||||
nx = utf8_in(&c_temp, pw); | nx = utf8_in(&c_temp, pw); | ||||
memcpy(&number_buf[2], pw, nx); | memcpy(&number_buf[2], pw, nx); |
MAKE_MEM_UNDEFINED(&voice_languages, sizeof(voice_languages)); | MAKE_MEM_UNDEFINED(&voice_languages, sizeof(voice_languages)); | ||||
} | } | ||||
if ((vname == NULL || vname[0] == 0) && !(control & 8)) { | |||||
return NULL; | |||||
} | |||||
strncpy0(voicename, vname, sizeof(voicename)); | strncpy0(voicename, vname, sizeof(voicename)); | ||||
if (control & 0x10) { | if (control & 0x10) { | ||||
strcpy(buf, vname); | strcpy(buf, vname); | ||||
if (!tone_only) { | if (!tone_only) { | ||||
if (!!(control & 8/*compiling phonemes*/)) { | 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); | fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name); | ||||
ix = 0; | ix = 0; | ||||
} | } |
if (wvoice == NULL) | if (wvoice == NULL) | ||||
return; | return; | ||||
int x; | |||||
int x = 0; | |||||
int ix; | int ix; | ||||
static int Flutter_ix = 0; | static int Flutter_ix = 0; | ||||
// advance the pitch | // advance the pitch | ||||
wdata.pitch_ix += wdata.pitch_inc; | wdata.pitch_ix += wdata.pitch_inc; | ||||
if ((ix = wdata.pitch_ix>>8) > 127) ix = 127; | 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; | wdata.pitch = (x>>8) + wdata.pitch_base; | ||||
if(const_f0) | if(const_f0) | ||||
wdata.pitch = (const_f0<<12); | wdata.pitch = (const_f0<<12); | ||||
if (wdata.pitch < 102400) | if (wdata.pitch < 102400) | ||||
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12) | wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12) | ||||
static bool resume = false; | static bool resume = false; | ||||
static int echo_complete = 0; | static int echo_complete = 0; | ||||
if (wdata.pitch < 102400) | |||||
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12) | |||||
while (out_ptr < out_end) { | while (out_ptr < out_end) { | ||||
if (WcmdqUsed() <= 0) { | if (WcmdqUsed() <= 0) { | ||||
if (echo_complete > 0) { | if (echo_complete > 0) { |
shell_test(translate) | shell_test(translate) | ||||
shell_test(variants) | shell_test(variants) | ||||
shell_test(voices) | shell_test(voices) | ||||
shell_test(crash) | |||||
# shell_test(windows-data) | # shell_test(windows-data) | ||||
# shell_test(windows-installer) | # shell_test(windows-installer) |
#!/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 |
ã¦à»Vñ€¦ñ €¦V €äVñ€ãÂà¦æsññâñþâññà¶æØØsññâñþâññeeeeeeeeseee€ññûñ |
€¦Vń €ńVđŐhńůâ˙ńVDíZ»»ŐöÖÖÖÖÖÖÖÖÖě»»ş»ÖľÖÖÖÖÖÖ´ÖÖÖ»ţţ÷ÜÖÖÖ»»ş»ŐŞ»»®î˙˙€ę`v |
"[[-#,- -1-2. r--Ş#--O)C--!˙E-1‹@5-!-V-1-- |