/** * Find Security Bugs * Copyright (c) Philippe Arteau, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. */ package com.h3xstream.findsecbugs.taintanalysis; import com.h3xstream.findsecbugs.FindSecBugsGlobalConfig; import com.h3xstream.findsecbugs.common.ByteCode; import edu.umd.cs.findbugs.ba.AbstractFrameModelingVisitor; import edu.umd.cs.findbugs.ba.DataflowAnalysisException; import edu.umd.cs.findbugs.ba.InvalidBytecodeException; import edu.umd.cs.findbugs.ba.generic.GenericSignatureParser; import edu.umd.cs.findbugs.classfile.MethodDescriptor; import edu.umd.cs.findbugs.util.ClassName; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.bcel.Constants; import org.apache.bcel.generic.AALOAD; import org.apache.bcel.generic.AASTORE; import org.apache.bcel.generic.ACONST_NULL; import org.apache.bcel.generic.ANEWARRAY; import org.apache.bcel.generic.ARETURN; import org.apache.bcel.generic.BIPUSH; import org.apache.bcel.generic.CHECKCAST; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.GETFIELD; import org.apache.bcel.generic.GETSTATIC; import org.apache.bcel.generic.ICONST; import org.apache.bcel.generic.INVOKEINTERFACE; import org.apache.bcel.generic.INVOKESPECIAL; import org.apache.bcel.generic.INVOKESTATIC; import org.apache.bcel.generic.INVOKEVIRTUAL; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.InvokeInstruction; import org.apache.bcel.generic.LDC; import org.apache.bcel.generic.LDC2_W; import org.apache.bcel.generic.LoadInstruction; import org.apache.bcel.generic.NEW; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.SIPUSH; import org.apache.bcel.generic.StoreInstruction; /** * Visitor to make instruction transfer of taint values easier * * @author David Formanek (Y Soft Corporation, a.s.) */ public class TaintFrameModelingVisitor extends AbstractFrameModelingVisitor<Taint, TaintFrame> { private static final Map<String, Taint.Tag> REPLACE_TAGS; private final MethodDescriptor methodDescriptor; private final TaintConfig taintConfig; private final TaintMethodConfig analyzedMethodConfig; static { REPLACE_TAGS = new HashMap<String, Taint.Tag>(); REPLACE_TAGS.put("\r", Taint.Tag.CR_ENCODED); REPLACE_TAGS.put("\n", Taint.Tag.LF_ENCODED); REPLACE_TAGS.put("\"", Taint.Tag.QUOTE_ENCODED); REPLACE_TAGS.put("'", Taint.Tag.APOSTROPHE_ENCODED); REPLACE_TAGS.put("<", Taint.Tag.LT_ENCODED); } /** * Constructs the object and stores the parameters * * @param cpg constant pool gen for super class * @param method descriptor of analysed method * @param taintConfig current configured and derived taint summaries * @throws NullPointerException if arguments method or taintConfig is null */ public TaintFrameModelingVisitor(ConstantPoolGen cpg, MethodDescriptor method, TaintConfig taintConfig) { super(cpg); if (method == null) { throw new NullPointerException("null method descriptor"); } if (taintConfig == null) { throw new NullPointerException("null taint config"); } this.methodDescriptor = method; this.taintConfig = taintConfig; this.analyzedMethodConfig = new TaintMethodConfig(false); } private Collection<Integer> getMutableStackIndices(String signature) { assert signature != null && !signature.isEmpty(); ArrayList<Integer> indices = new ArrayList<Integer>(); int stackIndex = 0; GenericSignatureParser parser = new GenericSignatureParser(signature); Iterator<String> iterator = parser.parameterSignatureIterator(); while (iterator.hasNext()) { String parameter = iterator.next(); if ((parameter.startsWith("L") || parameter.startsWith("[")) && !taintConfig.isClassImmutable(parameter)) { indices.add(stackIndex); } if (parameter.equals("D") || parameter.equals("J")) { // double and long types takes two slots stackIndex += 2; } else { stackIndex++; } } for (int i = 0; i < indices.size(); i++) { int reverseIndex = stackIndex - indices.get(i) - 1; assert reverseIndex >= 0; indices.set(i, reverseIndex); } return indices; } @Override public void analyzeInstruction(Instruction ins) throws DataflowAnalysisException { //Print the bytecode instruction if it is globally configured if (FindSecBugsGlobalConfig.getInstance().isDebugPrintInvocationVisited() && ins instanceof InvokeInstruction) { ByteCode.printOpCode(ins, cpg); } else if (FindSecBugsGlobalConfig.getInstance().isDebugPrintInstructionVisited()) { ByteCode.printOpCode(ins, cpg); } super.analyzeInstruction(ins); } @Override public Taint getDefaultValue() { return new Taint(Taint.State.UNKNOWN); } @Override public void visitLDC(LDC ldc) { Taint taint = new Taint(Taint.State.SAFE); Object value = ldc.getValue(cpg); if (value instanceof String) { taint.setConstantValue((String) value); } if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { if (value instanceof String) { taint.setDebugInfo("\"" + value + "\""); } else { taint.setDebugInfo("LDC " + ldc.getType(cpg).getSignature()); } } getFrame().pushValue(taint); } @Override public void visitLDC2_W(LDC2_W obj) { // double and long type takes two slots in BCEL if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { pushSafeDebug("partial long/double"); pushSafeDebug("partial long/double"); } else { pushSafe(); pushSafe(); } } @Override public void visitBIPUSH(BIPUSH obj) { Taint taint = new Taint(Taint.State.SAFE); // assume each pushed byte is a char taint.setConstantValue(String.valueOf((char) obj.getValue().byteValue())); getFrame().pushValue(taint); } @Override public void visitSIPUSH(SIPUSH obj) { Taint taint = new Taint(Taint.State.SAFE); // assume each pushed short is a char (for non-ASCII characters) taint.setConstantValue(String.valueOf((char) obj.getValue().shortValue())); getFrame().pushValue(taint); } @Override public void visitGETSTATIC(GETSTATIC obj) { // Scala uses some classes to represent null instances of objects // If we find one of them, we will handle it as a Java Null if (obj.getLoadClassType(getCPG()).getSignature().equals("Lscala/collection/immutable/Nil$;")) { if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { getFrame().pushValue(new Taint(Taint.State.NULL).setDebugInfo("NULL")); } else { getFrame().pushValue(new Taint(Taint.State.NULL)); } } else { super.visitGETSTATIC(obj); } } @Override public void visitACONST_NULL(ACONST_NULL obj) { if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { getFrame().pushValue(new Taint(Taint.State.NULL).setDebugInfo("NULL")); } else { getFrame().pushValue(new Taint(Taint.State.NULL)); } } @Override public void visitICONST(ICONST obj) { Taint t = new Taint(Taint.State.SAFE); if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { t.setDebugInfo("" + obj.getValue().intValue()); } getFrame().pushValue(t); } @Override public void visitGETFIELD(GETFIELD obj) { Taint.State state = taintConfig.getClassTaintState(obj.getSignature(cpg), Taint.State.UNKNOWN); Taint taint = new Taint(state); if (!state.equals(Taint.State.SAFE)){ taint.addLocation(getTaintLocation(), false); } if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { taint.setDebugInfo("." + obj.getFieldName(cpg)); } modelInstruction(obj, getNumWordsConsumed(obj), getNumWordsProduced(obj), taint); } @Override public void visitNEW(NEW obj) { Taint taint = new Taint(Taint.State.SAFE); ObjectType type = obj.getLoadClassType(cpg); taint.setRealInstanceClass(type); if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { taint.setDebugInfo("new " + type.getClassName() + "()"); } getFrame().pushValue(taint); } @Override public void handleStoreInstruction(StoreInstruction obj) { try { int numConsumed = obj.consumeStack(cpg); if (numConsumed == Constants.UNPREDICTABLE) { throw new InvalidBytecodeException("Unpredictable stack consumption"); } int index = obj.getIndex(); while (numConsumed-- > 0) { Taint value = new Taint(getFrame().popValue()); value.setVariableIndex(index); getFrame().setValue(index++, value); } } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException(ex.toString(), ex); } } @Override public void handleLoadInstruction(LoadInstruction obj) { int numProduced = obj.produceStack(cpg); if (numProduced == Constants.UNPREDICTABLE) { throw new InvalidBytecodeException("Unpredictable stack production"); } int index = obj.getIndex() + numProduced; while (numProduced-- > 0) { Taint value = getFrame().getValue(--index); assert value.hasValidVariableIndex() : "index not set in " + methodDescriptor; assert index == value.getVariableIndex() : "bad index in " + methodDescriptor; getFrame().pushValue(new Taint(value)); } } @Override public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) { visitInvoke(obj); } @Override public void visitINVOKESPECIAL(INVOKESPECIAL obj) { visitInvoke(obj); } @Override public void visitINVOKESTATIC(INVOKESTATIC obj) { visitInvoke(obj); } @Override public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) { prepopulatePageContextTaint(obj); visitInvoke(obj); } private void prepopulatePageContextTaint(InvokeInstruction obj) { String methodName = obj.getMethodName(cpg); if (!methodName.equals("setVar") && !methodName.equals("setVarImpl")) { return; } String className = getInstanceClassName(obj); if (!className.equals("com/liferay/taglib/portlet/ActionURLTag") && !className.equals("com/liferay/taglib/portlet/RenderURLTag") && !className.equals("com/liferay/taglib/portlet/ResourceURLTag") && !className.equals("com/liferay/taglib/core/ConditionalTagSupport") && !className.equals("com/liferay/taglib/core/IfTag") && !className.equals("com/liferay/taglib/core/OtherwiseTag") && !className.equals("com/liferay/taglib/core/WhenTag") && !className.equals("com/liferay/taglib/security/DoAsURLTag") && !className.equals("com/liferay/taglib/security/PermissionsURLTag") && !className.equals("com/liferay/taglib/ui/SearchContainerTag") && !className.equals("com/liferay/taglib/util/BufferTag") && !className.equals("com/liferay/taglib/util/GetUrlTag")) { return; } try { assert getFrame().getStackDepth() > 0; Taint taint = getFrame().getStackValue(0); String value = taint.getConstantValue(); assert value != null; String taintMethodConfigWithArgumentsAndLocationConfig = "javax/servlet/jsp/PageContext.findAttribute(\"" + value + "\"):SAFE@" + methodDescriptor.getSlashedClassName(); taintConfig.load(new ByteArrayInputStream(taintMethodConfigWithArgumentsAndLocationConfig.getBytes()), false); } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("Not enough values on the stack", ex); } catch (IOException ex) { throw new RuntimeException(ex); } } @Override public void visitANEWARRAY(ANEWARRAY obj) { try { getFrame().popValue(); if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { pushSafeDebug("new " + obj.getLoadClassType(cpg).getClassName() + "[]"); } else { pushSafe(); } } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("Array length not in the stack", ex); } } @Override public void visitAASTORE(AASTORE obj) { try { Taint valueTaint = getFrame().popValue(); getFrame().popValue(); // array index Taint arrayTaint = getFrame().popValue(); Taint merge = Taint.merge(valueTaint, arrayTaint); setLocalVariableTaint(merge, arrayTaint); Taint stackTop = null; if (getFrame().getStackDepth() > 0) { stackTop = getFrame().getTopValue(); } // varargs use duplicated values if (stackTop == arrayTaint) { getFrame().popValue(); getFrame().pushValue(new Taint(merge)); } } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("Not enough values on the stack", ex); } } @Override public void visitAALOAD(AALOAD obj) { try { getFrame().popValue(); // array index // just transfer the taint from array to value at any index } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("Not enough values on the stack", ex); } } @Override public void visitCHECKCAST(CHECKCAST obj) { // cast to a safe object type ObjectType objectType = obj.getLoadClassType(cpg); if (objectType == null) { return; } String objectTypeSignature = objectType.getSignature(); if(!taintConfig.isClassTaintSafe(objectTypeSignature)) { return; } try { getFrame().popValue(); pushSafe(); } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("empty stack for checkcast", ex); } } @Override public void visitARETURN(ARETURN obj) { try { Taint returnTaint = getFrame().getTopValue(); Taint currentTaint = analyzedMethodConfig.getOutputTaint(); analyzedMethodConfig.setOuputTaint(Taint.merge(returnTaint, currentTaint)); } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("empty stack before reference return", ex); } handleNormalInstruction(obj); } /** * Regroup the method invocations (INVOKEINTERFACE, INVOKESPECIAL, * INVOKESTATIC, INVOKEVIRTUAL) * * @param obj one of the invoke instructions */ private void visitInvoke(InvokeInstruction obj) { assert obj != null; try { TaintMethodConfig methodConfig = getMethodConfig(obj); ObjectType realInstanceClass = (methodConfig == null) ? null : methodConfig.getOutputTaint().getRealInstanceClass(); Taint taint = getMethodTaint(methodConfig); assert taint != null; if (FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { taint.setDebugInfo(obj.getMethodName(cpg) + "()"); } if (taint.isUnknown()) { taint.addLocation(getTaintLocation(), false); } taintMutableArguments(methodConfig, obj); transferTaintToMutables(methodConfig, taint); // adds variable index to taint too Taint taintCopy = new Taint(taint); // return type is not always the instance type taintCopy.setRealInstanceClass(realInstanceClass); modelInstruction(obj, getNumWordsConsumed(obj), getNumWordsProduced(obj), taintCopy); } catch (Exception e) { String className = ClassName.toSlashedClassName(obj.getReferenceType(cpg).toString()); String methodName = obj.getMethodName(cpg); String signature = obj.getSignature(cpg); throw new RuntimeException("Unable to call " + className + '.' + methodName + signature, e); } } private TaintMethodConfig getMethodConfig(InvokeInstruction obj) { String signature = obj.getSignature(cpg); String returnType = getReturnType(signature); String className = getInstanceClassName(obj); String methodName = obj.getMethodName(cpg); String methodId = "." + methodName + signature; TaintMethodConfig config = taintConfig.getMethodConfig(getFrame(), methodDescriptor, className, methodId); if (config != null) { config = getConfigWithReplaceTags(config, className, methodName); } if (config != null && config.isConfigured()) { return config; } if (taintConfig.isClassTaintSafe(returnType)) { return TaintMethodConfig.SAFE_CONFIG; } if (config != null) { return config; } if (Constants.CONSTRUCTOR_NAME.equals(methodName) && !taintConfig.isClassTaintSafe("L" + className + ";")) { try { int stackSize = getFrame().getNumArgumentsIncludingObjectInstance(obj, cpg); return TaintMethodConfig.getDefaultConstructorConfig(stackSize); } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException(ex.getMessage(), ex); } } return null; } private TaintMethodConfig getConfigWithReplaceTags( TaintMethodConfig config, String className, String methodName) { if (!"java/lang/String".equals(className)) { return config; } boolean isRegex = "replaceAll".equals(methodName); if (!isRegex && !"replace".equals(methodName)) { // not a replace method return config; } try { String toReplace = getFrame().getStackValue(1).getConstantValue(); if (toReplace == null) { // we don't know the exact value return config; } Taint taint = config.getOutputTaint(); for (Map.Entry<String, Taint.Tag> replaceTag : REPLACE_TAGS.entrySet()) { String tagString = replaceTag.getKey(); if ((isRegex && toReplace.contains(tagString)) || toReplace.equals(tagString)) { taint.addTag(replaceTag.getValue()); } } TaintMethodConfig configCopy = new TaintMethodConfig(config); configCopy.setOuputTaint(taint); return configCopy; } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException(ex.getMessage(), ex); } } private String getInstanceClassName(InvokeInstruction invoke) { try { int instanceIndex = getFrame().getNumArgumentsIncludingObjectInstance(invoke, cpg) - 1; if (instanceIndex != -1) { assert instanceIndex < getFrame().getStackDepth(); Taint instanceTaint = getFrame().getStackValue(instanceIndex); String className = instanceTaint.getRealInstanceClassName(); if (className != null) { return className; } } } catch (DataflowAnalysisException ex) { assert false : ex.getMessage(); } String dottedClassName = invoke.getReferenceType(cpg).toString(); return ClassName.toSlashedClassName(dottedClassName); } private static String getReturnType(String signature) { assert signature != null && signature.contains(")"); return signature.substring(signature.indexOf(')') + 1); } private Taint getMethodTaint(TaintMethodConfig methodConfig) { if (methodConfig == null) { return getDefaultValue(); } Taint taint = methodConfig.getOutputTaint(); assert taint != null; assert taint != methodConfig.getOutputTaint() : "defensive copy not made"; Taint taintCopy = new Taint(taint); if (taint.isUnknown() && taint.hasParameters()) { Taint merge = mergeTransferParameters(taint.getParameters()); assert merge != null; // merge removes tags so we made a taint copy before taint = Taint.merge(Taint.valueOf(taint.getNonParametricState()), merge); } if (taint.isTainted()) { taint.addLocation(getTaintLocation(), true); } // don't add tags to safe values if (!taint.isSafe() && taintCopy.hasTags()) { for (Taint.Tag tag : taintCopy.getTags()) { taint.addTag(tag); } } if (taintCopy.isRemovingTags()) { for (Taint.Tag tag : taintCopy.getTagsToRemove()) { taint.removeTag(tag); } } return taint; } private void taintMutableArguments(TaintMethodConfig methodConfig, InvokeInstruction obj) { if (methodConfig != null && methodConfig.isConfigured()) { return; } Collection<Integer> mutableStackIndices = getMutableStackIndices(obj.getSignature(cpg)); for (Integer index : mutableStackIndices) { assert index >= 0 && index < getFrame().getStackDepth(); try { Taint stackValue = getFrame().getStackValue(index); Taint taint = Taint.merge(stackValue, getDefaultValue()); if (stackValue.hasValidVariableIndex()) { // set back the index removed during merging taint.setVariableIndex(stackValue.getVariableIndex()); } taint.setRealInstanceClass(stackValue.getRealInstanceClass()); taint.addLocation(getTaintLocation(), false); getFrame().setValue(getFrame().getStackLocation(index), taint); setLocalVariableTaint(taint, taint); } catch (DataflowAnalysisException ex) { throw new InvalidBytecodeException("Not enough values on the stack", ex); } } } private Taint mergeTransferParameters(Collection<Integer> transferParameters) { assert transferParameters != null && !transferParameters.isEmpty(); Taint taint = null; for (Integer transferParameter : transferParameters) { try { Taint value = getFrame().getStackValue(transferParameter); taint = Taint.merge(taint, value); } catch (DataflowAnalysisException ex) { throw new RuntimeException("Bad transfer parameter specification", ex); } } assert taint != null; return taint; } private void transferTaintToMutables(TaintMethodConfig methodConfig, Taint taint) { assert taint != null; if (methodConfig == null || !methodConfig.hasMutableStackIndices()) { return; } try { int stackDepth = getFrame().getStackDepth(); for (Integer mutableStackIndex : methodConfig.getMutableStackIndices()) { assert mutableStackIndex >= 0; if (mutableStackIndex >= stackDepth) { if (!Constants.CONSTRUCTOR_NAME.equals(methodDescriptor.getName()) && !Constants.STATIC_INITIALIZER_NAME.equals(methodDescriptor.getName())) { assert false : "Out of bounds mutables in " + methodDescriptor; } continue; // ignore if assertions disabled or if in constructor } Taint stackValue = getFrame().getStackValue(mutableStackIndex); setLocalVariableTaint(taint, stackValue); Taint taintCopy = new Taint(taint); // do not set instance to return values, can be different type taintCopy.setRealInstanceClass(stackValue.getRealInstanceClass()); getFrame().setValue(getFrame().getStackLocation(mutableStackIndex), taintCopy); } } catch (DataflowAnalysisException ex) { assert false : ex.getMessage(); // stack depth is checked } } private void setLocalVariableTaint(Taint valueTaint, Taint indexTaint) { assert valueTaint != null && indexTaint != null; if (!indexTaint.hasValidVariableIndex()) { return; } int index = indexTaint.getVariableIndex(); if (index >= getFrame().getNumLocals()) { assert false : "Out of bounds local variable index in " + methodDescriptor; return; // ignore if assertions disabled } valueTaint.setVariableIndex(index); getFrame().setValue(index, valueTaint); } /** * Push a value to the stack */ private void pushSafe() { getFrame().pushValue(new Taint(Taint.State.SAFE)); } /** * Push a value to the stack * The information passed will be viewable when the stack will be print. (See printStackState()) * @param debugInfo String representation of the value push */ private void pushSafeDebug(String debugInfo) { getFrame().pushValue(new Taint(Taint.State.SAFE).setDebugInfo(debugInfo)); } private TaintLocation getTaintLocation() { return new TaintLocation(methodDescriptor, getLocation().getHandle().getPosition()); } /** * This method must be called from outside at the end of the method analysis */ public void finishAnalysis() { assert analyzedMethodConfig != null; Taint outputTaint = analyzedMethodConfig.getOutputTaint(); if (outputTaint == null) { // void methods return; } String returnType = getReturnType(methodDescriptor.getSignature()); if (taintConfig.isClassTaintSafe(returnType) && outputTaint.getState() != Taint.State.NULL) { // we do not have to store summaries with safe output return; } String realInstanceClassName = outputTaint.getRealInstanceClassName(); if (returnType.equals("L" + realInstanceClassName + ";")) { // storing it in method summary is useless outputTaint.setRealInstanceClass(null); analyzedMethodConfig.setOuputTaint(outputTaint); } String className = methodDescriptor.getSlashedClassName(); String methodId = "." + methodDescriptor.getName() + methodDescriptor.getSignature(); if (analyzedMethodConfig.isInformative() || taintConfig.getSuperMethodConfig(className, methodId) != null) { String fullMethodName = className.concat(methodId); if (!taintConfig.containsKey(fullMethodName)) { // prefer configured summaries to derived taintConfig.put(fullMethodName, analyzedMethodConfig); } } } /** * For debugging purpose. * Print the state of the stack with information about the values in place. * * Usage: Call this function from your debugger (From your IDE UI: See Watches/Expressions) */ private void printStackState() { printStackState(getFrame()); } public static void printStackState(TaintFrame frame) { try { System.out.println("============================"); if(!FindSecBugsGlobalConfig.getInstance().isDebugTaintState()) { System.out.println(" /!\\ Warning : The taint debugging is not fully activated."); } System.out.println("[[ Stack ]]"); int stackDepth = frame.getStackDepth(); for (int i = 0; i < stackDepth; i++) { Taint taintValue = frame.getStackValue(i); System.out.println(String.format("%s. %s {%s}", i, taintValue.getState().toString(), taintValue.getDebugInfo())); } if (stackDepth == 0) { System.out.println("Empty"); } System.out.println("============================"); } catch (DataflowAnalysisException e) { System.out.println("Oups "+e.getMessage()); } } } /* @generated */