From 564e88cd2732765015c55e7b7beb5d3df7405b49 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Sat, 17 Oct 2026 18:46:50 +0800 Subject: [PATCH] Fix Crash Selecting a User Printer Whose Parent Preset Is Not Loaded A user preset can inherit a system preset from a vendor that is not installed. get_preset_base() returns null for it, and get_selected_preset_base() dereferenced that unconditionally, so picking the printer crashed while building the shared profiles notification URL. Fall back to the selected preset itself. The create printer and create filament dialogs had the same null dereference on such presets. --- src/libslic3r/Preset.hpp | 9 ++++++++- src/slic3r/GUI/CreatePresetsDialog.cpp | 4 ++-- tests/libslic3r/test_preset_bundle_loading.cpp | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index b6dc3b37bb..8d5372c5e4 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -756,7 +756,14 @@ public: Preset& get_edited_preset() { return m_edited_preset; } const Preset& get_edited_preset() const { return m_edited_preset; } - const Preset& get_selected_preset_base() const { return *get_preset_base(m_presets[m_idx_selected]); } + // The root of the selected preset's inherits chain, or the selected preset itself when + // the chain names a preset that is not loaded (e.g. a vendor that is not installed). + const Preset& get_selected_preset_base() const + { + const Preset &selected = m_presets[m_idx_selected]; + const Preset *base = get_preset_base(selected); + return base ? *base : selected; + } // Return the last saved preset. // const Preset& get_saved_preset() const { return m_saved_preset; } diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index 0d5f688948..92925227ac 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -3151,7 +3151,7 @@ void CreatePrinterPresetDialog::set_current_visible_printer() m_printer_name_to_preset.clear(); for (const Preset &printer_preset : printer_presets) { if (!printer_preset.is_visible) continue; - if (preset_bundle->printers.get_preset_base(printer_preset)->name != printer_preset.name) continue; + if (preset_bundle->printers.get_preset_base(printer_preset) != &printer_preset) continue; if (auto printer_model = dynamic_cast(const_cast(printer_preset).config.option("printer_model", false))) { if (m_printer_name_to_preset.find(printer_model->value) == m_printer_name_to_preset.end()) { printer_choice.push_back(from_u8(printer_model->value)); @@ -4962,7 +4962,7 @@ void CreatePresetForPrinterDialog::get_visible_printer_and_compatible_filament_p if (filament_preset.is_default || !filament_preset.is_compatible || filament_preset.is_project_embedded) continue; ConfigOptionStrings *filament_types; const Preset * filament_preset_base = m_preset_bundle->filaments.get_preset_base(filament_preset); - if (filament_preset_base == &filament_preset) { + if (filament_preset_base == &filament_preset || filament_preset_base == nullptr) { filament_types = dynamic_cast(const_cast(&filament_preset)->config.option("filament_type")); } else { filament_types = dynamic_cast(const_cast(filament_preset_base)->config.option("filament_type")); diff --git a/tests/libslic3r/test_preset_bundle_loading.cpp b/tests/libslic3r/test_preset_bundle_loading.cpp index 7ea1452042..1b4927f08c 100644 --- a/tests/libslic3r/test_preset_bundle_loading.cpp +++ b/tests/libslic3r/test_preset_bundle_loading.cpp @@ -1219,6 +1219,24 @@ TEST_CASE("find_preset2 auto-matches removed Generic vendor profiles to the libr CHECK(bundle.filaments.find_preset2("BrandX Generic PETG", /*auto_match=*/true) == nullptr); } +TEST_CASE("The selected preset is its own base when its parent is not loaded", "[Preset]") +{ + PresetCollection printers(Preset::TYPE_PRINTER, Preset::printer_options(), + static_cast(FullPrintConfig::defaults())); + add_inmemory_preset(printers, "Printer"); + add_inmemory_preset(printers, "User Printer", "Printer"); + add_inmemory_preset(printers, "Orphan Printer", "Uninstalled Vendor Printer"); + + printers.select_preset_by_name("User Printer", true); + REQUIRE(printers.get_selected_preset_name() == "User Printer"); + CHECK(printers.get_selected_preset_base().name == "Printer"); + + printers.select_preset_by_name("Orphan Printer", true); + REQUIRE(printers.get_selected_preset_name() == "Orphan Printer"); + REQUIRE(printers.get_preset_base(printers.get_selected_preset()) == nullptr); + CHECK(printers.get_selected_preset_base().name == "Orphan Printer"); +} + TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Preset][Rename]") { ScopedTemporaryDir temp_dir;