diff --git a/src/lens_map.c b/src/lens_map.c index f4a007c..92aedf6 100644 --- a/src/lens_map.c +++ b/src/lens_map.c @@ -297,6 +297,7 @@ int lens_map_read(const char *path, LensMapProvenance *provenance, read_u32(file, &end_id, &crc) || read_u32(file, &outcome, &crc) || read_u32(file, &reason, &crc) || outcome > RAY_OUTCOME_INCOMPLETE || !ray_reason_valid((RayReason)reason); + if (failed) break; v->end_id = (SpacetimeEndId)end_id; v->outcome = (RayOutcome)outcome; v->reason = (RayReason)reason; diff --git a/tests/test_frame.c b/tests/test_frame.c index e1f080b..589b114 100644 --- a/tests/test_frame.c +++ b/tests/test_frame.c @@ -1469,6 +1469,31 @@ int main(void) { lens_map_destroy(&dloaded); unlink(dp_path); goto done; } lens_map_destroy(&dloaded); + /* Truncate within the first v3 vertex, including each terminal field. + * Failed reads must reject the map and release its partially read mesh. */ + { + unsigned char prefix[316]; + FILE *fixture = fopen(dp_path, "rb"); + int fixture_failed = fixture == NULL || + fread(prefix, 1, sizeof prefix, fixture) != sizeof prefix; + if (fixture != NULL && fclose(fixture)) fixture_failed = 1; + if (fixture_failed) { + fputs("lens-map truncation fixture read failed\n", stderr); + unlink(dp_path); goto done; + } + const size_t cuts[] = {232, 303, 304, 307, 308, 311, 312, 315}; + for (size_t c = 0; c < sizeof cuts / sizeof cuts[0]; ++c) { + FILE *short_file = fopen(dp_path, "wb"); + int short_failed = short_file == NULL || + fwrite(prefix, 1, cuts[c], short_file) != cuts[c]; + if (short_file != NULL && fclose(short_file)) short_failed = 1; + if (short_failed || !lens_map_read(dp_path, NULL, &dloaded) || + dloaded.frames != NULL || dloaded.frame_count != 0) { + fputs("lens-map truncated vertex rejection regression failed\n", stderr); + lens_map_destroy(&dloaded); unlink(dp_path); goto done; + } + } + } /* Unknown wire code and non-finite/out-of-bounds DP fields must be rejected * by the shared schema validator, not accepted as a usable map. */ {