Zcu.saveZirCache: mark safety buffer defined for valgrind
In safety-checked builds, Zir.Inst.Data is a tagged union where @sizeOf(Data) > 8 due to the safety tag. saveZirCache strips these tags by reinterpreting each Data as a HackDataLayout and copying the first 8 bytes into a safety_buffer. Union variants that use fewer than 8 bytes of payload leave the remaining bytes uninitialised. The bulk copy propagates these uninitialised V-bits into safety_buffer, causing valgrind to report: Syscall param pwritev(vector[...]) points to uninitialised byte(s) when the buffer is written to the cache file. This is harmless: loadZirCache reconstructs the safety tag from the tag array, and each variant only reads its own fields — the padding is never interpreted. @memset before the copy does not help: the assignment `safety_buffer[i] = as_struct.data` copies all 8 bytes from the source union, and valgrind propagates the per-byte defined/undefined status (V-bits) from source to destination, re-tainting the padding. Use makeMemDefined after the copy loop to inform valgrind that the padding contents are intentional. This compiles to a no-op when not running under valgrind. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2932,6 +2932,12 @@ pub fn saveZirCache(gpa: Allocator, cache_file: std.fs.File, stat: std.fs.File.S
|
||||
const as_struct: *const HackDataLayout = @ptrCast(data);
|
||||
safety_buffer[i] = as_struct.data;
|
||||
}
|
||||
// Union variants smaller than 8 bytes leave padding uninitialised.
|
||||
// The copy above propagates those V-bits into safety_buffer, so
|
||||
// valgrind flags the subsequent pwritev. The padding is never
|
||||
// interpreted on read-back (the tag array determines the active
|
||||
// variant), so tell valgrind the buffer contents are intentional.
|
||||
std.valgrind.memcheck.makeMemDefined(std.mem.sliceAsBytes(safety_buffer));
|
||||
}
|
||||
|
||||
const header: Zir.Header = .{
|
||||
|
||||
Reference in New Issue
Block a user