Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++: Add more documentation to the cpp/invalid-pointer-deref query #13774

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

MathiasVP
Copy link
Contributor

@MathiasVP MathiasVP commented Jul 19, 2023

This PR adds a bunch of high-level (and a good amount of low-level) documentation to the files relevant for the cpp/invalid-pointer-deref query.

In the process of writing, I think I found two quite fundamental, but easy to fix, problems in the logic of the query. The documentation currently describes the implemented-and-what-I-think-is-incorrect behavior with a big TODO that specifies why I think this is wrong and how it should be fixed.

@jketema I'm keen to hear if any of this clarifies how the query picks the set of interesting bounds. I'd be happy to iterate over this a few times to make sure it's actually helpful for future us.

@MathiasVP MathiasVP requested a review from a team as a code owner July 19, 2023 13:46
@github-actions github-actions bot added the C++ label Jul 19, 2023
@MathiasVP MathiasVP added the no-change-note-required This PR does not need a change note label Jul 19, 2023
…InvalidPointerToDereference.qll

Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
@geoffw0
Copy link
Contributor

geoffw0 commented Jul 21, 2023

Otherwise LGTM, I think @jketema plans to have a look as well.

Comment on lines 17 to 19
* 1. `a` is equal to the left-hand side of `pai`, and
* 2. `b` is a dataflow node that represents an operand that _non-strictly_ upper bounds the right-hand side of `pai`.
* See `pointerAddInstructionHasBounds` for the implementation of this.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very hard for me to follow, and probably one of the core reasons I still have difficulty wrapping my head around this query.

First, LHS and RHS are pretty generic terms and on first reading I though that LHS meant the result of the PAI, which is clearly incorrect. It seems that you're talking about the 1st and 2nd (left and right) operand of the PAI and not the result.

Second, "non-strictly upper bounds" takes me a few seconds to understand every time I read it. I would be helped if we could just express this as a formula with a <= in it. Also, I think it would help if some intuition was provided here. Why are with interested in a non-strict upper bound? Most values in the range that this upper bound provides are non-problematic, because they fall within the allocated array, but apparently this doesn't hold for all of them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, LHS and RHS are pretty generic terms and on first reading I though that LHS meant the result of the PAI, which is clearly incorrect. It seems that you're talking about the 1st and 2nd (left and right) operand of the PAI and not the result.

Ah, I see your confusion. My reason for calling them left-hand side and right-hand side was because that's the names of the predicates on the PAI class. i.e., for an instruction

r3(glval<char>)     = PointerAdd[1]            : r1, r2

I call r1 the left-hand side and r2 the right-hand side because that's corresponds to calling getLeftOperand() and getRightOperand() on the PointerArithmeticInstruction class.

Would it be more clear if I write something like:

* This is done using the product-flow library. The configuration tracks flow from the
* pair `(allocation, size of allocation)` to a pair `(a, b)` where there exists a
* pointer-arithmetic instruction `pai = a + r` such that `b` is a dataflow node that
* represents an operand that _non-strictly_ upper bounds `r`. See
* `pointerAddInstructionHasBounds` for the implementation of this.

? (note that the above text hasn't yet accounted for your second comment about non-strictly upper bounds).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have been fine with just left and right operand, but this works too (somewhat better even).

Also the pointer represented by a will always be equal to the result of the allocation, right? This is something that should probably also be mentioned.

Copy link
Contributor Author

@MathiasVP MathiasVP Jul 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second, "non-strictly upper bounds" takes me a few seconds to understand every time I read it. I would be helped if we could just express this as a formula with a <= in it.

I can definitely rewrite it to use a formula instead, sure.

Also, I think it would help if some intuition was provided here. Why are with interested in a non-strict upper bound? Most values in the range that this upper bound provides are non-problematic, because they fall within the allocated array, but apparently this doesn't hold for all of them?

I can describe that a bit more clearly, sure. To wrap your head around why we're interested in a non-strict upper bound it's useful to think of an example like:

char* p = new char[size];
char* end = p + size;
char* q = somehow_get_pointer(p);
if(q <= end) {
  *q = '\0' // BUG: This may be equal to `end`
}

and the we're looking for a pointer that's non-strictly upper bound by the end-pointer (in a formula: a pointer ptr such that ptr <= end) because this means that there's some guard condition that is "too loose" (i.e., like in the above where <= should have been <). This is what we can learn by using the new range analysis: semBounded(a, b, delta, true, g) and delta >= 0 tells us that a <= b + delta because of the guard g. If it holds that semBounded(a, b, -1, true, g) this would mean that a <= b - 1 (i.e., a < b) because of g - and in this case the pointer would be properly guarded.

This is what I tried to communicate in the top of the query file:

The goal of this query is to identify issues such as:
```cpp
1. int* base = new int[size];
2. int* end = base + size;
3. for(int* p = base; p <= end; ++p) {
4.   *p = 0; // BUG: Should have been bounded by `p < end`.
5. }
```cpp

but maybe that wasn't as clear as I would've hoped.

Would an explanation such as the above help with wrapping your head around the query?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the pointer represented by a will always be equal to the result of the allocation, right? This is something that should probably also be mentioned.

Yep. I'll make sure to mention this as well 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I was trying to be somewhat provocative with regard to asking for an intuition. I think the critical part I always have to think about is that if I do anything with the base + size pointer, then I need to be very careful, as that points to something that is "just a bit" out-of-bounds due to having a non-strict bound here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5270cf6 fixes the first part.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related: #13789 updates the QLDoc in the query predicates to use <= instead of writing out [...] is upper-bounded by [...].

…InvalidPointerToDereference.qll

Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ no-change-note-required This PR does not need a change note
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants