This is an archive of the discontinued LLVM Phabricator instance.

[-Wunsafe-buffer-usage] Add a facility for debugging low fixit coverage.
ClosedPublic

Authored by t-rasmud on Jul 10 2023, 12:53 PM.

Details

Summary

This patch adds extra notes to -Wunsafe-buffer-usage warnings, which explain why a fixit wasn't produced. When applied to a large body of real-world code, it'll help us gather statistics that will help us figure out which fixable gadgets (or other features of the fixit machine) to "invest" into.

This is a debugging facility intended for developer use only; it is activated by passing -mllvm -debug-only=SafeBuffers to clang, so it's carefully hidden and undiscoverable, and it's only available in builds with assertions.

Offline we've identified the following sources of false negatives which these notes can help us categorize:

  1. unsafe operation not performed on a supported kind of variable (eg. member variable);
  2. use site of the unsafe variable not claimed by any fixable gadgets (so we need to cover it with a new fixable);
  3. one of the "implicated" variables has unclaimed uses (so we can't build the implication graph);
  4. fixit generation for the declaration of the variable has failed (eg. declaration is in a macro);
  5. fixit generation for one of the fixable gadgets has failed (eg. the use is in a macro).

Currently this patch covers #5; it probably makes sense to cover all of these except maybe #1 (this one's usually obvious).

Diff Detail

Event Timeline

NoQ created this revision.Jul 10 2023, 12:53 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 10 2023, 12:54 PM
NoQ requested review of this revision.Jul 10 2023, 12:54 PM
NoQ added inline comments.Jul 10 2023, 1:03 PM
clang/lib/Analysis/UnsafeBufferUsage.cpp
577–581

I changed this to make F->getBaseStmt() available more often, but ran into more problems of this kind that were much more strange than this one. I'll probably investigate more.

NoQ updated this revision to Diff 538852.Jul 10 2023, 3:28 PM

private: => public:

(for some reason it didn't complain until I did a full rebuild)

NoQ updated this revision to Diff 538862.Jul 10 2023, 3:54 PM

Add the other missing base statement. Debugger was acting weirdly so I thought we had bigger problems, but it's just the other thing missing. It's likely that we ultimately want a better interface for that anyway.

t-rasmud commandeered this revision.Jul 17 2023, 11:01 AM
t-rasmud edited reviewers, added: NoQ; removed: t-rasmud.

I will add debug notes for the rest of the cases not addressed in this patch.

t-rasmud updated this revision to Diff 541745.Jul 18 2023, 2:47 PM

This patch addresses cases 2, 3, and 4 described in the summary (i.e) adds debug notes for unclaimed uses of variables and for failed fixit generation of variable declarations.

NoQ added a comment.Jul 18 2023, 3:59 PM

Awesome!!

Did you try running it on some real code? Does this actually cover most cases? (I suspect that (1.) is going to be the most popular case, but that's also the easiest case to diagnose visually. We might still want a note if we wanted to prioritize among non-variables, but that's some work, maybe we can get back to this later.)

I have a few minor nitpicks, but generally LGTM!

clang/lib/Analysis/UnsafeBufferUsage.cpp
1755–1756

We might want to silence -Wunused-parameter in release builds.

Maybe it's better to put the entire parameter under #ifndef NDEBUG, but it's definitely more clumsy.

There's also __attribute__((unused)) aka [[maybe_unused]] but it looks like we're not supposed to use it in LLVM for variables:

173 // Some compilers warn about unused functions. When a function is sometimes
174 // used or not depending on build settings (e.g. a function only called from
175 // within "assert"), this attribute can be used to suppress such warnings.
176 //
177 // However, it shouldn't be used for unused *variables*, as those have a much
178 // more portable solution:
179 //   (void)unused_var_name;
180 // Prefer cast-to-void wherever it is sufficient.
181 #if __has_attribute(unused)
182 #define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__))
183 #else
184 #define LLVM_ATTRIBUTE_UNUSED
185 #endif
1771–1772

This comment is probably duplicated everywhere by accident; it doesn't make much sense in most of these places.

1796
2353

getName() crashes on various anonymous variables, whereas getNameAsString() always gives an answer (even if it's not always a "good" answer), which is more suitable for diagnostics.

2354

Do you want to include a "failde to produce fixit..." text in this message and the next message, for consistency?

NoQ added inline comments.Jul 18 2023, 4:01 PM
clang/lib/Analysis/UnsafeBufferUsage.cpp
2174–2175

This is my original comment right? It's actually also outdated and should be removed, I already erased the runtime check, and I already moved the architectural recommendation to implementation of getBaseStmt() in individual gadgets for which it doesn't make sense.

This is a lot of work, thank you @t-rasmud & @NoQ !

I have a minor suggestion: can we use some macros to make the debug stub even shorter?
The prefix "failed to produce fixit for declaration" is used in many places so probably we do not have to repeat it everywhere. And, maybe some prefixes could be a bit more blurry so that they can be shared. For example, we can just replace "failed to produce fixit for parm var decl" with "failed to produce fixit for declaration". We have source location and more specific message attached to the note so we are not losing information I think.

I'm imagining something like this:

#define DEBUG_NOTE_DECL_FAIL(D, Msg)  \
Handler.addDebugNoteForVar((D), (D)->getBeginLoc(),  "failed to produce fixit for declaration "##Msg)

#define DEBUG_NOTE_GADGET_FAIL(Gadget, Msg)  ...

Does it make sense to you?

t-rasmud updated this revision to Diff 544510.Jul 26 2023, 2:08 PM
t-rasmud retitled this revision from [-Wunsafe-buffer-usage][WIP] Add a facility for debugging low fixit coverage. to [-Wunsafe-buffer-usage] Add a facility for debugging low fixit coverage..
t-rasmud marked 6 inline comments as done.Jul 26 2023, 2:09 PM

This is a lot of work, thank you @t-rasmud & @NoQ !

I have a minor suggestion: can we use some macros to make the debug stub even shorter?
The prefix "failed to produce fixit for declaration" is used in many places so probably we do not have to repeat it everywhere. And, maybe some prefixes could be a bit more blurry so that they can be shared. For example, we can just replace "failed to produce fixit for parm var decl" with "failed to produce fixit for declaration". We have source location and more specific message attached to the note so we are not losing information I think.

I'm imagining something like this:

#define DEBUG_NOTE_DECL_FAIL(D, Msg)  \
Handler.addDebugNoteForVar((D), (D)->getBeginLoc(),  "failed to produce fixit for declaration "##Msg)

#define DEBUG_NOTE_GADGET_FAIL(Gadget, Msg)  ...

Does it make sense to you?

I like this suggestion. I've made changes to replace Handler.addDebugNoteForVar for declarations. The Gadget case appears just once as of now, so I've left it as is.

NoQ added inline comments.Jul 26 2023, 2:17 PM
clang/lib/Analysis/UnsafeBufferUsage.cpp
1733–1734

Ooo that's actually really nice! Maybe you can go even further and add this extra harness, so that to eliminate the need for #ifndef NDEBUG at every use.

t-rasmud updated this revision to Diff 544521.Jul 26 2023, 2:40 PM
t-rasmud marked an inline comment as done.
NoQ accepted this revision.Jul 26 2023, 3:35 PM

LGTM! I'm excited to learn what this new facility discovers!

clang/lib/Analysis/UnsafeBufferUsage.cpp
1733–1738

An extra bit of paranoia.

This revision is now accepted and ready to land.Jul 26 2023, 3:35 PM
t-rasmud updated this revision to Diff 544547.Jul 26 2023, 4:00 PM
t-rasmud marked an inline comment as done.
This revision was landed with ongoing or failed builds.Jul 26 2023, 5:08 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJul 26 2023, 5:08 PM