[clang][Analyzer] Add errno state to standard functions modeling.

This updates StdLibraryFunctionsChecker to set the state of 'errno'
by using the new errno_modeling functionality.
The errno value is set in the PostCall callback. Setting it in call::Eval
did not work for some reason and then every function should be
EvalCallAsPure which may be bad to do. Now the errno value and state
is not allowed to be checked in any PostCall checker callback because
it is unspecified if the errno was set already or will be set later
by this checker.

Reviewed By: martong, steakhal

Differential Revision: https://reviews.llvm.org/D125400
This commit is contained in:
Balázs Kéri 2022-06-21 08:29:31 +02:00
parent ed8fceaa09
commit 957014da2d
3 changed files with 528 additions and 132 deletions

View File

@ -0,0 +1,49 @@
// RUN: %clang_analyze_cc1 -verify -analyzer-output text %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-checker=apiModeling.Errno \
// RUN: -analyzer-checker=alpha.unix.Errno \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true
#include "Inputs/errno_var.h"
int access(const char *path, int amode);
void clang_analyzer_warnIfReached();
void test1() {
access("path", 0); // no note here
access("path", 0);
// expected-note@-1{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
if (errno != 0) {
// expected-warning@-1{{An undefined value may be read from 'errno'}}
// expected-note@-2{{An undefined value may be read from 'errno'}}
}
}
void test2() {
if (access("path", 0) == -1) {
// expected-note@-1{{Taking true branch}}
// Failure path.
if (errno != 0) {
// expected-note@-1{{'errno' is not equal to 0}}
// expected-note@-2{{Taking true branch}}
clang_analyzer_warnIfReached(); // expected-note {{REACHABLE}} expected-warning {{REACHABLE}}
} else {
clang_analyzer_warnIfReached(); // no-warning: We are on the failure path.
}
}
}
void test3() {
if (access("path", 0) != -1) {
// Success path.
// expected-note@-2{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
// expected-note@-3{{Taking true branch}}
if (errno != 0) {
// expected-warning@-1{{An undefined value may be read from 'errno'}}
// expected-note@-2{{An undefined value may be read from 'errno'}}
}
}
}

View File

@ -0,0 +1,56 @@
// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=debug.ExprInspection \
// RUN: -analyzer-checker=apiModeling.StdCLibraryFunctions \
// RUN: -analyzer-checker=apiModeling.Errno \
// RUN: -analyzer-checker=alpha.unix.Errno \
// RUN: -analyzer-config apiModeling.StdCLibraryFunctions:ModelPOSIX=true
#include "Inputs/errno_var.h"
typedef typeof(sizeof(int)) size_t;
typedef __typeof(sizeof(int)) off_t;
typedef size_t ssize_t;
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
off_t lseek(int fildes, off_t offset, int whence);
void clang_analyzer_warnIfReached();
void clang_analyzer_eval(int);
int unsafe_errno_read(int sock, void *data, int data_size) {
if (send(sock, data, data_size, 0) != data_size) {
if (errno == 1) {
// expected-warning@-1{{An undefined value may be read from 'errno'}}
return 0;
}
}
return 1;
}
int errno_lseek(int fildes, off_t offset) {
off_t result = lseek(fildes, offset, 0);
if (result == (off_t)-1) {
// Failure path.
// check if the function is modeled
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
return 2;
}
if (result != offset) {
// Not success path (?)
// not sure if this is a valid case, allow to check 'errno'
if (errno == 1) { // no warning
return 1;
}
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
if (result == offset) {
// The checker does not differentiate for this case.
// In general case no relation exists between the arg 2 and the returned
// value, only for SEEK_SET.
if (errno == 1) { // no warning
return 1;
}
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
return 0;
}