Skip to content

Add check: invalidPointerOverlapTest for unsafe pointer overflow comparison#8620

Open
tmleman wants to merge 1 commit into
cppcheck-opensource:mainfrom
tmleman:topic/checkcondition/new/pointer_overflow
Open

Add check: invalidPointerOverlapTest for unsafe pointer overflow comparison#8620
tmleman wants to merge 1 commit into
cppcheck-opensource:mainfrom
tmleman:topic/checkcondition/new/pointer_overflow

Conversation

@tmleman

@tmleman tmleman commented Jun 2, 2026

Copy link
Copy Markdown

Add a new inconclusive warning that detects the pointer overlap idiom

p1 >= p2 && p1 < p2 + n        (or its mirror / cast variants)

where p1 and p2 are distinct pointers and n is an unsigned offset, e.g. the memcpy_s overlap check '(dest >= src && dest < src + count) || ...'.

The 'p1 < p2 + n' clause assumes 'p2 + n' does not overflow past the end of the object. If n is large enough that 'p2 + n' wraps (UB), some mainstream compilers assume the wrap cannot happen and fold the comparison, silently dropping the check. The remedy in user code is to cast the pointers to uintptr_t and compare in integer space, with an explicit overflow guard.

To avoid false positives, the warning only fires when the comparison is paired (via &&) with another comparison of the same two pointers, which identifies the overlap idiom; plain bounds checks like 'p < buf + len' and room checks like 'cur + need <= limit' are not flagged. The check is gated behind --enable=inconclusive. The same-pointer case 'p < p + n' is left to the existing invalidTestForOverflow check.

This pattern was found in a real memcpy_s overlap check via fuzzing with UndefinedBehaviorSanitizer; the check is added so similar issues can be caught statically in the future.

@tmleman tmleman force-pushed the topic/checkcondition/new/pointer_overflow branch from fcbffc1 to 8d9b4ba Compare June 2, 2026 20:27
…arison

Add a new inconclusive warning that detects the pointer overlap idiom

    p1 >= p2 && p1 < p2 + n        (or its mirror / cast variants)

where p1 and p2 are distinct pointers and n is an unsigned offset, e.g.
the memcpy_s overlap check '(dest >= src && dest < src + count) || ...'.

The 'p1 < p2 + n' clause assumes 'p2 + n' does not overflow past the end
of the object. If n is large enough that 'p2 + n' wraps (UB), some
mainstream compilers assume the wrap cannot happen and fold the
comparison, silently dropping the check. The remedy in user code is to
cast the pointers to uintptr_t and compare in integer space, with an
explicit overflow guard.

To avoid false positives, the warning only fires when the comparison is
paired (via &&) with another comparison of the same two pointers, which
identifies the overlap idiom; plain bounds checks like 'p < buf + len'
and room checks like 'cur + need <= limit' are not flagged. The check is
gated behind --enable=inconclusive. The same-pointer case 'p < p + n' is
left to the existing invalidTestForOverflow check.

This pattern was found in a real memcpy_s overlap check via fuzzing with
UndefinedBehaviorSanitizer; the check is added so similar issues can be
caught statically in the future.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
@tmleman

tmleman commented Jun 3, 2026

Copy link
Copy Markdown
Author

Why this check is deliberately narrow:

This check targets a specific undefined-behavior pattern I hit in real code: a pointer-overlap test of the form p1 >= p2 && p1 < p2 + n where p2 + n can overflow past the end of the object (UB), and some mainstream compilers then assume the overflow can't happen and fold the comparison away. I found this via UBSan fuzzing in a memcpy_s implementation (issue (thesofproject/sof#9768), fix (thesofproject/sof@7d11802)).

I had a broader version of this check but it fired a flood of false positives on completely normal C and since cppcheck prioritizes a low false-positive rate, such a check would just get disabled by users and help no one.

@tmleman tmleman marked this pull request as ready for review June 3, 2026 19:31
@danmar

danmar commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

I don't understand why compilers would fold the comparison? even if there is never overflow the comparison is not redundant?

char buf[100];
char *p1 = &buf[20];
char *p2 = buf;

n = 10 => p1 < p2 + n => false
n = 30 => p1 < p2 + n => true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants