Skip to content

Commit f8f6324

Browse files
committed
[Sema] Fix PR30346: relax __builtin_object_size checks.
This patch makes us act more conservatively when trying to determine the objectsize for an array at the end of an object. This is in response to code like the following: ``` struct sockaddr { /* snip */ char sa_data[14]; }; void foo(const char *s) { size_t slen = strlen(s) + 1; size_t added_len = slen <= 14 ? 0 : slen - 14; struct sockaddr *sa = malloc(sizeof(struct sockaddr) + added_len); strcpy(sa->sa_data, s); // ... } ``` `__builtin_object_size(sa->sa_data, 1)` would return 14, when there could be more than 14 bytes at `sa->sa_data`. Code like this is apparently not uncommon. FreeBSD's manual even explicitly mentions this pattern: https://www.freebsd.org/doc/en/books/developers-handbook/sockets-essential-functions.html (section 7.5.1.1.2). In light of this, we now just give up on any array at the end of an object if we can't find the object's initial allocation. I lack numbers for how much more conservative we actually become as a result of this change, so I chose the fix that would make us as compatible with GCC as possible. If we want to be more aggressive, I'm happy to consider some kind of whitelist or something instead. llvm-svn: 281277
1 parent a340eff commit f8f6324

File tree

3 files changed

+40
-11
lines changed

3 files changed

+40
-11
lines changed

clang/lib/AST/ExprConstant.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6826,14 +6826,21 @@ static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type,
68266826
// struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) + strlen(Bar));
68276827
// strcpy(&F->c[0], Bar);
68286828
//
6829-
// So, if we see that we're examining a 1-length (or 0-length) array at the
6830-
// end of a struct with an unknown base, we give up instead of breaking code
6831-
// that behaves this way. Note that we only do this when Type=1, because
6832-
// Type=3 is a lower bound, so answering conservatively is fine.
6829+
// So, if we see that we're examining an array at the end of a struct with an
6830+
// unknown base, we give up instead of breaking code that behaves this way.
6831+
// Note that we only do this when Type=1, because Type=3 is a lower bound, so
6832+
// answering conservatively is fine.
6833+
//
6834+
// We used to be a bit more aggressive here; we'd only be conservative if the
6835+
// array at the end was flexible, or if it had 0 or 1 elements. This broke
6836+
// some common standard library extensions (PR30346), but was otherwise
6837+
// seemingly fine. It may be useful to reintroduce this behavior with some
6838+
// sort of whitelist. OTOH, it seems that GCC is always conservative with the
6839+
// last element in structs (if it's an array), so our current behavior is more
6840+
// compatible than a whitelisting approach would be.
68336841
if (End.InvalidBase && SubobjectOnly && Type == 1 &&
68346842
End.Designator.Entries.size() == End.Designator.MostDerivedPathLength &&
68356843
End.Designator.MostDerivedIsArrayElement &&
6836-
End.Designator.MostDerivedArraySize < 2 &&
68376844
isDesignatorAtObjectEnd(Info.Ctx, End))
68386845
return false;
68396846

clang/test/CodeGen/object-size.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ void test23(struct Test23Ty *p) {
276276

277277
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
278278
gi = __builtin_object_size(&p->t[5], 0);
279-
// CHECK: store i32 20
279+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
280280
gi = __builtin_object_size(&p->t[5], 1);
281281
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
282282
gi = __builtin_object_size(&p->t[5], 2);
@@ -444,7 +444,7 @@ void test29(struct DynStructVar *dv, struct DynStruct0 *d0,
444444

445445
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
446446
gi = __builtin_object_size(ss->snd, 0);
447-
// CHECK: store i32 2
447+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
448448
gi = __builtin_object_size(ss->snd, 1);
449449
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
450450
gi = __builtin_object_size(ss->snd, 2);
@@ -505,7 +505,7 @@ void test31() {
505505
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
506506
gi = __builtin_object_size(ds1[9].snd, 1);
507507

508-
// CHECK: store i32 2
508+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
509509
gi = __builtin_object_size(&ss[9].snd[0], 1);
510510

511511
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
@@ -517,3 +517,22 @@ void test31() {
517517
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
518518
gi = __builtin_object_size(&dsv[9].snd[0], 1);
519519
}
520+
521+
// CHECK-LABEL: @PR30346
522+
void PR30346() {
523+
struct sa_family_t {};
524+
struct sockaddr {
525+
struct sa_family_t sa_family;
526+
char sa_data[14];
527+
};
528+
529+
struct sockaddr *sa;
530+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
531+
gi = __builtin_object_size(sa->sa_data, 0);
532+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
533+
gi = __builtin_object_size(sa->sa_data, 1);
534+
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
535+
gi = __builtin_object_size(sa->sa_data, 2);
536+
// CHECK: store i32 14
537+
gi = __builtin_object_size(sa->sa_data, 3);
538+
}

clang/test/CodeGen/pass-object-size.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ void test1() {
5959

6060
// CHECK-LABEL: define void @test2
6161
void test2(struct Foo *t) {
62-
// CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36)
62+
// CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
63+
// CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 [[VAR]])
6364
gi = ObjectSize1(&t->t[1]);
6465
// CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36)
6566
gi = ObjectSize3(&t->t[1]);
@@ -168,7 +169,8 @@ void test4(struct Foo *t) {
168169

169170
// CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 %{{.*}})
170171
gi = NoViableOverloadObjectSize0(&t[1].t[1]);
171-
// CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 36)
172+
// CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
173+
// CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 [[VAR]])
172174
gi = NoViableOverloadObjectSize1(&t[1].t[1]);
173175
// CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 %{{.*}})
174176
gi = NoViableOverloadObjectSize2(&t[1].t[1]);
@@ -274,7 +276,8 @@ void test7() {
274276

275277
// CHECK-LABEL: define void @test8
276278
void test8(struct Foo *t) {
277-
// CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
279+
// CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
280+
// CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 [[VAR]])
278281
gi = AsmObjectSize1(&t[1].t[1]);
279282
// CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
280283
gi = AsmObjectSize3(&t[1].t[1]);

0 commit comments

Comments
 (0)