test(libnest2d): fix use-after-free crash in NfpPlacer lifetime tests

NfpPlacer stores std::reference_wrapper to the items it packs and re-reads
them from finalAlign() in its destructor (via clearItems()). Two placer
tests declared the placer before the items in the same scope, so the items
were destroyed first and the destructor dereferenced dangling references.

On macOS this is a deterministic SIGSEGV: libmalloc poisons the freed block
on free, so the item's point vector reads back as ~null (deref at 0x8). On
Linux/glibc the freed bytes usually survive, which is why it slipped through
upstream CI (introduced by #14267).

Declare the items before the placer so they outlive it, matching the pattern
the sibling 'packs many items' and 'obstacle' tests already use. Test-only;
the library lifetime contract (items must outlive the placer) is unchanged
and honored in production via _Nester in Arrange.cpp.
This commit is contained in:
SoftFever
2026-07-10 01:55:00 +08:00
parent 00816968a2
commit b58dcf5978

View File

@@ -56,8 +56,10 @@ struct NfpPlacerFixture {
} // namespace
TEST_CASE_METHOD(NfpPlacerFixture, "NfpPlacer places a single item inside the bin", "[Nesting][Placer]") {
NfpPlacer placer = placer_with();
// The placer only keeps references to the items it packs and re-reads them
// from finalAlign() in its destructor, so the item must outlive the placer.
RectangleItem item{100000000, 100000000};
NfpPlacer placer = placer_with();
REQUIRE(place(placer, item));
REQUIRE(placer.getItems().size() == 1u);
@@ -103,12 +105,15 @@ TEST_CASE_METHOD(NfpPlacerFixture, "NfpPlacer packs many items without overlap",
}
TEST_CASE_METHOD(NfpPlacerFixture, "NfpPlacer evaluates the rotation candidates", "[Nesting][Placer]") {
// The placer re-reads its packed items from finalAlign() in its destructor,
// so the items must outlive the placer — declare them first.
std::vector<RectangleItem> rects = {
{180000000, 40000000}, {180000000, 40000000}, {180000000, 40000000}};
Cfg cfg;
cfg.rotations = {0.0, Pi / 2.0}; // exercise the rotation search loop
NfpPlacer placer = placer_with(cfg);
std::vector<RectangleItem> rects = {
{180000000, 40000000}, {180000000, 40000000}, {180000000, 40000000}};
place_all(placer, rects);
require_disjoint_in_bin(rects);
}