Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions clang/include/clang/AST/ExprUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ class ExprUtil {
// pointer). Returns false if E is nullptr.
static bool ReadsMemoryViaPointer(Expr *E, bool IncludeAllMemberExprs = false);

// IsDereferenceOrSubscript returns true if the expression e is a pointer
// dereference *e1 or an array subscript expression e1[e2].
static bool IsDereferenceOrSubscript(Expr *E);

// IsReturnValueExpr return true if the expression E is a _Return_value
// expression.
static bool IsReturnValueExpr(Expr *E);

// FindLValue returns true if the given lvalue expression occurs in E.
static bool FindLValue(Sema &S, Expr *LValue, Expr *E);

Expand Down
125 changes: 115 additions & 10 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11386,6 +11386,10 @@ def err_bounds_type_annotation_lost_checking : Error<
"argument has unknown bounds, bounds expected because the "
"%ordinal0 parameter has bounds">;

def err_expected_bounds_for_return : Error<
"return value has unknown bounds, bounds expected because the "
"function %0 has bounds">;

def err_initializer_expected_with_bounds : Error<
"automatic variable %0 with bounds must have initializer">;

Expand Down Expand Up @@ -11507,7 +11511,7 @@ def err_bounds_type_annotation_lost_checking : Error<
"variable arguments function cannot be made in a checked scope">;

def err_checked_scope_no_variadic_func_for_expression : Error<
"cannot use a variable arguments function in a checked scope or function">;
"cannot use this variable arguments function in a checked scope or function">;

def err_checked_scope_no_assume_bounds_casting : Error<
"_Assume_bounds_cast not allowed in a checked scope or function">;
Expand All @@ -11520,6 +11524,12 @@ def err_bounds_type_annotation_lost_checking : Error<
"%select{'_Unchecked'|'_Checked _Bounds_only|'_Checked'}0 "
"can only appear on functions">;

def err_checked_scope_invalid_format_specifier_argument : Error<
"in a checked scope %0 format specifier requires %1 argument">;

def err_checked_scope_scanf_width : Error<
"in a checked scope width is not allowed with format specifier in scanf">;

def err_pragma_pop_checked_scope_mismatch : Error<
"#pragma CHECKED_SCOPE pop with no matching #pragma CHECKED_SCOPE push">;

Expand Down Expand Up @@ -11619,8 +11629,32 @@ def err_bounds_type_annotation_lost_checking : Error<
def error_static_cast_bounds_invalid : Error<
"cast source bounds are too narrow for %0">;

def error_modified_return_bounds : Error<
"modified expression '%0' used in the declared return bounds for %1">;

def error_return_bounds_invalid : Error<
"return value bounds do not imply declared return bounds for %0">;

def error_return_bounds_unprovable: Error<
"it is not possible to prove that return value bounds "
"imply declared return bounds for %0">;

def warn_return_bounds_invalid: Warning<
"cannot prove return value bounds imply declared return bounds for %0">,
InGroup<CheckBoundsDeclsUnchecked>;

def warn_checked_scope_return_bounds_invalid : Warning<
"cannot prove return value bounds imply declared return bounds for %0">,
InGroup<CheckBoundsDeclsChecked>;

def note_declared_return_bounds : Note<
"(expanded) declared return bounds are '%0'">;

def note_inferred_return_bounds : Note<
"(expanded) inferred return value bounds are '%0'">;

def error_out_of_bounds_access : Error<
"out-of-bounds %select{||memory access|base value}0">;
"out-of-bounds %select{|||memory access|base value}0">;

def note_source_bounds_empty : Note<"source bounds are an empty range">;

Expand All @@ -11631,21 +11665,22 @@ def err_bounds_type_annotation_lost_checking : Error<
def note_destination_bounds_invalid : Note<"destination bounds are an invalid range">;

def note_bounds_too_narrow : Note<
"%select{destination bounds are|target bounds are|memory accessed is|"
"struct/union pointed to by base is}0 wider than the "
"%select{source|source||}0 bounds">;
"%select{destination bounds are|target bounds are|declared return bounds are|"
"memory accessed is|struct/union pointed to by base is|}0 wider "
"than the %select{source|source|return value|source|source}0 bounds">;

def note_lower_out_of_bounds : Note<
"%select{destination lower bound is|target lower bound is|accesses memory|"
"base value is}0 below %select{source|source|the|its}0 lower bound">;
"%select{destination lower bound is|target lower bound is|"
"declared return lower bound is|accesses memory|base value is}0 "
"below %select{source|source|return value|the|its}0 lower bound">;

def note_upper_out_of_bounds : Note<
"%select{destination upper bound is|target upper bound is|"
"accesses memory at or|base value is}0 "
"above %select{source|source|the|its}0 upper bound">;
"declared return upper bound is|accesses memory at or|base value is}0 "
"above %select{source|source|return value|the|its}0 upper bound">;

def note_bounds_partially_overlap : Note<
"%select{||accesses memory that|struct/union pointed to by base value}0 is "
"%select{|||accesses memory that|struct/union pointed to by base value}0 is "
"only partially in bounds">;

def no_prototype_generic_function : Error<
Expand Down Expand Up @@ -11680,5 +11715,75 @@ def err_bounds_type_annotation_lost_checking : Error<
def err_expanding_cycle : Error<
"expanding cycle in struct definition">;

// -Wformat warnings issued as errors in checked scope.
def err_format_nonliteral_noargs : Error<
"format string is not a string literal (potentially insecure)">;
def err_format_nonliteral : Error<
"format string is not a string literal">;
def err_printf_insufficient_data_args : Error<
"more '%%' conversions than data arguments">;
def err_printf_data_arg_not_used : Error<
"data argument not used by format string">;
def err_format_invalid_conversion : Error<
"invalid conversion specifier '%0'">;
def err_printf_incomplete_specifier : Error<
"incomplete format specifier">;
def err_missing_format_string : Error<
"format string missing">;
def err_scanf_nonzero_width : Error<
"zero field width in scanf format string is unused">;
def err_format_conversion_argument_type_mismatch : Error<
"format specifies type %0 but the argument has "
"%select{type|underlying type}2 %1">;
def err_format_conversion_argument_type_mismatch_pedantic : Error<
err_format_conversion_argument_type_mismatch.Text>;
def err_format_conversion_argument_type_mismatch_confusion : Error<
err_format_conversion_argument_type_mismatch.Text>;
def err_format_argument_needs_cast : Error<
"%select{values of type|enum values with underlying type}2 '%0' should not "
"be used as format arguments; add an explicit cast to %1 instead">;
def err_format_argument_needs_cast_pedantic : Error<
err_format_argument_needs_cast.Text>;
def err_printf_positional_arg_exceeds_data_args : Error <
"data argument position '%0' exceeds the number of data arguments (%1)">;
def err_format_invalid_positional_specifier : Error<
"invalid position specified for %select{field width|field precision}0">;
def err_format_mix_positional_nonpositional_args : Error<
"cannot mix positional and non-positional arguments in format string">;
def err_empty_format_string : Error<
"format string is empty">;
def err_format_string_is_wide_literal : Error<
"format string should not be a wide string">;
def err_printf_format_string_contains_null_char : Error<
"format string contains '\\0' within the string body">;
def err_printf_format_string_not_null_terminated : Error<
"format string is not null-terminated">;
def err_printf_asterisk_missing_arg : Error<
"'%select{*|.*}0' specified field %select{width|precision}0 is missing a matching 'int' argument">;
def err_printf_asterisk_wrong_type : Error<
"field %select{width|precision}0 should have type %1, but argument has type %2">;
def err_printf_nonsensical_optional_amount: Error<
"%select{field width|precision}0 used with '%1' conversion specifier, resulting in undefined behavior">;
def err_printf_nonsensical_flag: Error<
"flag '%0' results in undefined behavior with '%1' conversion specifier">;
def err_format_nonsensical_length: Error<
"length modifier '%0' results in undefined behavior or no effect with '%1' conversion specifier">;
def err_format_non_standard_positional_arg: Error<
"positional arguments are not supported by ISO C">;
def err_format_non_standard: Error<
"'%0' %select{length modifier|conversion specifier}1 is not supported by ISO C">;
def err_format_non_standard_conversion_spec: Error<
"using length modifier '%0' with conversion specifier '%1' is not supported by ISO C">;
def err_format_invalid_annotation : Error<
"using '%0' format specifier annotation outside of os_log()/os_trace()">;
def err_format_P_no_precision : Error<
"using '%%P' format specifier without precision">;
def err_printf_ignored_flag: Error<
"flag '%0' is ignored when flag '%1' is present">;
def err_scanf_scanlist_incomplete : Error<
"no closing ']' for '%%[' in scanf format string">;
def err_format_bool_as_character : Error<
"using '%0' format specifier, but argument has boolean value">;

} // end of Checked C Category
} // end of sema component.
51 changes: 51 additions & 0 deletions clang/lib/AST/ExprUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,25 @@ bool ExprUtil::ReadsMemoryViaPointer(Expr *E, bool IncludeAllMemberExprs) {
}
}

bool ExprUtil::IsDereferenceOrSubscript(Expr *E) {
if (!E)
return false;
E = E->IgnoreParens();
if (isa<ArraySubscriptExpr>(E))
return true;
UnaryOperator *UO = dyn_cast<UnaryOperator>(E);
if (!UO)
return false;
return UO->getOpcode() == UnaryOperatorKind::UO_Deref;
}

bool ExprUtil::IsReturnValueExpr(Expr *E) {
BoundsValueExpr *BVE = dyn_cast_or_null<BoundsValueExpr>(E);
if (!BVE)
return false;
return BVE->getKind() == BoundsValueExpr::Kind::Return;
}

namespace {
class FindLValueHelper : public RecursiveASTVisitor<FindLValueHelper> {
private:
Expand Down Expand Up @@ -341,6 +360,22 @@ namespace {
return true;
}

bool VisitUnaryOperator(UnaryOperator *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return true;
if (Lex.CompareExprSemantically(E, LValue))
Found = true;
return true;
}

bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return true;
if (Lex.CompareExprSemantically(E, LValue))
Found = true;
return true;
}

// Do not traverse the child of a BoundsValueExpr.
// Expressions within a BoundsValueExpr should not be considered
// when looking for LValue.
Expand Down Expand Up @@ -430,6 +465,22 @@ namespace {
return true;
}

bool VisitUnaryOperator(UnaryOperator *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return true;
if (Lex.CompareExprSemantically(E, LValue))
++Count;
return true;
}

bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return true;
if (Lex.CompareExprSemantically(E, LValue))
++Count;
return true;
}

// Do not traverse the child of a BoundsValueExpr.
// If a BoundsValueExpr uses the expression LValue (or a variable whose
// declaration matches V), this should not count toward the total
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/PreorderAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,14 @@ void PreorderAST::CreateUnaryOperator(UnaryOperator *E, Node *Parent) {
}

void PreorderAST::CreateArraySubscript(ArraySubscriptExpr *E, Node *Parent) {
// e1[e2] has the same canonical form as *(e1 + e2).
// e1[e2] has the same canonical form as *(e1 + e2 + 0).
auto *DerefExpr = BinaryOperator::Create(Ctx, E->getBase(), E->getIdx(),
BinaryOperatorKind::BO_Add, E->getType(),
E->getValueKind(), E->getObjectKind(),
E->getExprLoc(), FPOptionsOverride());
auto *N = new UnaryOperatorNode(UnaryOperatorKind::UO_Deref, Parent);
AttachNode(N, Parent);
Create(DerefExpr, N);
AddZero(DerefExpr, N);
}

void PreorderAST::CreateMember(MemberExpr *E, Node *Parent) {
Expand Down
55 changes: 39 additions & 16 deletions clang/lib/Sema/BoundsUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ BoundsExpr *BoundsUtil::ExpandToRange(Sema &S, VarDecl *D, BoundsExpr *B) {
BoundsExpr *BoundsUtil::ReplaceLValueInBounds(Sema &S, BoundsExpr *Bounds,
Expr *LValue, Expr *OriginalValue,
CheckedScopeSpecifier CSS) {
if (Bounds->isUnknown() || Bounds->isAny())
return Bounds;
Expr *Replaced = ReplaceLValue(S, Bounds, LValue, OriginalValue, CSS);
if (!Replaced)
return CreateBoundsUnknown(S);
Expand Down Expand Up @@ -223,10 +225,9 @@ namespace {
if (Lex.CompareExpr(V, E) == Lexicographic::Result::Equal) {
if (OriginalValue)
return OriginalValue;
else
return ExprError();
} else
return E;
return ExprError();
}
return E;
}

ExprResult TransformMemberExpr(MemberExpr *E) {
Expand All @@ -236,34 +237,56 @@ namespace {
if (Lex.CompareExprSemantically(M, E)) {
if (OriginalValue)
return OriginalValue;
else
return ExprError();
} else
return ExprError();
}
return E;
}

ExprResult TransformUnaryOperator(UnaryOperator *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return E;
if (Lex.CompareExprSemantically(LValue, E)) {
if (OriginalValue)
return OriginalValue;
return ExprError();
}
return E;
}

ExprResult TransformArraySubscriptExpr(ArraySubscriptExpr *E) {
if (!ExprUtil::IsDereferenceOrSubscript(LValue))
return E;
if (Lex.CompareExprSemantically(LValue, E)) {
if (OriginalValue)
return OriginalValue;
return ExprError();
}
return E;
}

// Overriding TransformImplicitCastExpr is necessary since TreeTransform
// does not preserve implicit casts.
ExprResult TransformImplicitCastExpr(ImplicitCastExpr *E) {
// Replace V with OV (if applicable) in the subexpression of E.
// Replace LValue with OriginalValue (if applicable) in the
// subexpression of E.
ExprResult ChildResult = TransformExpr(E->getSubExpr());
if (ChildResult.isInvalid())
return ChildResult;

Expr *Child = ChildResult.get();
CastKind CK = E->getCastKind();

// Only cast children of lvalue to rvalue or array to pointer casts
// to an rvalue if necessary. The transformed child expression may
// no longer be an lvalue, depending on the original value.
// For example, if x is transformed to the original value x + 1, it
// does not need to be cast to an rvalue.
if (CK == CastKind::CK_LValueToRValue ||
CK == CastKind::CK_ArrayToPointerDecay)
// Only cast children of lvalue to rvalue casts to an rvalue if
// necessary. The transformed child expression may no longer be
// an lvalue, depending on the original value. For example, if x
// is transformed to the original value x + 1, it does not need to
// be cast to an rvalue.
return ExprCreatorUtil::EnsureRValue(SemaRef, Child);
else
return ExprCreatorUtil::CreateImplicitCast(SemaRef, Child,
CK, E->getType());

return ExprCreatorUtil::CreateImplicitCast(SemaRef, Child,
CK, E->getType());
}
};
}
Expand Down
Loading