Skip to content

Commit 85de6dd

Browse files
authored
C++: make BufferWrite changes backward compatible
1 parent 88d65b8 commit 85de6dd

File tree

5 files changed

+103
-32
lines changed

5 files changed

+103
-32
lines changed

cpp/ql/lib/semmle/code/cpp/commons/Printf.qll

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
1010
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
1111

1212
private newtype TBufferWriteEstimationReason =
13+
TNoSpecifiedEstimateReason() or
1314
TTypeBoundsAnalysis() or
1415
TValueFlowAnalysis()
1516

@@ -18,26 +19,50 @@ private newtype TBufferWriteEstimationReason =
1819
*/
1920
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
2021
/**
21-
* Returns a human readable representation of this reason
22+
* Returns the name of the concrete class
2223
*/
2324
abstract string toString();
2425

26+
/**
27+
* Returns a human readable representation of this reason
28+
*/
29+
abstract string getDescription();
30+
2531
/**
2632
* Combine estimate reasons. Used to give a reason for the size of a format string
2733
* conversion given reasons coming from its individual specifiers
2834
*/
2935
abstract BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other);
3036
}
3137

38+
/**
39+
* No particular reason given. This is currently used for backward compatibility so that
40+
* classes derived from BufferWrite and overriding getMaxData\0 still work with the
41+
* queries as intended
42+
*/
43+
class NoSpecifiedEstimateReason extends BufferWriteEstimationReason, TNoSpecifiedEstimateReason {
44+
override string toString() { result = "NoSpecifiedEstimateReason" }
45+
46+
override string getDescription() { result = "no reason specified" }
47+
48+
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
49+
// this reason should not be used in format specifiers, so it should not be combined
50+
// with other reasons
51+
none()
52+
}
53+
}
54+
3255
/**
3356
* The estimation comes from rough bounds just based on the type (e.g.
3457
* `0 <= x < 2^32` for an unsigned 32 bit integer)
3558
*/
3659
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
37-
override string toString() { result = "based on type bounds" }
60+
override string toString() { result = "TypeBoundsAnalysis" }
61+
62+
override string getDescription() { result = "based on type bounds" }
3863

3964
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
40-
result = TTypeBoundsAnalysis() and other = other
65+
other != TNoSpecifiedEstimateReason() and result = TTypeBoundsAnalysis()
4166
}
4267
}
4368

