Skip to content
Open
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
65 changes: 65 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -3490,6 +3490,8 @@ def StrnCaseCmp : GNULibBuiltin<"strings.h"> {
let RequiresUndef = 1;
}

// POSIX unistd.h

def GNU_Exit : GNULibBuiltin<"unistd.h"> {
let Spellings = ["_exit"];
let Attributes = [NoReturn];
Expand All @@ -3502,6 +3504,69 @@ def VFork : LibBuiltin<"unistd.h"> {
let Prototype = "pid_t()";
}

def Read : LibBuiltin<"unistd.h"> {
let Spellings = ["read"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void*, size_t)";
let AddBuiltinPrefixedAlias = 1;
}

def PRead : LibBuiltin<"unistd.h"> {
let Spellings = ["pread"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void*, size_t, off_t)";
let AddBuiltinPrefixedAlias = 1;
}

def PRead64 : LibBuiltin<"unistd.h"> {
let Spellings = ["pread64"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void*, size_t, off64_t)";
let AddBuiltinPrefixedAlias = 1;
}

def Write : LibBuiltin<"unistd.h"> {
let Spellings = ["write"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void const*, size_t)";
let AddBuiltinPrefixedAlias = 1;
}

def PWrite : LibBuiltin<"unistd.h"> {
let Spellings = ["pwrite"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void const*, size_t, off_t)";
let AddBuiltinPrefixedAlias = 1;
}

def PWrite64 : LibBuiltin<"unistd.h"> {
let Spellings = ["pwrite64"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, void const*, size_t, off64_t)";
let AddBuiltinPrefixedAlias = 1;
}

def GetCWD : LibBuiltin<"unistd.h"> {
let Spellings = ["getcwd"];
let Attributes = [NoThrow];
let Prototype = "char*(char*, size_t)";
let AddBuiltinPrefixedAlias = 1;
}

def ReadLink : LibBuiltin<"unistd.h"> {
let Spellings = ["readlink"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(char const* restrict, char* restrict, size_t)";
let AddBuiltinPrefixedAlias = 1;
}

def ReadLinkAt : LibBuiltin<"unistd.h"> {
let Spellings = ["readlinkat"];
let Attributes = [NoThrow];
let Prototype = "ssize_t(int, char const* restrict, char* restrict, size_t)";
let AddBuiltinPrefixedAlias = 1;
}

// POSIX pthread.h

def PthreadCreate : GNULibBuiltin<"pthread.h"> {
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -950,9 +950,17 @@ def warn_builtin_chk_overflow : Warning<

def warn_fortify_source_overflow
: Warning<warn_builtin_chk_overflow.Summary>, InGroup<FortifySource>;
def warn_fortify_destination_over_read
: Warning<"'%0' will always over-read; source buffer has size %1,"
" but size argument is %2">,
InGroup<FortifySource>;
def warn_fortify_source_size_mismatch : Warning<
"'%0' size argument is too large; destination buffer has size %1,"
" but size argument is %2">, InGroup<FortifySource>;
def warn_fortify_destination_size_mismatch
: Warning<"'%0' size argument is too large; source buffer has size %1,"
" but size argument is %2">,
InGroup<FortifySource>;

def warn_fortify_strlen_overflow: Warning<
"'%0' will always overflow; destination buffer has size %1,"
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12528,9 +12528,12 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'b'!");
Type = Context.BoolTy;
break;
case 'z': // size_t.
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'z'!");
Type = Context.getSizeType();
case 'z': // size_t and ssize_t.
assert(HowLong == 0 && "Bad modifiers for 'z'!");
if (Signed)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are both branches covered in testing? Please specify which test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think so.
Could you give me some guidance as to what tests would be suitable for this? Or pointers to relevant existing code?

Type = Context.getSignedSizeType();
else
Type = Context.getSizeType();
break;
case 'w': // wchar_t.
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'w'!");
Expand Down
174 changes: 135 additions & 39 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1239,9 +1239,19 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
return llvm::APSInt::getUnsigned(Result + 1).extOrTrunc(SizeTypeWidth);
};

// Size of the memory read from
std::optional<llvm::APSInt> SourceSize;
// Size of the memory written to
std::optional<llvm::APSInt> DestinationSize;
unsigned DiagID = 0;
// Maximum operation size for detecting possible out of bounds access
std::optional<llvm::APSInt> MaxOperationSize;
// Minimum operation size for detecting definate out of bounds access
std::optional<llvm::APSInt> MinOperationSize;

unsigned DiagOverflowID = diag::warn_fortify_source_overflow;
unsigned DiagMayOverflowID = diag::warn_fortify_source_size_mismatch;
unsigned DiagOverReadID = diag::warn_fortify_destination_over_read;
unsigned DiagMayOverReadID = diag::warn_fortify_destination_size_mismatch;
bool IsChkVariant = false;

auto GetFunctionName = [&]() {
Expand All @@ -1267,16 +1277,16 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BIstpcpy:
case Builtin::BI__builtin_strcpy:
case Builtin::BIstrcpy: {
DiagID = diag::warn_fortify_strlen_overflow;
SourceSize = ComputeStrLenArgument(1);
DiagOverflowID = diag::warn_fortify_strlen_overflow;
MinOperationSize = ComputeStrLenArgument(1);
DestinationSize = ComputeSizeArgument(0);
break;
}

case Builtin::BI__builtin___stpcpy_chk:
case Builtin::BI__builtin___strcpy_chk: {
DiagID = diag::warn_fortify_strlen_overflow;
SourceSize = ComputeStrLenArgument(1);
DiagOverflowID = diag::warn_fortify_strlen_overflow;
MinOperationSize = ComputeStrLenArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
break;
Expand All @@ -1302,12 +1312,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,

auto Diagnose = [&](unsigned ArgIndex, unsigned DestSize,
unsigned SourceSize) {
DiagID = diag::warn_fortify_scanf_overflow;
unsigned Index = ArgIndex + DataIndex;
std::string FunctionName = GetFunctionName();
DiagRuntimeBehavior(TheCall->getArg(Index)->getBeginLoc(), TheCall,
PDiag(DiagID) << FunctionName << (Index + 1)
<< DestSize << SourceSize);
PDiag(diag::warn_fortify_scanf_overflow)
<< FunctionName << (Index + 1) << DestSize
<< SourceSize);
};

auto ShiftedComputeSizeArgument = [&](unsigned Index) {
Expand Down Expand Up @@ -1338,11 +1348,11 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
if (!analyze_format_string::ParsePrintfString(
H, FormatBytes, FormatBytes + StrLen, getLangOpts(),
Context.getTargetInfo(), false)) {
DiagID = H.isKernelCompatible()
? diag::warn_format_overflow
: diag::warn_format_overflow_non_kprintf;
SourceSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
.extOrTrunc(SizeTypeWidth);
DiagOverflowID = H.isKernelCompatible()
? diag::warn_format_overflow
: diag::warn_format_overflow_non_kprintf;
MinOperationSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
.extOrTrunc(SizeTypeWidth);
if (BuiltinID == Builtin::BI__builtin___sprintf_chk) {
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
Expand All @@ -1354,6 +1364,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
return;
}

case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BI__builtin___memset_chk:
Expand All @@ -1364,8 +1375,9 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin___stpncpy_chk:
case Builtin::BI__builtin___memccpy_chk:
case Builtin::BI__builtin___mempcpy_chk: {
DiagID = diag::warn_builtin_chk_overflow;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
DiagOverflowID = diag::warn_builtin_chk_overflow;
MinOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
IsChkVariant = true;
Expand All @@ -1374,8 +1386,8 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,

case Builtin::BI__builtin___snprintf_chk:
case Builtin::BI__builtin___vsnprintf_chk: {
DiagID = diag::warn_builtin_chk_overflow;
SourceSize = ComputeExplicitObjectSizeArgument(1);
DiagOverflowID = diag::warn_builtin_chk_overflow;
MinOperationSize = ComputeExplicitObjectSizeArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(3);
IsChkVariant = true;
break;
Expand All @@ -1392,8 +1404,16 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
// diagnostic isn't quite right. We should still diagnose passing a buffer
// size larger than the destination buffer though; this is a runtime abort
// in _FORTIFY_SOURCE mode, and is quite suspicious otherwise.
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
break;
}

case Builtin::BImemset:
case Builtin::BI__builtin_memset: {
MinOperationSize = MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
break;
}
Expand All @@ -1402,25 +1422,66 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
case Builtin::BI__builtin_memcpy:
case Builtin::BImemmove:
case Builtin::BI__builtin_memmove:
case Builtin::BImemset:
case Builtin::BI__builtin_memset:
case Builtin::BImempcpy:
case Builtin::BI__builtin_mempcpy: {
DiagID = diag::warn_fortify_source_overflow;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
MinOperationSize = MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
SourceSize = ComputeSizeArgument(1);
break;
}

case Builtin::BIread:
case Builtin::BI__builtin_read:
case Builtin::BIreadlink:
case Builtin::BI__builtin_readlink:
case Builtin::BIreadlinkat:
case Builtin::BI__builtin_readlinkat:
case Builtin::BIgetcwd:
case Builtin::BI__builtin_getcwd: {
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
break;
}

case Builtin::BIpread:
case Builtin::BI__builtin_pread:
case Builtin::BIpread64:
case Builtin::BI__builtin_pread64: {
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
break;
}

case Builtin::BIwrite:
case Builtin::BI__builtin_write: {
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
break;
}

case Builtin::BIpwrite:
case Builtin::BI__builtin_pwrite:
case Builtin::BIpwrite64:
case Builtin::BI__builtin_pwrite64: {
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
MaxOperationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
break;
}

case Builtin::BIsnprintf:
case Builtin::BI__builtin_snprintf:
case Builtin::BIvsnprintf:
case Builtin::BI__builtin_vsnprintf: {
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(1);
MaxOperationSize = ComputeExplicitObjectSizeArgument(1);
const auto *FormatExpr = TheCall->getArg(2)->IgnoreParenImpCasts();
StringRef FormatStrRef;
size_t StrLen;
if (SourceSize &&
if (MaxOperationSize &&
ProcessFormatStringLiteral(FormatExpr, FormatStrRef, StrLen, Context)) {
EstimateSizeFormatHandler H(FormatStrRef);
const char *FormatBytes = FormatStrRef.data();
Expand All @@ -1430,13 +1491,13 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
llvm::APSInt FormatSize =
llvm::APSInt::getUnsigned(H.getSizeLowerBound())
.extOrTrunc(SizeTypeWidth);
if (FormatSize > *SourceSize && *SourceSize != 0) {
if (FormatSize > *MaxOperationSize && *MaxOperationSize != 0) {
unsigned TruncationDiagID =
H.isKernelCompatible() ? diag::warn_format_truncation
: diag::warn_format_truncation_non_kprintf;
SmallString<16> SpecifiedSizeStr;
SmallString<16> FormatSizeStr;
SourceSize->toString(SpecifiedSizeStr, /*Radix=*/10);
MaxOperationSize->toString(SpecifiedSizeStr, /*Radix=*/10);
FormatSize.toString(FormatSizeStr, /*Radix=*/10);
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(TruncationDiagID)
Expand All @@ -1446,22 +1507,57 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
break;
}
}

if (!SourceSize || !DestinationSize ||
llvm::APSInt::compareValues(*SourceSize, *DestinationSize) <= 0)
return;

std::string FunctionName = GetFunctionName();
SmallString<16> MaxOpStr;
SmallString<16> MinOpStr;

if (MinOperationSize)
MinOperationSize->toString(MinOpStr, /*Radix=*/10);
if (MaxOperationSize)
MaxOperationSize->toString(MaxOpStr, /*Radix=*/10);

if (DestinationSize) {
SmallString<16> DestinationStr;
DestinationSize->toString(DestinationStr, /*Radix=*/10);
// Check for definate overflow
if (MinOperationSize &&
llvm::APSInt::compareValues(*MinOperationSize, *DestinationSize) > 0) {
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(DiagOverflowID)
<< FunctionName << DestinationStr << MinOpStr);
}
// Check for possible overflow
else if (MaxOperationSize && llvm::APSInt::compareValues(
*MaxOperationSize, *DestinationSize) > 0) {
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(DiagMayOverflowID)
<< FunctionName << DestinationStr << MaxOpStr);
}
}

if (SourceSize) {
SmallString<16> SourceStr;
SourceSize->toString(SourceStr, /*Radix=*/10);
// Check for definate over-read
if (MinOperationSize &&
llvm::APSInt::compareValues(*MinOperationSize, *SourceSize) > 0) {
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(DiagOverReadID)
<< FunctionName << SourceStr << MinOpStr);

SmallString<16> DestinationStr;
SmallString<16> SourceStr;
DestinationSize->toString(DestinationStr, /*Radix=*/10);
SourceSize->toString(SourceStr, /*Radix=*/10);
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(DiagID)
<< FunctionName << DestinationStr << SourceStr);
}
// Check for possible over-read
else if (MaxOperationSize &&
llvm::APSInt::compareValues(*MaxOperationSize, *SourceSize) > 0) {
DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall,
PDiag(DiagMayOverReadID)
<< FunctionName << SourceStr << MaxOpStr);
}
}
}

static bool BuiltinSEHScopeCheck(Sema &SemaRef, CallExpr *TheCall,
Expand Down
Loading
Loading