[PATCH] D60047: [CaptureTracking] Don't let comparisons against null escape inbounds pointers

Johannes Doerfert via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Sun May 5 23:31:03 PDT 2019


jdoerfert added a comment.

In D60047#1491532 <https://reviews.llvm.org/D60047#1491532>, @sanjoy wrote:

> In D60047#1491414 <https://reviews.llvm.org/D60047#1491414>, @jdoerfert wrote:
>
> > Should we interpret your comment as "the reasoning seems sound", or did you only want to point out the special case which I discuss below?
>
>
> You should interpret it as a drive by comment. :)  I'm fine if you've thought about the case I suggest and are convinced that this change is sound nevertheless.


These things are always tricky. It's better if multiple ppl think about the corner cases and implications :)

>>> @sanjoy wrote:
>>>  However, this example is kind of iffy because in general the `deref_or_null` attribute is not valid since `p` in `main` could have been something other than `-4`.  But *if* the execution has `p` == `-4` then the attribute is well defined.
>> 
>> I thought about this case, though I didn't spell it out explicitly in my earlier comment (the part: //as the addition "with infinitely precise signed arithmetic" would not overflow//).
>> 
>> Let me start by stating that I interpret pointers as unsigned values which, under certain conditions, are allowed to overflow. (Maybe that is in itself a problem in my thinking.)
>>  So for me `p` is not `-4` but always some positive value, probably `2^N - 1 - 4` where `N` is the pointer bit-width. If that is acceptable, then the inbounds wording in the lang ref
>>  prevents the problem you describe. Any addition with a positive value, e.g., the one you choose `(2^N - 1 - 4) + 4`, cannot be `null` without an overflow and inbounds prohibits this overflow.
> 
> I'm not sure if inbounds play into this, and I also noticed my example has typos and also is needlessly confusing; corrected example below:
> 
>   int** global_ptr;
>   
>   void unknown_func();
>   
>   void foo(int32* /*deref_or_null(4)*/ ptr) {
>     if (ptr == null) {
>       int32* ptr_leaked = (int32*)(intptr_t)-4;
>       *global_ptr = ptr_leaked;
>     }
>   }
>   
>   void bar() {
>     int32* p = new int32;
>     // p happens to numerically be -4 == 2^64-4
>     int32* p_with_offset = p + 1; // non-inbounds GEP, evaluates to null
>     foo(p_with_offset);
>   
>     // p has been escaped and we can't DCE the first store:
>     *p = 10;
>     unknown_func();
>     *p = 20;
>   }
> 
> 
> Now lets say we're only looking at `foo`.  I think the current code will conclude that `foo` does not capture `ptr` (we're comparing a `deref_or_null` pointer against `null`) and we will (lets assume) mark `ptr` as `nocapture` which I believe is incorrect, modulo the iffy bit I mentioned before.

I see your point. My initial argumentation is problematic here: "//If Ptr is null to begin with, we are fine (I think).//"
Due to the information leakage when a modified pointer is compared to `null`, which can actually be `null`, capturing is possible. Though, one basically has to guess the pointer. Idk. if it is useful/problematic to treat that as no-capture. If it is problematic, then we need to require `dereferenceable` (without the `or_null` part).

Btw. GEP inbounds (with a non-null offset) transition from `dereferenceable_or_null` to `dereferenceable`, though idk if there is already logic for that upstream.


Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D60047/new/

https://reviews.llvm.org/D60047





More information about the llvm-commits mailing list