Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When I analyze a java file,it encountered StackOverflowError #1472

Closed
HaHarden opened this issue Mar 23, 2024 · 5 comments
Closed

When I analyze a java file,it encountered StackOverflowError #1472

HaHarden opened this issue Mar 23, 2024 · 5 comments
Labels
bug Something isn't working java Related to Java language parsing

Comments

@HaHarden
Copy link

The stacktrace is that:

Caused by: java.lang.StackOverflowError
	at kotlin.jvm.JvmClassMappingKt.getJavaObjectType(JvmClassMapping.kt)
	at kotlin.reflect.jvm.internal.KClassImpl.hashCode(KClassImpl.kt:296)
	at de.fraunhofer.aisec.cpg.frontends.Language.hashCode(Language.kt:116)
	at java.base/java.util.Arrays.hashCode(Arrays.java:4499)
	at java.base/java.util.Objects.hash(Objects.java:133)
	at de.fraunhofer.aisec.cpg.graph.types.Type.hashCode(Type.kt:189)
	at de.fraunhofer.aisec.cpg.graph.types.ObjectType.hashCode(ObjectType.kt:117)
	at java.base/java.util.Arrays.hashCode(Arrays.java:4499)
	at java.base/java.util.Objects.hash(Objects.java:133)
	at de.fraunhofer.aisec.cpg.graph.types.Type$Ancestor.hashCode(Type.kt:206)
	at java.base/java.util.HashMap.hash(HashMap.java:338)
	at java.base/java.util.HashMap.put(HashMap.java:610)
	at java.base/java.util.HashSet.add(HashSet.java:221)
	at de.fraunhofer.aisec.cpg.TypeManagerKt.getAncestors(TypeManager.kt:281)
	at de.fraunhofer.aisec.cpg.TypeManagerKt.getAncestors(TypeManager.kt:261)
	at de.fraunhofer.aisec.cpg.frontends.Language.isDerivedFrom(Language.kt:222)
	at de.fraunhofer.aisec.cpg.TypeManagerKt.isDerivedFrom(TypeManager.kt:295)
	at de.fraunhofer.aisec.cpg.frontends.Language.hasSignature(Language.kt:260)
	at de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration.hasSignature(FunctionDeclaration.kt:128)
	at de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration.hasSignature(FunctionDeclaration.kt:114)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromRecord(SymbolResolver.kt:802)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromParents(SymbolResolver.kt:817)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromParents(SymbolResolver.kt:831)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromParents(SymbolResolver.kt:831)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromParents(SymbolResolver.kt:831)
	at de.fraunhofer.aisec.cpg.passes.SymbolResolver.getInvocationCandidatesFromParents(SymbolResolver.kt:831)

Absolutely, the class SymbolResolver is unreliable.
In this example, it analyze only one java file. My test code is that:

