org.sosy_lab.cpachecker.cfa.postprocessing.function.NullPointerChecks.java Source code

Java tutorial

Introduction

Here is the source code for org.sosy_lab.cpachecker.cfa.postprocessing.function.NullPointerChecks.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package org.sosy_lab.cpachecker.cfa.postprocessing.function;

import static org.sosy_lab.cpachecker.util.CFAUtils.leavingEdges;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.common.configuration.Option;
import org.sosy_lab.common.configuration.Options;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.cpachecker.cfa.CFACreationUtils;
import org.sosy_lab.cpachecker.cfa.MutableCFA;
import org.sosy_lab.cpachecker.cfa.ast.FileLocation;
import org.sosy_lab.cpachecker.cfa.ast.c.CArraySubscriptExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CAssignment;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpression.BinaryOperator;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpressionBuilder;
import org.sosy_lab.cpachecker.cfa.ast.c.CCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CComplexCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpressionStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CFieldReference;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializers;
import org.sosy_lab.cpachecker.cfa.ast.c.CIntegerLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CPointerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CRightHandSideVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression.UnaryOperator;
import org.sosy_lab.cpachecker.cfa.ast.c.CVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.DefaultCExpressionVisitor;
import org.sosy_lab.cpachecker.cfa.model.AssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.BlankEdge;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.c.CAssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CDeclarationEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CReturnStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CStatementEdge;
import org.sosy_lab.cpachecker.cfa.types.c.CNumericTypes;
import org.sosy_lab.cpachecker.exceptions.CParserException;
import org.sosy_lab.cpachecker.exceptions.UnrecognizedCCodeException;

import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

/******************************************************************+
 * NullPointerDetection
 *
 * Using detectNullPointers, before every occurence of *p we insert a test on
 * p == 0 in order to detect null pointers.
 */
@Options(prefix = "cfa.checkNullPointers")
public class NullPointerChecks {

    @Option(secure = true, description = "Whether to have a single target node per function"
            + " for all invalid null pointer dereferences or to have separate nodes for each dereference")
    private boolean singleTargetPerFunction = true;

    private final LogManager logger;

    public NullPointerChecks(LogManager pLogger, Configuration config) throws InvalidConfigurationException {
        config.inject(this);
        logger = pLogger;
    }

    public void addNullPointerChecks(final MutableCFA cfa) throws CParserException {

        CBinaryExpressionBuilder binBuilder = new CBinaryExpressionBuilder(cfa.getMachineModel(), logger);

        for (final String function : cfa.getAllFunctionNames()) {

            // This supplier creates the appropriate target nodes that get added
            // to the CFA for the case the dereference fails.
            Supplier<CFANode> targetNodeSupplier = new Supplier<CFANode>() {
                @Override
                public CFANode get() {

                    CFANode startNode = new CFANode(function);
                    CFANode endNode = new CFANode(function);
                    BlankEdge endEdge = new BlankEdge("null-deref", FileLocation.DUMMY, startNode, endNode,
                            "null-deref");
                    CFACreationUtils.addEdgeUnconditionallyToCFA(endEdge);

                    BlankEdge loopEdge = new BlankEdge("", FileLocation.DUMMY, endNode, endNode, "");
                    CFACreationUtils.addEdgeUnconditionallyToCFA(loopEdge);

                    cfa.addNode(startNode);
                    cfa.addNode(endNode);
                    return startNode;
                }
            };

            if (singleTargetPerFunction) {
                // Only a single target node per function,
                // memoize the first created one and reuse it
                targetNodeSupplier = Suppliers.memoize(targetNodeSupplier);
            }

            for (CFANode node : ImmutableList.copyOf(cfa.getFunctionNodes(function))) {
                switch (node.getNumLeavingEdges()) {
                case 0:
                    break;
                case 1:
                    handleEdge(node.getLeavingEdge(0), cfa, targetNodeSupplier, binBuilder);
                    break;
                case 2:
                    if (node.getLeavingEdge(0) instanceof AssumeEdge
                            && node.getLeavingEdge(1) instanceof AssumeEdge) {
                        // handle only one edge, both contain the same expression
                        handleEdge(node.getLeavingEdge(0), cfa, targetNodeSupplier, binBuilder);
                    } else {
                        handleEdge(node.getLeavingEdge(0), cfa, targetNodeSupplier, binBuilder);
                        handleEdge(node.getLeavingEdge(1), cfa, targetNodeSupplier, binBuilder);
                    }
                    break;
                default:
                    throw new AssertionError("Too many leaving edges on CFANode");
                }
            }
        }
    }

