Merge branch 'master' into rdmarsh/cpp/ir-constructor-side-effects

This commit is contained in:
Robert Marsh
2019-11-12 10:31:04 -08:00
127 changed files with 1910 additions and 1002 deletions

View File

@@ -19,6 +19,7 @@ where
pointlessSelfComparison(cmp) and
not nanTest(cmp) and
not overflowTest(cmp) and
not cmp.isFromTemplateInstantiation(_) and
not exists(MacroInvocation mi |
// cmp is in mi
mi.getAnExpandedElement() = cmp and

View File

@@ -0,0 +1,3 @@
bool foo(int n1, unsigned short delta) {
return n1 + delta < n1; // BAD
}

View File

@@ -0,0 +1,4 @@
bool bar(unsigned short n1, unsigned short delta) {
// NB: Comparison is always false
return n1 + delta < n1; // GOOD (but misleading)
}

View File

@@ -0,0 +1,4 @@
#include <limits.h>
bool foo(int n1, unsigned short delta) {
return n1 > INT_MAX - delta; // GOOD
}

View File

@@ -0,0 +1,3 @@
bool bar(unsigned short n1, unsigned short delta) {
return (unsigned short)(n1 + delta) < n1; // GOOD
}

View File

@@ -0,0 +1,115 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
When checking for integer overflow, you may often write tests like
<code>a + b &lt; a</code>. This works fine if <code>a</code> or
<code>b</code> are unsigned integers, since any overflow in the addition
will cause the value to simply "wrap around." However, using
<i>signed</i> integers is problematic because signed overflow has undefined
behavior according to the C and C++ standards. If the addition overflows
and has an undefined result, the comparison will likewise be undefined;
it may produce an unintended result, or may be deleted entirely by an
optimizing compiler.
</p>
</overview>
<recommendation>
<p>
Solutions to this problem can be thought of as falling into one of two
categories: (1) rewrite the signed expression so that overflow cannot occur
but the signedness remains, or (2) rewrite (or cast) the signed expression
into unsigned form.
</p>
<p>
Below we list examples of expressions where signed overflow may
occur, along with proposed solutions. The list should not be
considered exhaustive.
</p>
<p>
Given <code>unsigned short i, delta</code> and <code>i + delta &lt; i</code>,
it is possible to rewrite it as <code>(unsigned short)(i + delta)&nbsp;&lt;&nbsp;i</code>.
Note that <code>i + delta</code>does not actually overflow, due to <code>int</code> promotion
</p>
<p>
Given <code>unsigned short i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>USHORT_MAX - delta</code>. It must be true
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is possible to rewrite it as <code>INT_MAX - delta</code>. It must be true
that <code>delta &gt; 0</code> and the <code>limits.h</code> or <code>climits</code>
header has been included.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>(unsigned)i + delta &lt; i</code>.
Note that program semantics are affected by this change.
</p>
<p>
Given <code>int i, delta</code> and <code>i + delta &lt; i</code>,
it is also possible to rewrite it as <code>unsigned int i, delta</code> and
<code>i + delta &lt; i</code>. Note that program semantics are
affected by this change.
</p>
</recommendation>
<example>
<p>
In the following example, even though <code>delta</code> has been declared
<code>unsigned short</code>, C/C++ type promotion rules require that its
type is promoted to the larger type used in the addition and comparison,
namely a <code>signed int</code>. Addition is performed on
signed integers, and may have undefined behavior if an overflow occurs.
As a result, the entire (comparison) expression may also have an undefined
result.
</p>
<sample src="SignedOverflowCheck-bad1.cpp" />
<p>
The following example builds upon the previous one. Instead of
performing an addition (which could overflow), we have re-framed the
solution so that a subtraction is used instead. Since <code>delta</code>
is promoted to a <code>signed int</code> and <code>INT_MAX</code> denotes
the largest possible positive value for an <code>signed int</code>,
the expression <code>INT_MAX - delta</code> can never be less than zero
or more than <code>INT_MAX</code>. Hence, any overflow and underflow
are avoided.
</p>
<sample src="SignedOverflowCheck-good1.cpp" />
<p>
In the following example, even though both <code>n</code> and <code>delta</code>
have been declared <code>unsigned short</code>, both are promoted to
<code>signed int</code> prior to addition. Because we started out with the
narrower <code>short</code> type, the addition is guaranteed not to overflow
and is therefore defined. But the fact that <code>n1 + delta</code> never
overflows means that the condition <code>n1 + delta &lt; n1</code> will never
hold true, which likely is not what the programmer intended. (see also the
<code>cpp/bad-addition-overflow-check</code> query).
</p>
<sample src="SignedOverflowCheck-bad2.cpp" />
<p>
The next example provides a solution to the previous one. Even though
<code>i + delta</code> does not overflow, casting it to an
<code>unsigned short</code> truncates the addition modulo 2^16,
so that <code>unsigned short</code> "wrap around" may now be observed.
Furthermore, since the left-hand side is now of type <code>unsigned short</code>,
the right-hand side does not need to be promoted to a <code>signed int</code>.
</p>
<sample src="SignedOverflowCheck-good2.cpp" />
</example>
<references>
<li><a href="http://c-faq.com/expr/preservingrules.html">comp.lang.c FAQ list · Question 3.19 (Preserving rules)</a></li>
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+lost+or+misinterpreted+data">INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data</a></li>
<li>W. Dietz, P. Li, J. Regehr, V. Adve. <a href="https://www.cs.utah.edu/~regehr/papers/overflow12.pdf">Understanding Integer Overflow in C/C++</a></li>
</references>
</qhelp>

View File

@@ -0,0 +1,31 @@
/**
* @name Undefined result of signed test for overflow
* @description Testing for overflow by adding a value to a variable
* to see if it "wraps around" works only for
* unsigned integer values.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/signed-overflow-check
* @tags reliability
* security
*/
import cpp
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2
where
ro.getAnOperand() = add and
add.getAnOperand() = expr1 and
ro.getAnOperand() = expr2 and
globalValueNumber(expr1) = globalValueNumber(expr2) and
add.getUnspecifiedType().(IntegralType).isSigned() and
not exists(MacroInvocation mi | mi.getAnAffectedElement() = add) and
exprMightOverflowPositively(add) and
exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
not c.getAnArgument() = "-fwrapv" and
not c.getAnArgument() = "-fno-strict-overflow"
)
select ro, "Testing for signed overflow may produce undefined results."

View File

