Port libFuzzer to windows-msvc.
This patch allows libFuzzer targets to be built and run on Windows, using -fsanitize=fuzzer and/or fsanitize=fuzzer-no-link. It allows these forms of coverage instrumentation to work on Windows as well.
It does not fix all issues, such as those with -fsanitize-coverage=stack-depth, which is not usable on Windows as of this patch.
It also does not fix any libFuzzer integration tests. Nearly all of them fail to compile, fixing them will come in a later patch, so libFuzzer tests are disabled on Windows until them.
Details
- Reviewers
morehouse rnk - Commits
- rG7e042bb1d18a: [libFuzzer] Port to Windows
rG245ebd71ef2f: [libFuzzer] Port to Windows
rGc6fff3b6f5b9: [libFuzzer] Port to Windows
rCRT341082: [libFuzzer] Port to Windows
rC341082: [libFuzzer] Port to Windows
rL341082: [libFuzzer] Port to Windows
rCRT340949: [libFuzzer] Port to Windows
rL340949: [libFuzzer] Port to Windows
rC340949: [libFuzzer] Port to Windows
rC340860: [libFuzzer] Port to Windows
rCRT340860: [libFuzzer] Port to Windows
rL340860: [libFuzzer] Port to Windows
Diff Detail
Event Timeline
@morehouse Can you please take a look?
llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll | ||
---|---|---|
1 ↗ | (On Diff #161783) | @kcc I wrote a lit test for a basic sanity check. |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
---|---|---|
815 | Nit: else is unnecessary. |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
35 | Can we make an OS-specific macro for __attribute__((tls_model("initial-exec"))) and/or thread_local instead? | |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
23 | What happens without align(1) and uint64_t? | |
llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll | ||
23 ↗ | (On Diff #161797) | Does body of foo matter for this test? Can it just return? |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
35 | I'm looking into this, but it looks a bit complicated to get this working, as there are two problems here:
Will investigate this further. | |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
23 | Without align(1) the arrays get aligned so that in between __start___sancov_cntrs and __stop___sancov_cntrs and in between __start___sancov_pcs and __stop___sancov_pcs there are padding bytes, causing libFuzzer to think there is an incorrect number of PCs and counters, which causes an early exit. tl;dr without either (or without incremental:no) I get: ERROR: The size of coverage PC tables does not match the number of instrumented PCs. This might be a compiler bug, please contact the libFuzzer developers. Also check https://bugs.llvm.org/show_bug.cgi?id=34636 for possible workarounds (tl;dr: don't use the old GNU ld) | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
815 | Done. | |
llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll | ||
23 ↗ | (On Diff #161797) | No. Yes it can return. |
llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll | ||
---|---|---|
23 ↗ | (On Diff #161797) | Can it be simplified more? For example, inline-8bit-counters.ll does: define void @foo() { entry: ret void } |
llvm/test/Instrumentation/SanitizerCoverage/coff-pc-table-inline-8bit-counters.ll | ||
---|---|---|
23 ↗ | (On Diff #161797) | Ah my bad. I think it is minimal now. |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
527 | Rather than having these ifdefs, can we adjust the parameters passed to these init functions (i.e. modify SecStart and SecEnd in SanitizerCoverage.cpp)? Then users of SanCov won't have to do this manual adjustment for Windows either. |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
527 | I don't think we can adjust the parameters using the current approach. |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
527 | Actually, I have an idea for how to do this. I'll get back to you. |
compiler-rt/lib/fuzzer/FuzzerTracePC.cpp | ||
---|---|---|
527 | I now do this addition in SanitizerCoverage.cpp as you suggested. There are no more #ifdefs in this file. Thank you for helping me figure out how to do this offline. |
Reid could you please take a look.
This patch gets libFuzzer working on Windows.
Most functionality seems to work, a few features are not yet supported and the tests don't yet work. I plan on fixing these issues soon.
compiler-rt/lib/fuzzer/tests/FuzzerUnittest.cpp | ||
---|---|---|
33–38 | Basename should be fixed to handle / and \ on windows. | |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
23 | I'm not sure align(1) really addresses this problem. The Microsoft linker will add padding either way for incremental links. For ASan global metadata registration, we had to teach the runtime to tolerate zero padding. | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
276–281 | I kind of prefer ifdefs in runtime C code over platform-specific instrumentation. @morehouse, wdyt? | |
814 | I would recommend naming your sections: Your start markers and end markers will work just the same as .SCOV$[CPG][AZ]. You should also add a /merge:.SCOV=.data flag with #pragma comment linker in the sanitizer coverage runtime. That way the final PE file won't have a .SCOV section, it'll all be in .data, laid out the way you want. |
@rnk thank you for the feedback.
I will fix the issues you have raised shortly, but I have some questions on the feedback you left.
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
---|---|---|
23 | Ouch. I will take a look at what ASAN does to handle padding and try to implement something similar. | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
814 | Can the PCTable which is constant go into .data? |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
---|---|---|
276–281 | Why would you prefer that way? My reasoning is that its better to fix this in one place rather than expect every client of SanCov to place ifdefs for Windows in their code. |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
---|---|---|
23 | @rnk and @kcc (assuming we can't prevent incremental links and need to deal with padding): |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
---|---|---|
23 | I'm not sure we can. I think if your approach works, we might want to commit it and then debug the problems in alternative VC link configurations. It might be OK if libfuzzer only works on windows with lld. | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
814 | I see, I checked and I thought I didn't see any constant data. That should use a separate section, maybe .SCOVP$M as originally designed. The important thing isn't having less COFF sections, it's having fewer PE sections. You can check that they get eliminated with dumpbin /summary on a libfuzzer exe. |
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
---|---|---|
23 | I've merged the guard and counter array with the .data section.
My approach works with a decent amount of examples I've tried (both with use-ld=lld and without). I suppose we will learn more once we start using it in Chrome. Have you seem the linker adding padding in spite of /incremental:no being used in ASAN builds? | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
276–281 | @rnk do you still want me to change this? |
One last thing and I think we can merge this.
compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_sections.cc | ||
---|---|---|
23 | Any reason not to do add this? #pragma comment(linker, "/MERGE:.SCOVP=.rdata") It should get the readonly behavior you want without making an extra PE section.
No, in a non-incremental build, MSVC will only add padding to satisfy the alignment of each entry. I guess I see why you downgrade the uin64_t's to one byte alignment, it's mainly to avoid the zero entry at the end of the array. | |
llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp | ||
276–281 | Normally we follow the principle that the more complexity we can move from instrumentation to runtime code, the better. If there are multiple clients, then keeping it here makes sense. |
Thank you @rnk and @morehouse for your reviews.
This can't be landed if it causes ninja check-all to fail right?
This is happening on Windows now because of fuzzer test failures (check-all is passing on Linux and check-llvm check-clang and check-asan are passing on Windows).
If it can't be landed because of this then I guess I will disable libFuzzer on Windows from being built (or maybe just all of the fuzzer tests if possible).
Also, could one of you please commit this patch for me? I'm not an LLVM committer so I'm unable to do this myself.
(If it can't be landed then I'll just ping whoever volunteers)
Thanks!
I thought we removed libfuzzer from check-all. I'll check the situation.
Also, could one of you please commit this patch for me? I'm not an LLVM committer so I'm unable to do this myself.
(If it can't be landed then I'll just ping whoever volunteers)Thanks!
We will need to disable failing tests for Windows. libFuzzer does run as part of check-all now.
Pretty much all lit tests are failing.
I've disabled all libFuzzer tests on Windows. check-all passes on Windows now.
Can we make an OS-specific macro for __attribute__((tls_model("initial-exec"))) and/or thread_local instead?