Reputation: 524
I understood, that __builtin_object_size
should give information about the size of an object, if it can be determined at compile time or the relevant call to malloc
uses the attribute alloc_size
. I think I compiled the code ok with gcc 9.3 on ubuntu 20.04 with
gcc -g -std=gnu11 -Wall -O2 compstrcpyos.c -o compstrcpyos.exe -lbsd
The code should at least find out in doit
the size, but it doesn't. I get -1 as result.
void doit(char *buf1, char *buf2, const char *src) {
printf("bos1=%zd, bos2=%zd\n",
__builtin_object_size(buf1, 0),
__builtin_object_size(buf2, 0));
strlcpy(buf1, src, __builtin_object_size(buf1, 0));
strlcpy(buf2, src, __builtin_object_size(buf2, 0));
for (size_t i=0; i < strlen(buf1); ++i) {
buf1[i] = tolower(src[i]);
}
for (size_t i=0; i < strlen(buf2); ++i) {
buf2[i] = tolower(src[i]);
}
}
void *malloc(size_t s) __attribute__((alloc_size (1)));
int main(int argc, char *argv[]) {
if (argc < 2) { return -1; }
char buf1[20];
char *buf2 = malloc(20);
printf("bos1=%zd, bos2=%zd\n",
__builtin_object_size(buf1, 0),
__builtin_object_size(buf2, 0));
doit(buf1, buf2, argv[1]);
printf("%s\n%s\n", buf1, buf2);
}
Running the code gives.
$ ./compstrcpyos.exe ABCDefghijklmnopq
bos1=20, bos2=20
bos1=-1, bos2=-1
abcdefghijklmnopq
abcdefghijklmnopq
I tried to add the option -fsanitize=object-size
, I tried with the constants 1, 2 and 3 instead 0 in the call to __builtin_object_size
, but never got 20 in doit
. What do I need to do get 20, 20 on the second line (that is in doit
) as well.
Any help appreciated.
Upvotes: 0
Views: 1082
Reputation: 241881
I think you are over-estimating the reach of the compiler:
I understood, that
__builtin_object_size
should give information about the size of an object, if it can be determined at compile time
Up to here, fine.
or the relevant call to malloc uses the attribute
alloc_size
.
But this is not a disjunction ("or"). The compiler can only know what is known at compile time. So there is no alternative to being able to determine the size at compile-time. Now, if an object is allocated dynamically, the compiler may still be able to determine its size, provided that:
For the first two points, the compiler relies on the alloc_size
attribute. If the allocation function is not marked with this attribute, the compiler cannot determine the size of the object. So it's a conjunction ("and"). But even that is not sufficient; the compiler also needs to be able to determine the value of the arguments used when the object was allocated.
For that to work through a function call, the function call basically must be inlined. Static functions will normally be inlined if they are only called in one place. (They may be inlined elsewhere, but a large function is unlikely to be inlined if it is called -- or potentially called -- from more than one call site.)
In short, you would have been more accurate had you said:
__builtin_object_size
should give information about the size of an object, if it can be determined at compile time, which requires the relevant call tomalloc
to have the attributealloc_size
and that its arguments can be determined at compile time.
Upvotes: 1
Reputation: 71048
Indirecting through the function call erases the ability of the compiler to feel confident in determining the sizes. In theory, that function could be called by other callers, including ones outside the translation unit (file). If you add a static
qualifier to the doit
function declaration that would signal that there are never external callers, and it's then possible (in this particular example) to make a definitive compile-time determination.
For what it's worth, __builtin_object_size
is not the idiomatic way of saying "how big is this thing". It appears intended to be used in ways like the example here in super defensive coding inside one scope.
The idiomatic way of passing a buffer with an explicit size to a function is to pass the buffer and also the size of it as separate arguments (or as part of a context struct or whatnot)
Upvotes: 3