@@ -52,12 +77,12 @@ class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysi
5277
* ```
5378
*/
5479
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
55-
override string toString() { result = "based on flow analysis of value bounds" }
80+
override string toString() { result = "ValueFlowAnalysis" }
81+
82+
override string getDescription() { result = "based on flow analysis of value bounds" }
5683

5784
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
58-
other = TTypeBoundsAnalysis() and result = TTypeBoundsAnalysis()
59-
or
60-
other = TValueFlowAnalysis() and result = TValueFlowAnalysis()
85+
other != TNoSpecifiedEstimateReason() and result = other
6186
}
6287
}
6388

cpp/ql/lib/semmle/code/cpp/security/BufferWrite.qll

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,26 @@ abstract class BufferWrite extends Expr {
6868
/**
6969
* Gets an upper bound to the amount of data that's being written (if one
7070
* can be found).
71+
* DEPRECATED: getMaxData\1 should be used and overridden instead
7172
*/
72-
int getMaxData() { result = max(getMaxData(_)) }
73+
deprecated int getMaxData() { none() }
7374

7475
/**
7576
* Gets an upper bound to the amount of data that's being written (if one
76-
* can be found), except that float to string conversions are assumed to be
77-
* much smaller (8 bytes) than their true maximum length. This can be
78-
* helpful in determining the cause of a buffer overflow issue.
77+
* can be found), specifying the reason for the estimation
7978
*/
80-
int getMaxDataLimited() { result = max(getMaxDataLimited(_)) }
79+
int getMaxData(BufferWriteEstimationReason reason) {
80+
reason instanceof NoSpecifiedEstimateReason and result = getMaxData()
81+
}
8182

8283
/**
8384
* Gets an upper bound to the amount of data that's being written (if one
84-
* can be found), specifying the reason for the estimation
85+
* can be found), except that float to string conversions are assumed to be
86+
* much smaller (8 bytes) than their true maximum length. This can be
87+
* helpful in determining the cause of a buffer overflow issue.
88+
* DEPRECATED: getMaxDataLimited\1 should be used and overridden instead
8589
*/
86-
int getMaxData(BufferWriteEstimationReason reason) { none() }
90+
deprecated int getMaxDataLimited() { result = getMaxData() }
8791

8892
/**
8993
* Gets an upper bound to the amount of data that's being written (if one
@@ -92,7 +96,9 @@ abstract class BufferWrite extends Expr {
9296
* than their true maximum length. This can be helpful in determining the
9397
* cause of a buffer overflow issue.
9498
*/
95-
int getMaxDataLimited(BufferWriteEstimationReason reason) { result = getMaxData(reason) }
99+
int getMaxDataLimited(BufferWriteEstimationReason reason) {
100+
result = getMaxData(reason)
101+
}
96102

97103
/**
98104
* Gets the size of a single character of the type this
@@ -150,12 +156,16 @@ class StrCopyBW extends BufferWriteCall {
150156
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
151157
}
152158

153-
override int getMaxData(BufferWriteEstimationReason reason) {
159+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
154160
// when result exists, it is an exact flow analysis
155161
reason instanceof ValueFlowAnalysis and
156162
result =
157163
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
158164
}
165+
166+
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
167+
168+
deprecated override int getMaxData() { result = max(getMaxDataImpl(_)) }
159169
}
160170

161171
/**
@@ -190,12 +200,20 @@ class StrCatBW extends BufferWriteCall {
190200
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
191201
}
192202

193-
override int getMaxData(BufferWriteEstimationReason reason) {
203+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
194204
// when result exists, it is an exact flow analysis
195205
reason instanceof ValueFlowAnalysis and
196206
result =
197207
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
198208
}
209+
210+
override int getMaxData(BufferWriteEstimationReason reason) {
211+
result = getMaxDataImpl(reason)
212+
}
213+
214+
deprecated override int getMaxData() {
215+
result = max(getMaxDataImpl(_))
216+
}
199217
}
200218

201219
/**
@@ -252,19 +270,27 @@ class SprintfBW extends BufferWriteCall {
252270

253271
override Expr getDest() { result = this.getArgument(f.getOutputParameterIndex(false)) }
254272

255-
override int getMaxData(BufferWriteEstimationReason reason) {
273+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
256274
exists(FormatLiteral fl |
257275
fl = this.(FormattingFunctionCall).getFormat() and
258276
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
259277
)
260278
}
261279

262-
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
280+
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
281+
282+
deprecated override int getMaxData() { result = max(getMaxDataImpl(_)) }
283+
284+
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
263285
exists(FormatLiteral fl |
264286
fl = this.(FormattingFunctionCall).getFormat() and
265287
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
266288
)
267289
}
290+
291+
override int getMaxDataLimited(BufferWriteEstimationReason reason) { result = getMaxDataLimitedImpl(reason) }
292+
293+
deprecated override int getMaxDataLimited() { result = max(getMaxDataLimitedImpl(_)) }
268294
}
269295

270296
/**
@@ -355,19 +381,27 @@ class SnprintfBW extends BufferWriteCall {
355381
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
356382
}
357383

358-
override int getMaxData(BufferWriteEstimationReason reason) {
384+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
359385
exists(FormatLiteral fl |
360386
fl = this.(FormattingFunctionCall).getFormat() and
361387
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
362388
)
363389
}
364390

365-
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
391+
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
392+
393+
deprecated override int getMaxData() { result = max(getMaxDataImpl(_)) }
394+
395+
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
366396
exists(FormatLiteral fl |
367397
fl = this.(FormattingFunctionCall).getFormat() and
368398
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
369399
)
370400
}
401+
402+
override int getMaxDataLimited(BufferWriteEstimationReason reason) { result = getMaxDataLimitedImpl(reason) }
403+
404+
deprecated override int getMaxDataLimited() { result = max(getMaxDataLimitedImpl(_)) }
371405
}
372406

373407
/**
@@ -455,7 +489,7 @@ class ScanfBW extends BufferWrite {
455489

456490
override Expr getDest() { result = this }
457491

458-
override int getMaxData(BufferWriteEstimationReason reason) {
492+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
459493
// when this returns, it is based on exact flow analysis
460494
reason instanceof ValueFlowAnalysis and
461495
exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg |
@@ -465,6 +499,12 @@ class ScanfBW extends BufferWrite {
465499
)
466500
}
467501

502+
override int getMaxData(BufferWriteEstimationReason reason) {
503+
result = getMaxDataImpl(reason)
504+
}
505+
506+
deprecated override int getMaxData() { result = max(getMaxDataImpl(_)) }
507+
468508
override string getBWDesc() {
469509
exists(FunctionCall fc |
470510
this = fc.getArgument(_) and
@@ -495,10 +535,14 @@ class RealpathBW extends BufferWriteCall {
495535

496536
override Expr getASource() { result = this.getArgument(0) }
497537

498-
override int getMaxData(BufferWriteEstimationReason reason) {
538+
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
499539
// although there may be some unknown invariants guaranteeing that a real path is shorter than PATH_MAX, we can consider providing less than PATH_MAX a problem with high precision
500540
reason instanceof ValueFlowAnalysis and
501541
result = path_max() and
502542
this = this // Suppress a compiler warning
503543
}
544+
545+
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
546+
547+
deprecated override int getMaxData() { result = max(getMaxDataImpl(_)) }
504548
}

cpp/ql/src/Security/CWE/CWE-120/OverrunWrite.ql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ import semmle.code.cpp.commons.Alloc
2121
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
2222
*/
2323

24-
from BufferWrite bw, Expr dest, int destSize, BufferWriteEstimationReason reason
24+
from BufferWrite bw, Expr dest, int destSize, int estimated, BufferWriteEstimationReason reason
2525
where
2626
not bw.hasExplicitLimit() and // has no explicit size limit
2727
dest = bw.getDest() and
2828
destSize = getBufferSize(dest, _) and
29+
estimated = bw.getMaxDataLimited(reason) and
2930
// we can deduce that too much data may be copied (even without
3031
// long '%f' conversions)
31-
bw.getMaxDataLimited(reason) > destSize
32+
estimated > destSize
3233
select bw,
33-
"This '" + bw.getBWDesc() + "' operation requires " + bw.getMaxData() +
34-
" bytes but the destination is only " + destSize + " bytes (" + reason.toString() + ")."
34+
"This '" + bw.getBWDesc() + "' operation requires " + estimated +
35+
" bytes but the destination is only " + destSize + " bytes (" + reason.getDescription() + ")."

cpp/ql/src/Security/CWE/CWE-120/OverrunWriteFloat.ql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ import semmle.code.cpp.security.BufferWrite
2121
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
2222
*/
2323

24-
from BufferWrite bw, int destSize
24+
from BufferWrite bw, int destSize, int estimated, BufferWriteEstimationReason reason
2525
where
2626
not bw.hasExplicitLimit() and
2727
// has no explicit size limit
2828
destSize = getBufferSize(bw.getDest(), _) and
29-
bw.getMaxData() > destSize and
29+
estimated = bw.getMaxData(reason) and
30+
estimated > destSize and
3031
// and we can deduce that too much data may be copied
31-
bw.getMaxDataLimited() <= destSize // but it would fit without long '%f' conversions
32+
bw.getMaxDataLimited(reason) <= destSize // but it would fit without long '%f' conversions
3233
select bw,
33-
"This '" + bw.getBWDesc() + "' operation may require " + bw.getMaxData() +
34+
"This '" + bw.getBWDesc() + "' operation may require " + estimated +
3435
" bytes because of float conversions, but the target is only " + destSize + " bytes."

cpp/ql/src/Security/CWE/CWE-120/UnboundedWrite.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import TaintedWithPath
4444

4545
predicate isUnboundedWrite(BufferWrite bw) {
4646
not bw.hasExplicitLimit() and // has no explicit size limit
47-
not exists(bw.getMaxData()) // and we can't deduce an upper bound to the amount copied
47+
not exists(bw.getMaxData(_)) // and we can't deduce an upper bound to the amount copied
4848
}
4949

5050
/*

0 commit comments

Comments
 (0)