brintos

brintos / linux public Read only

0
0

wasm32: dup3(2) (#292) not wired in the syscall table -> freopen() ENOSYS (breaks hexdump <file> and any freopen user) #12

Open sasha opened this issue · 0 comments
S sasha commented

Symptom

On the deployed wasm32-hwjs glibc image, hexdump <file> (util-linux, argv form) fails:

$ printf ABC > /tmp/f && hexdump -C /tmp/f
hexdump: /tmp/f: Function not implemented

while reading the same file on stdin works:

$ printf ABC | hexdump -C
00000000  41 42 43                                          |ABC|

and coreutils od /tmp/f reads the file fine. So it is not a file/VFS problem — it is specific to how hexdump opens a named file.

Root cause

util-linux hexdump opens a named file by reopening it onto stdin: freopen(name, "r", stdin) (text-utils/hexdump-display.c:next()). On glibc 2.43 freopen implements the fd move with dup3(newfd, 0, 0). In arch/wasm32/kernel/syscall_table.c the dup3 slot (x86_64 #292) is default-NULLdup (#32), dup2 (#33) and fcntl (#72) are wired, but dup3 is not — so do_syscall returns -ENOSYS, freopen returns NULL, and hexdump reports the errno. od uses a plain open()/fopen() with no fd move, which is why it is unaffected.

This is not hexdump-specific: any freopen() user, and any direct dup3(2) caller, hits -ENOSYS on this kernel.

Reproduction

Deployed machine: sasha/core-image-brintos-emcraft-glibc (util-linux 2.39.3, glibc 2.43). In the browser terminal:

printf ABC > /tmp/f && hexdump -C /tmp/f      # -> hexdump: /tmp/f: Function not implemented
printf ABC | hexdump -C                       # -> 00000000  41 42 43  |ABC|  (works)

Evidence in-tree: grep -nE '\[(32|33|292)\]\s*=' arch/wasm32/kernel/syscall_table.c shows [32]/[33] wired and no [292].

Proposed fix

Wire dup3 in the wasm32 table, in the same shape as the existing dup2 entry (3-arg: dup3(oldfd, newfd, flags)):

--- a/arch/wasm32/kernel/syscall_table.c
+++ b/arch/wasm32/kernel/syscall_table.c
@@
 extern long sys_dup2(unsigned int oldfd, unsigned int newfd);
+extern long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags);
@@
 static long w_sys_dup2(long a0, long a1, long a2, long a3, long a4, long a5)
 { IGNORE_TAIL_4; return sys_dup2((unsigned int)a0, (unsigned int)a1); }
+
+/* dup3(oldfd, newfd, flags) — 3 args (glibc freopen()/dup2 -> dup3) */
+static long w_sys_dup3(long a0, long a1, long a2, long a3, long a4, long a5)
+{ IGNORE_TAIL_3; return sys_dup3((unsigned int)a0, (unsigned int)a1, (int)a2); }
@@
 	[33]  = w_sys_dup2,             /* dup2 */
+	[292] = w_sys_dup3,             /* dup3 (glibc freopen; util-linux hexdump <file>) */

Candidate, not yet built — flagged because of the alias-materialization caveat from #10: if the plain sys_dup3 reference leaves a dangling 0xFFFFFFFF relocation (the SYSCALL_DEFINE alias non-materialization that broke the kernel link for sys_capget/sys_capset), reference __se_sys_dup3 instead, exactly as #10 recommends for capget/capset. dup2 links today with the plain sys_dup2, so the plain form is the first thing to try.

Workaround in the meantime: dump a file on stdin (hexdump -C < file), the identical formatting path.

Proposed fix targets brintos/linux only — no distro submodule pins moved. All distros share one lineage, so pin bumps are coordinated on merge, not per-engineer.