Skip to content

Commit ba9f1ed

Browse files
committed
[clang][analyzer] fix crash when modelling 'getline' function in checkers
1 parent 7468718 commit ba9f1ed

File tree

4 files changed

+169
-13
lines changed

4 files changed

+169
-13
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,9 @@ impact the linker behaviour like the other `-static-*` flags.
10601060
Crash and bug fixes
10611061
^^^^^^^^^^^^^^^^^^^
10621062

1063+
- Fixed a crash in ``UnixAPIMisuseChecker`` and ``MallocChecker`` when analyzing
1064+
code with non-standard ``getline`` or ``getdelim`` function signatures.
1065+
10631066
Improvements
10641067
^^^^^^^^^^^^
10651068

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,10 +1518,15 @@ void MallocChecker::checkGetdelim(ProgramStateRef State, const CallEvent &Call,
15181518
if (!CE)
15191519
return;
15201520

1521-
const auto LinePtr =
1522-
getPointeeVal(Call.getArgSVal(0), State)->getAs<DefinedSVal>();
1523-
const auto Size =
1524-
getPointeeVal(Call.getArgSVal(1), State)->getAs<DefinedSVal>();
1521+
auto LinePtrOpt = getPointeeVal(Call.getArgSVal(0), State);
1522+
if (!LinePtrOpt)
1523+
return;
1524+
const auto LinePtr = LinePtrOpt->getAs<DefinedSVal>();
1525+
1526+
auto SizeOpt = getPointeeVal(Call.getArgSVal(1), State);
1527+
if (!SizeOpt)
1528+
return;
1529+
const auto Size = SizeOpt->getAs<DefinedSVal>();
15251530
if (!LinePtr || !Size || !LinePtr->getAsRegion())
15261531
return;
15271532

clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,21 @@ ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
332332

333333
// We have a pointer to a pointer to the buffer, and a pointer to the size.
334334
// We want what they point at.
335-
auto LinePtrSVal = getPointeeVal(LinePtrPtrSVal, State)->getAs<DefinedSVal>();
335+
auto LinePtrValOpt = getPointeeVal(LinePtrPtrSVal, State);
336+
if (!LinePtrValOpt)
337+
return nullptr;
338+
339+
auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
336340
auto NSVal = getPointeeVal(SizePtrSVal, State);
337341
if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
338342
return nullptr;
339343

340344
assert(LinePtrPtrExpr && SizePtrExpr);
341345

346+
// Add defensive check to ensure we can handle the assume operation
347+
if (!LinePtrSVal->getAs<DefinedSVal>())
348+
return nullptr;
349+
342350
const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
343351
if (LinePtrNotNull && !LinePtrNull) {
344352
// If `*lineptr` is not null, but `*n` is undefined, there is UB.
@@ -350,9 +358,16 @@ ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
350358
// If it is defined, and known, its size must be less than or equal to
351359
// the buffer size.
352360
auto NDefSVal = NSVal->getAs<DefinedSVal>();
361+
if (!NDefSVal)
362+
return LinePtrNotNull;
363+
353364
auto &SVB = C.getSValBuilder();
354-
auto LineBufSize =
355-
getDynamicExtent(LinePtrNotNull, LinePtrSVal->getAsRegion(), SVB);
365+
366+
const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
367+
if (!LinePtrRegion)
368+
return LinePtrNotNull;
369+
370+
auto LineBufSize = getDynamicExtent(LinePtrNotNull, LinePtrRegion, SVB);
356371
auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
357372
*NDefSVal, SVB.getConditionType())
358373
.getAs<DefinedOrUnknownSVal>();
@@ -370,23 +385,29 @@ ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
370385
void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,
371386
const CallEvent &Call) const {
372387
ProgramStateRef State = C.getState();
388+
if (Call.getNumArgs() < 2)
389+
return;
390+
391+
const Expr *Arg0 = Call.getArgExpr(0);
392+
const Expr *Arg1 = Call.getArgExpr(1);
393+
394+
if (!Arg0->getType()->isPointerType() || !Arg1->getType()->isPointerType())
395+
return;
373396

374397
// The parameter `n` must not be NULL.
375398
SVal SizePtrSval = Call.getArgSVal(1);
376-
State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
399+
State = EnsurePtrNotNull(SizePtrSval, Arg1, C, State, "Size");
377400
if (!State)
378401
return;
379402

380403
// The parameter `lineptr` must not be NULL.
381404
SVal LinePtrPtrSVal = Call.getArgSVal(0);
382-
State =
383-
EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
405+
State = EnsurePtrNotNull(LinePtrPtrSVal, Arg0, C, State, "Line");
384406
if (!State)
385407
return;
386408

387-
State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
388-
Call.getArgExpr(0),
389-
Call.getArgExpr(1), C, State);
409+
State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval, Arg0,
410+
Arg1, C, State);
390411
if (!State)
391412
return;
392413

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_CORRECT
2+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_1
3+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_2
4+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_3
5+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_4
6+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_5
7+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix -verify %s -DTEST_GETLINE_GH144884
8+
9+
// emulator of "system-header-simulator.h" because of redefinition of 'getline' function
10+
typedef struct _FILE FILE;
11+
typedef __typeof(sizeof(int)) size_t;
12+
typedef long ssize_t;
13+
#define NULL 0
14+
15+
int fclose(FILE *fp);
16+
FILE *tmpfile(void);
17+
18+
#ifdef TEST_CORRECT
19+
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
20+
ssize_t getdelim(char **lineptr, size_t *n, int delimiter, FILE *stream);
21+
22+
void test_correct() {
23+
FILE *F1 = tmpfile();
24+
if (!F1)
25+
return;
26+
char *buffer = NULL;
27+
getline(&buffer, NULL, F1); // expected-warning {{Size pointer might be NULL}}
28+
fclose(F1);
29+
}
30+
31+
void test_delim_correct() {
32+
FILE *F1 = tmpfile();
33+
if (!F1)
34+
return;
35+
char *buffer = NULL;
36+
getdelim(&buffer, NULL, ',', F1); // expected-warning {{Size pointer might be NULL}}
37+
fclose(F1);
38+
}
39+
#endif
40+
41+
#ifdef TEST_GETLINE_1
42+
// expected-no-diagnostics
43+
ssize_t getline(int lineptr);
44+
45+
void test() {
46+
FILE *F1 = tmpfile();
47+
if (!F1)
48+
return;
49+
int buffer = 0;
50+
getline(buffer);
51+
fclose(F1);
52+
}
53+
#endif
54+
55+
#ifdef TEST_GETLINE_2
56+
ssize_t getline(char **lineptr, size_t *n);
57+
58+
void test() {
59+
FILE *F1 = tmpfile();
60+
if (!F1)
61+
return;
62+
char *buffer = NULL;
63+
getline(&buffer, NULL); // expected-warning {{Size pointer might be NULL}}
64+
fclose(F1);
65+
}
66+
#endif
67+
68+
#ifdef TEST_GETLINE_3
69+
// expected-no-diagnostics
70+
ssize_t getline(char **lineptr, size_t n, FILE *stream);
71+
72+
void test() {
73+
FILE *F1 = tmpfile();
74+
if (!F1)
75+
return;
76+
char *buffer = NULL;
77+
getline(&buffer, 0, F1);
78+
fclose(F1);
79+
}
80+
#endif
81+
82+
#ifdef TEST_GETLINE_4
83+
ssize_t getline(char **lineptr, size_t *n, int stream);
84+
ssize_t getdelim(char **lineptr, size_t *n, int delimiter, int stream);
85+
86+
void test() {
87+
FILE *F1 = tmpfile();
88+
if (!F1)
89+
return;
90+
char *buffer = NULL;
91+
getline(&buffer, NULL, 1); // expected-warning {{Size pointer might be NULL}}
92+
fclose(F1);
93+
}
94+
95+
void test_delim() {
96+
FILE *F1 = tmpfile();
97+
if (!F1)
98+
return;
99+
char *buffer = NULL;
100+
getdelim(&buffer, NULL, ',', 1); // expected-warning {{Size pointer might be NULL}}
101+
fclose(F1);
102+
}
103+
#endif
104+
105+
#ifdef TEST_GETLINE_5
106+
ssize_t getdelim(char **lineptr, size_t *n, const char* delimiter, FILE *stream);
107+
108+
void test_delim() {
109+
FILE *F1 = tmpfile();
110+
if (!F1)
111+
return;
112+
char *buffer = NULL;
113+
getdelim(&buffer, NULL, ",", F1); // expected-warning {{Size pointer might be NULL}}
114+
fclose(F1);
115+
}
116+
#endif
117+
118+
#ifdef TEST_GETLINE_GH144884
119+
// expected-no-diagnostics
120+
struct AW_string {};
121+
void getline(int *, struct AW_string);
122+
void top() {
123+
struct AW_string line;
124+
int getline_file_info;
125+
getline(&getline_file_info, line);
126+
}
127+
#endif

0 commit comments

Comments
 (0)