Page MenuHomePhabricator

Handle alloc_size attribute on function pointers
AcceptedPublic

Authored by arichardson on Dec 3 2018, 4:48 AM.

Details

Summary

I have been trying to statically find and analyze all calls to heap
allocation functions to determine how many of them use sizes known at
compile time vs only at runtime. While doing so I saw that quite a few
projects use replaceable function pointers for heap allocation and noticed
that clang was not able to annotate functions pointers with alloc_size.
I have changed the Sema checks to allow alloc_size on all function pointers
and typedefs for function pointers now and added checks that these
attributes are propagated to the LLVM IR correctly.

With this patch we can also compute __builtin_object_size() for calls to
allocation function pointers with the alloc_size attribute.

Diff Detail

Event Timeline

arichardson created this revision.Dec 3 2018, 4:48 AM

Remove RUN: line added for debugging

aaron.ballman added inline comments.Dec 3 2018, 6:12 AM
include/clang/Basic/Attr.td
1101

Does GCC support writing alloc_size on function pointers?

lib/Sema/SemaDecl.cpp
6269

const TypedefNameDecl * ?

6270

const auto *

6723

const auto * and can elide the braces.

This should probably be generalized at some point into something declarative in Attr.td, though the type checking may be hard to do that for (I'm not certain). It doesn't need to be done as part of this patch, though.

test/Sema/alloc-size.c
46

What should happen in these situations?

typedef void * (__attribute__((alloc_size(1))) * my_malloc_fn_pointer_type)(int);
typedef void * (* my_other_malloc_fn_pointer_type)(int);

void *fn(int i);
__attribute__((alloc_size(1))) void *fn2(int i);

int main() {
  my_malloc_fn_pointer_type f = fn; // ???
  my_other_malloc_fn_pointer_type f2 = fn2; // ???
}

Should this code do something special?

typedef void * (__attribute__((alloc_size(1))) * my_malloc_fn_pointer_type)(int);
typedef void * (* my_other_malloc_fn_pointer_type)(int);

void overloaded(my_malloc_fn_pointer_type fn);
void overloaded(my_other_malloc_fn_pointer_type fn);

void *fn(int i);
__attribute__((alloc_size(1))) void *fn2(int i);

int main() {
  overloaded(fn);
  overloaded(fn2);
}
arichardson marked 5 inline comments as done.Dec 3 2018, 8:02 AM
arichardson added inline comments.
include/clang/Basic/Attr.td
1101

Yes it does and it seems to be used by some projects. I first discovered this while compiling libxml2: https://github.com/GNOME/libxml2/blob/35e83488505d501864826125cfe6a7950d6cba78/include/libxml/xmlmemory.h#L66

test/Sema/alloc-size.c
46

If I define two overloaded functions that only differ on the attribute GCC gives me the following error:

<source>:14:6: error: redefinition of 'overloaded'

   14 | void overloaded(my_other_malloc_fn_pointer_type fn) {

      |      ^~~~~~~~~~

<source>:11:6: note: previous definition of 'overloaded' was here

   11 | void overloaded(my_malloc_fn_pointer_type fn) {

      |      ^~~~~~~~~~

Assigning function pointers with and without the attribute seems to work just fine:
https://godbolt.org/z/-i5zUK

  • address review comments
  • add test that we can assign between function pointers with and without alloc_size attribute.
aaron.ballman added inline comments.Dec 3 2018, 11:08 AM
include/clang/Basic/Attr.td
1101

Parsed and ignored is different than supported. For instance, I can't seem to get GCC to produce different behavior here: https://godbolt.org/z/MI5k_m

Am I missing something?

test/Sema/alloc-size.c
46

Great, that's what I was hoping to hear. Can you add a C++ tests for both of those? The C test is a good start, but C++ is more finicky about type identity.

48–49

Spurious newlines can be removed.

arichardson marked an inline comment as done.Dec 3 2018, 12:23 PM

Thanks for the review! I'll write a C++ test tomorrow.

include/clang/Basic/Attr.td
1101

Ah yes, it does seem like it is ignored. I assumed it would work for GCC since it handles, e.g. the format attribute on function pointers.

However, I would like it if we could support it on function pointers even if GCC just ignores is.

aaron.ballman added inline comments.Dec 3 2018, 12:41 PM
include/clang/Basic/Attr.td
1101

However, I would like it if we could support it on function pointers even if GCC just ignores is.

We can, but the question is: under what spelling(s)? I think we can support this under __attribute__((alloc_size(N))) without issue. If GCC is looking to implement this same behavior, then I think we can also support it under [[gnu::alloc_size(N)]].

If GCC doesn't have plans to add this functionality any time soon, we'd be stepping on their implementation namespace by implementing this functionality under the gnu vendor namespace and users would then be confused as to whose bug it is. In that case, I'd recommend we add [[clang::alloc_size(N)]] that behaves identically to [[gnu::alloc_size(N)]]' except that it can be applied to function pointer types, and not change the behavior of [[gnu::alloc_size(N)]]. This can be implemented as a single semantic attribute by looking at the semantic spelling of the attribute and issuing a compatibility warning if you see [[gnu::alloc_size(N)]] being applied to a function pointer type, and can even recommend a fix-it to switch to [[clang::alloc_size(N)]] in that case. (See uses of getSemanticSpelling()` elsewhere in the project for examples of how to test which spelling a given sematic attribute was spelled with.)

arichardson marked an inline comment as done.Dec 6 2018, 5:23 AM
arichardson added inline comments.
include/clang/Basic/Attr.td
1101

It seems like GCC will also implement this behaviour so using [[gnu::alloc_size()]] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88372#c1) should be fine.

I think all that's missing are some C++ tests and some nits.

include/clang/Basic/Attr.td
1101

Fantastic, thank you for coordinating this! I think using the GCC spelling is the right way to go then.

Add a C++ test for alloc_size

aaron.ballman accepted this revision.Dec 6 2018, 1:54 PM

LGTM aside from two small style nits with the new test file.

test/SemaCXX/alloc-size.cpp
7

Can you run this new test file through clang-format?

22

Add a full stop at the end of the sentence.

This revision is now accepted and ready to land.Dec 6 2018, 1:54 PM

clang-format
fix a failing test

arichardson marked 3 inline comments as done.

fix typo

arichardson marked 5 inline comments as done.Dec 10 2018, 8:29 AM
arichardson added inline comments.
test/Misc/pragma-attribute-supported-attributes-list.test
15

This seems to also affect __attribute((format)) and others so I'm not sure whether removing AllocSize from this list is a blocker for this patch.

aaron.ballman added inline comments.Dec 10 2018, 11:59 AM
test/Misc/pragma-attribute-supported-attributes-list.test
15

I believe you may need to add an AttrSubjectMatcherSubRule in Attr.td to expose the attribute for #pragma clang attribute support. Can you see if you can support it instead of shrinking this list? Otherwise, I worry that this change will break code that was using the pragma to apply the attribute to declarations.

arichardson marked an inline comment as done.
arichardson added inline comments.
test/Misc/pragma-attribute-supported-attributes-list.test
15

The problem here already has a fixme in attr.td:

// FIXME: There's a matcher ambiguity with objc methods and blocks since
// functionType excludes them but functionProtoType includes them.
// AttrSubjectMatcherSubRule<"functionProtoType", [HasFunctionProto]>

It seems like this was already added as part of the initial pragma clang attribute commit in https://reviews.llvm.org/D30009

I had a look at the AttrEmitter.cpp in tablegen but couldn't find a straightforward solution.
I'll add @arphaman to the review.

I don't see an easy way of fixing the pragma clang attribute support for this.
Would it be okay to commit this change anyway?
Since alloc_size is only used for very few functions I'd be very surprised if there's any existing code that relies on using #pragma clang atrribute with alloc_size.

By the way GCC now also supports the attribute on function pointers: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267158

I don't see an easy way of fixing the pragma clang attribute support for this.
Would it be okay to commit this change anyway?

It causes a regression in behavior, so I'm not certain we should move forward with it just yet (unless there's a pressing reason?).

Since alloc_size is only used for very few functions I'd be very surprised if there's any existing code that relies on using #pragma clang atrribute with alloc_size.

This feature has already been available in a public release and there's no way to know how much code it will break (if any). It does seem rather unlikely, but I've been surprised by how people use this feature in the past.

By the way GCC now also supports the attribute on function pointers: https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=267158

Yeah, my concern isn't with the new functionality so much as it breaking old functionality.

I'm also adding Erik and Duncan to the review because they may have some more insights into whether alloc_size is being used with #pragma clang attribute in the wild. My feeling is: if we can't spot any uses of that feature being used to apply alloc_size with a reasonable amount of looking for it, then we can go ahead with this patch even if it removes support for alloc_size from the pragma. If we get push back from the community, we can fix or revert at that time. However, given that this is plausibly a breaking change, I'd rather not commit to trunk until after we branch to give folks more time to react. WDYT?

test/Misc/pragma-attribute-supported-attributes-list.test
15

Ah, good to know that this was already a known issue. Hopefully @arphaman has some ideas on how to support this so we don't remove a supported attribute from the list.