Skip to content

Commit 667fc33

Browse files
author
Nikita Kraiouchkine
committed
Update OutOfBounds.qll
1 parent 11406b1 commit 667fc33

File tree

1 file changed

+123
-96
lines changed

1 file changed

+123
-96
lines changed

c/common/src/codingstandards/c/OutOfBounds.qll

+123-96
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ module OutOfBounds {
1818
bindingset[name, result]
1919
private string getNameOrInternalName(string name) {
2020
result = name or
21-
name.regexpMatch("__.*_+(?:" + result + ")")
21+
result.regexpMatch("__.*_+" + name + "_.*")
2222
}
2323

2424
/**
2525
* MISRA-C Rule 21.17 function table of names and parameter indices
2626
* which covers functions from <string.h> that rely on null-terminated strings.
27-
*
27+
*
2828
* This table is a subset of `libraryFunctionNameParamTable`.
2929
*
3030
* Note: These functions do not share a common semantic pattern of source and destination
@@ -33,11 +33,11 @@ module OutOfBounds {
3333
* The `SimpleStringLibraryFunction` base class provides an appropriate
3434
* interface for analyzing the functions in the below table.
3535
*/
36-
private Function libraryFunctionNameParamTableSimpleString(string name,
37-
int dst, int src, int src_sz, int dst_sz)
38-
{
39-
result.hasGlobalOrStdName(name) and
40-
src_sz = -1 and
36+
private Function libraryFunctionNameParamTableSimpleString(
37+
string name, int dst, int src, int src_sz, int dst_sz
38+
) {
39+
result.getName() = getNameOrInternalName(name) and
40+
src_sz = -1 and
4141
dst_sz = -1 and
4242
(
4343
name = "strcat" and
@@ -48,7 +48,7 @@ module OutOfBounds {
4848
dst = -1 and
4949
src = 0
5050
or
51-
name = ["strcmp", "strcoll"] and
51+
name = ["strcmp", "strcoll"] and
5252
dst = -1 and
5353
src = [0, 1]
5454
or
@@ -86,18 +86,6 @@ module OutOfBounds {
8686
)
8787
}
8888

89-
/**
90-
* An expansion of `libraryFunctionNameParamTableSimpleString` to include internal functions with
91-
* prefixes/suffixes such as "__builtin_%"" or "%_chk" (e.g. `__builtin___strcpy_chk`)
92-
*/
93-
bindingset[name]
94-
Function libraryFunctionNameParamTableSimpleStringRegex(string name, int dst, int src, int src_sz, int dst_sz) {
95-
exists(string stdName |
96-
result = libraryFunctionNameParamTableSimpleString(stdName, dst, src, src_sz, dst_sz) and
97-
getNameOrInternalName(name) = stdName
98-
)
99-
}
100-
10189
/**
10290
* A relation of the indices of buffer and size parameters of standard library functions
10391
* which are defined in rules CERT ARR38-C and MISRA-C rules 21.17 and 21.18.
@@ -107,7 +95,7 @@ module OutOfBounds {
10795
) {
10896
result = libraryFunctionNameParamTableSimpleString(name, dst, src, src_sz, dst_sz)
10997
or
110-
result.hasGlobalOrStdName(name) and
98+
result.getName() = getNameOrInternalName(name) and
11199
(
112100
name = ["fgets", "fgetws"] and
113101
dst = 0 and
@@ -217,55 +205,41 @@ module OutOfBounds {
217205
)
218206
}
219207

220-
/**
221-
* An expansion of `libraryFunctionNameParamTable` to include internal functions with
222-
* prefixes/suffixes such as "__builtin_%"" or "%_chk" (e.g. `__builtin___strncpy_chk`)
223-
*/
224-
bindingset[name]
225-
Function libraryFunctionNameParamTableRegex(string name, int dst, int src, int src_sz, int dst_sz) {
226-
exists(string stdName |
227-
result = libraryFunctionNameParamTable(stdName, dst, src, src_sz, dst_sz) and
228-
getNameOrInternalName(name) = stdName
229-
)
230-
}
231-
232208
/**
233209
* A library function that accesses one or more buffers supplied via arguments.
234210
*/
235211
class BufferAccessLibraryFunction extends Function {
236-
BufferAccessLibraryFunction() {
237-
this = libraryFunctionNameParamTableRegex(this.getName(), _, _, _, _)
238-
}
212+
BufferAccessLibraryFunction() { this = libraryFunctionNameParamTable(_, _, _, _, _) }
239213

240214
/**
241215
* Returns the indices of parameters that are a destination buffer.
242216
*/
243217
int getWriteParamIndex() {
244-
this = libraryFunctionNameParamTableRegex(this.getName(), result, _, _, _) and
218+
this = libraryFunctionNameParamTable(_, result, _, _, _) and
245219
result >= 0
246220
}
247221

248222
/**
249223
* Returns the indices of parameters that are a source buffer.
250224
*/
251225
int getReadParamIndex() {
252-
this = libraryFunctionNameParamTableRegex(this.getName(), _, result, _, _) and
226+
this = libraryFunctionNameParamTable(_, _, result, _, _) and
253227
result >= 0
254228
}
255229

256230
/**
257231
* Returns the index of the parameter that is the source buffer size.
258232
*/
259233
int getReadSizeParamIndex() {
260-
this = libraryFunctionNameParamTableRegex(this.getName(), _, _, result, _) and
234+
this = libraryFunctionNameParamTable(_, _, _, result, _) and
261235
result >= 0
262236
}
263237

264238
/**
265239
* Returns the index of the parameter that is the destination buffer size.
266240
*/
267241
int getWriteCountParamIndex() {
268-
this = libraryFunctionNameParamTableRegex(this.getName(), _, _, _, result) and
242+
this = libraryFunctionNameParamTable(_, _, _, _, result) and
269243
result >= 0
270244
}
271245

@@ -338,15 +312,19 @@ module OutOfBounds {
338312
*/
339313
class SimpleStringLibraryFunction extends BufferAccessLibraryFunction {
340314
SimpleStringLibraryFunction() {
341-
this = libraryFunctionNameParamTableSimpleStringRegex(this.getName(), _, _, _, _)
315+
this = libraryFunctionNameParamTable(this.getName(), _, _, _, _)
342316
}
343317

344318
override predicate getANullTerminatedParameterIndex(int i) {
345319
// by default, require null-terminated parameters for src but
346320
// only if the type of src is a plain char pointer.
347321
this.getReadParamIndex() = i and
348-
this.getReadParam().getType().getUnspecifiedType().
349-
(PointerType).getBaseType().getUnspecifiedType() instanceof PlainCharType
322+
this.getReadParam()
323+
.getType()
324+
.getUnspecifiedType()
325+
.(PointerType)
326+
.getBaseType()
327+
.getUnspecifiedType() instanceof PlainCharType
350328
}
351329
}
352330

@@ -358,7 +336,8 @@ module OutOfBounds {
358336
/**
359337
* A `BufferAccessLibraryFunction` modelling `strcat`
360338
*/
361-
class StrcatLibraryFunction extends StringConcatenationFunctionLibraryFunction, SimpleStringLibraryFunction
339+
class StrcatLibraryFunction extends StringConcatenationFunctionLibraryFunction,
340+
SimpleStringLibraryFunction
362341
{
363342
StrcatLibraryFunction() { this.getName() = getNameOrInternalName("strcat") }
364343

@@ -380,6 +359,18 @@ module OutOfBounds {
380359
}
381360
}
382361

362+
/**
363+
* A `BufferAccessLibraryFunction` modelling `strncpy`
364+
*/
365+
class StrncpyLibraryFunction extends BufferAccessLibraryFunction {
366+
StrncpyLibraryFunction() { this.getName() = getNameOrInternalName("strncpy") }
367+
368+
override predicate getANullTerminatedParameterIndex(int i) {
369+
// `strncpy` does not require null-terminated parameters
370+
none()
371+
}
372+
}
373+
383374
/**
384375
* A `FunctionCall` to a `BufferAccessLibraryFunction` that provides predicates for
385376
* reasoning about buffer overflow and other buffer access violations.
@@ -417,15 +408,16 @@ module OutOfBounds {
417408
int getStatedAllocValue(Expr e) {
418409
// `upperBound(e)` defaults to `exprMaxVal(e)` when `e` isn't analyzable. So to get a meaningful
419410
// result in this case we pick the minimum value obtainable from dataflow and range analysis.
420-
if upperBound(e) = exprMaxVal(e)
421-
then
422-
result = max(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())
411+
if upperBound(e) = exprMaxVal(e)
412+
then result = max(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt())
423413
else
424-
result =
425-
upperBound(e)
426-
.minimum(min(Expr source | DataFlow::localExprFlow(source, e) | source.getValue().toInt()))
427-
428-
414+
result =
415+
upperBound(e)
416+
.minimum(min(Expr source |
417+
DataFlow::localExprFlow(source, e)
418+
|
419+
source.getValue().toInt()
420+
))
429421
}
430422

431423
int getStatedValue(Expr e) {
@@ -475,8 +467,9 @@ module OutOfBounds {
475467
abstract predicate isNotNullTerminated();
476468
}
477469

478-
class DynamicAllocationSource extends PointerToObjectSource
479-
instanceof AllocationExpr, FunctionCall {
470+
class DynamicAllocationSource extends PointerToObjectSource instanceof AllocationExpr,
471+
FunctionCall
472+
{
480473
DynamicAllocationSource() {
481474
// exclude OperatorNewAllocationFunction to only deal with raw malloc-style calls,
482475
// which do not apply a multiple to the size of the allocation passed to them.
@@ -509,23 +502,32 @@ module OutOfBounds {
509502
* 2. `size_t sz = strlen(src); malloc(sz + 1);`
510503
*/
511504
Expr getSizeExprSource(Expr base, int offset) {
512-
if
513-
exists(Variable v, AddExpr ae |
514-
// case 1: variable_access + const in the size expression
515-
this.getSizeExpr() = ae and
516-
result = v.getAnAssignedValue() and
517-
base = ae.getLeftOperand() and
518-
offset = constOrZero(ae.getRightOperand()) and
519-
DataFlow::localExprFlow(result, base)
505+
if this.getSizeExpr() instanceof AddExpr
506+
then
507+
exists(AddExpr ae |
508+
exists(Variable v |
509+
// case 1: variable access + const in the size expression
510+
this.getSizeExpr() = ae and
511+
result = v.getAnAssignedValue() and
512+
base = ae.getLeftOperand() and
513+
offset = constOrZero(ae.getRightOperand()) and
514+
DataFlow::localExprFlow(result, base)
515+
or
516+
// case 2: expr + const in the variable assignment
517+
v.getAnAssignedValue() = ae and
518+
result = ae and
519+
base = ae.getLeftOperand() and
520+
offset = constOrZero(ae.getRightOperand()) and
521+
DataFlow::localExprFlow(result, this.getSizeExpr())
522+
)
520523
or
521-
// case 2: expr + const in the variable assignment
522-
v.getAnAssignedValue() = ae and
524+
// case 3: function call + const
523525
result = ae and
524-
base = ae.getLeftOperand() and
525-
offset = constOrZero(ae.getRightOperand()) and
526-
DataFlow::localExprFlow(result, this.getSizeExpr())
526+
this.getSizeExpr() = ae and
527+
ae.getLeftOperand() = base and
528+
ae.getLeftOperand() instanceof FunctionCall and
529+
offset = constOrZero(ae.getRightOperand())
527530
)
528-
then any() // all logic handled in the `if` clause
529531
else (
530532
offset = 0 and
531533
// case 3: a variable is read in the size expression
@@ -608,9 +610,8 @@ module OutOfBounds {
608610
}
609611

610612
override predicate isSink(DataFlow::Node sink) {
611-
exists(BufferAccessLibraryFunctionCall call, Expr arg |
612-
arg = call.getAnArgument()
613-
and
613+
exists(BufferAccessLibraryFunctionCall call, Expr arg |
614+
arg = call.getAnArgument() and
614615
(
615616
sink.asExpr() = arg
616617
or
@@ -627,9 +628,8 @@ module OutOfBounds {
627628
useOrChild = use
628629
or
629630
getArithmeticOffsetValue(use) > 0 and
630-
useOrChild = use.getAChild*()
631-
)
632-
and
631+
useOrChild = use.getAChild*()
632+
) and
633633
config.hasFlow(DataFlow::exprNode(source), DataFlow::exprNode(useOrChild))
634634
)
635635
}
@@ -652,7 +652,8 @@ module OutOfBounds {
652652
or
653653
// computable source value that flows to the size expression
654654
size = source.(DynamicAllocationSource).getFixedSize() and
655-
hasFlowFromBufferOrSizeExprToUse(source.(DynamicAllocationSource).getSizeExprSource(_, _), sizeExpr)
655+
hasFlowFromBufferOrSizeExprToUse(source.(DynamicAllocationSource).getSizeExprSource(_, _),
656+
sizeExpr)
656657
}
657658

658659
int getArithmeticOffsetValue(Expr expr) {
@@ -694,42 +695,65 @@ module OutOfBounds {
694695
hasFlowFromBufferOrSizeExprToUse(allocSize, bufferSizeArg)
695696
}
696697

697-
predicate isBufferSizeExprGreaterThanSourceSizeExpr (
698+
predicate isBufferSizeExprGreaterThanSourceSizeExpr(
698699
Expr bufferUse, Expr bufferSize, Expr sizeSource, PointerToObjectSource sourceBufferAllocation,
699-
int bufSize, int size, BufferAccessLibraryFunctionCall fc
700+
int bufSize, int size, BufferAccessLibraryFunctionCall fc, int offset, Expr base
700701
) {
701702
exists(float sizeMult |
702703
(
703-
bufferUse = fc.getWriteArg() and bufferSize = fc.getWriteSizeArg() and sizeMult = fc.getWriteSizeArgMult() or
704-
bufferUse = fc.getReadArg() and bufferSize = fc.getReadSizeArg() and sizeMult = fc.getReadSizeArgMult()
705-
)
706-
and
704+
bufferUse = fc.getWriteArg() and
705+
bufferSize = fc.getWriteSizeArg() and
706+
sizeMult = fc.getWriteSizeArgMult()
707+
or
708+
bufferUse = fc.getReadArg() and
709+
bufferSize = fc.getReadSizeArg() and
710+
sizeMult = fc.getReadSizeArgMult()
711+
) and
707712
(
713+
offset = 0 and
714+
base = bufferSize and
708715
bufferUseComputableBufferSize(bufferUse, sourceBufferAllocation, bufSize) and
709716
sizeExprComputableSize(bufferSize, sizeSource, size) and
710-
(
711-
bufSize - getArithmeticOffsetValue(bufferUse) < (sizeMult * (float)(size + getArithmeticOffsetValue(bufferSize)))
712-
or
717+
(
718+
bufSize - getArithmeticOffsetValue(bufferUse) <
719+
(sizeMult * (size + getArithmeticOffsetValue(bufferSize)).(float))
720+
or
713721
size = 0
714722
)
715-
or
716-
exists(int offset, Expr base |
717-
sizeSource = sourceBufferAllocation.(DynamicAllocationSource).getSizeExprSource(base, offset) and
718-
bufferUseNonComputableSize(bufferUse, sourceBufferAllocation) and
719-
not globalValueNumber(sizeSource) = globalValueNumber(bufferSize) and
720-
globalValueNumber(base) = globalValueNumber(bufferSize.getAChild*()) and
721-
bufSize = getArithmeticOffsetValue(bufferUse) and
722-
size = getArithmeticOffsetValue(bufferSize) and
723-
bufSize >= size - offset
724-
)
725723
)
726724
)
727725
}
728726

727+
predicate isBufferSizeOffsetOfGVN(
728+
BufferAccessLibraryFunctionCall fc, Expr bufferSize, Expr bufferUse,
729+
DynamicAllocationSource source, Expr sourceSizeExpr, Expr sourceSizeExprBase,
730+
int sourceSizeExprOffset, int sizeMult, int sizeArgOffset, int bufferArgOffset
731+
) {
732+
(
733+
bufferUse = fc.getWriteArg() and
734+
bufferSize = fc.getWriteSizeArg() and
735+
sizeMult = fc.getWriteSizeArgMult()
736+
or
737+
bufferUse = fc.getReadArg() and
738+
bufferSize = fc.getReadSizeArg() and
739+
sizeMult = fc.getReadSizeArgMult()
740+
) and
741+
sourceSizeExpr = source.getSizeExprSource(sourceSizeExprBase, sourceSizeExprOffset) and
742+
bufferUseNonComputableSize(bufferUse, source) and
743+
not globalValueNumber(sourceSizeExpr) = globalValueNumber(bufferSize) and
744+
exists(Expr offsetExpr |
745+
offsetExpr = bufferSize.getAChild*() and
746+
sizeArgOffset = getArithmeticOffsetValue(offsetExpr)
747+
) and
748+
bufferArgOffset = getArithmeticOffsetValue(bufferUse) and
749+
sourceSizeExprOffset + bufferArgOffset < sizeArgOffset
750+
}
751+
729752
predicate problems(BufferAccessLibraryFunctionCall fc, string msg) {
730753
exists(Expr bufferUse, PointerToObjectSource source |
731754
exists(int bufSize, int size, Expr bufferSize, Expr sizeSource |
732-
isBufferSizeExprGreaterThanSourceSizeExpr(bufferUse, bufferSize, sizeSource, source, bufSize, size, fc) and
755+
isBufferSizeExprGreaterThanSourceSizeExpr(bufferUse, bufferSize, sizeSource, source,
756+
bufSize, size, fc, _, _) and
733757
msg = "Buffer size is smaller than size arg."
734758
)
735759
or
@@ -740,6 +764,9 @@ module OutOfBounds {
740764
hasFlowFromBufferOrSizeExprToUse(source, bufferUse.getAChild*()) and
741765
msg = "Buffer " + bufferUse.toString() + " is not null-terminated."
742766
)
767+
or
768+
isBufferSizeOffsetOfGVN(fc, _, bufferUse, source, _, _, _, _, _, _) and
769+
msg = "Buffer size is offset of GVN."
743770
)
744771
}
745-
}
772+
}

0 commit comments

Comments
 (0)