Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug in freeing geo polygon memory #104

Merged
merged 3 commits into from
Dec 3, 2020

Conversation

nrabinowitz
Copy link
Collaborator

Closes #103

This fixes some errors in freeing GeoPolygon memory, which could get the Emscripten virtual memory management into a bad state.

Explanation of the issue: We were expecting the GeoPolygon reference to Geofence to be a pointer, but it’s not, the struct is inlined into GeoPolygon. So I was trying to free the value at the geofence position, because I thought it was a pointer, but I’m actually getting the first value in the Geofence struct, which is numVerts, and trying to free that. C would probably segfault at this point, Emscripten just gets confused and ends up with undefined behavior in future calls.

@coveralls
Copy link

coveralls commented Dec 2, 2020

Pull Request Test Coverage Report for Build 217

  • 7 of 7 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 100.0%

Totals Coverage Status
Change from base Build 213: 0.0%
Covered Lines: 399
Relevant Lines: 399

💛 - Coveralls

// Offset of the geofence vertex array pointer within the Geofence struct
const geofenceArrayOffset = SZ_INT;
// Free the outer vertex array
C._free(C.getValue(geoPolygon + geofenceOffset + geofenceArrayOffset, 'i8*'));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the main issue, including the geofenceArrayOffset when looking up the vertex array pointer.

lib/h3core.js Outdated
for (let i = 0; i < numHoles; i++) {
C._free(C.getValue(geoPolygon + holesOffset + SZ_GEOFENCE * i, 'i8*'));
C._free(C.getValue(holes + SZ_GEOFENCE * i + geofenceArrayOffset, 'i8*'));
Copy link
Collaborator Author

@nrabinowitz nrabinowitz Dec 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a test case that showed the issue, but this was pretty clearly incorrect - we weren't looking up the holes in the right way at all.

Do I need to free holes here too? It seems like I might...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates to free holes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you need to free the holes, they're a pointer in the GeoPolygon struct

for (let i = 0; i < numHoles; i++) {
C._free(C.getValue(holes + SZ_GEOFENCE * i + geofenceArrayOffset, 'i8*'));
}
C._free(holes);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you need to go into each hole struct and free the inner coordinates array, too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what L257 does - gets the array pointer for each hole and frees it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bleh, I clearly shouldn't be reviewing C-memory-management-in-JS code while also working on my own code. Somehow I thought the loop was the outer array being deallocated.

Copy link
Collaborator

@isaacbrodsky isaacbrodsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any ways to test for this class of bugs? For example is there an equivalent to Valgrind that can be used to check that the allocated/freed buffers match up?

test/h3core.spec.js Outdated Show resolved Hide resolved
@nrabinowitz
Copy link
Collaborator Author

nrabinowitz commented Dec 2, 2020

Are there any ways to test for this class of bugs? For example is there an equivalent to Valgrind that can be used to check that the allocated/freed buffers match up?

I honestly don't know. The memory starts off clean, so all of the issues are deterministic. The original test case (the pair of polygons in #103) was deterministic, so I could test via script and determine whether the counts matched or not. The test I put into the test suite was deterministic too, but not in the way I'd like - if it mismanaged the memory, that test still passed, but 23 other subsequent tests failed with weird behavior. So I can reasonably assert that I fixed the original bug. But I doubt there's any Valgrind equivalent just for Emscripten-compiled code :/. I'm a bit worried here that I don't have a great way to assert that the holes arrays are freed correctly in this PR - I couldn't manage a failing test here, even though I can see that the previous code doesn't look correct.

@nrabinowitz nrabinowitz merged commit 3c7ab0f into uber:master Dec 3, 2020
@nrabinowitz nrabinowitz deleted the destroy-polygon-fix branch December 3, 2020 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Inconsistent results from polyfill after calling polyfill with complex polygon crossing antimeridian
4 participants