|
| 1 | +/** |
| 2 | + * @name Сonfusion In Detecting And Handling Memory Allocation Errors |
| 3 | + * @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error. |
| 4 | + * --the programmer can get confused when check the error that occurs when allocating memory incorrectly. |
| 5 | + * --Making a call of this type may result in a zero byte being written just outside the buffer. |
| 6 | + * @kind problem |
| 7 | + * @id cpp/detect-and-handle-memory-allocation-errors |
| 8 | + * @problem.severity warning |
| 9 | + * @precision medium |
| 10 | + * @tags correctness |
| 11 | + * security |
| 12 | + * external/cwe/cwe-570 |
| 13 | + */ |
| 14 | + |
| 15 | +import cpp |
| 16 | + |
| 17 | +/** |
| 18 | + * Lookup if condition compare with 0 |
| 19 | + */ |
| 20 | +class IfCompareWithZero extends IfStmt { |
| 21 | + IfCompareWithZero() { |
| 22 | + this.getCondition().(EQExpr).getAChild().getValue() = "0" |
| 23 | + or |
| 24 | + this.getCondition().(NEExpr).getAChild().getValue() = "0" and |
| 25 | + this.hasElse() |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +/** |
| 30 | + * lookup for calls to `operator new`, with incorrect error handling. |
| 31 | + */ |
| 32 | +class WrongCheckErrorOperatorNew extends FunctionCall { |
| 33 | + Expr exp; |
| 34 | + |
| 35 | + WrongCheckErrorOperatorNew() { |
| 36 | + this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and |
| 37 | + ( |
| 38 | + this.getTarget().hasGlobalOrStdName("operator new") |
| 39 | + or |
| 40 | + this.getTarget().hasGlobalOrStdName("operator new[]") |
| 41 | + ) |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * Holds if handler `try ... catch` exists. |
| 46 | + */ |
| 47 | + predicate isExistsTryCatchBlock() { |
| 48 | + exists(TryStmt tb, AssignExpr aex, Initializer it | |
| 49 | + tb.getAChild*() = exp |
| 50 | + or |
| 51 | + exp = it.getExpr() and |
| 52 | + tb.getAChild*().(DeclStmt).getADeclaration() = it.getDeclaration() |
| 53 | + or |
| 54 | + aex.getAChild*() = exp and |
| 55 | + tb.getAChild*().(AssignExpr) = aex |
| 56 | + ) |
| 57 | + } |
| 58 | + |
| 59 | + /** |
| 60 | + * Holds if results call `operator new` check in `operator if`. |
| 61 | + */ |
| 62 | + predicate isExistsIfCondition() { |
| 63 | + exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it | |
| 64 | + // call `operator new` directly from the condition of `operator if`. |
| 65 | + this = ifc.getCondition().getAChild() |
| 66 | + or |
| 67 | + // check results call `operator new` with variable appropriation |
| 68 | + postDominates(ifc, this) and |
| 69 | + aex.getAChild() = exp and |
| 70 | + ifc.getCondition().getAChild().(VariableAccess).getTarget() = |
| 71 | + aex.getLValue().(VariableAccess).getTarget() |
| 72 | + or |
| 73 | + // check results call `operator new` with declaration variable |
| 74 | + postDominates(ifc, this) and |
| 75 | + exp = it.getExpr() and |
| 76 | + it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget() |
| 77 | + ) |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Holds if `(std::nothrow)` exists in call `operator new`. |
| 82 | + */ |
| 83 | + predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" } |
| 84 | +} |
| 85 | + |
| 86 | +from WrongCheckErrorOperatorNew op |
| 87 | +where |
| 88 | + // use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if` |
| 89 | + op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock() |
| 90 | + or |
| 91 | + // use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block |
| 92 | + not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition() |
| 93 | +select op, "memory allocation error check is incorrect or missing" |
0 commit comments