This emits an st_size that represents the actual useable size of an object before the redzone is added.
Details
Diff Detail
Event Timeline
This matches gcc's behavior of keeping an objects st_size as it was before the redzone is added. Keeping the smaller size is beneficial for reducing the amount copied in copy relocations.
This is insufficient justification to me. With compilation for executables moving from -fno-pic to -fpie, we cannot see copy relocations.
Copy relocations really only happen with uncommon configurations now and decreasing st_size doesn't save many bytes any way. (Copy relocations can also be avoided with -fno-direct-access-external-data.)
The enlarged st_size value gives users a quick way to know whether the area has been enlarged.
I do not see why we need to match GCC's behavior here.
In what cases do you imagine it would be useful to quickly see whether an area has been enlarged? If anything this makes abi tracking mechanisms through nm, like what libcxx does, and we plan to do with compiler_rt with llvm-ifs more difficult because symbol sizes differ between clang and gcc. Granted compiler_rt is a bad example here because it cannot be itself sanitized, likewise libcxx doesn't track object sizes, however I think the point is still valid.
This is insufficient justification to me. With compilation for executables moving from -fno-pic to -fpie, we cannot see copy relocations.
Copy relocations really only happen with uncommon configurations now and decreasing st_size doesn't save many bytes any way. (Copy relocations can also be avoided with -fno-direct-access-external-data.)
Sure, I think regardless of rarity of the use-case it is objectively better to make copy relocations smaller for when they are used.
I do not see why we need to match GCC's behavior here.
Matching GCC when their behavior is better seems like a worthy goal, especially if the cost to do so here isn't high. Do you expect the complexity of whatever solution this would end up with isn't worth the benefit?
It is more like: "we do not care much about which st_size to use, but I prefer the way with little complexity".
This patch tries to change the status quo with a bit more complexity, so I'd challenge it with the justification.
Your llvm-ifs argument in the comment would make a better summary, but see below I have a minor concern with regard to copy relocations.
In addition, asan instruments global variables so the ABI is different anyway. I do not see a very justification that unifying the two cases.
In what cases do you imagine it would be useful to quickly see whether an area has been enlarged?
Mixing instrumented and uninstrumented global variables are error-prone, so knowing whether some have actually different IR sizes helps debugging if a program breaks.
Also imagine the scenario that asan improves to place some non-zero data into the trailing redzone, then this approach will actually break copy relocations.
Matching GCC when their behavior is better seems like a worthy goal, especially if the cost to do so here isn't high. Do you expect the complexity of whatever solution this would end up with isn't worth the benefit?
I believe in many of the sanitizer cases Clang has the reference implementation and GCC is following.
In such cases changes need to have their own merits, the GCC compatibility is just a tie-breaker.
Actually I have some vague memory that GCC hasn't thought hard about what st_size it should use, as I vaguely remember some global variable instrumentation in GCC.
I've updated this patch with a more permanent solution. I think how it is now, the complexity is not a large concern. WDYT?
This patch tries to change the status quo with a bit more complexity, so I'd challenge it with the justification.
Your llvm-ifs argument in the comment would make a better summary, but see below I have a minor concern with regard to copy relocations.
In addition, asan instruments global variables so the ABI is different anyway. I do not see a very justification that unifying the two cases.
I think in this case it is important that llvm-ifs report the same abi when building with either gcc or clang. These abi checking tools would already report an abi difference between instrumented binaries because of the addition of the __asan_* symbols.
In what cases do you imagine it would be useful to quickly see whether an area has been enlarged?
Mixing instrumented and uninstrumented global variables are error-prone, so knowing whether some have actually different IR sizes helps debugging if a program breaks.
Also imagine the scenario that asan improves to place some non-zero data into the trailing redzone, then this approach will actually break copy relocations.
This is true. But I don't think the potential for future work should block this. That would be a large overhaul to asan, it could easily remove the 4 lines that emit the explicit_size metadata tag when it's semantics change.
Thinking this again, I think using the original st_size makes sense, but I do not agree with the justification put on the patch.
The original st_size makes sense because the size is the inherent property of the global variable and should not change due to error-catching instrumentation.
I.e. if the size 4 is grown to 6, the user application can only access [0,4), not [4,6).
Matching GCC behavior is very minor pros in this case (depending on cases, it may matter more). The copy relocations benefit is even minor and not worthwhile to be mentioned in LangRef.
Mostly fine to me.
I cannot think of a GlobalMerge similar optimization which may need adaptation. (!associated doesn't seem to have any)
llvm/docs/LangRef.rst | ||
---|---|---|
7100 | Suggest: The `explicit_size` metadata may be attached to a global variable definition with a size different from the object's total size. This can be useful when an instrumentation enlarges the object while the symbol size should reflect the accessible or meaningful part of the object. I think this is not meaningful to functions, ifuncs, comdats, etc, and likely not meaningful to scalar types, but it may not be necessary to catch the error in the IR verifier. | |
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | ||
694 | Such IIFE is not common. Why not just remove the lambda? | |
llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp | ||
2516 | More idiom way is something like | |
llvm/test/CodeGen/X86/explicit-size-metadata.ll | ||
7 | Test an array | |
llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll | ||
55–56 | Update the two CHECK lines as well. |
This seems really obscure, but makes sense.
llvm/docs/LangRef.rst | ||
---|---|---|
7100 | Probably makes sense to diagnose on anything that isn't a definition of a variable. Probably not worth trying to catch other dubious cases. Maybe explicitly note that this doesn't make sense for all object formats? |
llvm/docs/LangRef.rst | ||
---|---|---|
7100 | Thanks @MaskRay I've used this comment verbatim. Plus added some about how this is only meaningful on ELF.
How do you reckon I would do this? It seems like the current behavior is to just ignore metadata that doesn't get used. Seems difficult to warn about places where !explicit_size isn't meaningful. Do you know of an example where this happens in llvm currently I could look at? | |
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | ||
694 | Sure. I was thinking it would be too many nested if's. But I guess it looks fine. |
llvm/docs/LangRef.rst | ||
---|---|---|
7100 | It would be pretty easy to add something to Verifier::visitGlobalValue. I guess we don't actually do much verification for most metadata at the moment, though. But see Verifier::verifyFunctionMetadata. |
llvm/test/Verifier/explicit_size.ll | ||
---|---|---|
3 | Thanks. Sorry about that. |
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | ||
---|---|---|
697 | Use cast which asserts non-null. | |
701 | Use cast which asserts non-null. | |
llvm/test/CodeGen/X86/explicit-size-metadata.ll | ||
2 | Try making tests more general by checking the generic ELF behavior, not linux-gnu specific ones. | |
llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll | ||
9 | ![[#TAG10:]] (match a decimal) Reference it below with [[#TAG10]] This is better than .* which matches too much, and {{[0-9]+}} which is too verbose. | |
55–56 | ![[#]] |
Please wait for a sanitizer reviewer (@vitalybuka) to confirm this is fine.
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | ||
---|---|---|
695 | Omit this: getOperand(0) has an assert |
LGTM
@hctim is in a middle of asan globals refactoring, maybe he can spot something unusual.
I just did a quick check against llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll, compiling and linking it as a DSO, and it looks right (only the sizes change).
One thing to bear in mind is that some sanitizers (like HWASan) also change the alignment of the global variables as well as the size, so sanitizers will always be an ABI break, but I agree this patch makes things less confusing rather than more confusing.
This change breaks a lot of our internal asan tests. I am going to revert for investigation.
Hi folks wanted to update here why this was breaking.
When we decreased the size non-pie's needed a copy relocation, part of the intention here was to make them smaller. In these cases the linker would have only created enough space based on the st_size of the object. Which just became too small. The caveat here, is alignment if you have sufficient alignment you may have enough space for the object and it's redzone. This is of course not guaranteed, except for small objects where the alignment is likely to fit both. What ended up happening was objects being placed too close together and being in the redzone of the previous object.
As for gcc's implementation, they use the smaller st_size, the difference is the array they pass to __asan_register_globals is a .Lsymbol, generating relative relocations to the globals this is as if you passed -mllvm -asan-use-private-alias to clang. In the non-pie case, gcc is registering the global in the shared object which got preempted by the one in the executable and poisoning nothing. I doubt this behavior is intended on their end.
Suggest: The `explicit_size` metadata may be attached to a global variable definition with a size different from the object's total size. This can be useful when an instrumentation enlarges the object while the symbol size should reflect the accessible or meaningful part of the object.
I think this is not meaningful to functions, ifuncs, comdats, etc, and likely not meaningful to scalar types, but it may not be necessary to catch the error in the IR verifier.