{"record":{"id":"73e490dcb4060c1f","repo":"windmill-labs/windmill","slug":"fputs-is-not-supported-73e490","errorCode":null,"errorMessage":"fputs is not supported","messagePattern":"fputs is not supported","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/parsers/windmill-parser-r/src/wasm_libc.rs","lineNumber":241,"sourceCode":"}\n\n/* --------------------------------- ctype.h -------------------------------- */\n\n#[no_mangle]\npub unsafe extern \"C\" fn isprint(c: c_int) -> bool {\n    c >= 32 && c <= 126\n}\n\n/* --------------------------------- stdio.h -------------------------------- */\n\n#[no_mangle]\npub unsafe extern \"C\" fn fprintf(_file: *mut c_void, _format: *const c_void, _args: ...) -> c_int {\n    panic!(\"fprintf is not supported\");\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int {\n    panic!(\"fputs is not supported\");\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn fputc(_c: c_int, _file: *mut c_void) -> c_int {\n    panic!(\"fputc is not supported\");\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn fdopen(_fd: c_int, _mode: *const c_void) -> *mut c_void {\n    panic!(\"fdopen is not supported\");\n}\n\n#[no_mangle]\npub unsafe extern \"C\" fn fclose(_file: *mut c_void) -> c_int {\n    panic!(\"fclose is not supported\");\n}\n\n#[no_mangle]","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/parsers/windmill-parser-r/src/wasm_libc.rs#L223-L259","documentation":"windmill-parser-r compiles R analysis to WASM and only stubs the libc symbols the binary links against; unsupported ones are `no_mangle` shims that panic unconditionally. `fputs` (write a string to a FILE* stream) has no WASM backing here, so any call aborts with this panic. It signals the parsed/embedded R code path tried to do buffered stdio output the sandbox does not implement.","triggerScenarios":"Calling the exported `fputs` shim in backend/parsers/windmill-parser-r/src/wasm_libc.rs, typically reached when R/compiled code writes to stdout/stderr or a file via fputs.","commonSituations":"R code in a script does `cat()`/`writeLines()` compiled down to fputs, or a C-shim/static library linked into the WASM calls fputs; running the parser outside the intended output channel where stdout is not wired up.","solutions":["Rewrite the R code to use the parser's supported output mechanism instead of fputs (e.g. functions whose output the host captures via supported primitives)","Check the wasm_libc.rs stub table and add a real implementation that forwards the bytes to the host if string output is required","Remove or replace the dependent library code path that emits via FILE* streams","Report/track as unsupported-feature if the R source legitimately needs stdio"],"exampleFix":"// before (stub)\n#[no_mangle]\npub unsafe extern \"C\" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int {\n    panic!(\"fputs is not supported\");\n}\n// after (forward to host)\n#[no_mangle]\npub unsafe extern \"C\" fn fputs(s: *const c_char, _file: *mut c_void) -> c_int {\n    let cstr = CStr::from_ptr(s).to_string_lossy();\n    print!(\"{}\", cstr);\n    cstr.len() as c_int\n}","handlingStrategy":"try-catch","validationCode":"// Static check before running the R payload through the parser\nconst rSource = \"...\";\nif (/\\b(fputs|fputc|fwrite|fdopen|fclose)\\s*\\(/.test(rSource) || /\\.C\\(|\\.Call\\(/.test(rSource)) {\n  throw new Error(\"R source uses stdio/file APIs unsupported by the R parser WASM sandbox\");\n}","typeGuard":"// Rust: never assume a libc stub is functional; treat FILE*-taking externs as unsupported\nfn stream_io_supported(symbol: &str) -> bool {\n    !matches!(symbol, \"fputs\" | \"fputc\" | \"fwrite\" | \"fdopen\" | \"fclose\" | \"fprintf\" | \"fopen\")\n}","tryCatchPattern":"match std::panic::catch_unwind(|| parser_run_r(source)) {\n    Ok(result) => result,\n    Err(p) => {\n        let msg = p.downcast_ref::<String>().map(String::as_str)\n            .or_else(|| p.downcast_ref::<&str>().copied())\n            .unwrap_or(\"unknown panic\");\n        if msg.contains(\"not supported\") {\n            // fall back: reject or rewrite the R source to avoid stdio calls\n        } else { std::panic::resume_unwind(p); }\n    }\n}","preventionTips":["Audit R/C payloads for FILE*-based stdio calls before feeding them to the parser","Prefer return-value or supported print channels for script output","Keep the list of stubbed symbols in wasm_libc.rs documented and checked by CI","Wrap WASM execution in catch_unwind so unsupported-symbol panics become actionable errors"],"tags":["wasm","libc","rust","r-parser"],"backgroundTag":"wasm-libc-function-not-supported","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}