Fixes the bug.
typedef unsigned long long size_t; const char a[] = "aabbcc"; char f1(size_t len) { return a[len+1]; // false-positive warning: Out of bound memory access (access exceeds upper limit of memory block) }
Previousl, the analyzer did these things:
- Calculates the RawOffset of the access, resulting in: BaseRegion: a and ByteOffset: len + 1
- Checks the lower bound via transforming (simplifying) the question len + 1 < 0 into len < -1. However, the analyzer can not prove nor disprove this, so it assumes that it holds. This assumption will constrain the len to be UINT_MAX since the < operator will promote the -1 into UINT_MAX.
- Checks the upper bound via transforming (simplifying) the question len + 1 >= 7 into len < 6. Since the analyzer perfectly constrained len at the previous step, we wrongly diagnose an out-of-bound error.
Proposed solution:
Skip the lower bound check if the simplified root expression (in the current example len) is unsigned and the simplified index holds a negative value.
We know for sure that no out-of-bound error can happen in this part, since how could an unsigned symbolic value be less than a negative constant?
Note that we don't deal with wrapping here.
I hope that this fix gets this checker closer to the stage when it can leave alpha.
I really feel that this check would have a better place in the implementation of eval. This seems really counter-intuitive to do this stuff at the Checker's level. Is there any reason why we can't do this in eval?
evalBinOpNN could return with Unknown, and the state should remain unchanged. Am I missing something?