[clang] [llvm] [Clang] Correct __builtin_dynamic_object_size for subobject types (PR #78526)
Bill Wendling via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 18 02:04:11 PST 2024
bwendling wrote:
> We've discussed this before, years ago. Previously, the sticking point was: how is LLVM going to know what the frontend considers the closest surrounding subobject to be? The LLVM type doesn't give you that information, and it seems like there's nowhere else that LLVM can get it from either, so this flag ends up not being useful and the best we can do is to give a conservatively-correct answer -- the complete object size if we want an upper bound, or 0 if we want a lower bound.
Right now we're giving a wrong answer (see below), so that's no good. If we're going to err on the side of caution, then we should return the default "can't calculate the size" value.
When you say that we can't detect what the front-end considers the "closest surrounding subobject" to be, is that mostly due to corner cases or is it a more general concern? (Note, we're really only interested in supporting this for C structs. C++ structs / classes would require therapy.)
> Clang does respect the "subobject" flag if it can symbolically evaluate the operand of `__builtin_object_size` sufficiently to determine which subobject is being referenced. Previously we've thought that that was the best we could do.
This is why the current behavior is wrong, in my opinion. The motivating example is below:
```
struct suspend_stats {
int success;
int fail;
int failed_freeze;
int failed_prepare;
int failed_suspend;
int failed_suspend_late;
int failed_suspend_noirq;
int failed_resume;
int failed_resume_early;
int failed_resume_noirq;
#define REC_FAILED_NUM 2
int last_failed_dev;
char failed_devs[REC_FAILED_NUM][40]; /* offsetof(struct suspend_stats, failed_devs) == 44 */
int last_failed_errno;
int bar;
};
#define report(x) __builtin_printf(#x ": %zu\n", x)
int main(int argc, char *argv[])
{
struct suspend_stats foo;
report(sizeof(foo.failed_devs[1]));
report(sizeof(foo.failed_devs[argc]));
report(__builtin_dynamic_object_size(&foo.fail, 0));
report(__builtin_dynamic_object_size(&foo.fail, 1));
report(__builtin_dynamic_object_size(&foo.failed_freeze, 0));
report(__builtin_dynamic_object_size(foo.failed_devs[1], 0));
report(__builtin_dynamic_object_size(foo.failed_devs[1], 1));
report(__builtin_dynamic_object_size(foo.failed_devs[argc], 0));
report(__builtin_dynamic_object_size(foo.failed_devs[argc], 1));
return 0;
}
```
The output with this change is now:
```
__builtin_dynamic_object_size(&foo.fail, 0): 128
__builtin_dynamic_object_size(&foo.fail, 1): 4
__builtin_dynamic_object_size(&foo.failed_freeze, 0): 124
__builtin_dynamic_object_size(foo.failed_devs[1], 0): 48
__builtin_dynamic_object_size(foo.failed_devs[1], 1): 40
__builtin_dynamic_object_size(foo.failed_devs[argc], 0): 48
__builtin_dynamic_object_size(foo.failed_devs[argc], 1): 40
```
Without the change, the last line is:
```
__builtin_dynamic_object_size(foo.failed_devs[argc], 1): 48
```
Which isn't correct according to GNU's documentation. So if we can't honor the TYPE bit, then we should return `-1 / 0` here, right?
https://github.com/llvm/llvm-project/pull/78526
More information about the cfe-commits
mailing list