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
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,10 @@ def warn_fortify_source_overflow
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 %2,"
" but size argument is %1">,
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)
Type = Context.getSignedSizeType();
else
Type = Context.getSizeType();
break;
case 'w': // wchar_t.
assert(HowLong == 0 && !Signed && !Unsigned && "Bad modifiers for 'w'!");
Expand Down
70 changes: 68 additions & 2 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,14 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
std::optional<llvm::APSInt> DestinationSize;
unsigned DiagID = 0;
bool IsChkVariant = false;
bool IsTriggered = false;

auto CompareSizeSourceToDest = [&]() {
return SourceSize && DestinationSize
? std::optional<int>{llvm::APSInt::compareValues(
*SourceSize, *DestinationSize)}
: std::nullopt;
};

auto GetFunctionName = [&]() {
std::string FunctionNameStr =
Expand Down Expand Up @@ -1270,6 +1278,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_strlen_overflow;
SourceSize = ComputeStrLenArgument(1);
DestinationSize = ComputeSizeArgument(0);
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

Expand All @@ -1279,6 +1288,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
SourceSize = ComputeStrLenArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(2);
IsChkVariant = true;
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

Expand Down Expand Up @@ -1349,11 +1359,13 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
} else {
DestinationSize = ComputeSizeArgument(0);
}
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
}
return;
}

case Builtin::BI__builtin___memcpy_chk:
case Builtin::BI__builtin___memmove_chk:
case Builtin::BI__builtin___memset_chk:
Expand All @@ -1369,6 +1381,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
IsChkVariant = true;
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

Expand All @@ -1378,6 +1391,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
SourceSize = ComputeExplicitObjectSizeArgument(1);
DestinationSize = ComputeExplicitObjectSizeArgument(3);
IsChkVariant = true;
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

Expand All @@ -1395,6 +1409,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

Expand All @@ -1409,8 +1424,58 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
DiagID = diag::warn_fortify_source_overflow;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(0);
IsTriggered = CompareSizeSourceToDest() > 0;
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: {
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

case Builtin::BIpread:
case Builtin::BI__builtin_pread:
case Builtin::BIpread64:
case Builtin::BI__builtin_pread64: {
DiagID = diag::warn_fortify_source_size_mismatch;
SourceSize = ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
DestinationSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}

case Builtin::BIwrite:
case Builtin::BI__builtin_write: {
DiagID = diag::warn_fortify_destination_size_mismatch;
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 2);
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 1);
IsTriggered = CompareSizeSourceToDest() < 0;
break;
}

case Builtin::BIpwrite:
case Builtin::BI__builtin_pwrite:
case Builtin::BIpwrite64:
case Builtin::BI__builtin_pwrite64: {
DiagID = diag::warn_fortify_destination_size_mismatch;
SourceSize = ComputeSizeArgument(TheCall->getNumArgs() - 3);
DestinationSize =
ComputeExplicitObjectSizeArgument(TheCall->getNumArgs() - 2);
IsTriggered = CompareSizeSourceToDest() < 0;
break;
}

case Builtin::BIsnprintf:
case Builtin::BI__builtin_snprintf:
case Builtin::BIvsnprintf:
Expand Down Expand Up @@ -1446,11 +1511,12 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
}
DestinationSize = ComputeSizeArgument(0);
IsTriggered = CompareSizeSourceToDest() > 0;
break;
}
}

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

std::string FunctionName = GetFunctionName();
Expand Down
55 changes: 55 additions & 0 deletions clang/test/Sema/warn-fortify-source.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,61 @@ void call_memset(void) {
__builtin_memset(buf, 0xff, 11); // expected-warning {{'memset' will always overflow; destination buffer has size 10, but size argument is 11}}
}

void call_read(void) {
char buf[10];
__builtin_read(0, buf, 10);
__builtin_read(0, buf, 20); // expected-warning {{'read' size argument is too large; destination buffer has size 10, but size argument is 20}}
}

void call_pread(void) {
char buf[10];
__builtin_pread(0, buf, 10, 0);
__builtin_pread(0, buf, 20, 0); // expected-warning {{'pread' size argument is too large; destination buffer has size 10, but size argument is 20}}
}

void call_pread64(void) {
char buf[10];
__builtin_pread64(0, buf, 10, 0);
__builtin_pread64(0, buf, 20, 0); // expected-warning {{'pread64' size argument is too large; destination buffer has size 10, but size argument is 20}}
}

void call_write(void) {
char buf[10];
__builtin_write(0, buf, 10);
__builtin_write(0, buf, 20); // expected-warning {{'write' size argument is too large; source buffer has size 10, but size argument is 20}}
}

void call_pwrite(void) {
char buf[10];
__builtin_pwrite(0, buf, 10, 0);
__builtin_pwrite(0, buf, 20, 0); // expected-warning {{'pwrite' size argument is too large; source buffer has size 10, but size argument is 20}}
}

void call_pwrite64(void) {
char buf[10];
__builtin_pwrite64(0, buf, 10, 0);
__builtin_pwrite64(0, buf, 20, 0); // expected-warning {{'pwrite64' size argument is too large; source buffer has size 10, but size argument is 20}}
}

void call_getcwd(void) {
char buf[10];
__builtin_getcwd(buf, 10);
__builtin_getcwd(buf, 20); // expected-warning {{'getcwd' size argument is too large; destination buffer has size 10, but size argument is 20}}
}

void call_readlink(void) {
char buf[10];
__builtin_readlink("path", buf, 10);
__builtin_readlink("path", buf, 20); // expected-warning {{'readlink' size argument is too large; destination buffer has size 10, but size argument is 20}}
}

void call_readlinkat(void) {
char buf[10];
__builtin_readlinkat(0, "path", buf, 10);
__builtin_readlinkat(0, "path", buf, 20); // expected-warning {{'readlinkat' size argument is too large; destination buffer has size 10, but size argument is 20}}
}


void call_snprintf(double d, int n) {
char buf[10];
__builtin_snprintf(buf, 10, "merp");
Expand Down
3 changes: 3 additions & 0 deletions clang/utils/TableGen/ClangBuiltinsEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,15 @@ class PrototypeParser {
.Case("msint32_t", "Ni")
.Case("msuint32_t", "UNi")
.Case("objc_super", "M")
.Case("off_t", "Li")
.Case("off64_t", "Wi")
.Case("pid_t", "p")
.Case("ptrdiff_t", "Y")
.Case("SEL", "H")
.Case("short", "s")
.Case("sigjmp_buf", "SJ")
.Case("size_t", "z")
.Case("ssize_t", "Sz")
.Case("ucontext_t", "K")
.Case("uint32_t", "UZi")
.Case("uint64_t", "UWi")
Expand Down