[llvm-dev] Possible soundness issue with available_externally (split from "RFC: Add guard intrinsics")
Sanjoy Das via llvm-dev
llvm-dev at lists.llvm.org
Fri Feb 26 09:03:48 PST 2016
Couple of other problematic transforms:
# undef refinement
After thinking about this a bit, I think undef refinement happens a
lot more often than I initially thought, and it happens implicitly.
Consider the following case:
void @foo(int* ptr) available_externally {
int k = *ptr;
if (k == 1 && k == 2) print("X");
}
void main() {
int* ptr = malloc();
*ptr = 200;
@foo(ptr)
}
=>
void @foo(int* ptr) readnone available_externally {
//int k = *ptr;
//if (false) print("X");
}
void main() {
int* ptr = malloc();
*ptr = 200;
@foo(ptr)
}
==>
void @foo(int* ptr) readnone available_externally {
//int k = *ptr;
//if (false) print("X");
}
void main() {
int* ptr = malloc();
@foo(ptr) // since this is readnone
*ptr = 200;
}
This is a problem if we replace @foo with an unoptimized version
(`undef` can be both `== 1` and `== 2`): in such a case we can print
`"X"` while in the original program that wasn't possible.
The problem here is that we're "implicitly" folding / constraining
`undef` when folding `(k == 1 && k == 2)` to `false`. In the case
where `k` is `undef` (the non-undef case is trivial), the fold is
justified by first constraining the two `undef` s to one value, and
then folding the comparison. And we do this kind of implicit `undef`
refinement all over the place -- the rewrite
i = 0;
N = ...;
do {
} while (i++ != N);
=>
i = 0;
N = ...;
//do {
//} while (i++ != N);
i = N + 1;
makes the same refinement, that **if** `N` is `undef` then all of the
uses of `N` through the loops iteration space are the same `N`. OTOH
If we *knew* `N` was `undef`, we could have correctly replaced the
loop with `while (true)` (so that above loop is also problematic in
the same way as the firat `k == 1 && k == 2` case.
# dereferenceability
We cannot reorder `readnone nounwind available_externally` functions
across `@free` since there could have been:
void @foo(int* ptr) available_externally {
int unused = *ptr;
}
where the load was optimized away in the current TU but is present in
the -O0 TU.
This is similar to the dead-arg-elimination transform Andy Ayers and I
mentioned earlier (passing in `undef` or `null` for `ptr` is also a
problem for similar reasons).
-- Sanjoy
More information about the llvm-dev
mailing list