    private static void handleEdge(CFAEdge edge, MutableCFA cfa, Supplier<CFANode> targetNode,
            CBinaryExpressionBuilder builder) throws CParserException {
        ContainsPointerVisitor visitor = new ContainsPointerVisitor();
        if (edge instanceof CReturnStatementEdge) {
            Optional<CExpression> returnExp = ((CReturnStatementEdge) edge).getExpression();
            if (returnExp.isPresent()) {
                returnExp.get().accept(visitor);
            }
        } else if (edge instanceof CStatementEdge) {
            CStatement stmt = ((CStatementEdge) edge).getStatement();
            if (stmt instanceof CFunctionCallStatement) {
                ((CFunctionCallStatement) stmt).getFunctionCallExpression().accept(visitor);
            } else if (stmt instanceof CExpressionStatement) {
                ((CExpressionStatement) stmt).getExpression().accept(visitor);
            } else if (stmt instanceof CAssignment) {
                ((CAssignment) stmt).getRightHandSide().accept(visitor);
                ((CAssignment) stmt).getLeftHandSide().accept(visitor);
            }
        } else if (edge instanceof CDeclarationEdge) {
            CDeclaration decl = ((CDeclarationEdge) edge).getDeclaration();
            if (!decl.isGlobal() && decl instanceof CVariableDeclaration) {
                try {
                    for (CAssignment assignment : CInitializers.convertToAssignments((CVariableDeclaration) decl,
                            edge)) {
                        // left-hand side can be ignored (it is the currently declared variable
                        assignment.getRightHandSide().accept(visitor);
                    }
                } catch (UnrecognizedCCodeException e) {
                    throw new CParserException(e);
                }
            }
        } else if (edge instanceof CAssumeEdge) {
            ((CAssumeEdge) edge).getExpression().accept(visitor);
        }

        for (CExpression exp : Lists.reverse(visitor.dereferencedExpressions)) {
            edge = insertNullPointerCheck(edge, exp, cfa, targetNode, builder);
        }
    }

    private static CFAEdge insertNullPointerCheck(CFAEdge edge, CExpression exp, MutableCFA cfa,
            Supplier<CFANode> targetNode, CBinaryExpressionBuilder binBuilder) {
        CFANode predecessor = edge.getPredecessor();
        CFANode successor = edge.getSuccessor();
        CFACreationUtils.removeEdgeFromNodes(edge);

        CFANode falseNode = new CFANode(predecessor.getFunctionName());

        for (CFAEdge otherEdge : leavingEdges(predecessor).toList()) {
            CFAEdge newEdge = createOldEdgeWithNewNodes(falseNode, otherEdge.getSuccessor(), otherEdge);
            CFACreationUtils.removeEdgeFromNodes(otherEdge);
            CFACreationUtils.addEdgeUnconditionallyToCFA(newEdge);
        }

        CBinaryExpression assumeExpression = binBuilder.buildBinaryExpressionUnchecked(exp,
                new CIntegerLiteralExpression(exp.getFileLocation(), CNumericTypes.INT, BigInteger.valueOf(0)),
                BinaryOperator.EQUALS);
        AssumeEdge trueEdge = new CAssumeEdge(edge.getRawStatement(), edge.getFileLocation(), predecessor,
                targetNode.get(), assumeExpression, true);
        AssumeEdge falseEdge = new CAssumeEdge(edge.getRawStatement(), edge.getFileLocation(), predecessor,
                falseNode, assumeExpression, false);

        CFACreationUtils.addEdgeUnconditionallyToCFA(trueEdge);
        CFACreationUtils.addEdgeUnconditionallyToCFA(falseEdge);

        CFAEdge newEdge = createOldEdgeWithNewNodes(falseNode, successor, edge);
        CFACreationUtils.addEdgeUnconditionallyToCFA(newEdge);

        cfa.addNode(falseNode);

        return newEdge;
    }

