This is an archive of the discontinued LLVM Phabricator instance.

Add a new builtin: __builtin_dynamic_object_size
ClosedPublic

Authored by erik.pilkington on Jan 15 2019, 4:52 PM.

Details

Summary

This builtin has the same UI as __builtin_object_size, but has the potential to be evaluated dynamically. It is meant to be used as a drop-in replacement for libraries that use __builtin_object_size when a dynamic checking mode is enabled. For instance, __builtin_object_size fails to provide any extra checking in the following function:

void f(size_t alloc) {
  char* p = malloc(alloc);
  strcpy(p, "foobar"); // expands to __builtin___strcpy_chk(p, "foobar", __builtin_object_size(p, 0))
}

This is an overflow if alloc < 7, but because LLVM can't fold the object size intrinsic statically, it folds __builtin_object_size to -1. With __builtin_dynamic_object_size, alloc is passed through to __builtin___strcpy_chk.

rdar://problem/32212419 ER: evaluate builtin_objectsize (or a successor) at runtime, at least when alloc_size is available

Thanks for taking a look!
Erik

Diff Detail

Repository
rL LLVM

Event Timeline

Thanks, this seems like a useful feature to me.

Would it make sense to model this as an (optional) extra flag bit for __builtin_object_size rather than as a new builtin? It'd seem reasonable to me to ask on the gcc dev list if they'd be interested in such an extension to their builtin -- if not, then we should use a new name, and something like this seems fine to me.

Thanks for this!

Would it make sense to model this as an (optional) extra flag bit for __builtin_object_size rather than as a new builtin?

+1. That way, we could avoid making a pass_dynamic_object_size (assuming we wouldn't want to have a different API between that and __builtin_object_size), as well. :)

jfb added a subscriber: jfb.Jan 18 2019, 10:42 AM
jfb added a comment.Jan 23 2019, 9:16 AM

I pointed out this review to Jonathan Wakely, who posted it to the GCC list. Jakub replied with:

The current modes (0-3) certainly must not be changed and must return a
constant, that is what huge amounts of code in the wild relies on.

The reason to choose constants only was the desire to make _FORTIFY_SOURCE
cheap at runtime. For the dynamically computed expressions, the question
is how far it should go, how complex expressions it wants to build and how
much code and runtime can be spent on computing that.

The rationale for __builtin_dynamic_object_size lists only very simple
cases, where the builtin is just called on result of malloc, so that is
indeed easy, the argument is already evaluated before the malloc call, so
you can just save it in a temporary and use later. Slightly more complex
is calloc, where you need to multiply two numbers (do you handle overflow
somehow, or not?). But in real world, it can be arbitrarily more complex,
there can be pointer arithmetics with constant or variable offsets,
there can be conditional adjustments of pointers or PHIs with multiple
"dynamic" expressions for the sizes (shall the dynamic expression evaluate
as max over expressions for different phi arguments (that is essentially
what is done for the constant __builtin_object_size, but for dynamic
expressions max needs to be computed at runtime, or shall it reconstruct
the conditional or remember it and compute whatever ? val1 : val2),
loops which adjust pointers, etc. and all that can be done many times in
between where the objects are allocated and where the builtin is used.

@rsmith, what do you think and how do you want to proceed? We think something like what Erik implemented will catch things _FORTIFY_SOURCE currently cannot. We agree there are valid code generation complexity concerns, yet it seems like having a different spelling for the builtin helps mitigate those concerns.

In D56760#1367915, @jfb wrote:

@rsmith, what do you think and how do you want to proceed? We think something like what Erik implemented will catch things _FORTIFY_SOURCE currently cannot. We agree there are valid code generation complexity concerns, yet it seems like having a different spelling for the builtin helps mitigate those concerns.

Yeah, I think the codegen explosion concerns are somewhat valid. It seems like for the most part its just a matter of keeping a value alive or doing a multiply or add here or there, which doesn't seem like the end of the world if its opt-in. The kind of pathological expressions that this is addressing seems like exactly the places where you would want the extra dynamic checks, like where you're indexing into an object with dynamically computed value with weird control flow or something. That being said, we could probably bail out of folding this in LLVM if the expression gets too complex.

So it seems like the GCC people want to keep __builtin_object_size static. In that case, I think that this current patch is the way to go. I'll post a patch to fix up pass_object_size too.

Thanks @jfb!

So it seems like the GCC people want to keep __builtin_object_size static.

I don't see any evidence of that. Jakub said that modes 0-3 should stay static, but that's in line with what we suggested.

jfb added a comment.Jan 23 2019, 12:22 PM

So it seems like the GCC people want to keep __builtin_object_size static.

I don't see any evidence of that. Jakub said that modes 0-3 should stay static, but that's in line with what we suggested.

You'd originally asked:

Would it make sense to model this as an (optional) extra flag bit for __builtin_object_size rather than as a new builtin? It'd seem reasonable to me to ask on the gcc dev list if they'd be interested in such an extension to their builtin -- if not, then we should use a new name, and something like this seems fine to me.

I agree Jakub wants to keep 0-3 static, but I didn't see a preference expressed for an extra bit versus __builtin_dynamic_object_size. Given this, which direction do you want to take?

I don't see any evidence of that. Jakub said that modes 0-3 should stay static, but that's in line with what we suggested.

I don't think Jakub really said anything about whether we should go with a new builtin or use the flag bit. I'm basing this on Jonathan Wakely's comment:

I know Jakub is concerned about arbitrarily complex expressions, when
__builtin_object_size is supposed to always be efficient and always
evaluate at compile time (which would imply the dynamic behaviour
should be a separate builtin, if it exists at all)
.

So your call. FWIW I'd prefer the __builtin_object_size spelling too, but it doesn't seem like the GCC folks are super crazy about it to me. So it seems likely to me that if we implement it it will just be a clang extension for at least the medium term (if not permanently). I guess that's fine, so long as the GCC people are aware that it would be bad to extend their builtin to use type&4.

rsmith accepted this revision.Jan 29 2019, 2:10 PM

FWIW I'd prefer the __builtin_object_size spelling too, but it doesn't seem like the GCC folks are super crazy about it to me. So it seems likely to me that if we implement it it will just be a clang extension for at least the medium term (if not permanently). I guess that's fine, so long as the GCC people are aware that it would be bad to extend their builtin to use type&4.

In the absence of a commitment from the GCC folks, I think we should use the __builtin_dynamic_object_size approach for now. If they later change their mind we can deprecate that spelling in favor of a flag bit.

This revision is now accepted and ready to land.Jan 29 2019, 2:10 PM
This revision was automatically updated to reflect the committed changes.