CString::new(...).unwrap() in lib.rs panics if the string contains an interior NUL byte. Two of the three sites carry user data:
lib.rs:307 - warning text
lib.rs:329 - error text, which splices raw source into the message at lib.rs:186
Since Rust 1.81 a panic inside an extern "C" fn is non-unwinding, so this aborts the host process instead of surfacing an error.
Reproduced:
source: #set text(font: sys.inputs.f)
sys.inputs: {"f":"A\u0000B"}
panicked at NulError(22, [... unknown font family: a,0,b])
panic in a function that cannot unwind -> abort, exit 127
sys.inputs values come from JSON, which allows \u0000, so this is reachable from ordinary input rather than only from a hand-crafted .typ file. For anyone rendering per HTTP request, one bad value takes down the worker process and every in-flight render with it.
Suggested fix: sanitise before constructing the CString, e.g. CString::new(msg.replace('\0', "\u{fffd}")), and wrap the body of compile in catch_unwind, returning the panic as a CompileResult error. Typst has internal panics of its own, so the catch_unwind seems worth having regardless.
CString::new(...).unwrap()inlib.rspanics if the string contains an interior NUL byte. Two of the three sites carry user data:lib.rs:307- warning textlib.rs:329- error text, which splices raw source into the message atlib.rs:186Since Rust 1.81 a panic inside an
extern "C"fn is non-unwinding, so this aborts the host process instead of surfacing an error.Reproduced:
sys.inputsvalues come from JSON, which allows\u0000, so this is reachable from ordinary input rather than only from a hand-crafted.typfile. For anyone rendering per HTTP request, one bad value takes down the worker process and every in-flight render with it.Suggested fix: sanitise before constructing the
CString, e.g.CString::new(msg.replace('\0', "\u{fffd}")), and wrap the body ofcompileincatch_unwind, returning the panic as aCompileResulterror. Typst has internal panics of its own, so thecatch_unwindseems worth having regardless.