Skip to content

Commit cf0d756

Browse files
committed
Implement RULE-21-13
1 parent ea40969 commit cf0d756

File tree

2 files changed

+34
-25
lines changed

2 files changed

+34
-25
lines changed

c/misra/src/rules/RULE-21-13/CtypeFunctionArgNotUnsignedCharOrEof.ql

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,36 @@ import codingstandards.c.misra
1515
import codingstandards.cpp.ReadErrorsAndEOF
1616
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
1717
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
18-
import semmle.code.cpp.dataflow.DataFlow // TODO use this...
1918

20-
query predicate isCtypeFunction(Function function) {
21-
function.getADeclaration().getAFile().(HeaderFile).getShortName() = "_ctype" // TODO: change it back to `ctype`
19+
class CtypeFunction extends Function {
20+
CtypeFunction() { this.getADeclaration().getAFile().(HeaderFile).getShortName() = "_ctype" }
2221
}
2322

24-
query predicate isInUnsignedCharRange(Expr var) {
25-
// TODO: shouldn't be an Expr, instead get it as an argument from a FunctionCall that isCtypeFunction
23+
predicate unsignedCharRange(int lower, int upper, EOFInvocation eof) {
2624
exists(UnsignedCharType unsignedChar |
27-
// Consider cases where the argument's value is cast to some smaller type, clipping the range.
28-
typeLowerBound(unsignedChar) <= lowerBound(var.getFullyConverted()) and
29-
upperBound(var.getFullyConverted()) <= typeUpperBound(unsignedChar)
25+
lower = typeLowerBound(unsignedChar) and
26+
upper = upperBound(eof.getExpr()) and
27+
typeLowerBound(unsignedChar) <= lowerBound(eof.getExpr()) and
28+
upperBound(eof.getExpr()) <= typeUpperBound(unsignedChar)
3029
)
3130
}
3231

33-
// Uh oh, this is empty
34-
query predicate isEOFInvocation(EOFInvocation eof) {
35-
any()
32+
predicate isEquivToEOF(Expr expr) {
33+
exists(EOFInvocation eof | DataFlow::localFlow(DataFlow::exprNode(eof.getExpr()), DataFlow::exprNode(expr)))
3634
}
3735

38-
/* very early draft */
39-
query predicate equivToEOF(FunctionCall fc, EOFInvocation eof) {
40-
// var is a param of ctypefunctioncall
41-
isCtypeFunction(fc.getTarget()) and
42-
DataFlow::localFlow(DataFlow::exprNode(eof.getExpr()), DataFlow::exprNode(fc.getArgument(0)))
43-
}
44-
from Element x
36+
from FunctionCall ctypeCall
4537
where
46-
not isExcluded(x, StandardLibraryFunctionTypesPackage::ctypeFunctionArgNotUnsignedCharOrEofQuery()) and
47-
any()
48-
select 1
38+
not isExcluded(ctypeCall,
39+
StandardLibraryFunctionTypesPackage::ctypeFunctionArgNotUnsignedCharOrEofQuery()) and
40+
exists(CtypeFunction ctype, UnsignedCharType unsignedChar |
41+
ctypeCall = ctype.getACallToThisFunction()
42+
|
43+
/* The argument's value should be in the `unsigned char` range. */
44+
typeLowerBound(unsignedChar) <= lowerBound(ctypeCall.getAnArgument().getExplicitlyConverted()) and // consider casts
45+
upperBound(ctypeCall.getAnArgument().getExplicitlyConverted()) <= typeUpperBound(unsignedChar)
46+
or
47+
/* The argument's value is reachable from EOF. */
48+
exists(EOFInvocation eof | DataFlow::localFlow(DataFlow::exprNode(eof.getExpr()), DataFlow::exprNode(ctypeCall.getAnArgument())))
49+
)
50+
select ctypeCall, ctypeCall.getAnArgument()

c/misra/test/rules/RULE-21-13/test.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33

44
void sample() {
55
unsigned char c1 = 'c';
6-
int r1 = isalnum(c1); // compliant
7-
unsigned char c2 = EOF;
8-
int r2 = isalnum(c2); // compliant
6+
int r1 = isalnum(c1); // COMPLIANT: ASCII 99 is within unsigned char range of [0, 255]
7+
unsigned char x1 = EOF;
8+
unsigned char x2 = x1;
9+
unsigned char c2 = x2 + 1;
10+
int r2 = isdigit(c2); // COMPLIANT: EOF (-1)
11+
12+
int x3 = 256;
13+
int x4 = x3;
14+
int c3 = x4;
15+
int r3 = islower(c3); // NON_COMPLIANT: is outside unsigned char range of[0, 255]
916
}
1017

11-
int main() { return 0; }
18+
int main() { return 0; }

0 commit comments

Comments
 (0)