emscripten-core / emscripten-core/emscripten

std::random_device fails on windows with NODERAWFS

Open
#9,628 11 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
27.6k
Forks
3.6k
Avg merge
1d 1h
Merged PRs (30d)
105

Description

Given the following emscripten-bug.cpp
```cpp
#include
#include

#ifdef __cplusplus
extern "C" {
#endif

void emscripten_test() {
std::random_device rd;
// Fails on windows if NODERAWFS is enabled
std::cerr << "Random " << rd() << "\n";
std::cerr << "Random " << rd() << "\n";
std::cerr << "Random " << rd() << "\n";
}

#ifdef __cplusplus
}
#endif
```

Compiled via
```
em++ -Wall -O3 -pipe -DNDEBUG -fomit-frame-pointer -std=c++11 -c \
-o build/emscripten-bug.o emscripten-bug.cpp
emcc build/emscripten-bug.o -o build/emscripten-bug.js \
--js-opts 0 -O3 \
-s WASM=0 \
-s DETERMINISTIC=1 \
-s EXPORTED_FUNCTIONS="['_emscripten_test']" \
-s USE_CLOSURE_COMPILER=0 \
-s ALLOW_MEMORY_GROWTH=1 \
-s ENVIRONMENT=node \
-s NODERAWFS=1 \
--memory-init-file 0
```

And invoked by
```js
const sass = require('./build/emscripten-bug');
sass._emscripten_test();
```

This fails to work on windows. Either removing the `random_device` or using `NODERAWFS=0` fixes the problem, but I really want to have both :/

The reason why it fails is because `NODERAWFS` overwrites the virtual FS with the following code:

```js
if (ENVIRONMENT_IS_NODE) {
var _wrapNodeError = function(func) {
return function() {
try {
return func.apply(this, arguments)
} catch (e) {
if (!e.code) throw e;
throw new FS.ErrnoError(ERRNO_CODES[e.code]);
}
}
};
var VFS = Object.assign({}, FS);
for (var _key in NODERAWFS) {
FS[_key] = _wrapNodeError(NODERAWFS[_key]);
}
} else {
throw new Error("NODERAWFS is currently only supported on Node.js environment.");
}
```

The error happens when it tries to read `/dev/urandom`. Logically this file will not be available on windows and it seems that's why the original virtual FS defines those directly via:

```js
FS.createDevice('/dev', 'random', random_device);
FS.createDevice('/dev', 'urandom', random_device);
```

So instead of using the "shimmed" device it tries to use the real ones.

The fix seems somewhat simple, but I'm not sure what the best way is.
I think it's reasonable to use the VFS for `dev` and `proc` even if `NODERAWFS` is enabled, as those are probably never really portable. Maybe another flag is needed (or use `NODERAWFS=2`) so users can decide whether they want to use "emulated" `dev` and `proc` instead of real ones.

Following is a quick hack/hotfix to get it working:

```js
if (ENVIRONMENT_IS_NODE) {
var VFS = Object.assign({}, FS);
var _wrapNodeError = function(key, func) {
return function() {
if (arguments.length && arguments['0'].match && arguments['0'].match(/^\/+(?:dev|proc)\//)) {
return VFS[key].apply(this, arguments);
}
try {
return func.apply(this, arguments)
} catch (e) {
if (!e.code) throw e;
throw new FS.ErrnoError(ERRNO_CODES[e.code]);
}
}
};
for (var _key in NODERAWFS) {
FS[_key] = _wrapNodeError(_key, NODERAWFS[_key]);
}
} else {
throw new Error("NODERAWFS is currently only supported on Node.js environment.");
}
```

I might even be able to come up with a PR once it's decided how you want to move forward with this. Thank you and have a nice day!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.