This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] bugprone-infinite-loop: Fix false positives with volatile addresses.
ClosedPublic

Authored by NoQ on Aug 27 2021, 1:02 AM.

Details

Summary

Low-level code may occasionally deal with direct access by concrete addresses such as 0x1234. Values at these addresses act like globals: they can change at any time. They typically wear volatile qualifiers.

Suppress all warnings on loops with conditions that involve casting anything to a pointer-to-...-pointer-to-volatile type.

We should probably suppress loops that dereference concrete addresses regardless of volatile qualifiers but it's pretty difficult to figure out whether the address is indeed concrete (say, it may have an unknown offset, eg. (char *)(0x1234 + i), and now it can be interpreted as either ((char *)0x1234)[i] or ((char *)i)[0x1234] so it's unclear whether this should be suppressed). I also suspect that such code's behavior is undefined so this isn't a very important case to support. So for now i'm sticking to supporting the explicitly volatile-qualified case which seems straightforward.

The closely related bugprone-redundant-branch-condition check doesn't seem to be affected. I added a test just in case.

Diff Detail

Event Timeline

NoQ created this revision.Aug 27 2021, 1:02 AM
NoQ requested review of this revision.Aug 27 2021, 1:02 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 27 2021, 1:02 AM

What happens for something like the following? Maybe it's also worth a test case?

bool wait_analogue_pin(volatile int* address, int threshold) {
  while (*address < threshold) {}
  return true;
}
gribozavr2 accepted this revision.Aug 27 2021, 1:46 AM
This revision is now accepted and ready to land.Aug 27 2021, 1:46 AM

What happens for something like the following? Maybe it's also worth a test case?

WDYT about the test on line 614?

What happens for something like the following? Maybe it's also worth a test case?

WDYT about the test on line 614?

Ah! The fact that the actual value was there misled me.

NoQ added a comment.EditedAug 27 2021, 2:23 AM

What happens for something like the following? Maybe it's also worth a test case?

bool wait_analogue_pin(volatile int* address, int threshold) {
  while (*address < threshold) {}
  return true;
}

These checkers dodge these cases by noticing that the loop condition relies on pointer-type variables which, regardless of volatileness, may potentially alias with other things in the program and therefore be mutated at any moment without us noticing. The problem with concrete addresses wasn't that the check didn't know pointers cause problems, but that there weren't any variables in the expression to pay attention to. So I taught this sub-system to recognize these non-variables.

Banning pointer defererence operations entirely may still be a nicer solution that'd also allow us to simplify some code. I'll take a look.