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

IEP-1265: Debug Process Hangs #1023

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*******************************************************************************/
package com.espressif.idf.core.util;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import com.espressif.idf.core.logging.Logger;
Expand All @@ -24,13 +27,15 @@

public static boolean isPortAvailable(int port)
{
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$
try (ServerSocket serverSocket = new ServerSocket(port, 50, InetAddress.getByName("127.0.0.1"))) //$NON-NLS-1$
{
return false;
serverSocket.setReuseAddress(true);
return true;
}
catch (Exception e)

Check warning on line 35 in bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java

View workflow job for this annotation

GitHub Actions / spotbugs

REC_CATCH_EXCEPTION

Exception is caught when Exception is not thrown in com.espressif.idf.core.util.PortChecker.isPortAvailable(int)
Raw output
This method uses a try-catch block that catches Exception objects, but Exception is not thrown within the try block, and RuntimeException is not explicitly caught. It is a common bug pattern to say try { ... } catch (Exception e) { something } as a shorthand for catching a number of types of exception each of whose catch blocks is identical, but this construct also accidentally catches RuntimeException as well, masking potential bugs.

A better approach is to either explicitly catch the specific exceptions that are thrown, or to explicitly catch RuntimeException exception, rethrow it, and then catch all non-Runtime Exceptions, as shown below:

try {
    ...
} catch (RuntimeException e) {
    throw e;
} catch (Exception e) {
    ... deal with all non-runtime exceptions ...
}

Check warning on line 35 in bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java

View workflow job for this annotation

GitHub Actions / spotbugs

REC_CATCH_EXCEPTION

Exception is caught when Exception is not thrown in com.espressif.idf.core.util.PortChecker.isPortAvailable(int)
Raw output
This method uses a try-catch block that catches Exception objects, but Exception is not thrown within the try block, and RuntimeException is not explicitly caught. It is a common bug pattern to say try { ... } catch (Exception e) { something } as a shorthand for catching a number of types of exception each of whose catch blocks is identical, but this construct also accidentally catches RuntimeException as well, masking potential bugs.

A better approach is to either explicitly catch the specific exceptions that are thrown, or to explicitly catch RuntimeException exception, rethrow it, and then catch all non-Runtime Exceptions, as shown below:

try {
    ...
} catch (RuntimeException e) {
    throw e;
} catch (Exception e) {
    ... deal with all non-runtime exceptions ...
}
{
return true;
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
import java.util.List;

import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.debug.gdbjtag.core.IGDBJtagConstants;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.embedcdt.core.EclipseUtils;
import org.eclipse.embedcdt.core.StringUtils;
import org.eclipse.embedcdt.debug.gdbjtag.core.DebugUtils;
Expand Down Expand Up @@ -93,9 +95,12 @@ public static String[] getGdbServerCommandLineArray(ILaunchConfiguration configu
lst.add(executable);

int port = PortChecker
.getAvailablePort(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_GDB_PORT_NUMBER,
DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));

.getAvailablePort(DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);

ILaunchConfigurationWorkingCopy configurationWorkingCopy = configuration.getWorkingCopy();
configurationWorkingCopy.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
configurationWorkingCopy.doSave();

lst.add("-c"); //$NON-NLS-1$
lst.add("gdb_port " + port); //$NON-NLS-1$

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*******************************************************************************

Check warning on line 1 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION

Method lists Exception in its throws clause.
Raw output
Method lists Exception in its throws clause.
When declaring a method, the types of exceptions in the throws clause should be the most specific. Therefore, using Exception in the throws clause would force the caller to either use it in its own throws clause, or use it in a try-catch block (when it does not necessarily contain any meaningful information about the thrown exception).

For more information, see the SEI CERT ERR07-J rule [https://wiki.sei.cmu.edu/confluence/display/java/ERR07-J.+Do+not+throw+RuntimeException%2C+Exception%2C+or+Throwable].

Check warning on line 1 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION

Method lists Exception in its throws clause.
Raw output
Method lists Exception in its throws clause.
When declaring a method, the types of exceptions in the throws clause should be the most specific. Therefore, using Exception in the throws clause would force the caller to either use it in its own throws clause, or use it in a try-catch block (when it does not necessarily contain any meaningful information about the thrown exception).

For more information, see the SEI CERT ERR07-J rule [https://wiki.sei.cmu.edu/confluence/display/java/ERR07-J.+Do+not+throw+RuntimeException%2C+Exception%2C+or+Throwable].
* Copyright (c) 2013 Liviu Ionescu.
*
* This program and the accompanying materials
Expand Down Expand Up @@ -70,7 +70,7 @@
System.out.println("openocd.Launch.launch(" + launchConfiguration.getName() + "," + mode + ") " + this);
}

fConfig = launchConfiguration;

Check warning on line 73 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

new com.espressif.idf.debug.gdbjtag.openocd.dsf.Launch(ILaunchConfiguration, String, ISourceLocator) may expose internal representation by storing an externally mutable object into Launch.fConfig
Raw output
This code stores a reference to an externally mutable object into the internal representation of the object.  If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.

Check warning on line 73 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

new com.espressif.idf.debug.gdbjtag.openocd.dsf.Launch(ILaunchConfiguration, String, ISourceLocator) may expose internal representation by storing an externally mutable object into Launch.fConfig
Raw output
This code stores a reference to an externally mutable object into the internal representation of the object.  If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.
fExecutor = (DefaultDsfExecutor) getDsfExecutor();
fSession = getSession();
}
Expand Down Expand Up @@ -147,9 +147,10 @@
fDefaultPreferences.getGdbClientExecutable());
}