fun myAnalyze(): TranslationResult {
        val builder =
            TranslationConfiguration.builder()
                .sourceLocations(listOf(File("myTestJavaFile"))
                .loadIncludes(false)
                .debugParser(true)
                .failOnError(true)
                .useParallelFrontends(true).
                .registerLanguage<JavaLanguage>()
                .registerPass<SymbolResolver>()
        val config = builder.build()
        val analyzer = TranslationManager.builder().config(config).build()
        return analyzer.analyze().get()
    }

The java file is that:

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2002-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.netmgt.config;

import static org.opennms.core.utils.InetAddressUtils.addr;
import static org.opennms.core.utils.InetAddressUtils.toIpAddrBytes;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.collections.ListUtils;
import org.apache.commons.io.IOUtils;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.core.xml.JaxbUtils;
import org.opennms.netmgt.config.collectd.CollectdConfiguration;
import org.opennms.netmgt.config.collectd.Collector;
import org.opennms.netmgt.config.collectd.Package;
import org.opennms.netmgt.config.poller.Monitor;
import org.opennms.netmgt.filter.FilterDaoFactory;
import org.opennms.netmgt.model.OnmsIpInterface;
import org.opennms.netmgt.model.OnmsMonitoredService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is the singleton class used to load the configuration for the OpenNMS
 * Collection Daemon from the collectd-configuration.xml file.
 *
 * A mapping of the configured URLs to the IP list they contain is built at
 * init() time so as to avoid numerous file reads.
 *
 * <strong>Note: </strong>Users of this class should make sure the
 * <em>init()</em> is called before calling any other method to ensure the
 * config is loaded before accessing other convenience methods.
 *
 * @author <a href="mailto:jamesz@opennms.com">James Zuo</a>
 * @author <a href="mailto:mike@opennms.org">Mike Davidson</a>
 * @author <a href="mailto:sowmya@opennms.org">Sowmya Nataraj</a>
 */
public class CollectdConfigFactory implements org.opennms.netmgt.config.api.CollectdConfigFactory {
    private static final Logger LOG = LoggerFactory.getLogger(CollectdConfigFactory.class);
    public static final String SELECT_METHOD_MIN = "min";

    private CollectdConfiguration localCollectdConfig;
    private final Object m_collectdConfigMutex = new Object();

    private final String m_fileName;

    private List<Package> extendedPackages = Collections.emptyList();
    private List<Package> mergedPackages = Collections.emptyList();
    private List<Collector> extendedCollectors = Collections.emptyList();
    private List<Collector> mergedCollectors = Collections.emptyList();

    public CollectdConfigFactory() throws IOException {
        m_fileName = ConfigFileConstants.getFile(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME).getPath();

        init(new FileInputStream(m_fileName));
    }

    /**
     * For testing purposes only.
     * 
     * @param stream
     * @throws IOException
     */
    public CollectdConfigFactory(InputStream stream) throws IOException {
        m_fileName = null;

        init(stream);
    }

    /**
     * <p>Constructor for CollectdConfigFactory.</p>
     *
     * @param stream a {@link java.io.InputStream} object.
     * @throws IOException
     */
    private void init(final InputStream stream) throws IOException {
        InputStreamReader isr = null;
        try {
            isr = new InputStreamReader(stream);
            CollectdConfiguration config = JaxbUtils.unmarshal(CollectdConfiguration.class, isr);
            synchronized (m_collectdConfigMutex) {
                this.localCollectdConfig = config;
            }
        } finally {
            IOUtils.closeQuietly(isr);
        }

        this.setUpInternalData();
    }

    private void setUpInternalData() {
        synchronized (this.m_collectdConfigMutex) {
            this.mergedCollectors = ListUtils.union(localCollectdConfig.getCollectors(), this.extendedCollectors);
            this.mergedPackages = ListUtils.union(localCollectdConfig.getPackages(), this.extendedPackages);
        }
    }

    /**
     * Reload the config from the default config file
     *
     * @exception java.io.IOException
     *                Thrown if the specified config file cannot be read/loaded
     * @throws java.io.IOException if any.
     */
    public void reload() throws IOException {
        init(new FileInputStream(m_fileName));
    }

    /**
     * Saves the current in-memory configuration to disk and reloads
     *
     * @throws java.io.IOException if any.
     */
    public void saveCurrent() throws IOException {
        File cfgFile = ConfigFileConstants.getFile(ConfigFileConstants.COLLECTD_CONFIG_FILE_NAME);

        CollectdConfiguration config = null;
        synchronized (m_collectdConfigMutex) {
            config = localCollectdConfig;
        }

        FileWriter writer = null;
        try {
            writer = new FileWriter(cfgFile);
            JaxbUtils.marshal(config, writer);
        } finally {
            IOUtils.closeQuietly(writer);
        }

        reload();
    }

    /**
     * <p>getCollectdConfig</p>
     *
     * @return a {@link org.opennms.netmgt.config.collectd.CollectdConfiguration} object.
     */
    public CollectdConfiguration getLocalCollectdConfig() {
        synchronized (m_collectdConfigMutex) {
            return localCollectdConfig;
        }
    }

    @Override
    public Integer getThreads() {
        return this.localCollectdConfig.getThreads();
    }

    /**
     * Returns true if collection package exists
     *
     * @param name
     *            The package name to check
     * @return True if the package exists
     */
    public boolean packageExists(String name) {
        synchronized (m_collectdConfigMutex) {
            for (final Package pkg : this.mergedPackages) {
                if (pkg.getName().equals(name)) {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * <p>getPackage</p>
     *
     * @param name a {@link java.lang.String} object.
     * @return a {@link org.opennms.netmgt.config.collectd.Package} object.
     */
    public Package getPackage(final String name) {
        synchronized (m_collectdConfigMutex) {
            for (Package pkg : this.mergedPackages) {
                if (pkg.getName().equals(name)) {
                    return pkg;
                }
            }
            return null;
        }
    }

    @Override
    public List<Package> getPackages() {
        return this.mergedPackages;
    }

    @Override
    public List<Collector> getCollectors() {
        return this.mergedCollectors;
    }

    /**
     * Returns true if collection domain exists
     *
     * @param name
     *            The domain name to check
     * @return True if the domain exists
     */
    public boolean domainExists(final String name) {
        synchronized (m_collectdConfigMutex) {
            for (Package pkg : this.mergedPackages) {
                if ((pkg.getIfAliasDomain() != null)
                        && pkg.getIfAliasDomain().equals(name)) {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * Returns true if the specified service's interface is included by at least one
     * package which has the specified service and that service is enabled (set
     * to "on").
     *
     * @param service
     *            {@link OnmsMonitoredService} to check
     * @return true if Collectd config contains a package which includes the
     *         specified interface and has the specified service enabled.
     */
    public boolean isServiceCollectionEnabled(final OnmsMonitoredService service) {
        return isServiceCollectionEnabled(service.getIpInterface(), service.getServiceName());
    }

    /**
     * Returns true if the specified interface is included by at least one
     * package which has the specified service and that service is enabled (set
     * to "on").
     *
     * @param iface
     *            {@link OnmsIpInterface} to lookup
     * @param svcName
     *            The service name to lookup
     * @return true if Collectd config contains a package which includes the
     *         specified interface and has the specified service enabled.
     */
    public boolean isServiceCollectionEnabled(final OnmsIpInterface iface, final String svcName) {
        for (Package wpkg : this.mergedPackages) {

            // Does the package include the interface?
            if (interfaceInPackage(iface, wpkg)) {
                // Yes, now see if package includes
                // the service and service is enabled
                //
                if (wpkg.serviceInPackageAndEnabled(svcName)) {
                    // Thats all we need to know...
                	return true;
                }
            }
        }

        return false;
    }

    /**
     * Returns true if the specified interface is included by at least one
     * package which has the specified service and that service is enabled (set
     * to "on").
     *
     * @deprecated This function should take normal model objects instead of bare IP addresses
     * and service names. Use {@link #isServiceCollectionEnabled(OnmsIpInterface, String)}
     * instead.
     *
     * @param ipAddr
     *            IP address of the interface to lookup
     * @param svcName
     *            The service name to lookup
     * @return true if Collectd config contains a package which includes the
     *         specified interface and has the specified service enabled.
     */
    public boolean isServiceCollectionEnabled(final String ipAddr, final String svcName) {
        synchronized (m_collectdConfigMutex) {
            for (Package wpkg : this.mergedPackages) {

                // Does the package include the interface?
                //
                if (interfaceInPackage(ipAddr, wpkg)) {
                    // Yes, now see if package includes
                    // the service and service is enabled
                    //
                    if (wpkg.serviceInPackageAndEnabled(svcName)) {
                        // Thats all we need to know...
                    	return true;
                    }
                }
            }

            return false;
        }
    }

    public boolean interfaceInFilter(String iface, Package pkg) {
        String filter = pkg.getFilter().getContent();
        if (iface == null) return false;
        final InetAddress ifaceAddress = addr(iface);

        boolean filterPassed = false;

        //
        // Get a list of IP address per package against the filter rules from
        // database and populate the package, IP list map.
        //

        LOG.debug("interfaceInFilter: package is {}. filter rules are {}", pkg.getName(), filter);
        try {
            final List<InetAddress> ipList = FilterDaoFactory.getInstance().getActiveIPAddressList(filter);
            filterPassed = ipList.contains(ifaceAddress);
            if (!filterPassed) {
                LOG.debug("interfaceInFilter: Interface {} passed filter for package {}?: false", iface, pkg.getName());
            }
        } catch (Throwable t) {
            LOG.error("interfaceInFilter: Failed to map package: {} to an IP List with filter \"{}\"", pkg.getName(), pkg.getFilter().getContent(), t);
        }

        return filterPassed;
    }

    /**
     * This method is used to determine if the named interface is included in
     * the passed package definition. If the interface belongs to the package
     * then a value of true is returned. If the interface does not belong to the
     * package a false value is returned.
     *
     * <strong>Note: </strong>Evaluation of the interface against a package
     * filter will only work if the IP is already in the database.
     *
     * @deprecated This function should take normal model objects instead of bare IP 
     * addresses. Move this implementation into {@link #interfaceInPackage(OnmsIpInterface, Package)}.
     *
     * @param iface
     *            The interface to test against the package.
     * @return True if the interface is included in the package, false
     *         otherwise.
     */
    public boolean interfaceInPackage(final String iface, Package pkg) {
        boolean filterPassed = interfaceInFilter(iface, pkg);

        if (!filterPassed) {
            return false;
        }

        //
        // Ensure that the interface is in the specific list or
        // that it is in the include range and is not excluded
        //

        byte[] addr = toIpAddrBytes(iface);

        boolean has_range_include = pkg.hasIncludeRange(iface);
        boolean has_specific = pkg.hasSpecific(addr);

        has_specific = pkg.hasSpecificUrl(iface, has_specific);
        boolean has_range_exclude = pkg.hasExcludeRange(iface);

        boolean packagePassed = has_specific || (has_range_include && !has_range_exclude);
        if(packagePassed) {
            LOG.info("interfaceInPackage: Interface {} passed filter and specific/range for package {}?: {}", iface, pkg.getName(), packagePassed);
        } else {
            LOG.debug("interfaceInPackage: Interface {} passed filter and specific/range for package {}?: {}", iface, pkg.getName(), packagePassed);
        }
        return packagePassed;
    }

    /**
     * This method is used to determine if the named interface is included in
     * the passed package definition. If the interface belongs to the package
     * then a value of true is returned. If the interface does not belong to the
     * package a false value is returned.
     *
     * <strong>Note: </strong>Evaluation of the interface against a package
     * filter will only work if the IP is already in the database.
     *
     * @param iface
     *            The interface to test against the package.
     * @return True if the interface is included in the package, false
     *         otherwise.
     */
    public boolean interfaceInPackage(final OnmsIpInterface iface, Package pkg) {
        return interfaceInPackage(iface.getIpAddressAsString(), pkg);
    }

    @Override
    public void setExternalData(final List<Package> packages, final List<Collector> collectors) {
        synchronized (this.m_collectdConfigMutex) {
            this.extendedPackages = packages;
            this.extendedCollectors = collectors;

            this.setUpInternalData();
        }
    }
}
@oxisto oxisto added bug Something isn't working java Related to Java language parsing labels Mar 26, 2024
@konradweiss
Copy link
Collaborator

Thanks for submitting the Issue, I was able to reproduce it and will look into it.

@HaHarden
Copy link
Author

HaHarden commented Apr 2, 2024

When approximately can this issue be fixed?

1 similar comment
@HaHarden
Copy link
Author

When approximately can this issue be fixed?

@KuechA
Copy link
Contributor

KuechA commented May 17, 2024

Hi, we're currently about to refactor some things related to imports, types and their (local vs. fqn) naming. I could identify the problem causing your issue and just proposed a fix in #1559 but since this topic is rather complicated and potentially impacts other current developments, we need some more discussion. However, I hope that we can fix your issue soon

@KuechA
Copy link
Contributor

KuechA commented May 21, 2024

Hi @HaHarden, we merged #1536 into the main branch, could you please check if this issue is resolved now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working java Related to Java language parsing
Projects
None yet
Development

No branches or pull requests

4 participants