From e985f2724b67acd4c4cbfc1a460a29a341529ea5 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 6 Oct 2026 16:50:41 +0200 Subject: [PATCH] test(host-cache): changed declarations recompile project.so HostCacheSourceStamp already edits a module source with a backdated mtime and expects the PCM cache to be rebuilt. Its own header names the real danger, a project.so compiled against declarations that no longer match the process it is loaded into, but it never looked at the project.so. It now records the file's mtime before the edit and expects a newer one after it, with project.cpp untouched, so only the stamp can force the rebuild. Fails without the previous commit: the stamp was the version string alone and the project.so was reused. Co-Authored-By: Claude Fable 5.1 --- tests/HostCacheSourceStamp/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/HostCacheSourceStamp/main.cpp b/tests/HostCacheSourceStamp/main.cpp index b6904e8..4110c82 100644 --- a/tests/HostCacheSourceStamp/main.cpp +++ b/tests/HostCacheSourceStamp/main.cpp @@ -119,6 +119,17 @@ int main() { Check(fs::last_write_time(probePcm) == afterCold, "unchanged sources do not rebuild the cache"); Check(ReadStamp(moduleCacheDir) == firstStamp, "unchanged sources keep the same stamp"); + // The compiled project file is the other half of the same hazard: it is + // loaded into this process and must have been compiled against the + // declarations this process was built from. + fs::path projectSo; + for (const fs::directory_entry& entry : fs::recursive_directory_iterator(projectDir)) { + if (entry.path().filename() == "project.so") projectSo = entry.path(); + } + Check(!projectSo.empty(), "the project file was compiled to a project.so"); + if (projectSo.empty()) return 1; + const fs::file_time_type soBeforeEdit = fs::last_write_time(projectSo); + // Now the case the mtime rule cannot see. The edit lands in a module the // probe does not name, because the cached PCMs import each other: a change // anywhere in the set invalidates all of them. @@ -139,6 +150,11 @@ int main() { Check(fs::last_write_time(probePcm) > afterCold, "a content change rebuilds the cache even when the source's mtime is older"); Check(ReadStamp(moduleCacheDir) != firstStamp, "the recorded stamp follows the sources"); + // project.cpp itself is untouched, so only the stamp can force this. It + // once covered the version string alone (the rest sat behind a NUL in a + // format string): a project.so built against a class of another size was + // reused and corrupted the heap when it filled one in. + Check(fs::last_write_time(projectSo) > soBeforeEdit, "changed declarations recompile project.so even though project.cpp is unchanged"); fs::remove_all(scratch, ec);