This is an archive of the discontinued LLVM Phabricator instance.

[analyzer][attr] Add docs to ownership_* attributes
Needs ReviewPublic

Authored by Szelethus on Aug 1 2023, 6:08 AM.

Details

Summary

Pretty much what it says on the tin! I worked a bunch on this checker so I had some vague memory (hehe) what these annotations mean, and also found this thread in the mailing list:
https://discourse.llvm.org/t/ownership-attribute-for-malloc-etc-checking/16260

Diff Detail

Event Timeline

Szelethus created this revision.Aug 1 2023, 6:08 AM
Szelethus requested review of this revision.Aug 1 2023, 6:08 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 1 2023, 6:08 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Thank you for adding documentation for these attributes, it's greatly appreciated!

clang/include/clang/Basic/AttrDocs.td
1171–1175
1177–1180
1189

I'm a bit confused on this last bit; how is the user supposed to statically know that value? I read this as claiming my_malloc returns 1 byte of dynamic storage; can the user tie the allocation to a parameter value instead?

1192–1193

We should document whether this is a 1-based index or a 0-based index, and we should also explain how the index works for member functions where there may be an implicit this argument involved.

I haven't looked at the content but I have the feeling that we should probably backport this to release/17.x

steakhal added inline comments.Aug 3 2023, 12:55 AM
clang/test/Analysis/malloc-annotations.c
151–155

Please, consider elaborating on this test with some comments, as it's not clear to me at first glance - without reading anything about the attributes - what's going on here.

Szelethus updated this revision to Diff 548116.Aug 8 2023, 2:12 AM
Szelethus marked 4 inline comments as done.

Fixes according to reviewer comments.

Szelethus added inline comments.Aug 8 2023, 2:15 AM
clang/include/clang/Basic/AttrDocs.td
1189

I share your confusion, I guess. What I've written here is my best understanding of how this works, unfortunately, these annotations were made in ~2010 (when I was still in elementary school) and abandoned since.

I read this as claiming my_malloc returns 1 byte of dynamic storage;

Thats what the corresponding code in the Static Analyzer leads me to believe as well.

can the user tie the allocation to a parameter value instead?

No.

donat.nagy added inline comments.Aug 8 2023, 4:07 AM
clang/include/clang/Basic/AttrDocs.td
1189

I'd guess that this annotation was designed for factory functions that construct an instance of a concrete struct type on the heap. Allowing "generic" malloc variants (where the allocation size is specified by a parameter) would be a good additional feature, but I think that the current annotation also has its own uses.

Perhaps it would be good to provide examples like

void __attribute((ownership_takes(malloc, 1))) free_widget(struct widget *);
struct widget *__attribute((ownership_returns(malloc, sizeof(struct widget)))) create_widget(void);
void __attribute((ownership_holds(malloc, 1))) hold_widget(struct widget *);

that correspond to this use case. (By the way, does the checker handle non-void* pointers?)

aaron.ballman added inline comments.Aug 8 2023, 7:57 AM
clang/include/clang/Basic/AttrDocs.td
1189

I share your confusion, I guess. What I've written here is my best understanding of how this works, unfortunately, these annotations were made in ~2010 (when I was still in elementary school) and abandoned since.

This comment came on the same week when I had someone much younger than me ask me what Tetris was. I'll go shake my cane at some passing clouds, now. ;-)

1189

That code doesn't compile because the argument is somehow "out of bounds": https://godbolt.org/z/3e4TbhcT6

I did some digging and the second argument in all three cases is a parameter index, at least according to the attribute handling code: https://github.com/llvm/llvm-project/blob/895c4ac33fc8b21bd69b017d1cecf874ca47e178/clang/lib/Sema/SemaDeclAttr.cpp#L1857

The static analyzer uses that information to determine which expression specifies the size: https://github.com/llvm/llvm-project/blob/895c4ac33fc8b21bd69b017d1cecf874ca47e178/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp#L1693

So I think the way to document this is that the second argument to ownership_returns specifies the parameter index which will specify the size of the the allocation. e.g.,

__attribute__((ownership_returns(malloc, 2))) void *func(unsigned val, unsigned size);

this doesn't return 2 bytes of allocated storage, it's saying the second parameter specifies the size of what's returned, in bytes.

(Someone should double-check my logic against how the static analyzer actually behaves just to be sure I'm right about this.)