int availableRemotePort = PortChecker.getAvailablePort(config.getAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER,
DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));
config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, availableRemotePort);
if (Configuration.getDoStartGdbServer(config))
{
config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT);
}

config.setAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID, CustomIdfProcessFactory.ID);
}
Expand All @@ -171,9 +172,7 @@
{

// Add the GDB server process to the launch tree
newProcess = addServerProcess(Configuration.getGdbServerCommandName(fConfig));

Check warning on line 175 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

DLS_DEAD_LOCAL_STORE

Dead store to newProcess in com.espressif.idf.debug.gdbjtag.openocd.dsf.Launch.initializeServerConsole(IProgressMonitor)
Raw output
This instruction assigns a value to a local variable, but the value is not read or used in any subsequent instruction. Often, this indicates an error, because the value computed is never used.

Note that Sun's javac compiler often generates dead stores for final local variables. Because SpotBugs is a bytecode-based tool, there is no easy way to eliminate these false positives.

Check warning on line 175 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

View workflow job for this annotation

GitHub Actions / spotbugs

DLS_DEAD_LOCAL_STORE

Dead store to newProcess in com.espressif.idf.debug.gdbjtag.openocd.dsf.Launch.initializeServerConsole(IProgressMonitor)
Raw output
This instruction assigns a value to a local variable, but the value is not read or used in any subsequent instruction. Often, this indicates an error, because the value computed is never used.

Note that Sun's javac compiler often generates dead stores for final local variables. Because SpotBugs is a bytecode-based tool, there is no easy way to eliminate these false positives.
newProcess.setAttribute(IProcess.ATTR_CMDLINE, Configuration.getGdbServerCommandLine(fConfig));

monitor.worked(1);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*******************************************************************************

Check warning on line 1 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION

Method lists Exception in its throws clause.
Raw output
Method lists Exception in its throws clause.
When declaring a method, the types of exceptions in the throws clause should be the most specific. Therefore, using Exception in the throws clause would force the caller to either use it in its own throws clause, or use it in a try-catch block (when it does not necessarily contain any meaningful information about the thrown exception).

For more information, see the SEI CERT ERR07-J rule [https://wiki.sei.cmu.edu/confluence/display/java/ERR07-J.+Do+not+throw+RuntimeException%2C+Exception%2C+or+Throwable].

Check warning on line 1 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION

Method lists Exception in its throws clause.
Raw output
Method lists Exception in its throws clause.
When declaring a method, the types of exceptions in the throws clause should be the most specific. Therefore, using Exception in the throws clause would force the caller to either use it in its own throws clause, or use it in a try-catch block (when it does not necessarily contain any meaningful information about the thrown exception).

For more information, see the SEI CERT ERR07-J rule [https://wiki.sei.cmu.edu/confluence/display/java/ERR07-J.+Do+not+throw+RuntimeException%2C+Exception%2C+or+Throwable].
* Copyright (c) 2013 Liviu Ionescu.
*
* This program and the accompanying materials
Expand Down Expand Up @@ -209,8 +209,8 @@
try
{
stream = process.getInputStream();
Reader r = new InputStreamReader(stream);

Check warning on line 212 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

DM_DEFAULT_ENCODING

Found reliance on default encoding in com.espressif.idf.debug.gdbjtag.openocd.dsf.LaunchConfigurationDelegate.getGDBVersion(ILaunchConfiguration, String): new java.io.InputStreamReader(InputStream)
Raw output
Found a call to a method which will perform a byte to String (or String to byte) conversion, and will assume that the default platform encoding is suitable. This will cause the application behavior to vary between platforms. Use an alternative API and specify a charset name or Charset object explicitly.

Check warning on line 212 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

DM_DEFAULT_ENCODING

Found reliance on default encoding in com.espressif.idf.debug.gdbjtag.openocd.dsf.LaunchConfigurationDelegate.getGDBVersion(ILaunchConfiguration, String): new java.io.InputStreamReader(InputStream)
Raw output
Found a call to a method which will perform a byte to String (or String to byte) conversion, and will assume that the default platform encoding is suitable. This will cause the application behavior to vary between platforms. Use an alternative API and specify a charset name or Charset object explicitly.
BufferedReader reader = new BufferedReader(r);

Check warning on line 213 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

OS_OPEN_STREAM

com.espressif.idf.debug.gdbjtag.openocd.dsf.LaunchConfigurationDelegate.getGDBVersion(ILaunchConfiguration, String) may fail to close stream
Raw output
The method creates an IO stream object, does not assign it to any fields, pass it to other methods that might close it, or return it, and does not appear to close the stream on all paths out of the method.  This may result in a file descriptor leak.  It is generally a good idea to use a finally block to ensure that streams are closed.

Check warning on line 213 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/LaunchConfigurationDelegate.java

View workflow job for this annotation

GitHub Actions / spotbugs

OS_OPEN_STREAM

com.espressif.idf.debug.gdbjtag.openocd.dsf.LaunchConfigurationDelegate.getGDBVersion(ILaunchConfiguration, String) may fail to close stream
Raw output
The method creates an IO stream object, does not assign it to any fields, pass it to other methods that might close it, or return it, and does not appear to close the stream on all paths out of the method.  This may result in a file descriptor leak.  It is generally a good idea to use a finally block to ensure that streams are closed.

String line;
while ((line = reader.readLine()) != null)
Expand Down Expand Up @@ -698,6 +698,19 @@
}
// --------------------------------------------------------------------
}

@Override
/**
* This method takes care of cleaning up any resources allocated by the launch, as early as
* the call to getLaunch(), whenever the launch is cancelled or does not complete properly.
* @since 5.0 */
protected void cleanupLaunch(ILaunch launch) throws DebugException
{
if (launch instanceof GdbLaunch)
{
launch.terminate();
}
}
Comment on lines +701 to +713
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Potential race condition in cleanupLaunch method.
The cleanupLaunch method lacks synchronization, which may lead to race conditions when multiple debug sessions run concurrently.

🔗 Analysis chain

Verify cleanup behavior during concurrent debug sessions.

The cleanup implementation should be tested for race conditions when multiple debug sessions are running simultaneously.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential race conditions in cleanup handling
rg -A 5 "terminate|cleanup" --type java

Length of output: 21202


Script:

#!/bin/bash
# Search for synchronization mechanisms in LaunchConfigurationDelegate.java related to cleanup
fd "LaunchConfigurationDelegate.java" | xargs ast-grep --pattern '
class LaunchConfigurationDelegate {
  $$$
  cleanupLaunch($_) {
    $$$
    synchronized($_) {
      $$$
    }
  }
}'

Length of output: 184


/**
* Perform some local validations before starting the debug session.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@

private Text fTargetIpAddress;
private Text fTargetPortNumber;
private Group gdbClientGroup;

protected Button fUpdateThreadlistOnSuspend;

Expand Down Expand Up @@ -470,6 +471,7 @@
public void widgetSelected(SelectionEvent e)
{
doStartGdbServerChanged();
gdbClientGroup.setEnabled(!fDoStartGdbServer.getSelection());
if (fDoStartGdbServer.getSelection())
{
fTargetIpAddress.setText(DefaultPreferences.REMOTE_IP_ADDRESS_LOCALHOST);
Expand Down Expand Up @@ -572,16 +574,16 @@
private void createGdbClientControls(Composite parent)
{

Group group = new Group(parent, SWT.NONE);
gdbClientGroup = new Group(parent, SWT.NONE);
{
GridLayout layout = new GridLayout();
group.setLayout(layout);
gdbClientGroup.setLayout(layout);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
group.setLayoutData(gd);
group.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$
gdbClientGroup.setLayoutData(gd);
gdbClientGroup.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$
}

Composite comp = new Composite(group, SWT.NONE);
Composite comp = new Composite(gdbClientGroup, SWT.NONE);
{
GridLayout layout = new GridLayout();
layout.numColumns = 5;
Expand Down Expand Up @@ -865,7 +867,7 @@
System.out.println("openocd.TabDebugger.initializeFrom() " + configuration.getName()); //$NON-NLS-1$
}

fConfiguration = configuration;

Check warning on line 870 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.initializeFrom(ILaunchConfiguration) may expose internal representation by storing an externally mutable object into TabDebugger.fConfiguration
Raw output
This code stores a reference to an externally mutable object into the internal representation of the object.  If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.

Check warning on line 870 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

EI_EXPOSE_REP2

com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.initializeFrom(ILaunchConfiguration) may expose internal representation by storing an externally mutable object into TabDebugger.fConfiguration
Raw output
This code stores a reference to an externally mutable object into the internal representation of the object.  If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.

try
{
Expand Down Expand Up @@ -1009,8 +1011,8 @@
// Allocate server console
if (EclipseUtils.isWindows())
{
fDoGdbServerAllocateConsole.setSelection(true);
}

Check warning on line 1015 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

DB_DUPLICATE_BRANCHES

com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.initializeFromDefaults() uses the same code for two branches
Raw output
This method uses the same code to implement two branches of a conditional branch. Check to ensure that this isn't a coding mistake.

Check warning on line 1015 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

DB_DUPLICATE_BRANCHES

com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.initializeFromDefaults() uses the same code for two branches
Raw output
This method uses the same code to implement two branches of a conditional branch. Check to ensure that this isn't a coding mistake.
else
{
fDoGdbServerAllocateConsole.setSelection(DefaultPreferences.DO_GDB_SERVER_ALLOCATE_CONSOLE_DEFAULT);
Expand Down Expand Up @@ -1299,7 +1301,7 @@
{
try
{
int port = Integer.valueOf(str).intValue();

Check warning on line 1304 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

DM_BOXED_PRIMITIVE_FOR_PARSING

Boxing/unboxing to parse a primitive com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.performApply(ILaunchConfigurationWorkingCopy)
Raw output
A boxed primitive is created from a String, just to extract the unboxed primitive value. It is more efficient to just call the static parseXXX method.

Check warning on line 1304 in bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java

View workflow job for this annotation

GitHub Actions / spotbugs

DM_BOXED_PRIMITIVE_FOR_PARSING

Boxing/unboxing to parse a primitive com.espressif.idf.debug.gdbjtag.openocd.ui.TabDebugger.performApply(ILaunchConfigurationWorkingCopy)
Raw output
A boxed primitive is created from a String, just to extract the unboxed primitive value. It is more efficient to just call the static parseXXX method.
configuration.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
}
catch (NumberFormatException e)
Expand Down
Loading