From adc6855c1115ea891d58eca7de77f02e4786d88c Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 09:50:55 +0300 Subject: [PATCH 1/8] klatt: fix possible buffer overrun --- src/libespeak-ng/klatt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libespeak-ng/klatt.c b/src/libespeak-ng/klatt.c index 78254409..0b601ff6 100644 --- a/src/libespeak-ng/klatt.c +++ b/src/libespeak-ng/klatt.c @@ -215,13 +215,13 @@ static double sampled_source(int source_num) temp_diff = ftemp - (double)itemp; - current_value = samples[itemp]; - next_value = samples[itemp+1]; + current_value = samples[(itemp) % kt_globals.num_samples]; + next_value = samples[(itemp+1) % kt_globals.num_samples]; diff_value = (double)next_value - (double)current_value; diff_value = diff_value * temp_diff; - result = samples[itemp] + diff_value; + result = samples[(itemp) % kt_globals.num_samples] + diff_value; result = result * kt_globals.sample_factor; } else result = 0; From 98dc5c67e9327d41e5f32dd351df618d5c29f92e Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 09:51:55 +0300 Subject: [PATCH 2/8] klatt: fix logic error causes buffer overrun --- src/libespeak-ng/klatt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libespeak-ng/klatt.c b/src/libespeak-ng/klatt.c index 0b601ff6..55d294f7 100644 --- a/src/libespeak-ng/klatt.c +++ b/src/libespeak-ng/klatt.c @@ -1013,7 +1013,7 @@ static void SetSynth_Klatt(int length, frame_t *fr1, frame_t *fr2, voice_t *wvoi } for (ix = 0; ix < N_KLATTP; ix++) { - if ((ix >= 5) && ((fr1->frflags & FRFLAG_KLATT) == 0)) { + if ((ix >= 5) || ((fr1->frflags & FRFLAG_KLATT) == 0)) { klattp1[ix] = klattp[ix] = 0; klattp_inc[ix] = 0; } else { From 1e279d3cb80822e772dd7cf285601c835b5e73c4 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 09:58:29 +0300 Subject: [PATCH 3/8] Add PRNG and use it instead of rand() --- src/libespeak-ng/common.c | 13 +++++++++++++ src/libespeak-ng/common.h | 3 +++ src/libespeak-ng/klatt.c | 7 ++----- src/libespeak-ng/speech.c | 3 +++ src/libespeak-ng/wavegen.c | 3 ++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/libespeak-ng/common.c b/src/libespeak-ng/common.c index f44cf5e3..8b2bb872 100644 --- a/src/libespeak-ng/common.c +++ b/src/libespeak-ng/common.c @@ -326,4 +326,17 @@ int towlower2(unsigned int c, Translator *translator) return ucd_tolower(c); } +static uint32_t espeak_rand_state = 0; + +long espeak_rand(long min, long max) { + // Ref: https://github.com/bminor/glibc/blob/glibc-2.36/stdlib/random_r.c#L364 + espeak_rand_state = (((uint64_t)espeak_rand_state * 1103515245) + 12345) % 0x7fffffff; + long res = (long)espeak_rand_state; + return (res % (max-min+1))-min; +} + +void espeak_srand(long seed) { + espeak_rand_state = (uint32_t)(seed); + (void)espeak_rand(0, 1); // Dummy flush a generator +} diff --git a/src/libespeak-ng/common.h b/src/libespeak-ng/common.h index 76fff8bf..ecec0d0e 100644 --- a/src/libespeak-ng/common.h +++ b/src/libespeak-ng/common.h @@ -26,6 +26,9 @@ extern ESPEAK_NG_API int GetFileLength(const char *filename); extern ESPEAK_NG_API void strncpy0(char *to, const char *from, int size); +void espeak_srand(long seed); +long espeak_rand(long min, long max); + int IsAlpha(unsigned int c); int IsBracket(int c); int IsDigit(unsigned int c); diff --git a/src/libespeak-ng/klatt.c b/src/libespeak-ng/klatt.c index 55d294f7..954553c0 100644 --- a/src/libespeak-ng/klatt.c +++ b/src/libespeak-ng/klatt.c @@ -35,6 +35,7 @@ #include #include "klatt.h" +#include "common.h" // for espeak_rand #include "synthesize.h" // for frame_t, WGEN_DATA, STEPSIZE, N_KLATTP, echo... #include "voice.h" // for voice_t, N_PEAKS #ifdef INCLUDE_SPEECHPLAYER @@ -46,11 +47,7 @@ extern unsigned char *out_end; static int nsamples; static int sample_count; -#ifdef _MSC_VER -#define getrandom(min, max) ((rand()%(int)(((max)+1)-(min)))+(min)) -#else -#define getrandom(min, max) ((rand()%(long)(((max)+1)-(min)))+(min)) -#endif +#define getrandom(min, max) espeak_rand((min), (max)) // function prototypes for functions private to this file diff --git a/src/libespeak-ng/speech.c b/src/libespeak-ng/speech.c index ed704623..6d05512b 100644 --- a/src/libespeak-ng/speech.c +++ b/src/libespeak-ng/speech.c @@ -404,6 +404,9 @@ ESPEAK_NG_API espeak_ng_STATUS espeak_ng_Initialize(espeak_ng_ERROR_CONTEXT *con option_phonemes = 0; option_phoneme_events = 0; + // Seed random generator + espeak_srand(time(NULL)); + return ENS_OK; } diff --git a/src/libespeak-ng/wavegen.c b/src/libespeak-ng/wavegen.c index cc9d9dd4..5762c988 100644 --- a/src/libespeak-ng/wavegen.c +++ b/src/libespeak-ng/wavegen.c @@ -33,6 +33,7 @@ #include #include "wavegen.h" +#include "common.h" // for espeak_rand #include "synthesize.h" // for WGEN_DATA, RESONATOR, frame_t #include "mbrola.h" // for MbrolaFill, MbrolaReset, mbrola... @@ -668,7 +669,7 @@ static int ApplyBreath(void) int ix; // use two random numbers, for alternate formants - noise = (rand() & 0x3fff) - 0x2000; + noise = espeak_rand(-0x2000, 0x1fff); for (ix = 1; ix < N_PEAKS; ix++) { int amp; From 09cdf8ce66bc328fb13c107a66278ef47d0e3732 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 09:59:06 +0300 Subject: [PATCH 4/8] Add public API to seed PRNG --- src/include/espeak-ng/espeak_ng.h | 3 +++ src/libespeak-ng/common.c | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/include/espeak-ng/espeak_ng.h b/src/include/espeak-ng/espeak_ng.h index 0bf2309b..81988e48 100644 --- a/src/include/espeak-ng/espeak_ng.h +++ b/src/include/espeak-ng/espeak_ng.h @@ -212,6 +212,9 @@ espeak_ng_SetOutputHooks(espeak_ng_OUTPUT_HOOKS* hooks); ESPEAK_NG_API espeak_ng_STATUS espeak_ng_SetConstF0(int f0); +ESPEAK_NG_API espeak_ng_STATUS +espeak_ng_SetRandSeed(long seed); + #ifdef __cplusplus } diff --git a/src/libespeak-ng/common.c b/src/libespeak-ng/common.c index 8b2bb872..5e1f4316 100644 --- a/src/libespeak-ng/common.c +++ b/src/libespeak-ng/common.c @@ -340,3 +340,10 @@ void espeak_srand(long seed) { (void)espeak_rand(0, 1); // Dummy flush a generator } +#pragma GCC visibility push(default) +ESPEAK_NG_API espeak_ng_STATUS +espeak_ng_SetRandSeed(long seed) { + espeak_srand(seed); + return ENS_OK; +} +#pragma GCC visibility pop From 24892aae061ed75fc8876bfb3fbd4ff99d4f378c Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 10:00:17 +0300 Subject: [PATCH 5/8] Support deterministic PRNG mode in CLI --- src/espeak-ng.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/espeak-ng.c b/src/espeak-ng.c index 0891a24f..515916f0 100644 --- a/src/espeak-ng.c +++ b/src/espeak-ng.c @@ -79,6 +79,7 @@ static const char *help_text = "-x\t Write phoneme mnemonics to stdout\n" "-X\t Write phonemes mnemonics and translation trace to stdout\n" "-z\t No final sentence pause at the end of the text\n" + "-D\t Enable deterministic random mode\n" "--compile=\n" "\t Compile pronunciation rules and dictionary from the current\n" "\t directory. specifies the language\n" @@ -353,6 +354,7 @@ int main(int argc, char **argv) int phoneme_options = 0; int option_linelength = 0; int option_waveout = 0; + bool deterministic = 0; espeak_VOICE voice_select; char filename[200]; @@ -368,7 +370,7 @@ int main(int argc, char **argv) option_punctlist[0] = 0; while (true) { - c = getopt_long(argc, argv, "a:b:d:f:g:hk:l:mp:qs:v:w:xXz", + c = getopt_long(argc, argv, "a:b:Dd:f:g:hk:l:mp:qs:v:w:xXz", long_options, &option_index); // Detect the end of the options. @@ -388,6 +390,9 @@ int main(int argc, char **argv) case 'd': strncpy0(devicename, optarg2, sizeof(devicename)); break; + case 'D': + deterministic = 1; + break; case 'h': printf("\n"); PrintVersion(); @@ -583,6 +588,11 @@ int main(int argc, char **argv) exit(1); } + if (deterministic) { + // Set random generator state to well-known + espeak_ng_SetRandSeed(1); + } + if (option_waveout || quiet) { // writing to a file (or no output), we can use synchronous mode result = espeak_ng_InitializeOutput(ENOUTPUT_MODE_SYNCHRONOUS, 0, devicename[0] ? devicename : NULL); From ca4b340cc35bcb00fb56f264744b9406c0fbf203 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 10:02:26 +0300 Subject: [PATCH 6/8] Use deterministic espeak-ng in tests --- tests/common | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/common b/tests/common index 6c841817..cc86c718 100755 --- a/tests/common +++ b/tests/common @@ -75,7 +75,7 @@ test_wav () { echo "testing ${VOICE}${MESSAGE}" ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} \ - $VALGRIND src/espeak-ng --stdout -v ${VOICE} "${TEST_TEXT}" \ + $VALGRIND src/espeak-ng -D --stdout -v ${VOICE} "${TEST_TEXT}" \ > actual.txt || exit 1 < actual.txt $sha1sum | awk '{ print $1 }' > sum.txt echo "${EXPECTED}" > expected.txt @@ -94,13 +94,13 @@ test_wav_grep () { MESSAGE=$4 echo "testing ${VOICE}${MESSAGE}" ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} \ - $VALGRIND src/espeak-ng --stdout -v ${VOICE} "${TEST_TEXT}" \ + $VALGRIND src/espeak-ng -D --stdout -v ${VOICE} "${TEST_TEXT}" \ > actual.txt || exit 1 < actual.txt $sha1sum | awk '{ print $1 }' > sum.txt if [ "$MESSAGE" = "Ignore" ] ; then cat sum.txt | grep -E "$EXPECTED" || (echo "... ignoring error" && true) else - cat sum.txt | grep -E "$EXPECTED" || { printf "wrong hash: "; cat actual.txt; exit 1; } + cat sum.txt | grep -E "$EXPECTED" || { printf "wrong hash: "; cat sum.txt; exit 1; } fi } From e2c44ab965553f1b54475e1d8c1cd38b4fc5c3ae Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 10:00:42 +0300 Subject: [PATCH 7/8] Add breath/klatt variants to tests --- tests/variants.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/variants.test b/tests/variants.test index 9b5000f1..972ccedf 100755 --- a/tests/variants.test +++ b/tests/variants.test @@ -9,3 +9,5 @@ test_wav "en" 029983e9084e04384af8a0816fb667e5c5e06389 "Testing variants" test_wav "en+nonexisting" 029983e9084e04384af8a0816fb667e5c5e06389 "Testing variants" test_wav "en+f1" dba359ac75ec33cc9cd4bd2af5031a3dbd84427c "Testing variants" test_wav "en+anikaRobot" d56012d8f4cfb4c36fdad31ad9ad7abda40ef474 "Testing variants" +test_wav "en+Demonic" cc24431d23ce6d0a36f6a46df374f995beeb2863 "Testing variants" +test_wav "en+klatt4" e03e3c6204de48d3e5b172cca4cca1ff09461ee1 "Testing variants" From 4d9b4ad1f9dc0394256ac825ac5b61821800ab1c Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 15 Nov 2022 10:01:55 +0300 Subject: [PATCH 8/8] Fix and re-enable klatt test --- tests/common | 11 ----------- tests/klatt.test | 14 +++++--------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/common b/tests/common index cc86c718..953d2e25 100755 --- a/tests/common +++ b/tests/common @@ -20,17 +20,6 @@ check_hash() { # Test some common commands to find the correct one for the system being tested on. } -# test if Klatt synthesizer is installed -is_klatt() { - echo -n "checking if klatt is installed ... " - if [ "`which klatt`" != "" ]; then - echo "yes" - else - echo "no" - exit - fi -} - # test if MBROLA synthesizer is installed is_mbrola() { echo -n "checking if MBROLA is installed ... " diff --git a/tests/klatt.test b/tests/klatt.test index a82bdb75..280de7bf 100755 --- a/tests/klatt.test +++ b/tests/klatt.test @@ -3,13 +3,9 @@ . "`dirname $0`/common" # and run needed checks before is_hash -is_klatt # call actual test functions -test_wav_grep en+klatt "4b91f47e4be93993e53bbc04cbee7378118f990a" "The quick brown fox jumps over the lazy dog" -test_wav_grep en+klatt2 "bc99291590ff83b1e877688633adf3c20a65cd9c" "The quick brown fox jumps over the lazy dog" -test_wav_grep en+klatt3 "b6e89f4027d173166f49e4d933361651b43f4ca8|2bcc5a6e6c2fc4cd43f41c6a4bdf026c370a1fe9" "The quick brown fox jumps over the lazy dog" -# this voice seems to make random sound/hashes -#test_wav en+klatt4 "377767780368115863cde5cc11c0203cfdd48476" "The quick brown fox jumps over the lazy dog" -test_wav_grep en+klatt5 "6665329b413244b9846533ce207f0ee3d6e55094" "The quick brown fox jumps over the lazy dog" - - +test_wav en+klatt "8155a2249db8f6fe5f3b1d4fd00c35d09ceb6c82" "The quick brown fox jumps over the lazy dog" +test_wav en+klatt2 "90559a2ff9973c3ccf1eb7e71579fedfedbd3c67" "The quick brown fox jumps over the lazy dog" +test_wav en+klatt3 "a636bc97d68fbec753c7cecc718b539c87627095" "The quick brown fox jumps over the lazy dog" +test_wav en+klatt4 "653e243019c9462c7a9a90f4b03326d45b45f41c" "The quick brown fox jumps over the lazy dog" +test_wav en+klatt5 "6665329b413244b9846533ce207f0ee3d6e55094" "The quick brown fox jumps over the lazy dog"