From 7d174004437c16d84ee3cde03475b647e4017465 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Wed, 8 Jul 2026 19:56:03 -0500 Subject: [PATCH] fix: dangling static lambda crashes support G-code export on repeated slices (#14677) GCode::extrude_support declared its per-path speed helper as a function-local static lambda that captures `this` by reference. The closure is built once, on the first extrude_support call, and reused for the rest of the process, so a second G-code export in the same process runs the helper against a `this` from the first export's stack frame, which has already returned. The stale `this` flows through NOZZLE_CONFIG(...) -> cur_extruder_index() -> GCodeWriter::filament(), reading a garbage current-extruder id and indexing with it. It is silent whenever the reused stack still holds a usable pointer, and an order-dependent SIGSEGV otherwise; AddressSanitizer reports it as a stack-use-after-return in GCodeWriter::filament(). It is the only static capturing lambda in libslic3r. Drop static so the closure is rebuilt each call against the live frame. Add an fff_print regression test that slices a support object twice in one process; it fails without the fix (stack-use-after-return under ASan) and passes with it. --- src/libslic3r/GCode.cpp | 3 ++- tests/fff_print/test_support_material.cpp | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0b62fd0d6f..8238f5be85 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6215,7 +6215,8 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill static constexpr const char* support_transition_label = "support transition"; static constexpr const char* support_ironing_label = "support ironing"; - static const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) { + // Not static: it captures `this` by reference. + const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) { if (!is_support(role) || length > SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_support_perimeter_threshold))) return default_speed; diff --git a/tests/fff_print/test_support_material.cpp b/tests/fff_print/test_support_material.cpp index 24c0e9cdc5..f854939c8c 100644 --- a/tests/fff_print/test_support_material.cpp +++ b/tests/fff_print/test_support_material.cpp @@ -93,3 +93,14 @@ SCENARIO("Support layer Z honors contact distance", "[SupportMaterial]") } } } + +// extrude_support once held a `static` lambda capturing `this`, so a second export in the +// same process dereferenced a returned stack frame (ASan: stack-use-after-return). +TEST_CASE("Support G-code emission survives a second slice in the same process", "[SupportMaterial][Regression]") +{ + const std::string first = slice({ TestMesh::overhang }, { { "enable_support", 1 } }); + REQUIRE(! layers_with_role(first, "support").empty()); + + const std::string second = slice({ TestMesh::overhang }, { { "enable_support", 1 } }); + REQUIRE(! layers_with_role(second, "support").empty()); +}