From 378fafa2850222b09c266b8d68762b6124677985 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2020 10:50:32 -0700 Subject: [PATCH 1/4] Use getentropy from std::random_device, avoiding FS usage of /dev/urandom. Fixes #10068. This does *not* change the random number mechanism, just it avoids going through the FS --- src/library.js | 41 ++++++++++++++++++++++++++++++ src/library_fs.js | 30 ++-------------------- system/include/compat/sys/random.h | 9 +++++++ system/include/compat/sys/unistd.h | 1 + system/include/libcxx/__config | 2 +- tests/core/test_random_device.cpp | 2 +- 6 files changed, 55 insertions(+), 30 deletions(-) create mode 100644 system/include/compat/sys/random.h diff --git a/src/library.js b/src/library.js index e2b42e443d387..1651d36d129cb 100644 --- a/src/library.js +++ b/src/library.js @@ -2845,6 +2845,47 @@ LibraryManager.library = { endgrent: function() { throw 'endgrent: TODO' }, setgrent: function() { throw 'setgrent: TODO' }, + // random.h + + // TODO: consider allowing the API to get a parameter for the number of + // bytes. + $getRandomDevice: function() { + if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { + // for modern web browsers + var randomBuffer = new Uint8Array(1); + return function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; + } else +#if ENVIRONMENT_MAY_BE_NODE + if (ENVIRONMENT_IS_NODE) { + // for nodejs with or without crypto support included + try { + var crypto_module = require('crypto'); + // nodejs has crypto support + return function() { return crypto_module['randomBytes'](1)[0]; }; + } catch (e) { + // nodejs doesn't have crypto support + } + } +#endif // ENVIRONMENT_MAY_BE_NODE + // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 +#if ASSERTIONS + return function() { abort("no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };"); }; +#else + return function() { abort("randomDevice"); }; +#endif + }, + + getentropy__deps: ['$getRandomDevice'], + getentropy: function(buffer, size) { + if (!_getentropy.randomDevice) { + _getentropy.randomDevice = getRandomDevice(); + } + for (var i = 0; i < size; i++) { + {{{ makeSetValue('buffer', 'i', '_getentropy.randomDevice()', 'i8') }}} + } + return 0; + }, + // ========================================================================== // emscripten.h // ========================================================================== diff --git a/src/library_fs.js b/src/library_fs.js index 528bbb3a2c1d9..ee4d55d4d591f 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -5,7 +5,7 @@ */ mergeInto(LibraryManager.library, { - $FS__deps: ['$setErrNo', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', + $FS__deps: ['$setErrNo', '$getRandomDevice', '$PATH', '$PATH_FS', '$TTY', '$MEMFS', #if LibraryManager.has('library_idbfs.js') '$IDBFS', #endif @@ -1344,33 +1344,7 @@ FS.staticInit();` + FS.mkdev('/dev/tty', FS.makedev(5, 0)); FS.mkdev('/dev/tty1', FS.makedev(6, 0)); // setup /dev/[u]random - var random_device; - if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { - // for modern web browsers - var randomBuffer = new Uint8Array(1); - random_device = function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; - } else -#if ENVIRONMENT_MAY_BE_NODE - if (ENVIRONMENT_IS_NODE) { - // for nodejs with or without crypto support included - try { - var crypto_module = require('crypto'); - // nodejs has crypto support - random_device = function() { return crypto_module['randomBytes'](1)[0]; }; - } catch (e) { - // nodejs doesn't have crypto support - } - } else -#endif // ENVIRONMENT_MAY_BE_NODE - {} - if (!random_device) { - // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 -#if ASSERTIONS - random_device = function() { abort("no cryptographic support found for random_device. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };"); }; -#else - random_device = function() { abort("random_device"); }; -#endif - } + var random_device = getRandomDevice(); FS.createDevice('/dev', 'random', random_device); FS.createDevice('/dev', 'urandom', random_device); // we're not going to emulate the actual shm device, diff --git a/system/include/compat/sys/random.h b/system/include/compat/sys/random.h new file mode 100644 index 0000000000000..3c6167436c532 --- /dev/null +++ b/system/include/compat/sys/random.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C" { +#endif + +int getentropy(void *buffer, size_t length); + +#ifdef __cplusplus +} +#endif diff --git a/system/include/compat/sys/unistd.h b/system/include/compat/sys/unistd.h index 1e823fbd53c6d..9aa99550e6758 100644 --- a/system/include/compat/sys/unistd.h +++ b/system/include/compat/sys/unistd.h @@ -1 +1,2 @@ #include +#include // for getentropy() diff --git a/system/include/libcxx/__config b/system/include/libcxx/__config index 088213618b1e7..34f4d7731fdba 100644 --- a/system/include/libcxx/__config +++ b/system/include/libcxx/__config @@ -308,7 +308,7 @@ // random data even when using sandboxing mechanisms such as chroots, // Capsicum, etc. # define _LIBCPP_USING_ARC4_RANDOM -#elif defined(__Fuchsia__) || defined(__wasi__) +#elif defined(__Fuchsia__) || defined(__wasi__) || defined(__EMSCRIPTEN__) # define _LIBCPP_USING_GETENTROPY #elif defined(__native_client__) // NaCl's sandbox (which PNaCl also runs in) doesn't allow filesystem access, diff --git a/tests/core/test_random_device.cpp b/tests/core/test_random_device.cpp index 9ab6d3c7f650a..4c8de8efe83c0 100644 --- a/tests/core/test_random_device.cpp +++ b/tests/core/test_random_device.cpp @@ -12,7 +12,7 @@ auto main() try { std::random_device rd; - std::cout << "random was read" << "\n"; + std::cout << rd() << ", a random was read\n"; } catch( const std::exception& e ) { From db3eeb9b9fb282e3283672c35a45e664f69fc5fa Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 16 Sep 2020 17:26:31 -0700 Subject: [PATCH 2/4] remove unnecessary include --- system/include/compat/sys/unistd.h | 1 - 1 file changed, 1 deletion(-) diff --git a/system/include/compat/sys/unistd.h b/system/include/compat/sys/unistd.h index 9aa99550e6758..1e823fbd53c6d 100644 --- a/system/include/compat/sys/unistd.h +++ b/system/include/compat/sys/unistd.h @@ -1,2 +1 @@ #include -#include // for getentropy() From 021df8a7147382462f895897a0e32f0acd5c8c1e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 17 Sep 2020 10:52:31 -0700 Subject: [PATCH 3/4] feedback --- ChangeLog.md | 2 ++ system/lib/libcxx/readme.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 455dc79c20223..6af38160f9ce1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,8 @@ See docs/process.md for how version tagging works. Current Trunk ------------- +- Add `getentropy` in `sys/random.h`, and use that from libc++'s + `random_device`. This is more efficient, see #12240. 2.0.4: 09/16/2020 ----------------- diff --git a/system/lib/libcxx/readme.txt b/system/lib/libcxx/readme.txt index 969e0884e5ae3..acd017aff5b98 100755 --- a/system/lib/libcxx/readme.txt +++ b/system/lib/libcxx/readme.txt @@ -20,3 +20,5 @@ Local modifications are marked with the comment: 'XXX EMSCRIPTEN' 3. Define _LIBCPP_ELAST in libcxx/include/config_elast.h 4. Set init_priority of __start_std_streams in libcxx/iostream.cpp + +5. Use _LIBCPP_USING_GETENTROPY (like wasi) From 97caa0741eae8738fd7ead4a5758ed4abb204f65 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Thu, 17 Sep 2020 16:31:11 -0700 Subject: [PATCH 4/4] comment on why we added this [ci skip] --- system/include/compat/sys/random.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/include/compat/sys/random.h b/system/include/compat/sys/random.h index 3c6167436c532..72f3bf7a26bd6 100644 --- a/system/include/compat/sys/random.h +++ b/system/include/compat/sys/random.h @@ -2,6 +2,11 @@ extern "C" { #endif +// This is used by libc++ as an efficient way to get high-quality random data +// (more efficiently than via the filesystem using /dev/urandom). +// Upstream musl added support for this, so we can switch to that, but it isn't +// where libc++ looks for it (which is here and not unistd.h), and it uses a +// syscall which is unnecessary indirection for us. int getentropy(void *buffer, size_t length); #ifdef __cplusplus