mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-08 18:48:04 +00:00
Fix out-of-bounds reads in the multi-extruder flush-volume matrix
flush_volumes_matrix stores one (filaments x filaments) block per nozzle, but set_extruder, WipeTower2::extract_wipe_volumes and the PresetBundle rebuild indexed each block with filament_colour.size() as the row stride. When the stored matrix does not match that assumption (a legacy project, or a just-switched printer -- e.g. a single 2x2 block while nozzle_diameter has 2 entries) the index runs past the sliced block and reads out of bounds. The read is undefined, so its value depends on the C++ std-lib/allocator layout: identical across Linux architectures but different on macOS/Windows. That surfaced as the "Toolchange temperature commands are unchanged when the wipe tower wait is off" regression failing on Linux CI while passing on macOS/Windows, and crashing hardened (-O0, _GLIBCXX_ASSERTIONS) builds. - Centralize the block-dimension derivation in get_flush_volumes_matrix_dims (sqrt(size / nozzles) with a filaments^2 * nozzles == size check and a single-block fallback) and use it wherever the matrix is sliced/indexed; bounds-guard the reads as defense in depth. It weighs both options that claim to say how many blocks are stored, since either can be stale: flush_multiplier, written with the matrix in the project config, and nozzle_diameter, which changes the moment a printer is selected. - PresetBundle::update_multi_material_filament_presets: rebuild the matrix on a nozzle-count-only change too (the old per-block gate missed those), and seed a brand-new nozzle from the first nozzle's tuned block. - is_flush_config_modified: stride the stored matrix by its own dimension rather than the current filament count, and bound the nozzle loop by the printer's extruder count, which CalcFlushingVolumes indexes as well. - is_flushing_matrix_error: the same derivation, which additionally divided by zero on an empty flush_multiplier. - update_slice_warnings: guard nozzle_hrc_lists, which is sized by the nullable nozzle_type option and can be shorter than the extruder count. With the read fixed the emitted trace is deterministic across builds and platforms, so regenerate the golden from it and stop comparing the rounded "time: <n>s" preheat comment (the tolerant lead time already carries that timing). Add unit tests for the flush-matrix rebuild and dimension logic.
This commit is contained in:
@@ -205,27 +205,29 @@ bool is_flush_config_modified()
|
||||
const std::vector<double> &config_matrix = (project_config.option<ConfigOptionFloats>("flush_volumes_matrix"))->values;
|
||||
const std::vector<double> &config_multiplier = (project_config.option<ConfigOptionFloats>("flush_multiplier"))->values;
|
||||
|
||||
bool has_modify = false;
|
||||
for (int i = 0; i < config_multiplier.size(); i++) {
|
||||
if (config_multiplier[i] != 1) {
|
||||
has_modify = true;
|
||||
break;
|
||||
}
|
||||
std::vector<std::vector<double>> default_matrix = WipingDialog::CalcFlushingVolumes(i);
|
||||
int len = default_matrix.size();
|
||||
for (int m = 0; m < len; m++) {
|
||||
for (int n = 0; n < len; n++) {
|
||||
int idx = i * len * len + m * len + n;
|
||||
if (config_matrix[idx] != default_matrix[m][n] * config_multiplier[i]) {
|
||||
has_modify = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_modify) break;
|
||||
}
|
||||
if (has_modify) break;
|
||||
for (double multiplier : config_multiplier)
|
||||
if (multiplier != 1)
|
||||
return true;
|
||||
|
||||
// Orca: take the row stride and the block count from the stored matrix, not from the current
|
||||
// filament count. The two disagree until update_multi_material_filament_presets rebuilds the
|
||||
// matrix (a project saved with fewer filaments, or a printer just switched), and indexing a
|
||||
// stale matrix with the current stride reads past the option. Clamp to the printer's extruder
|
||||
// count as well, since CalcFlushingVolumes indexes per-extruder options with the same id.
|
||||
const size_t extruder_count = wxGetApp().preset_bundle->get_printer_extruder_count();
|
||||
const FlushVolumesMatrixDims dims = get_flush_volumes_matrix_dims(config_matrix.size(), config_multiplier.size(), extruder_count);
|
||||
const size_t nozzle_nums = std::min(dims.nozzle_nums, extruder_count);
|
||||
for (size_t i = 0; i < nozzle_nums; i++) {
|
||||
std::vector<std::vector<double>> default_matrix = WipingDialog::CalcFlushingVolumes(int(i));
|
||||
if (default_matrix.size() != dims.filament_nums)
|
||||
return false; // Stored matrix predates the current filament count; it is about to be rebuilt.
|
||||
// Every multiplier is 1 here, so the stored block has to equal the defaults outright.
|
||||
for (size_t m = 0; m < dims.filament_nums; m++)
|
||||
for (size_t n = 0; n < dims.filament_nums; n++)
|
||||
if (config_matrix[(i * dims.filament_nums + m) * dims.filament_nums + n] != default_matrix[m][n])
|
||||
return true;
|
||||
}
|
||||
return has_modify;
|
||||
return false;
|
||||
}
|
||||
|
||||
void open_flushing_dialog(wxEvtHandler *parent, const wxEvent &event)
|
||||
|
||||
Reference in New Issue
Block a user