Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,16 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
dyn_cast<StringLiteral>(Node.getBase()->IgnoreParenImpCasts());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a FIXME above about refactoring Sema::CheckArrayAccess to avoid duplication, and I'm trying to decide if that makes sense or not, or if we should just copy the logic.

It seems like the main behavior difference is that Sema::CheckArrayAccess doesn't go looking for a Decl with a ConstantArray type, it just looks at the type of any old expression, which means it doesn't warn on cases involving casts or other expressions with constant array type, like these:

int arr[1];
int oob() {return (*(int(*)[2])&arr)[1]; } // currently warns, should we keep warning or trust the cast?
int *arrayAddr()[1] { return &arr; }
int safe() { return (*arrayAddr())[0] } // currently warns, but should we warn?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ultimately going to the Sema code is probably the better place to be, but I'm not sure who's working on that or if they have any urgency to land it. Ergo I'm in favor of landing this and working with the original owner to find the desired end state, as long as you all think that's an acceptable medium state. :)

uint64_t size;

if (!BaseDRE && !SLiteral)
return false;
if (!BaseDRE && !SLiteral) {
// Try harder to find something that looks like a DeclRefExpr
const auto *Member = dyn_cast<MemberExpr>(Node.getBase()->IgnoreParenImpCasts());
if (!Member) return false;

const auto *Value = Finder->getASTContext().getAsConstantArrayType(Member->getMemberDecl()->getType());
if (!Value) return false;

size = Value->getLimitedSize();
}

if (BaseDRE) {
if (!BaseDRE->getDecl())
Expand All @@ -463,6 +471,13 @@ AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think getting the integer constant expr is more general, we can remove the IntegerLiteral test here, and if all tests pass, I would consider it functionally equivalent. This code seems like it was tested to a reasonably high standard.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the code and tests still pass, it looks like you're correct. :)

}

// Array index wasn't an integer literal, let's see if it was an enum or
// something similar
const auto IntConst = Node.getIdx()->getIntegerConstantExpr(Finder->getASTContext());
if (IntConst && 0 <= *IntConst && *IntConst < size) {
return true;
}

return false;
}

Expand Down
22 changes: 22 additions & 0 deletions clang/test/SemaCXX/warn-unsafe-buffer-usage-array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,28 @@ void constant_idx_unsafe(unsigned idx) {
buffer[10] = 0; // expected-note{{used in buffer access here}}
}

enum FooEnum {
A = 0,
B = 1,
C = 2,
D
};

void constant_enum_safe() {
int buffer[FooEnum::D] = { 0, 1, 2 }; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
// expected-note@-1{{change type of 'buffer' to 'std::array' to label it for hardening}}
buffer[A] = 0; // no-warning
buffer[C] = 0; // no-warning
buffer[D] = 0; // expected-note{{used in buffer access here}}
}

void constant_enum_unsafe(FooEnum e) {
int buffer[FooEnum::D] = { 0, 1, 2 }; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
// expected-note@-1{{change type of 'buffer' to 'std::array' to label it for hardening}}

buffer[e] = 0; // expected-note{{used in buffer access here}}
}

void constant_id_string(unsigned idx) {
char safe_char = "abc"[1]; // no-warning
safe_char = ""[0];
Expand Down
1 change: 0 additions & 1 deletion clang/test/SemaCXX/warn-unsafe-buffer-usage-field-attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ void test_attribute_multiple_fields (D d) {

int v = d.buf[0]; //expected-warning{{field 'buf' prone to unsafe buffer manipulation}}

//expected-warning@+1{{unsafe buffer access}}
v = d.buf[5]; //expected-warning{{field 'buf' prone to unsafe buffer manipulation}}
}

Expand Down
21 changes: 10 additions & 11 deletions clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,25 +128,25 @@ T_t funRetT();
T_t * funRetTStar();

void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) {
foo(sp->a[1], // expected-warning{{unsafe buffer access}}
foo(sp->a[1],
sp->b[1], // expected-warning{{unsafe buffer access}}
sp->c.a[1], // expected-warning{{unsafe buffer access}}
sp->c.a[1],
sp->c.b[1], // expected-warning{{unsafe buffer access}}
s.a[1], // expected-warning{{unsafe buffer access}}
s.a[1],
s.b[1], // expected-warning{{unsafe buffer access}}
s.c.a[1], // expected-warning{{unsafe buffer access}}
s.c.a[1],
s.c.b[1], // expected-warning{{unsafe buffer access}}
sp2->a[1], // expected-warning{{unsafe buffer access}}
sp2->a[1],
sp2->b[1], // expected-warning{{unsafe buffer access}}
sp2->c.a[1], // expected-warning{{unsafe buffer access}}
sp2->c.a[1],
sp2->c.b[1], // expected-warning{{unsafe buffer access}}
s2.a[1], // expected-warning{{unsafe buffer access}}
s2.a[1],
s2.b[1], // expected-warning{{unsafe buffer access}}
s2.c.a[1], // expected-warning{{unsafe buffer access}}
s2.c.a[1],
s2.c.b[1], // expected-warning{{unsafe buffer access}}
funRetT().a[1], // expected-warning{{unsafe buffer access}}
funRetT().a[1],
funRetT().b[1], // expected-warning{{unsafe buffer access}}
funRetTStar()->a[1], // expected-warning{{unsafe buffer access}}
funRetTStar()->a[1],
funRetTStar()->b[1] // expected-warning{{unsafe buffer access}}
);
}
Expand Down Expand Up @@ -213,7 +213,6 @@ void testTypedefs(T_ptr_t p) {
// expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
foo(p[1], // expected-note{{used in buffer access here}}
p[1].a[1], // expected-note{{used in buffer access here}}
// expected-warning@-1{{unsafe buffer access}}
p[1].b[1] // expected-note{{used in buffer access here}}
// expected-warning@-1{{unsafe buffer access}}
);
Expand Down
Loading