    private static CFAEdge createOldEdgeWithNewNodes(CFANode predecessor, CFANode successor, CFAEdge edge) {
        switch (edge.getEdgeType()) {
        case AssumeEdge:
            return new CAssumeEdge(edge.getRawStatement(), edge.getFileLocation(), predecessor, successor,
                    ((CAssumeEdge) edge).getExpression(), ((CAssumeEdge) edge).getTruthAssumption());
        case ReturnStatementEdge:
            return new CReturnStatementEdge(edge.getRawStatement(), ((CReturnStatementEdge) edge).getRawAST().get(),
                    edge.getFileLocation(), predecessor, ((CReturnStatementEdge) edge).getSuccessor());
        case StatementEdge:
            return new CStatementEdge(edge.getRawStatement(), ((CStatementEdge) edge).getStatement(),
                    edge.getFileLocation(), predecessor, successor);
        case DeclarationEdge:
            return new CDeclarationEdge(edge.getRawStatement(), edge.getFileLocation(), predecessor, successor,
                    ((CDeclarationEdge) edge).getDeclaration());
        case CallToReturnEdge:
            assert (false);
            // $FALL-THROUGH$
        default:
            throw new AssertionError("more edge types valid than expected, more work to do here");
        }
    }

    /**
     * This visitor returns all Expressions where a Pointer is included
     */
    static class ContainsPointerVisitor extends DefaultCExpressionVisitor<Void, RuntimeException>
            implements CRightHandSideVisitor<Void, RuntimeException> {

        private final List<CExpression> dereferencedExpressions = new ArrayList<>();

        @Override
        public Void visit(CFunctionCallExpression pIastFunctionCallExpression) {
            pIastFunctionCallExpression.getFunctionNameExpression().accept(this);
            for (CExpression param : pIastFunctionCallExpression.getParameterExpressions()) {
                param.accept(this);
            }
            return null;
        }

        @Override
        public Void visit(CArraySubscriptExpression e) {
            e.getArrayExpression().accept(this);
            e.getSubscriptExpression().accept(this);
            return null;
        }

        @Override
        public Void visit(CCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(CComplexCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(CFieldReference e) {
            if (e.isPointerDereference()) {
                dereferencedExpressions.add(e.getFieldOwner());
            }
            return null;
        }

        @Override
        public Void visit(CUnaryExpression e) {
            if (e.getOperator() == UnaryOperator.SIZEOF) {
                // We do not want cases like sizeof(*p)
                return null;
            }
            if (e.getOperator() == UnaryOperator.AMPER) {
                if (e.getOperand() instanceof CFieldReference
                        && ((CFieldReference) e.getOperand()).isPointerDereference()) {
                    // &(s->f)
                    // ignore this dereference and visit "s"
                    return ((CFieldReference) e.getOperand()).getFieldOwner().accept(this);
                }
            }
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(CPointerExpression e) {
            dereferencedExpressions.add(e.getOperand());
            e.getOperand().accept(this);
            return null;
        }

        @Override
        public Void visit(CBinaryExpression pIastbBinaryExpression) {
            pIastbBinaryExpression.getOperand1().accept(this);
            pIastbBinaryExpression.getOperand2().accept(this);
            return null;
        }

        @Override
        protected Void visitDefault(CExpression pExp) {
            return null;
        }

    }
}