----------------------------------------------------------------------------- Check for a free() of an invalid address char* p = malloc(100); free(p + 10); -----------------------------------------------------------------------------
| 2738 | // free(p + 10); |
| 2739 | //----------------------------------------------------------------------------- |
| 2740 | void CheckOtherImpl::checkInvalidFree() |
| 2741 | { |
| 2742 | std::map<int, bool> inconclusive; |
| 2743 | std::map<int, std::string> allocation; |
| 2744 | |
| 2745 | logChecker("CheckOther::checkInvalidFree"); |
| 2746 | |
| 2747 | const bool printInconclusive = mSettings.certainty.isEnabled(Certainty::inconclusive); |
| 2748 | const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); |
| 2749 | for (const Scope * scope : symbolDatabase->functionScopes) { |
| 2750 | for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { |
| 2751 | |
| 2752 | // Keep track of which variables were assigned addresses to newly-allocated memory |
| 2753 | if ((tok->isCpp() && Token::Match(tok, "%var% = new")) || |
| 2754 | (Token::Match(tok, "%var% = %name% (") && mSettings.library.getAllocFuncInfo(tok->tokAt(2)))) { |
| 2755 | allocation.emplace(tok->varId(), tok->strAt(2)); |
| 2756 | inconclusive.emplace(tok->varId(), false); |
| 2757 | } |
| 2758 | |
| 2759 | // If a previously-allocated pointer is incremented or decremented, any subsequent |
| 2760 | // free involving pointer arithmetic may or may not be invalid, so we should only |
| 2761 | // report an inconclusive result. |
| 2762 | else if (Token::Match(tok, "%var% = %name% +|-") && |
| 2763 | tok->varId() == tok->tokAt(2)->varId() && |
| 2764 | allocation.find(tok->varId()) != allocation.end()) { |
| 2765 | if (printInconclusive) |
| 2766 | inconclusive[tok->varId()] = true; |
| 2767 | else { |
| 2768 | allocation.erase(tok->varId()); |
| 2769 | inconclusive.erase(tok->varId()); |
| 2770 | } |
| 2771 | } |
| 2772 | |
| 2773 | // If a previously-allocated pointer is assigned a completely new value, |
| 2774 | // we can't know if any subsequent free() on that pointer is valid or not. |
| 2775 | else if (Token::Match(tok, "%var% =")) { |
| 2776 | allocation.erase(tok->varId()); |
| 2777 | inconclusive.erase(tok->varId()); |
| 2778 | } |
| 2779 | |
| 2780 | // If a variable that was previously assigned a newly-allocated memory location is |
| 2781 | // added or subtracted from when used to free the memory, report an error. |
| 2782 | else if ((Token::Match(tok, "%name% ( %any% +|-") && mSettings.library.getDeallocFuncInfo(tok)) || |
| 2783 | Token::Match(tok, "delete [ ] ( %any% +|-") || |
| 2784 | Token::Match(tok, "delete %any% +|- %any%")) { |
| 2785 | |
| 2786 | const int varIndex = tok->strAt(1) == "(" ? 2 : |
| 2787 | tok->strAt(3) == "(" ? 4 : 1; |
| 2788 | const int var1 = tok->tokAt(varIndex)->varId(); |
| 2789 | const int var2 = tok->tokAt(varIndex + 2)->varId(); |
| 2790 | const auto alloc1 = utils::as_const(inconclusive).find(var1); |
| 2791 | const auto alloc2 = utils::as_const(inconclusive).find(var2); |
| 2792 | if (alloc1 != inconclusive.end()) { |
| 2793 | invalidFreeError(tok, allocation[var1], alloc1->second); |
| 2794 | } else if (alloc2 != inconclusive.end()) { |
| 2795 | invalidFreeError(tok, allocation[var2], alloc2->second); |
| 2796 | } |
| 2797 | } |
no test coverage detected