This mostly follows LLVM's InstrProfReader.cpp error handling.
Previously, attempting to merge corrupted profile data would result in
crashes. See https://crbug.com/1216811#c4.
Details
- Reviewers
rnk - Commits
- rG189428c8fc24: [Profile] Handle invalid profile data
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
I think it's worth the time to carefully refactor this code so we don't end up in a similar situation, maybe in the value profile data.
compiler-rt/lib/profile/InstrProfiling.h | ||
---|---|---|
110 | This is a bit of an ABI backwards compatibility concern, but I think adding a return type should be safe enough. | |
compiler-rt/lib/profile/InstrProfilingFile.c | ||
268 | The joys of not having RAII... | |
compiler-rt/lib/profile/InstrProfilingMerge.c | ||
100 | I'm having a hard time reasoning about this code. My favorite code pattern for safely reading binary data in C++ is the "consume" pattern, illustrated in the consume* APIs in RecordSerialization.h: Unfortunately, I don't think it translate to C very well. I guess the main recommendation I have would be to keep everything in bytes for as long as possible, and delay casting until after bounds checking. | |
133 | I think we should check SrcValueProfData + SrcValueProfData->TotalSize against ProfileSize while we're here. The NVK value seems like it's not really needed, it just checks for the absence of value data. |
Sorry about all the nits, bounds checking is all about attention to detail.
compiler-rt/lib/profile/InstrProfilingMerge.c | ||
---|---|---|
101 | size_t is unsigned, so this check is probably always false, and will probably trigger a warning in some build configurations. | |
114 | size_t is unsigned, so underflow is possible here. The < 0 check below doesn't catch anything. I think that's fine: any value in the range [0, MaxNumCounters) is OK. | |
118 | This is really nitty, but I think the overflow-safe way to do this addition and comparison is to use subtraction. We know both values are less than MaxNumCounters, so either subtraction is safe: NC >= MaxNumCounters - SrcCountersOffset Also, should these be >=? I think the final condition should be: if (SrcCountersOffset >= MaxNumCounters || NC >= MaxNumCounters - SrcCountersOffset) return 1; Hopefully I did that right. | |
120 | nit: this variable is used once in the loop, maybe inline the addition into the loop, save a line (SrcCountersStart[SrcCountersOffset + I]). It makes it clearer that we are notionally indexing into the SrcCountersStart array, which is of length MaxNumCounters. | |
134 | clang-tidy suggests a pointer cast here. Honestly, we'll have fewer casts if we change SrcValueProfData to be a const char * and cast when passing into VPMergeHook. |
sorry, I think I updated while you were reviewing and some of your comments are no longer relevant
there are a couple ways to handle the invalid profile
- remove it and bail
- remove it and overwrite with the current profile
- leave it and don't write anything more
- seems the least useful, 3) seems the most useful for diagnosing issues, 2) is the most "keep going in the face of failures"
perhaps we should go with 3)?
compiler-rt/test/profile/Linux/corrupted-profile.c | ||
---|---|---|
47 | I'm not sure this will reliably trigger the corruption check on big endian platforms. I'd recommend memsetting 8 bytes to 0xAB to reduce the likelihood of buildbot emails. I also worry about 32-bit buildbots, but I think it's fine. |
I agree, it's a choice between 2 and 3. 3 would be consistent with what happens when the existing profraw file is from another binary, right? Maybe do that? It risks losing coverage data, and means the corrupt file is persisted. I guess that's best, since it will cause llvm-profdata merge to fail later.
compiler-rt/lib/profile/InstrProfilingMerge.c | ||
---|---|---|
112 | Is the second check redundant ? It is already checked outside the loop. |
This is a bit of an ABI backwards compatibility concern, but I think adding a return type should be safe enough.