@@ -13,32 +13,33 @@ import semmle.code.cpp.security.boostorg.asio.protocols
class ExistsAnyFlowConfig extends DataFlow::Configuration {
ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
override predicate isSource(DataFlow::Node source) { any() }
override predicate isSource(DataFlow::Node source) {
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
}
override predicate isSink(DataFlow::Node sink) { any() }
override predicate isSink(DataFlow::Node sink) {
exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
f.getACallToThisFunction() = fcSetOptions and
fcSetOptions.getQualifier() = sink.asExpr()
)
}
}
bindingset[flag]
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
exists(
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
Expr optionsSink
|
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
exists(VariableAccess contextSetOptions |
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
contextSetOptions = fcSetOptions.getQualifier() and
forall(
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
Expr optionArgumentSource
|
optionArgument = fcSetOptions.getArgument(0) and
optionArgConfig
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
)
exists(ExistsAnyFlowConfig anyFlowConfig, VariableAccess contextSetOptions |
anyFlowConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
contextSetOptions = fcSetOptions.getQualifier() and
forall(
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
Expr optionArgumentSource
|
optionArgument = fcSetOptions.getArgument(0) and
optionArgConfig
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
)
)
)
@@ -46,43 +47,18 @@ predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
bindingset[flag]
predicate isOptionNotSet(ConstructorCall cc, int flag) {
not exists(
BoostorgAsio::SslContextFlowsToSetOptionConfig config, ExistsAnyFlowConfig testConfig,
Expr optionsSink
|
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
exists(VariableAccess contextSetOptions |
testConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
exists(FunctionCall fcSetOptions, BoostorgAsio::SslSetOptionsFunction f |
f.getACallToThisFunction() = fcSetOptions
|
contextSetOptions = fcSetOptions.getQualifier() and
forall(
Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
Expr optionArgumentSource
|
optionArgument = fcSetOptions.getArgument(0) and
optionArgConfig
.hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
)
)
)
)
not exists(FunctionCall fcSetOptions | isOptionSet(cc, flag, fcSetOptions))
}
from
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor,
BoostorgAsio::SslContextFlowsToSetOptionConfig config, Expr protocolSource, Expr protocolSink,
ConstructorCall cc, Expr e, string msg
BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor, Expr protocolSource,
Expr protocolSink, ConstructorCall cc, Expr e, string msg
where
configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
cc.getArgument(0) = protocolSink and
(
BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
not exists(Expr optionsSink |
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
not (
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
@@ -91,8 +67,7 @@ where
or
BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
not exists(Expr optionsSink |
config.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(optionsSink)) and
not (
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())

View File

@@ -126,7 +126,7 @@ class SALParameter extends Parameter {
}
/**
* A SAL element, i.e. a SAL annotation or a declaration entry
* A SAL element, that is, a SAL annotation or a declaration entry
* that may have SAL annotations.
*/
library class SALElement extends Element {

View File

@@ -34,7 +34,7 @@ characters before writing to the HTML page.</p>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html">XSS
(Cross Site Scripting) Prevention Cheat Sheet</a>.
</li>
<li>

View File

@@ -89,9 +89,9 @@ class ParameterNullCheck extends ParameterCheck {
(
va = this.(NotExpr).getOperand() or
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
va = getAssertedFalseCondition(this) or
va = getCheckedFalseCondition(this) or
va = any(NEExpr eq |
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
).getAnOperand()
)
or
@@ -101,7 +101,7 @@ class ParameterNullCheck extends ParameterCheck {
va = this or
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
va = any(EQExpr eq |
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
).getAnOperand()
)
)
@@ -567,7 +567,7 @@ Expr getAnInitializedArgument(Call call) { result = call.getArgument(initialized
* the call, under the given context and evidence.
*/
pragma[nomagic]
int conditionallyInitializedArgument(
private int conditionallyInitializedArgument(
Call call, ConditionalInitializationFunction target, Context c, Evidence e
) {
target = getTarget(call) and
@@ -588,7 +588,7 @@ Expr getAConditionallyInitializedArgument(
/**
* Gets the type signature for the functions parameters.
*/
string typeSig(Function f) {
private string typeSig(Function f) {
result = concat(int i, Type pt |
pt = f.getParameter(i).getType()
|
@@ -599,7 +599,7 @@ string typeSig(Function f) {
/**
* Holds where qualifiedName and typeSig make up the signature for the function.
*/
predicate functionSignature(Function f, string qualifiedName, string typeSig) {
private predicate functionSignature(Function f, string qualifiedName, string typeSig) {
qualifiedName = f.getQualifiedName() and
typeSig = typeSig(f)
}
@@ -611,7 +611,7 @@ predicate functionSignature(Function f, string qualifiedName, string typeSig) {
* This is useful for identifying call to target dependencies across libraries, where the libraries
* are never statically linked together.
*/
Function getAPossibleDefinition(Function undefinedFunction) {
private Function getAPossibleDefinition(Function undefinedFunction) {
not undefinedFunction.isDefined() and
exists(string qn, string typeSig |
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
@@ -684,7 +684,7 @@ FieldAccess getAFieldAccess(Variable v) {
}
/**
* Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern:
* Gets a condition which is checked to be false by the given `ne` expression, according to this pattern:
* ```
* int a = !!result;
* if (!a) { // <- ne
@@ -692,7 +692,7 @@ FieldAccess getAFieldAccess(Variable v) {
* }
* ```
*/
Expr getAssertedFalseCondition(NotExpr ne) {
private Expr getCheckedFalseCondition(NotExpr ne) {
exists(LocalVariable v |
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
ne.getOperand() = v.getAnAccess() and

View File

@@ -1,9 +1,8 @@
import cpp
/**
* Gets a `Field` that is nested within the given `Struct`.
*
* This identifies `Field`s which are located in the same memory
* Gets a `Field` that is within the given `Struct`, either directly or nested
* inside one or more levels of member structs.
*/
private Field getANestedField(Struct s) {
result = s.getAField()
@@ -15,7 +14,7 @@ private Field getANestedField(Struct s) {
}
/**
* Unwraps a series of field accesses to determine the inner-most qualifier.
* Unwraps a series of field accesses to determine the outer-most qualifier.
*/
private Expr getUltimateQualifier(FieldAccess fa) {
exists(Expr qualifier | qualifier = fa.getQualifier() |

View File

@@ -8,25 +8,32 @@ import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.implementations.Printf
class PrintfFormatAttribute extends FormatAttribute {
PrintfFormatAttribute() {
getArchetype() = "printf" or
getArchetype() = "__printf__"
}
}
/**
* A function that can be identified as a `printf` style formatting
* function by its use of the GNU `format` attribute.
*/
class AttributeFormattingFunction extends FormattingFunction {
FormatAttribute printf_attrib;
override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
AttributeFormattingFunction() {
printf_attrib = getAnAttribute() and
(
printf_attrib.getArchetype() = "printf" or
printf_attrib.getArchetype() = "__printf__"
) and
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
exists(PrintfFormatAttribute printf_attrib |
printf_attrib = getAnAttribute() and
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
)
}
override int getFormatParameterIndex() { result = printf_attrib.getFormatIndex() }
override int getFormatParameterIndex() {
forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() |
result = printf_attrib.getFormatIndex()
)
}
}
/**
@@ -124,15 +131,17 @@ class FormattingFunctionCall extends Expr {
}
/**
* Gets the argument corresponding to the nth conversion specifier
* Gets the argument corresponding to the nth conversion specifier.
*/
Expr getConversionArgument(int n) {
exists(FormatLiteral fl, int b, int o |
exists(FormatLiteral fl |
fl = this.getFormat() and
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
o = fl.getNumArgNeeded(n) and
o > 0 and
result = this.getFormatArgument(b + o - 1)
(
result = this.getFormatArgument(fl.getParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
not exists(fl.getParameterFieldValue(n))
)
)
}
@@ -142,11 +151,14 @@ class FormattingFunctionCall extends Expr {
* an explicit minimum field width).
*/
Expr getMinFieldWidthArgument(int n) {
exists(FormatLiteral fl, int b |
exists(FormatLiteral fl |
fl = this.getFormat() and
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
fl.hasImplicitMinFieldWidth(n) and
result = this.getFormatArgument(b)
(
result = this.getFormatArgument(fl.getMinFieldWidthParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
not exists(fl.getMinFieldWidthParameterFieldValue(n))
)
)
}
@@ -156,12 +168,14 @@ class FormattingFunctionCall extends Expr {
* precision).
*/
Expr getPrecisionArgument(int n) {
exists(FormatLiteral fl, int b, int o |
exists(FormatLiteral fl |
fl = this.getFormat() and
b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
(if fl.hasImplicitMinFieldWidth(n) then o = 1 else o = 0) and
fl.hasImplicitPrecision(n) and
result = this.getFormatArgument(b + o)
(
result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
not exists(fl.getPrecisionParameterFieldValue(n))
)
)
}
@@ -361,6 +375,14 @@ class FormatLiteral extends Literal {
*/
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
/**
* Gets the parameter field of the nth conversion specifier (if it has one) as a
* zero-based number.
*/
int getParameterFieldValue(int n) {
result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the flags of the nth conversion specifier.
*/
@@ -430,6 +452,14 @@ class FormatLiteral extends Literal {
*/
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
/**
* Gets the zero-based parameter number of the minimum field width of the nth
* conversion specifier, if it is implicit and uses a parameter field (such as `*1$`).
*/
int getMinFieldWidthParameterFieldValue(int n) {
result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the precision of the nth conversion specifier (empty string if none is given).
*/
@@ -460,6 +490,14 @@ class FormatLiteral extends Literal {
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
}
/**
* Gets the zero-based parameter number of the precision of the nth conversion
* specifier, if it is implicit and uses a parameter field (such as `*1$`).
*/
int getPrecisionParameterFieldValue(int n) {
result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the length flag of the nth conversion specifier.
*/
@@ -777,19 +815,49 @@ class FormatLiteral extends Literal {
)
}
/**
* Holds if the nth conversion specifier of this format string (if `mode = 2`), it's
* minimum field width (if `mode = 0`) or it's precision (if `mode = 1`) requires a
* format argument.
*
* Most conversion specifiers require a format argument, whereas minimum field width
* and precision only require a format argument if they are present and a `*` was
* used for it's value in the format string.
*/
private predicate hasFormatArgumentIndexFor(int n, int mode) {
mode = 0 and
this.hasImplicitMinFieldWidth(n)
or
mode = 1 and
this.hasImplicitPrecision(n)
or
mode = 2 and
exists(this.getConvSpecOffset(n)) and
not this.getConversionChar(n) = "m"
}
/**
* Gets the computed format argument index for the nth conversion specifier of this
* format string (if `mode = 2`), it's minimum field width (if `mode = 0`) or it's
* precision (if `mode = 1`). Has no result if that element is not present. Does
* not account for positional arguments (`$`).
*/
int getFormatArgumentIndexFor(int n, int mode) {
hasFormatArgumentIndexFor(n, mode) and
(3 * n) + mode = rank[result + 1](int n2, int mode2 |
hasFormatArgumentIndexFor(n2, mode2)
|
(3 * n2) + mode2
)
}
/**
* Gets the number of arguments required by the nth conversion specifier
* of this format string.
*/
int getNumArgNeeded(int n) {
exists(this.getConvSpecOffset(n)) and
not this.getConversionChar(n) = "%" and
exists(int n1, int n2, int n3 |
(if this.hasImplicitMinFieldWidth(n) then n1 = 1 else n1 = 0) and
(if this.hasImplicitPrecision(n) then n2 = 1 else n2 = 0) and
(if this.getConversionChar(n) = "m" then n3 = 0 else n3 = 1) and
result = n1 + n2 + n3
)
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
}
/**
@@ -801,7 +869,7 @@ class FormatLiteral extends Literal {
// At least one conversion specifier has a parameter field, in which case,
// they all should have.
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt())
else result = sum(int n, int toSum | toSum = this.getNumArgNeeded(n) | toSum)
else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode))
}
/**

View File

@@ -67,7 +67,7 @@ module VirtualDispatch {
/**
* Holds if `c` cannot inherit the member function `f`,
* i.e. `c` or one of its supertypes overrides `f`.
* that is, `c` or one of its supertypes overrides `f`.
*/
private predicate cannotInherit(Class c, MemberFunction f) {
exists(Class overridingType, MemberFunction override |

View File

@@ -34,7 +34,7 @@ private newtype TOpcode =
TPointerSub() or
TPointerDiff() or
TConvert() or
TConvertToBase() or
TConvertToNonVirtualBase() or
TConvertToVirtualBase() or
TConvertToDerived() or
TCheckedConvertOrNull() or
@@ -110,6 +110,8 @@ abstract class RelationalOpcode extends CompareOpcode { }
abstract class CopyOpcode extends Opcode { }
abstract class ConvertToBaseOpcode extends UnaryOpcode { }
abstract class MemoryAccessOpcode extends Opcode { }
abstract class ReturnOpcode extends Opcode { }
@@ -302,11 +304,11 @@ module Opcode {
final override string toString() { result = "Convert" }
}
class ConvertToBase extends UnaryOpcode, TConvertToBase {
final override string toString() { result = "ConvertToBase" }
class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase {
final override string toString() { result = "ConvertToNonVirtualBase" }
}
class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase {
class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase {
final override string toString() { result = "ConvertToVirtualBase" }
}

View File

@@ -991,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a direct non-virtual base class.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a virtual base class.
*/
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}

View File

@@ -109,7 +109,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToBaseInstruction convert |
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)

View File

@@ -991,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a direct non-virtual base class.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a virtual base class.
*/
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}

View File

@@ -1038,7 +1038,7 @@ class TranslatedInheritanceConversion extends TranslatedSingleInstructionConvers
then
if expr.(BaseClassConversion).isVirtual()
then result instanceof Opcode::ConvertToVirtualBase
else result instanceof Opcode::ConvertToBase
else result instanceof Opcode::ConvertToNonVirtualBase
else result instanceof Opcode::ConvertToDerived
}
}

View File

@@ -752,7 +752,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::ConvertToBase and
opcode instanceof Opcode::ConvertToNonVirtualBase and
resultType = getTypeForGLValue(call.getTarget().getDeclaringType())
}

View File

@@ -991,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a direct non-virtual base class.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
* Represents an instruction that converts from the address of a derived class
* to the address of a virtual base class.
*/
class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}

View File

@@ -109,7 +109,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
exists(ConvertToBaseInstruction convert |
exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)

View File

@@ -3,7 +3,7 @@ import semmle.code.cpp.dataflow.DataFlow
module BoostorgAsio {
/**
* Represents boost::asio::ssl::context enum
* Represents the `boost::asio::ssl::context` enum.
*/
class SslContextMethod extends Enum {
SslContextMethod() {
@@ -12,7 +12,7 @@ module BoostorgAsio {
}
/**
* returns the value for a banned protocol
* Gets an enumeration constant for a banned protocol.
*/
EnumConstant getABannedProtocolConstant() {
result = this.getAnEnumConstant() and
@@ -56,14 +56,15 @@ module BoostorgAsio {
}
/**
* returns the value for a approved protocols, but that are hard-coded (i.e. no protocol negotiation)
* Gets an enumeration constant for an approved protocol, that is hard-coded
* (no protocol negotiation).
*/
EnumConstant getAnApprovedButHardcodedProtocolConstant() {
result = this.getATls12ProtocolConstant()
}
/**
* returns the value for a TLS v1.2 protocol
* Gets an enumeration constant for a TLS v1.2 protocol.
*/
EnumConstant getATls12ProtocolConstant() {
result = this.getAnEnumConstant() and
@@ -80,7 +81,7 @@ module BoostorgAsio {
}
/**
* returns the value for a TLS v1.3 protocol
* Gets an enumeration constant for a TLS v1.3 protocol.
*/
EnumConstant getATls13ProtocolConstant() {
result = this.getAnEnumConstant() and
@@ -97,7 +98,7 @@ module BoostorgAsio {
}
/**
* returns the value of a generic TLS or SSL/TLS protocol
* Gets an enumeration constant for a generic TLS or SSL/TLS protocol.
*/
EnumConstant getAGenericTlsProtocolConstant() {
result = this.getAnEnumConstant() and
@@ -116,7 +117,7 @@ module BoostorgAsio {
}
/**
* returns the value of a generic SSL/TLS protocol
* Gets an enumeration constant for a generic SSL/TLS protocol.
*/
EnumConstant getASslv23ProtocolConstant() {
result = this.getAnEnumConstant() and
@@ -135,7 +136,9 @@ module BoostorgAsio {
}
/**
* NOTE: ignore - Modern versions of OpenSSL do not support SSL v2 anymore, so this option is for backwards compatibility only
* Gets the value for the no_sslv2 constant, right shifted by 16 bits.
*
* Note that modern versions of OpelSSL do not support SSL v2, so this option is for backwards compatibility only.
*/
int getShiftedSslOptionsNoSsl2() {
// SSL_OP_NO_SSLv2 was removed from modern OpenSSL versions
@@ -143,7 +146,7 @@ module BoostorgAsio {
}
/**
* RightShift(16) value for no_sslv3 constant
* Gets the value for the no_sslv3 constant, right shifted by 16 bits.
*/
int getShiftedSslOptionsNoSsl3() {
// SSL_OP_NO_SSLv3 == 0x02000000U
@@ -151,7 +154,7 @@ module BoostorgAsio {
}
/**
* RightShift(16) value for no_tlsv1 constant
* Gets the value for the no_tlsv1 constant, right shifted by 16 bits.
*/
int getShiftedSslOptionsNoTls1() {
// SSL_OP_NO_TLSv1 == 0x04000000U
@@ -159,7 +162,7 @@ module BoostorgAsio {
}
/**
* RightShift(16) value for no_tlsv1_1 constant
* Gets the value for the no_tlsv1_1 constant, right shifted by 16 bits.
*/
int getShiftedSslOptionsNoTls1_1() {
// SSL_OP_NO_TLSv1_1 == 0x10000000U
@@ -167,7 +170,7 @@ module BoostorgAsio {
}
/**
* RightShift(16) value for no_tlsv1_2 constant
* Gets the value for the no_tlsv1_2 constant, right shifted by 16 bits.
*/
int getShiftedSslOptionsNoTls1_2() {
// SSL_OP_NO_TLSv1_2 == 0x08000000U
@@ -175,7 +178,7 @@ module BoostorgAsio {
}
/**
* RightShift(16) value for no_tlsv1_3 constant
* Gets the value for the no_tlsv1_3 constant, right shifted by 16 bits.
*/
int getShiftedSslOptionsNoTls1_3() {
// SSL_OP_NO_TLSv1_2 == 0x20000000U
@@ -183,7 +186,7 @@ module BoostorgAsio {
}
/**
* Represents boost::asio::ssl::context class
* Represents the `boost::asio::ssl::context` class.
*/
class SslContextClass extends Class {
SslContextClass() { this.getQualifiedName() = "boost::asio::ssl::context" }
@@ -196,7 +199,7 @@ module BoostorgAsio {
}
/**
* Represents boost::asio::ssl::context::set_options member function
* Represents `boost::asio::ssl::context::set_options` member function.
*/
class SslSetOptionsFunction extends Function {
SslSetOptionsFunction() {
@@ -205,7 +208,7 @@ module BoostorgAsio {
}
/**
* holds if the expression represents a banned protocol
* Holds if the expression represents a banned protocol.
*/
predicate isExprBannedBoostProtocol(Expr e) {
exists(Literal va | va = e |
@@ -244,7 +247,7 @@ module BoostorgAsio {
}
/**
* holds if the expression represents a TLS v1.2 protocol
* Holds if the expression represents a TLS v1.2 protocol.
*/
predicate isExprTls12BoostProtocol(Expr e) {
exists(Literal va | va = e |
@@ -269,7 +272,7 @@ module BoostorgAsio {
}
/**
* holds if the expression represents a protocol that requires Crypto Board approval
* Holds if the expression represents a protocol that requires Crypto Board approval.
*/
predicate isExprTls13BoostProtocol(Expr e) {
exists(Literal va | va = e |
@@ -294,7 +297,7 @@ module BoostorgAsio {
}
/**
* holds if the expression represents a generic TLS or SSL/TLS protocol
* Holds if the expression represents a generic TLS or SSL/TLS protocol.
*/
predicate isExprTlsBoostProtocol(Expr e) {
exists(Literal va | va = e |
@@ -325,7 +328,7 @@ module BoostorgAsio {
}
/**
* holds if the expression represents a generic SSl/TLS protocol
* Holds if the expression represents a generic SSl/TLS protocol.
*/
predicate isExprSslV23BoostProtocol(Expr e) {
exists(Literal va | va = e |
@@ -351,7 +354,8 @@ module BoostorgAsio {
//////////////////////// Dataflow /////////////////////
/**
* Abstract - Protocol value Flows to the first argument of the context constructor
* Abstract class for flows of protocol values to the first argument of a context
* constructor.
*/
abstract class SslContextCallAbstractConfig extends DataFlow::Configuration {
bindingset[this]
@@ -366,7 +370,7 @@ module BoostorgAsio {
}
/**
* any Protocol value Flows to the first argument of the context constructor
* Any protocol value that flows to the first argument of a context constructor.
*/
class SslContextCallConfig extends SslContextCallAbstractConfig {
SslContextCallConfig() { this = "SslContextCallConfig" }
@@ -380,7 +384,7 @@ module BoostorgAsio {
}
/**
* a banned protocol value Flows to the first argument of the context constructor
* A banned protocol value that flows to the first argument of a context constructor.
*/
class SslContextCallBannedProtocolConfig extends SslContextCallAbstractConfig {
SslContextCallBannedProtocolConfig() { this = "SslContextCallBannedProtocolConfig" }
@@ -395,7 +399,7 @@ module BoostorgAsio {
}
/**
* a TLS 1.2 protocol value Flows to the first argument of the context constructor
* A TLS 1.2 protocol value that flows to the first argument of a context constructor.
*/
class SslContextCallTls12ProtocolConfig extends SslContextCallAbstractConfig {
SslContextCallTls12ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
@@ -410,7 +414,7 @@ module BoostorgAsio {
}
/**
* a TLS 1.3 protocol value Flows to the first argument of the context constructor
* A TLS 1.3 protocol value that flows to the first argument of a context constructor.
*/
class SslContextCallTls13ProtocolConfig extends SslContextCallAbstractConfig {
SslContextCallTls13ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
@@ -425,7 +429,7 @@ module BoostorgAsio {
}
/**
* a generic TLS protocol value Flows to the first argument of the context constructor
* A generic TLS protocol value that flows to the first argument of a context constructor.
*/
class SslContextCallTlsProtocolConfig extends SslContextCallAbstractConfig {
SslContextCallTlsProtocolConfig() { this = "SslContextCallTlsProtocolConfig" }
@@ -440,7 +444,7 @@ module BoostorgAsio {
}
/**
* a context constructor call flows to a call calling SetOptions()
* A context constructor call that flows to a call to `SetOptions()`.
*/
class SslContextFlowsToSetOptionConfig extends DataFlow::Configuration {
SslContextFlowsToSetOptionConfig() { this = "SslContextFlowsToSetOptionConfig" }
@@ -464,7 +468,7 @@ module BoostorgAsio {
}
/**
* an option value flows to the 1st parameter of SetOptions()
* An option value that flows to the first parameter of a call to `SetOptions()`.
*/
class SslOptionConfig extends DataFlow::Configuration {
SslOptionConfig() { this = "SslOptionConfig" }

View File

@@ -35,15 +35,15 @@
| escape.cpp:146:5:146:18 | CopyValue | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:146:7:146:17 | CopyValue | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:149:5:149:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:5:149:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:151:5:151:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:151:5:151:14 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
| escape.cpp:152:19:152:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:152:19:152:28 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
| escape.cpp:155:17:155:30 | CopyValue | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 |
| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 |

File diff suppressed because it is too large Load Diff

View File

@@ -700,65 +700,65 @@ test.cpp:
# 104| int inheritanceConversions(Derived*)
# 104| Block 0
# 104| v0_0(void) = EnterFunction :
# 104| m0_1(unknown) = AliasedDefinition :
# 104| v0_0(void) = EnterFunction :
# 104| m0_1(unknown) = AliasedDefinition :
# 104| valnum = unique
# 104| mu0_2(unknown) = UnmodeledDefinition :
# 104| mu0_2(unknown) = UnmodeledDefinition :
# 104| valnum = unique
# 104| r0_3(glval<Derived *>) = VariableAddress[pd] :
# 104| r0_3(glval<Derived *>) = VariableAddress[pd] :
# 104| valnum = r0_3
# 104| m0_4(Derived *) = InitializeParameter[pd] : &:r0_3
# 104| m0_4(Derived *) = InitializeParameter[pd] : &:r0_3
# 104| valnum = m0_4
# 105| r0_5(glval<int>) = VariableAddress[x] :
# 105| r0_5(glval<int>) = VariableAddress[x] :
# 105| valnum = unique
# 105| r0_6(glval<Derived *>) = VariableAddress[pd] :
# 105| r0_6(glval<Derived *>) = VariableAddress[pd] :
# 105| valnum = r0_3
# 105| r0_7(Derived *) = Load : &:r0_6, m0_4
# 105| r0_7(Derived *) = Load : &:r0_6, m0_4
# 105| valnum = m0_4
# 105| r0_8(Base *) = ConvertToBase[Derived : Base] : r0_7
# 105| r0_8(Base *) = ConvertToNonVirtualBase[Derived : Base] : r0_7
# 105| valnum = r0_8
# 105| r0_9(glval<int>) = FieldAddress[b] : r0_8
# 105| r0_9(glval<int>) = FieldAddress[b] : r0_8
# 105| valnum = r0_9
# 105| r0_10(int) = Load : &:r0_9, ~m0_1
# 105| r0_10(int) = Load : &:r0_9, ~m0_1
# 105| valnum = r0_10
# 105| m0_11(int) = Store : &:r0_5, r0_10
# 105| m0_11(int) = Store : &:r0_5, r0_10
# 105| valnum = r0_10
# 106| r0_12(glval<Base *>) = VariableAddress[pb] :
# 106| r0_12(glval<Base *>) = VariableAddress[pb] :
# 106| valnum = r0_12
# 106| r0_13(glval<Derived *>) = VariableAddress[pd] :
# 106| r0_13(glval<Derived *>) = VariableAddress[pd] :
# 106| valnum = r0_3
# 106| r0_14(Derived *) = Load : &:r0_13, m0_4
# 106| r0_14(Derived *) = Load : &:r0_13, m0_4
# 106| valnum = m0_4
# 106| r0_15(Base *) = ConvertToBase[Derived : Base] : r0_14
# 106| r0_15(Base *) = ConvertToNonVirtualBase[Derived : Base] : r0_14
# 106| valnum = r0_8
# 106| m0_16(Base *) = Store : &:r0_12, r0_15
# 106| m0_16(Base *) = Store : &:r0_12, r0_15
# 106| valnum = r0_8
# 107| r0_17(glval<int>) = VariableAddress[y] :
# 107| r0_17(glval<int>) = VariableAddress[y] :
# 107| valnum = r0_17
# 107| r0_18(glval<Base *>) = VariableAddress[pb] :
# 107| r0_18(glval<Base *>) = VariableAddress[pb] :
# 107| valnum = r0_12
# 107| r0_19(Base *) = Load : &:r0_18, m0_16
# 107| r0_19(Base *) = Load : &:r0_18, m0_16
# 107| valnum = r0_8
# 107| r0_20(glval<int>) = FieldAddress[b] : r0_19
# 107| r0_20(glval<int>) = FieldAddress[b] : r0_19
# 107| valnum = r0_9
# 107| r0_21(int) = Load : &:r0_20, ~m0_1
# 107| r0_21(int) = Load : &:r0_20, ~m0_1
# 107| valnum = r0_21
# 107| m0_22(int) = Store : &:r0_17, r0_21
# 107| m0_22(int) = Store : &:r0_17, r0_21
# 107| valnum = r0_21
# 109| r0_23(glval<int>) = VariableAddress[#return] :
# 109| r0_23(glval<int>) = VariableAddress[#return] :
# 109| valnum = r0_23
# 109| r0_24(glval<int>) = VariableAddress[y] :
# 109| r0_24(glval<int>) = VariableAddress[y] :
# 109| valnum = r0_17
# 109| r0_25(int) = Load : &:r0_24, m0_22
# 109| r0_25(int) = Load : &:r0_24, m0_22
# 109| valnum = r0_21
# 109| m0_26(int) = Store : &:r0_23, r0_25
# 109| m0_26(int) = Store : &:r0_23, r0_25
# 109| valnum = r0_21
# 104| r0_27(glval<int>) = VariableAddress[#return] :
# 104| r0_27(glval<int>) = VariableAddress[#return] :
# 104| valnum = r0_23
# 104| v0_28(void) = ReturnValue : &:r0_27, m0_26
# 104| v0_29(void) = UnmodeledUse : mu*
# 104| v0_30(void) = AliasedUse : ~m0_1
# 104| v0_31(void) = ExitFunction :
# 104| v0_28(void) = ReturnValue : &:r0_27, m0_26
# 104| v0_29(void) = UnmodeledUse : mu*
# 104| v0_30(void) = AliasedUse : ~m0_1
# 104| v0_31(void) = ExitFunction :
# 112| void test06()
# 112| Block 0

View File

@@ -1 +1,3 @@
| SignedOverflowCheck.cpp:35:9:35:23 | ... < ... | Bad overflow check. |
| SignedOverflowCheck.cpp:113:12:113:66 | ... < ... | Bad overflow check. |
| test.cpp:3:11:3:19 | ... < ... | Bad overflow check. |

View File

@@ -1,3 +1,4 @@
| templates.cpp:17:5:17:25 | ... < ... | Self comparison. |
| test.cpp:13:11:13:21 | ... == ... | Self comparison. |
| test.cpp:79:11:79:32 | ... == ... | Self comparison. |
| test.cpp:83:10:83:15 | ... == ... | Self comparison. |

View File

@@ -0,0 +1,130 @@
// Signed-comparison tests
/* 1. Signed-signed comparison. The semantics are undefined. */
bool cannotHoldAnother8(int n1) {
// clang 8.0.0 -O2: deleted (silently)
// gcc 9.2 -O2: deleted (silently)
// msvc 19.22 /O2: not deleted
return n1 + 8 < n1; // BAD
}
/* 2. Signed comparison with a narrower unsigned type. The narrower
type gets promoted to the (signed) larger type, and so the
semantics are undefined. */
bool cannotHoldAnotherUShort(int n1, unsigned short delta) {
// clang 8.0.0 -O2: deleted (silently)
// gcc 9.2 -O2: deleted (silently)
// msvc 19.22 /O2: not deleted
return n1 + delta < n1; // BAD
}
/* 3. Signed comparison with a non-narrower unsigned type. The
signed type gets promoted to (a possibly wider) unsigned type,
and the resulting comparison is unsigned. */
bool cannotHoldAnotherUInt(int n1, unsigned int delta) {
// clang 8.0.0 -O2: not deleted
// gcc 9.2 -O2: not deleted
// msvc 19.22 /O2: not deleted
return n1 + delta < n1; // GOOD
}
bool shortShort1(unsigned short n1, unsigned short delta) {
// BAD [BadAdditionOverflowCheck.ql]
// GOOD [SigneOverflowCheck.ql]: Test always fails, but will never overflow.
return n1 + delta < n1;
}
bool shortShort2(unsigned short n1, unsigned short delta) {
// clang 8.0.0 -O2: not deleted
// gcc 9.2 -O2: not deleted
// msvc 19.22 /O2: not deleted
return (unsigned short)(n1 + delta) < n1; // GOOD
}
/* Distinguish `varname` from `ptr->varname` and `obj.varname` */
struct N {
int n1;
} n, *np;
bool shortStruct1(unsigned short n1, unsigned short delta) {
return np->n1 + delta < n1; // GOOD
}
bool shortStruct1a(unsigned short n1, unsigned short delta) {
return n1 + delta < n.n1; // GOOD
}
bool shortStruct2(unsigned short n1, unsigned short delta) {
return (unsigned short)(n1 + delta) < n.n1; // GOOD
}
struct se {
int xPos;
short yPos;
short xSize;
short ySize;
};
extern se *getSo(void);
bool func1(se *so) {
se *o = getSo();
if (so->xPos + so->xSize < so->xPos // BAD
|| so->xPos > o->xPos + o->xSize) { // GOOD
// clang 8.0.0 -O2: not deleted
// gcc 9.2 -O2: not deleted
// msvc 19.22 /O2: not deleted
return false;
}
return true;
}
bool checkOverflow3(unsigned int a, unsigned short b) {
return (a + b < a); // GOOD
}
struct C {
unsigned int length;
};
int checkOverflow4(unsigned int ioff, C c) {
// not deleted by gcc or clang
if ((int)(ioff + c.length) < (int)ioff) return 0; // GOOD
return 1;
}
int overflow12(int n) {
// not deleted by gcc or clang
return (n + 32 <= (unsigned)n? -1: 1); // BAD: n + 32 can overflow
}
bool multipleCasts(char x) {
// BAD [UNDETECTED - BadAdditionOverflowCheck.ql]
// GOOD [SigneOverflowCheck.ql]: Test always fails, but will never overflow.
return (int)(unsigned short)x + 2 < (int)(unsigned short)x; // GOOD: cannot overflow
}
bool multipleCasts2(char x) {
// BAD [BadAdditionOverflowCheck.ql]
// GOOD [SigneOverflowCheck.ql]: Test always fails, but will never overflow.
return (int)(unsigned short)(x + '1') < (int)(unsigned short)x;
}
int does_it_overflow(int n1, unsigned short delta) {
return n1 + (unsigned)delta < n1; // GOOD: everything converted to unsigned
}
int overflow12b(int n) {
// not deleted by gcc or clang
return ((unsigned)(n + 32) <= (unsigned)n? -1: 1); // BAD: n + 32 may overflow
}
#define MACRO(E1, E2) (E1) <= (E2)? -1: 1
int overflow12_macro(int n) {
return MACRO((unsigned)(n + 32), (unsigned)n); // GOOD: inside a macro expansion
}

View File

@@ -0,0 +1,5 @@
| SignedOverflowCheck.cpp:8:12:8:22 | ... < ... | Testing for signed overflow may produce undefined results. |
| SignedOverflowCheck.cpp:18:12:18:26 | ... < ... | Testing for signed overflow may produce undefined results. |
| SignedOverflowCheck.cpp:73:6:73:36 | ... < ... | Testing for signed overflow may produce undefined results. |
| SignedOverflowCheck.cpp:99:10:99:30 | ... <= ... | Testing for signed overflow may produce undefined results. |
| SignedOverflowCheck.cpp:122:10:122:42 | ... <= ... | Testing for signed overflow may produce undefined results. |

View File

@@ -0,0 +1 @@
Likely Bugs/Arithmetic/SignedOverflowCheck.ql

View File

@@ -0,0 +1,22 @@
struct C1 {
static const int value = 5;
};
struct C2 {
static const int value = 6;
};
template<typename T1, typename T2>
bool compareValues() {
// Not all instantiations have T1 and T2 equal. Even if that's the case for
// all instantiations in the program, there could still be more such
// instantiations outside.
return
T1::value < T2::value || // GOOD
T1::value < T1::value || // BAD [NOT DETECTED]
C1::value < C1::value ; // BAD
}
bool callCompareValues() {
return compareValues<C1, C2> || compareValues<C1, C1>();
}

View File

@@ -1,11 +1,11 @@
// Test for BadAdditionOverflowCheck.
bool checkOverflow1(unsigned short a, unsigned short b) {
return (a + b < a); // BAD: a + b is automatically promoted to int.
return (a + b < a); // BAD: comparison always false (due to promotion).
}
// Test for BadAdditionOverflowCheck.
bool checkOverflow2(unsigned short a, unsigned short b) {
return ((unsigned short)(a + b) < a); // GOOD: explicit cast
return ((unsigned short)(a + b) < a); // GOOD
}
// Test for PointlessSelfComparison.

View File

@@ -0,0 +1,19 @@
__attribute__((format(printf, 1, 3)))
void myMultiplyDefinedPrintf(const char *format, const char *extraArg, ...)
{
// ...
}
__attribute__((format(printf, 1, 3)))
void myMultiplyDefinedPrintf2(const char *format, const char *extraArg, ...);
char *getString();
void test_custom_printf1()
{
myMultiplyDefinedPrintf("string", getString()); // GOOD
myMultiplyDefinedPrintf(getString(), "string"); // BAD [NOT DETECTED]
myMultiplyDefinedPrintf2("string", getString()); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
myMultiplyDefinedPrintf2(getString(), "string"); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
}

View File

@@ -0,0 +1,16 @@
__attribute__((format(printf, 2, 3)))
void myMultiplyDefinedPrintf(const char *extraArg, const char *format, ...); // this declaration does not match the definition
__attribute__((format(printf, 2, 3)))
void myMultiplyDefinedPrintf2(const char *extraArg, const char *format, ...);
char *getString();
void test_custom_printf2(char *string)
{
myMultiplyDefinedPrintf("string", getString()); // GOOD
myMultiplyDefinedPrintf(getString(), "string"); // BAD [NOT DETECTED]
myMultiplyDefinedPrintf2("string", getString()); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
myMultiplyDefinedPrintf2(getString(), "string"); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
}

View File

@@ -16,6 +16,43 @@
| printf1.h:114:18:114:18 | d | This argument should be of type 'long double' but is of type 'double' |
| printf1.h:147:19:147:19 | i | This argument should be of type 'long long' but is of type 'int' |
| printf1.h:148:19:148:20 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
| printf1.h:160:18:160:18 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:161:21:161:21 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:167:17:167:17 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:168:18:168:18 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:169:19:169:19 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:174:17:174:17 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:175:18:175:18 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:176:19:176:19 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:180:17:180:17 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:181:20:181:20 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:183:18:183:18 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:184:21:184:21 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:186:19:186:19 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:187:22:187:22 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:189:19:189:19 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:190:22:190:22 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:192:19:192:19 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:193:22:193:22 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:194:25:194:25 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:198:24:198:24 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:199:21:199:21 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:202:26:202:26 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:203:23:203:23 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:206:25:206:25 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:207:22:207:22 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:210:26:210:26 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:211:23:211:23 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:214:28:214:28 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:215:28:215:28 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:216:25:216:25 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:221:18:221:18 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:222:20:222:20 | s | This argument should be of type 'int' but is of type 'char *' |
| printf1.h:225:23:225:23 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:228:24:228:24 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:231:25:231:25 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:234:25:234:25 | i | This argument should be of type 'char *' but is of type 'int' |
| printf1.h:235:22:235:22 | s | This argument should be of type 'int' but is of type 'char *' |
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

View File

@@ -0,0 +1,16 @@
__attribute__((format(printf, 1, 3)))
void myMultiplyDefinedPrintf(const char *format, const char *extraArg, ...)
{
// ...
}
__attribute__((format(printf, 1, 3)))
void myMultiplyDefinedPrintf2(const char *format, const char *extraArg, ...);
void test_custom_printf1()
{
myMultiplyDefinedPrintf("%i", "%f", 1); // GOOD
myMultiplyDefinedPrintf("%i", "%f", 1.0f); // BAD [NOT DETECTED]
myMultiplyDefinedPrintf2("%i", "%f", 1); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
myMultiplyDefinedPrintf2("%i", "%f", 1.0f); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
}

View File

@@ -0,0 +1,14 @@
__attribute__((format(printf, 2, 3)))
void myMultiplyDefinedPrintf(const char *extraArg, const char *format, ...); // this declaration does not match the definition
__attribute__((format(printf, 2, 3)))
void myMultiplyDefinedPrintf2(const char *extraArg, const char *format, ...);
void test_custom_printf2()
{
myMultiplyDefinedPrintf("%i", "%f", 1); // GOOD
myMultiplyDefinedPrintf("%i", "%f", 1.0f); // BAD [NOT DETECTED]
myMultiplyDefinedPrintf2("%i", "%f", 1); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
myMultiplyDefinedPrintf2("%i", "%f", 1.0f); // GOOD (we can't tell which declaration is correct so we have to assume this is OK)
}

View File

@@ -151,3 +151,86 @@ void fun4()
printf("%qi\n", ll); // GOOD
printf("%qu\n", ull); // GOOD
}
void complexFormatSymbols(int i, const char *s)
{
// positional arguments
printf("%1$i", i, s); // GOOD
printf("%2$s", i, s); // GOOD
printf("%1$s", i, s); // BAD
printf("%2$i", i, s); // BAD
// width / precision
printf("%4i", i); // GOOD
printf("%.4i", i); // GOOD
printf("%4.4i", i); // GOOD
printf("%4s", i); // BAD
printf("%.4s", i); // BAD
printf("%4.4s", i); // BAD
printf("%4s", s); // GOOD
printf("%.4s", s); // GOOD
printf("%4.4s", s); // GOOD
printf("%4i", s); // BAD
printf("%.4i", s); // BAD
printf("%4.4i", s); // BAD
// variable width / precision
printf("%*s", i, s); // GOOD
printf("%*s", s, s); // BAD
printf("%*s", i, i); // BAD
printf("%.*s", i, s); // GOOD
printf("%.*s", s, s); // BAD
printf("%.*s", i, i); // BAD
printf("%*.4s", i, s); // GOOD
printf("%*.4s", s, s); // BAD
printf("%*.4s", i, i); // BAD
printf("%4.*s", i, s); // GOOD
printf("%4.*s", s, s); // BAD
printf("%4.*s", i, i); // BAD
printf("%*.*s", i, i, s); // GOOD
printf("%*.*s", s, i, s); // BAD
printf("%*.*s", i, s, s); // BAD
printf("%*.*s", i, i, i); // BAD
// positional arguments mixed with variable width / precision
printf("%2$*1$s", i, s); // GOOD
printf("%2$*2$s", i, s); // BAD
printf("%1$*1$s", i, s); // BAD
printf("%2$*1$.4s", i, s); // GOOD
printf("%2$*2$.4s", i, s); // BAD
printf("%1$*1$.4s", i, s); // BAD
printf("%2$.*1$s", i, s); // GOOD
printf("%2$.*2$s", i, s); // BAD
printf("%1$.*1$s", i, s); // BAD
printf("%2$4.*1$s", i, s); // GOOD
printf("%2$4.*2$s", i, s); // BAD
printf("%1$4.*1$s", i, s); // BAD
printf("%2$*1$.*1$s", i, s); // GOOD
printf("%2$*2$.*1$s", i, s); // BAD
printf("%2$*1$.*2$s", i, s); // BAD
printf("%1$*1$.*1$s", i, s); // BAD
// left justify flag
printf("%-4s", s); // GOOD
printf("%1$-4s", s); // GOOD
printf("%-4i", s); // BAD
printf("%1$-4i", s); // BAD
printf("%1$-4s", s, i); // GOOD
printf("%2$-4s", s, i); // BAD
printf("%1$-.4s", s, i); // GOOD
printf("%2$-.4s", s, i); // BAD
printf("%1$-4.4s", s, i); // GOOD
printf("%2$-4.4s", s, i); // BAD
printf("%1$-*2$s", s, i); // GOOD
printf("%2$-*2$s", s, i); // BAD
printf("%1$-*1$s", s, i); // BAD
}

View File

@@ -1,3 +1,14 @@
| test2.cpp:15:32:15:33 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:15:32:15:33 | call to context | boost::asio::ssl::context::context | test2.cpp:14:40:14:72 | sslv23 | sslv23 | test2.cpp:15:32:15:33 | call to context | no_sslv3 has not been set |
| test2.cpp:23:32:23:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:65 | call to context | boost::asio::ssl::context::context | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_sslv3 has not been set |
| test2.cpp:23:32:23:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:65 | call to context | boost::asio::ssl::context::context | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_tlsv1 has not been set |
| test2.cpp:23:32:23:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:65 | call to context | boost::asio::ssl::context::context | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_tlsv1_1 has not been set |
| test2.cpp:31:32:31:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:31:32:31:65 | call to context | boost::asio::ssl::context::context | test2.cpp:31:32:31:64 | sslv23 | sslv23 | test2.cpp:31:32:31:65 | call to context | no_sslv3 has not been set |
| test2.cpp:31:32:31:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:31:32:31:65 | call to context | boost::asio::ssl::context::context | test2.cpp:31:32:31:64 | sslv23 | sslv23 | test2.cpp:31:32:31:65 | call to context | no_tlsv1 has not been set |
| test2.cpp:31:32:31:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:31:32:31:65 | call to context | boost::asio::ssl::context::context | test2.cpp:31:32:31:64 | sslv23 | sslv23 | test2.cpp:31:32:31:65 | call to context | no_tlsv1_1 has not been set |
| test2.cpp:45:35:45:98 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:45:35:45:98 | call to context | boost::asio::ssl::context::context | test2.cpp:45:65:45:97 | sslv23 | sslv23 | test2.cpp:45:35:45:98 | call to context | no_sslv3 has not been set |
| test2.cpp:52:32:52:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:65 | call to context | boost::asio::ssl::context::context | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_sslv3 has not been set |
| test2.cpp:52:32:52:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:65 | call to context | boost::asio::ssl::context::context | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_tlsv1 has not been set |
| test2.cpp:52:32:52:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:65 | call to context | boost::asio::ssl::context::context | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_tlsv1_1 has not been set |
| test.cpp:25:32:25:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test.cpp:25:32:25:65 | call to context | boost::asio::ssl::context::context | test.cpp:25:32:25:64 | sslv23 | sslv23 | test.cpp:25:32:25:65 | call to context | no_sslv3 has not been set |
| test.cpp:31:32:31:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test.cpp:31:32:31:65 | call to context | boost::asio::ssl::context::context | test.cpp:31:32:31:64 | sslv23 | sslv23 | test.cpp:31:32:31:65 | call to context | no_sslv3 has not been set |
| test.cpp:31:32:31:65 | call to context | Usage of $@ with protocol $@ is not configured correctly: The option $@. | test.cpp:31:32:31:65 | call to context | boost::asio::ssl::context::context | test.cpp:31:32:31:64 | sslv23 | sslv23 | test.cpp:31:32:31:65 | call to context | no_tlsv1 has not been set |

View File

@@ -0,0 +1,55 @@
#include "asio/boost_simulation.hpp"
void good1()
{
// GOOD
boost::asio::ssl::context::method m = boost::asio::ssl::context::sslv23;
boost::asio::ssl::context ctx(m);
ctx.set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1 | boost::asio::ssl::context::no_sslv3);
}
void bad1()
{
// BAD: missing disable SSLv3
boost::asio::ssl::context::method m = boost::asio::ssl::context::sslv23;
boost::asio::ssl::context ctx(m);
ctx.set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1);
}
void good2()
{
// GOOD [FALSE POSITIVE x 3]
boost::asio::ssl::context::options opts = boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1 | boost::asio::ssl::context::no_sslv3;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_options(opts);
}
void bad2()
{
// BAD: missing disable SSLv3 [WITH FALSE POSITIVE x 2]
boost::asio::ssl::context::options opts = boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_options(opts);
}
void good3()
{
// GOOD
boost::asio::ssl::context *ctx = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
ctx->set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1 | boost::asio::ssl::context::no_sslv3);
}
void bad3()
{
// BAD: missing disable SSLv3
boost::asio::ssl::context *ctx = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
ctx->set_options(boost::asio::ssl::context::no_tlsv1 | boost::asio::ssl::context::no_tlsv1_1);
}
void bad4()
{
// BAD: missing disable SSLv3
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
}