Skip to content

Commit 483b437

Browse files
committed
gh-145177: Bump Emscripten to 6.0.0
And fix two problems due to changes in Emscripten libc: 1. There was a regression in getentropy that broke it if exactly one byte is requested. Replace it with a wrapped getentropy that replaces a one byte request with a two-byte request and then put the first byte in the original buffer. 2. Emscripten libc now supports umask. We need to initialize the emscripten libc umask to the ambient umask and zero out the ambient umask.
1 parent e99b319 commit 483b437

5 files changed

Lines changed: 56 additions & 14 deletions

File tree

Lib/test/test_platform.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,20 @@ def test_ios_ver(self):
534534

535535
def test_libc_ver(self):
536536
if support.is_emscripten:
537-
assert platform.libc_ver() == ("emscripten", "4.0.19")
537+
import tomllib
538+
from pathlib import Path
539+
540+
# Get expected emscripten version from emscripten config
541+
config_path = (
542+
Path(__file__).parents[2] / "Platforms/emscripten/config.toml"
543+
)
544+
with open(config_path, "rb") as fp:
545+
emscripten_version = tomllib.load(fp)["emscripten-version"]
546+
547+
self.assertEqual(
548+
platform.libc_ver(), ("emscripten", emscripten_version)
549+
)
550+
538551
return
539552
# check that libc_ver(executable) doesn't raise an exception
540553
if os.path.isdir(sys.executable) and \

Platforms/emscripten/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Any data that can vary between Python versions is to be kept in this file.
22
# This allows for blanket copying of the Emscripten build code between supported
33
# Python versions.
4-
emscripten-version = "4.0.19"
4+
emscripten-version = "6.0.0"
55
node-version = "24"
66
test-args = [
77
"-m", "test",

Python/emscripten_syscalls.c

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,27 @@ int __syscall_getuid32(void) {
2323
return __syscall_getuid32_js();
2424
}
2525

26-
EM_JS(int, __syscall_umask_js, (int mask), {
27-
// If we're in node and we can, call native process.umask()
26+
// Emscripten's syscall layer tracks the umask in SYSCALLS.currentUmask and
27+
// applies it itself when creating files and directories. We mount the real
28+
// filesystem via NODEFS, which applies proces.umask() to everything as well. To
29+
// avoid masking the mode twice, read and zero out process umask at startup,
30+
// and store it as emscripten's umask.
31+
EM_JS(void, __syscall_init_umask_js, (void), {
2832
if (ENVIRONMENT_IS_NODE) {
2933
try {
30-
return process.umask(mask);
31-
} catch(e) {
32-
// oops...
33-
// NodeJS docs: "In Worker threads, process.umask(mask) will throw an exception."
34-
// umask docs: "This system call always succeeds"
35-
return 0;
34+
// process.umask(0) returns the previous umask and sets it to 0.
35+
SYSCALLS.currentUmask = process.umask(0);
36+
} catch (e) {
37+
// NodeJS docs: "In Worker threads, process.umask(mask) will throw an
38+
// exception." In that case just keep emscripten's default umask.
3639
}
3740
}
38-
// Fall back to the stub case of returning 0.
39-
return 0;
4041
})
4142

42-
int __syscall_umask(int mask) {
43-
return __syscall_umask_js(mask);
43+
EM_JS_DEPS(__syscall_init_umask, "$SYSCALLS");
44+
45+
__attribute__((constructor)) void __syscall_init_umask(void) {
46+
__syscall_init_umask_js();
4447
}
4548

4649
#include <wasi/api.h>
@@ -290,6 +293,26 @@ int __syscall_poll(intptr_t fds, int nfds, int timeout) {
290293
return __block_for_int(p);
291294
}
292295

296+
297+
// Workaround for an Emscripten bug: getentropy(buffer, 1) returns the single
298+
// byte of entropy as the return code. Fixed upstream by
299+
// emscripten-core/emscripten#27122
300+
int __real_getentropy(void*, size_t);
301+
302+
int __wrap_getentropy(void *buffer, size_t len) {
303+
if (len != 1) {
304+
return __real_getentropy(buffer, len);
305+
}
306+
// Length is 1. Workaround is to get two bytes of entropy and write the
307+
// first one into the original target buffer.
308+
uint8_t tmp[2];
309+
int ret = __real_getentropy(tmp, 2);
310+
if (ret == 0) {
311+
*(uint8_t *)buffer = tmp[0];
312+
}
313+
return ret;
314+
}
315+
293316
#include <sys/ioctl.h>
294317

295318
int syscall_ioctl_orig(int fd, int request, void* varargs)

configure

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,10 @@ AS_CASE([$ac_sys_system],
24112411
dnl Avoid bugs in JS fallback string decoding path
24122412
AS_VAR_APPEND([LINKFORSHARED], [" -sTEXTDECODER=2"])
24132413
2414+
dnl Workaround for a bug in Emscipten libc's getentropy. See
2415+
dnl __wrap_getentropy in Python/emscripten_syscalls.c.
2416+
AS_VAR_APPEND([LDFLAGS_NODIST], [" -Wl,--wrap=getentropy"])
2417+
24142418
AS_VAR_IF([enable_wasm_dynamic_linking], [yes], [
24152419
AS_VAR_APPEND([LINKFORSHARED], [" -sMAIN_MODULE"])
24162420
])

0 commit comments

Comments
 (0)