/* * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.btrace.runtime; import com.sun.btrace.VerifierException; import static com.sun.btrace.org.objectweb.asm.Opcodes.*; import static com.sun.btrace.runtime.Constants.*; import com.sun.btrace.util.Messages; import com.sun.btrace.org.objectweb.asm.AnnotationVisitor; import com.sun.btrace.org.objectweb.asm.ClassVisitor; import com.sun.btrace.org.objectweb.asm.FieldVisitor; import com.sun.btrace.org.objectweb.asm.MethodVisitor; import com.sun.btrace.org.objectweb.asm.Opcodes; /** * This class verifies that a BTrace program is safe * and well-formed. * Also it fills the onMethods and onProbes structures with the data taken from * the annotations * * @author A. Sundararajan * @autohr J. Bachorik */ public class Verifier extends ClassVisitor { private boolean seenBTrace; private boolean classRenamed; private final boolean trustedAllowed; private final BTraceProbe cn; public Verifier(BTraceProbe cv, boolean trusted) { super(Opcodes.ASM5, cv); this.trustedAllowed = trusted; this.cn = cv; } public Verifier(BTraceProbe cv) { this(cv, false); } public boolean isClassRenamed() { return classRenamed; } public String getClassName() { return cn.name; } @Override public void visitEnd() { if (!cn.isTrusted()) { if (cn.getGraph().hasCycle()) { Verifier.this.reportSafetyError("execution.loop.danger"); } } super.visitEnd(); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (!cn.isTrusted()) { if ((access & ACC_INTERFACE) != 0 || (access & ACC_ENUM) != 0 ) { Verifier.this.reportSafetyError("btrace.program.should.be.class"); } if ((access & ACC_PUBLIC) == 0) { reportSafetyError("class.should.be.public", name); } if (! superName.equals(OBJECT_INTERNAL)) { reportSafetyError("object.superclass.required", superName); } if (interfaces != null && interfaces.length > 0) { Verifier.this.reportSafetyError("no.interface.implementation"); } } super.visit(version, access, name, signature, superName, interfaces); } @Override public AnnotationVisitor visitAnnotation(String type, boolean visible) { AnnotationVisitor delegate = super.visitAnnotation(type, visible); if (type.equals(BTRACE_DESC)) { seenBTrace = true; return new AnnotationVisitor(Opcodes.ASM5, delegate) { @Override public void visit(String name, Object value) { if (("unsafe".equals(name) || "trusted".equals(name)) && Boolean.TRUE.equals(value)) { if (!trustedAllowed) { Verifier.this.reportSafetyError("agent.unsafe.not.allowed"); } cn.setTrusted(); // Found @BTrace(..., trusted=true) } super.visit(name, value); } }; } return delegate; } @Override public FieldVisitor visitField(int access, final String name, String desc, String signature, Object value) { if (!seenBTrace) { reportSafetyError("not.a.btrace.program"); } if (!cn.isTrusted()) { if ((access & ACC_STATIC) == 0) { reportSafetyError("agent.no.instance.variables", name); } } return super.visitField(access, name, desc, signature, value); } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { if (!cn.isTrusted()) { if (cn.name.equals(outerName)) { reportSafetyError("no.nested.class"); } } } @Override public MethodVisitor visitMethod(final int access, final String methodName, final String methodDesc, String signature, String[] exceptions) { if (!seenBTrace) { reportSafetyError("not.a.btrace.program"); } if (!cn.isTrusted()) { if ((access & ACC_SYNCHRONIZED) != 0) { reportSafetyError("no.synchronized.methods", methodName + methodDesc); } if (! methodName.equals(CONSTRUCTOR)) { if ((access & ACC_STATIC) == 0) { reportSafetyError("no.instance.method", methodName + methodDesc); } } } return super.visitMethod(access, methodName, methodDesc, signature, exceptions); } @Override public void visitOuterClass(String owner, String name, String desc) { if (!cn.isTrusted()) { reportSafetyError("no.outer.class"); } } void reportSafetyError(String err) { reportSafetyError(err, null); } void reportSafetyError(String err, String msg) { reportError(err, msg); } public static void reportError(String err) { reportError(err, null); } public static void reportError(String err, String msg) { String str = Messages.get(err); if (msg != null) { str += ": " + msg; } throw new VerifierException(str); } private static void usage(String msg) { System.err.println(msg); System.exit(1); } }