From: Felix Fietkau Date: Mon, 6 Jul 2026 11:12:32 +0200 Subject: [PATCH] compiler: prevent unbounded recompilation on failed module imports A source's exports.offset of -1 marks a module whose compilation has been entered but not completed. When a module failed to compile, uc_compiler_finish freed its function and the error cascaded up through the importers via error recovery, leaving each involved source at offset -1 with its function gone. The re-entry guard only recognised an in-progress module by finding its function, so those freed modules were recompiled again on every subsequent import. Across a wide import graph this recompilation multiplies and the compiler appears to hang. Detect the offset -1 marker directly instead of relying on the presence of a function: report a circular dependency when the function is still around, and propagate the failure otherwise, rather than recursing into the module again. Signed-off-by: Felix Fietkau --- --- a/compiler.c +++ b/compiler.c @@ -3479,15 +3479,26 @@ uc_compiler_compile_module_source(uc_com uc_program_function_foreach(compiler->program, fn) { if (uc_program_function_source(fn) == source) { - if (source->exports.offset == (size_t)-1) - uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, - "Circular dependency"); - loaded = true; break; } } + /* An offset of -1 marks a source whose compilation was entered but not yet + * completed. Re-entering it is either a circular dependency (its function is + * still present) or a module whose earlier compilation failed and freed its + * function. Recompiling in either case recurses without bound across a large + * import graph, so report the cycle and propagate the failure instead. */ + if (source->exports.offset == (size_t)-1) { + if (loaded) + uc_compiler_syntax_error(compiler, compiler->parser->prev.pos, + "Circular dependency"); + else if (errp) + xasprintf(errp, "Module previously failed to compile\n"); + + return false; + } + if (!loaded) { /* We do not yet support linking precompiled modules at compile time, turn static import operation into dynamic load one. */