Repository navigation
fix(LBIE): keep getVertGrad on the grid (intermittent crash in compute_qef) - #762
Merged
Merged
Conversation
…e_qef) LbieMesherTest.HexaVerticesStayInsideAVolumeTheSurfaceLeaves hit an access violation on windows-2022 Release (#758, run 39637628700) in its r = 1.5 sphere, after "finished collapse" and before "finished compute qef". Octree::interpRect3Dpts_x/y/z take a second gradient sample at the grid point after the one an edge's crossing falls in. When the edge ends on the volume's far face exactly at the isovalue (ival == 1), that point is one plane off the grid, and getVertGrad()/getValue() read past orig_vol: up to about 1 KB for a z edge. The r = 1.5 sphere, sampled at 17^3 in [-1,1]^3, passes exactly through 24 grid points on the box's faces (8^2 + 4^2 + 8^2 = 12^2 in eighths); r = 1.2 passes through none. AddressSanitizer flags the read on every run: heap-buffer-overflow READ of size 4, 12 bytes after the 19652-byte orig_vol Octree::getValue octree.cpp:3598 Octree::getVertGrad octree.cpp:3661 Octree::interpRect3Dpts_z octree.cpp:3501 Octree::cell_comp octree.cpp:606 Octree::compute_qef octree.cpp:760 getVertGrad() now clamps its point onto the grid. The off-grid sample's weight is 0, so meshes are unchanged (344 vertices / 4374 quads and 64 / 162 for the test's spheres, as before). New LbieMesherTest.MeshesASurfaceThroughGridPointsOnTheFarFaces meshes that sphere as TETRA, HEXA, DOUBLE and TETRA2; without the fix ASan flags each of them (compute_qef or compute_qef_interval).
transfix
enabled auto-merge (squash)
October 22, 2026 05:16
transfix
added a commit
that referenced
this pull request
Oct 22, 2026
…763) Found under AddressSanitizer while fixing the compute_qef crash (#762); neither is that crash. QUAD: Octree::assign_refine_sign_quad visited every edge the surface crosses, including edges on the volume's boundary, where find_oc_id gives -1 for the cells outside the grid. It then called get_err_grad(-1), which reads orig_vol about 18 KB before its start, and read and wrote vtx_idx_arr_refine[-1]. polygonize_quad never makes a quad for such an edge (is_min_edge treats an off-grid neighbour as refined and rejects it), so both loops now skip it. An edge has either all four cells on the grid or two or three off it, so an off-grid cell could never complete the 4-of-4 or 3-of-4 / opposite-pair conditions anyway: only the undefined reads change. Any surface that leaves the volume reached it; CI's QUAD tests use a sphere inside the box. heap-buffer-overflow READ of size 4, 4 bytes before the 18724-byte vtx_idx_arr_refine (4681 octree cells) Octree::assign_refine_sign_quad octree.cpp:6528 Octree::polygonize_quad octree.cpp:5958 getGrad (OPTIMIZATION): t1, d(jacobian)/d(v0) for the edges v1-v0, v2-v0, v3-v0, indexed its second factors 1-based: v1[3], v2[3], v3[3] (z) read past the float[3] arrays, and the z line used [2] for y. With the indices lowered, all three lines equal dJ/dv0 (checked against central differences on 1000 random tetrahedra, max error 8e-10; the old z line was off by up to 4.8). ASan: stack-buffer-overflow in LbieMesherTest.HexaDoMeshOptimize, reading past mmv3 toward mmv4, the point optimization()'s line search fills next (still uninitialized on the first call). That is a likely cause of the NaN vertices arm64 Release left in that test. Tests: - New LbieMesherTest.QuadMeshesASurfaceThatLeavesTheVolume (r = 1.2 sphere, default and fine error). - HexaDoMeshOptimize checks finite coordinates again (expect_geoframe_valid); the arm64 Release lane is the real check of that. - Without the fixes ASan flags both tests in 40/40 runs (OMP_NUM_THREADS 1, 2, 8, default); with them 40/40 clean. lbie_mesher_test 37/37 ASan-clean apart from HexaVerticesStayInsideAVolumeTheSurfaceLeaves, whose read #762 fixes; algorithm_test 10/10 and geometry_test 111/111 clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
LbieMesherTest.HexaVerticesStayInsideAVolumeTheSurfaceLeaves, #677's own regression test, hit an access violation (0xC0000005) in therecipe / windows-2022 / Releaselane of #758 (run 39637628700, job 119352377930). It crashed in the r = 1.5 sphere, after "finished collapse" and before "finished compute qef". That is earlier than the hexa vertex walk #677 fixed. It is intermittent: the same lane passed on #758's previous head and in ~40 other recent runs.Cause.
Octree::interpRect3Dpts_x/y/zinterpolate an edge crossing's normal between the gradients at two grid points: the one the crossing falls in and the next one. When the edge ends on the volume's far face exactly at the isovalue (ival == 1), the second point is one plane off the grid (index 17 of 17), andgetVertGrad→getValueread pastorig_vol. For a z edge that is up to ~1.1 KB past the end, far enough to reach an unmapped page now and then on Windows.Why only the second sphere: sampled at 17³ in [-1, 1]³, the r = 1.5 sphere passes exactly through 24 grid points on the box's faces (8² + 4² + 8² = 12² in eighths), where
r - dis exactly 0. The r = 1.2 sphere passes through none.AddressSanitizer (Linux, GCC 13, RelWithDebInfo) flags it on every run:
What
getVertGrad()clamps its grid point onto the volume. In the case above the off-grid sample's interpolation weight is exactly 0, so meshes are unchanged:These are the counts #677 reported. Only the out-of-bounds read goes away.
Tests
LbieMesherTest.MeshesASurfaceThroughGridPointsOnTheFarFacesmeshes the r = 1.5 sphere as TETRA, HEXA, DOUBLE and TETRA2. It checks the geoframe and that every normal is finite.HexaVerticesStayInsideAVolumeTheSurfaceLeavesis flagged in 40 of 40 runs (OMP_NUM_THREADS = 1, 2, 8 and the default, 10 runs each);compute_qef, DOUBLE and TETRA2 incompute_qef_interval.lbie_mesher_test37/37, clean apart from the pre-existingHexaDoMeshOptimizeread listed below;algorithm_test10/10 andgeometry_test111/111, clean./fsanitize=address. This PR's Windows lanes are the first Windows run.Found, not fixed (pre-existing; not this crash)
Octree::assign_refine_sign_quad(frompolygonize_quad) reads index -1 of an octcell-indexed array wherever the surface leaves the volume. Both the r = 1.2 and r = 1.5 spheres reach it. It is the same family as LBIE: keep the hexa vertex walk on the grid (intermittent hexahedralize crash) #677. CI's QUAD tests use r = 0.5, so they never reach it, and that is why the new test leaves QUAD out.Octree::getGradreadsv1[3],v2[3]andv3[3]offloat[3]arrays. ASan reports it as a stack-buffer-overflow inHexaDoMeshOptimizeviaOctree::optimization. A stack overread can't fault, but it feeds garbage into OPTIMIZATION's gradient, which may explain the arm64 Release NaNs that test's comment describes.