GP-7242 tighten up samedir path validation, no co-mingling debuginfo

Split up debug-link and build-id debug info into separate classes to
make boundary between them more obvious.

When searching for a debug file in the "same dir" search location,
ensure its not a symlink.  (user never explicitly signaled
that they trusted the directory, they only tried to import a file from
there.)
This commit is contained in:
dev747368
2026-09-21 16:47:11 +00:00
committed by Ryan Kurtz
parent 6d874355c6
commit f822b7399a
15 changed files with 332 additions and 200 deletions

View File

@@ -76,8 +76,7 @@ public class DWARFLineInfoSourceMapScript extends GhidraScript {
popup("Unable to get reader for debug line info");
return;
}
ExternalDebugInfo extDebugInfo = ExternalDebugInfo.fromProgram(dprog.getGhidraProgram());
boolean hasBuildId = extDebugInfo != null && extDebugInfo.hasBuildId();
BuildIdDebugInfo buildId = BuildIdDebugInfo.fromProgram(dprog.getGhidraProgram());
ExternalDebugFilesService edfs =
ExternalDebugFilesService.forProgram(dprog.getGhidraProgram());
@@ -108,9 +107,9 @@ public class DWARFLineInfoSourceMapScript extends GhidraScript {
SourceFile sFile = new SourceFile(path, type, sfi.md5());
sourceManager.addSourceFile(sFile);
sourceFileInfoToSourceFile.put(sfi, sFile);
if (hasBuildId) {
ExternalDebugInfo srcFileDebugInfo =
extDebugInfo.withType(ObjectType.SOURCE, path);
if (buildId != null) {
BuildIdDebugInfo srcFileDebugInfo =
buildId.withType(ObjectType.SOURCE, path);
File srcFile = edfs.find(srcFileDebugInfo, monitor);
if (srcFile != null) {
println("Source file: " + srcFile);

View File

@@ -89,14 +89,17 @@ public class BuildIdDebugFileProvider implements DebugFileProvider {
@Override
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
throws IOException, CancelledException {
String buildId = debugInfo.getBuildId();
if (buildId == null || buildId.length() < 4 /* 2 bytes = 4 hex digits */ ) {
if (!(debugInfo instanceof BuildIdDebugInfo buildIdInfo)) {
return null;
}
File bucketDir = new File(rootDir, buildId.substring(0, 2));
File file = new File(bucketDir, buildId.substring(2) + ".debug");
String buildIdStr = buildIdInfo.getBuildIdHexString();
if (buildIdStr == null || buildIdStr.length() < 4 /* 2 bytes = 4 hex digits */ ) {
return null;
}
File bucketDir = new File(rootDir, buildIdStr.substring(0, 2));
File file = new File(bucketDir, buildIdStr.substring(2) + ".debug");
if (!rootDir.equals(bucketDir.getParentFile()) || !bucketDir.equals(file.getParentFile())) {
throw new IOException("Bad buildid: " + buildId);
throw new IOException("Bad buildid: " + buildIdStr);
}
return file.isFile() ? file : null;
}

View File

@@ -0,0 +1,129 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.app.util.bin.format.dwarf.external;
import java.util.*;
import ghidra.app.util.bin.format.elf.info.NoteGnuBuildId;
import ghidra.program.model.listing.Program;
/**
* Represents {@link ExternalDebugInfo} found in a build-id property embedded in an ELF file.
*/
public class BuildIdDebugInfo implements ExternalDebugInfo {
private static final int MIN_BUILDID_HASH_LENGTH = 20;
public static BuildIdDebugInfo fromProgram(Program program) {
NoteGnuBuildId buildId = NoteGnuBuildId.fromProgram(program);
return buildId != null && buildId.getDescription().length >= MIN_BUILDID_HASH_LENGTH
? new BuildIdDebugInfo(buildId.getDescription())
: null;
}
private final byte[] buildId;
private final ObjectType objectType;
private final String extra;
/**
* Creates a new {@link BuildIdDebugInfo} from the bytes of its hash digest
*
* @param buildId bytes of the hash digest
*/
public BuildIdDebugInfo(byte[] buildId) {
this(buildId, ObjectType.DEBUGINFO, null);
}
/**
* Creates a new {@link BuildIdDebugInfo} from the bytes of its hash digest, a specifier for the
* type of the object being pointed to, and an optional path used ObjectType.SOURCE instances.
*
* @param buildId build-id hash digest found in ".note.gnu.build-id" section
* @param objectType {@link ObjectType} specifies what kind of debug file is specified by the
* other info
* @param extra additional information used by {@link ObjectType#SOURCE}
*/
public BuildIdDebugInfo(byte[] buildId, ObjectType objectType, String extra) {
Objects.requireNonNull(buildId);
this.buildId = buildId;
this.objectType = objectType;
this.extra = extra;
}
/**
* Return the build-id.
*
* @return build-id hash string
*/
public String getBuildIdHexString() {
return HexFormat.of().formatHex(buildId);
}
/**
* {@return ObjectType that this build-id specifier points to}
*/
public ObjectType getObjectType() {
return objectType;
}
/**
* {@return extra info used by ObjectTypes that need it}
*/
public String getExtra() {
return extra;
}
/**
* {@return a new BuildIdDebugInfo instance, pointing to the same build-id hash but an
* alternate object associated with the base debug file}
* @param newObjectType the new {@link ObjectType}
* @param newExtra extra information used by the ObjectType to find the target file
*/
public BuildIdDebugInfo withType(ObjectType newObjectType, String newExtra) {
return new BuildIdDebugInfo(buildId, newObjectType, newExtra);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(buildId);
result = prime * result + Objects.hash(extra, objectType);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof BuildIdDebugInfo)) {
return false;
}
BuildIdDebugInfo other = (BuildIdDebugInfo) obj;
return Arrays.equals(buildId, other.buildId) && Objects.equals(extra, other.extra) &&
objectType == other.objectType;
}
@Override
public String toString() {
return "BuildIdDebugInfo [" +
(buildId != null ? "buildId=" + Arrays.toString(buildId) + ", " : "") +
(objectType != null ? "objectType=" + objectType + ", " : "") +
(extra != null ? "extra=" + extra : "") + "]";
}
}

View File

@@ -0,0 +1,87 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.app.util.bin.format.dwarf.external;
import java.util.Objects;
import ghidra.app.util.bin.format.elf.info.GnuDebugLink;
import ghidra.program.model.listing.Program;
/**
* Represents {@link ExternalDebugInfo} found in a debug-link property embedded in an ELF file.
*/
public class DebugLinkDebugInfo implements ExternalDebugInfo {
public static DebugLinkDebugInfo fromProgram(Program program) {
GnuDebugLink debugLink = GnuDebugLink.fromProgram(program);
return debugLink != null && !debugLink.getFilename().isEmpty()
? new DebugLinkDebugInfo(debugLink.getFilename(), debugLink.getCrc())
: null;
}
private final String filename;
private final int crc;
/**
* @param filename filename of external debug file
* @param crc crc32 of external debug file
*/
public DebugLinkDebugInfo(String filename, int crc) {
this.filename = filename;
this.crc = crc;
}
/**
* Return the filename of the external debug file
*
* @return String filename of external debug file
*/
public String getFilename() {
return filename;
}
/**
* Return the crc of the external debug file.
*
* @return int crc32 of external debug file.
*/
public int getCrc() {
return crc;
}
@Override
public int hashCode() {
return Objects.hash(Integer.valueOf(crc), filename);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DebugLinkDebugInfo)) {
return false;
}
DebugLinkDebugInfo other = (DebugLinkDebugInfo) obj;
return crc == other.crc && Objects.equals(filename, other.filename);
}
@Override
public String toString() {
return String.format("DebugLinkDebugInfo [filename=%s, crc=%s]", filename, crc);
}
}

View File

@@ -75,6 +75,24 @@ public class ExternalDebugFilesService {
providers.add(provider);
}
/**
* Searches for the specified external debug file.
*
* @param debugInfos list of information about the external debug file
* @param monitor {@link TaskMonitor}
* @return first found file, or {@code null} if not found
* @throws IOException if error
*/
public File find(List<ExternalDebugInfo> debugInfos, TaskMonitor monitor) throws IOException {
for (ExternalDebugInfo debugInfo : debugInfos) {
File result = find(debugInfo, monitor);
if (result != null) {
return result;
}
}
return null;
}
/**
* Searches for the specified external debug file.
*

View File

@@ -15,10 +15,10 @@
*/
package ghidra.app.util.bin.format.dwarf.external;
import ghidra.app.util.bin.format.elf.info.GnuDebugLink;
import ghidra.app.util.bin.format.elf.info.NoteGnuBuildId;
import java.util.ArrayList;
import java.util.List;
import ghidra.program.model.listing.Program;
import ghidra.util.NumericUtilities;
/**
* Metadata needed to find an ELF/DWARF external debug file, retrieved from an ELF binary's
@@ -27,134 +27,28 @@ import ghidra.util.NumericUtilities;
* The debuglink can provide a filename and crc of the external debug file, while the build-id
* can provide a hash that is converted to a filename that identifies the external debug file.
*/
public class ExternalDebugInfo {
public interface ExternalDebugInfo {
/**
* Create a new {@link ExternalDebugInfo} from information found in the specified program.
*
* @param program {@link Program} to query
* @return new {@link ExternalDebugInfo} or null if no external debug metadata found in
* program
* @return List of {@link ExternalDebugInfo} instances that were found in the program.
*/
public static ExternalDebugInfo fromProgram(Program program) {
GnuDebugLink debugLink = GnuDebugLink.fromProgram(program);
NoteGnuBuildId buildId = NoteGnuBuildId.fromProgram(program);
if (debugLink == null && buildId == null) {
return null;
public static List<ExternalDebugInfo> fromProgram(Program program) {
List<ExternalDebugInfo> results = new ArrayList<>(2);
BuildIdDebugInfo buildId = BuildIdDebugInfo.fromProgram(program);
if (buildId != null) {
results.add(buildId);
}
String filename = debugLink != null ? debugLink.getFilename() : null;
int crc = debugLink != null ? debugLink.getCrc() : 0;
String hash = buildId != null
? NumericUtilities.convertBytesToString(buildId.getDescription())
: null;
DebugLinkDebugInfo debugLink = DebugLinkDebugInfo.fromProgram(program);
if (debugLink != null) {
results.add(debugLink);
}
return new ExternalDebugInfo(filename, crc, hash, ObjectType.DEBUGINFO, null);
}
/**
* {@return a new ExternalDebugInfo instance created using the specified Build-Id value}
* @param buildId hex string
*/
public static ExternalDebugInfo forBuildId(String buildId) {
return new ExternalDebugInfo(null, 0, buildId, ObjectType.DEBUGINFO, null);
}
/**
* {@return a new ExternalDebugInfo instance created using the specified debuglink values}
* @param debugLinkFilename filename from debuglink section
* @param crc crc32 from debuglink section
*/
public static ExternalDebugInfo forDebugLink(String debugLinkFilename, int crc) {
return new ExternalDebugInfo(debugLinkFilename, crc, null, ObjectType.DEBUGINFO, null);
}
private final String filename;
private final int crc;
private final String buildId;
private final ObjectType objectType;
private final String extra;
/**
* Constructor to create an {@link ExternalDebugInfo} instance.
*
* @param filename filename of external debug file, or null
* @param crc crc32 of external debug file, or 0 if no filename
* @param buildId build-id hash digest found in ".note.gnu.build-id" section, or null if
* not present
* @param objectType {@link ObjectType} specifies what kind of debug file is specified by the
* other info
* @param extra additional information used by {@link ObjectType#SOURCE}
*/
public ExternalDebugInfo(String filename, int crc, String buildId, ObjectType objectType,
String extra) {
this.filename = filename;
this.crc = crc;
this.buildId = buildId;
this.objectType = objectType;
this.extra = extra;
}
/**
* Return true if there is a filename
*
* @return boolean true if filename is available, false if not
*/
public boolean hasDebugLink() {
return filename != null && !filename.isBlank();
}
/**
* Return the filename of the external debug file, or null if not specified.
*
* @return String filename of external debug file, or null if not specified
*/
public String getFilename() {
return filename;
}
/**
* Return the crc of the external debug file. Not valid if filename is missing.
*
* @return int crc32 of external debug file.
*/
public int getCrc() {
return crc;
}
/**
* Return the build-id.
*
* @return build-id hash string
*/
public String getBuildId() {
return buildId;
}
/**
* {@return true if buildId is available, false if not}
*/
public boolean hasBuildId() {
return buildId != null && !buildId.isBlank();
}
public ObjectType getObjectType() {
return objectType;
}
public String getExtra() {
return extra;
}
public ExternalDebugInfo withType(ObjectType newObjectType, String newExtra) {
return new ExternalDebugInfo(extra, crc, buildId, newObjectType, newExtra);
}
@Override
public String toString() {
return String.format(
"ExternalDebugInfo [filename=%s, crc=%s, hash=%s, objectType=%s, extra=%s]", filename,
crc, buildId, objectType, extra);
return results;
}
}

View File

@@ -91,13 +91,13 @@ public class HttpDebugInfoDProvider implements DebugStreamProvider {
return DebugInfoProviderStatus.UNKNOWN;
}
private HttpRequest.Builder request(ExternalDebugInfo id) throws IOException {
private HttpRequest.Builder request(BuildIdDebugInfo id) throws IOException {
try {
String extra = "";
if (id.getObjectType() == ObjectType.SOURCE) {
extra = "/" + Objects.requireNonNullElse(id.getExtra(), "");
}
String requestPath = "buildid/%s/%s%s".formatted(id.getBuildId(),
String requestPath = "buildid/%s/%s%s".formatted(id.getBuildIdHexString(),
id.getObjectType().getPathString(), extra);
return HttpRequest.newBuilder(serverURI.resolve(requestPath))
.setHeader("User-Agent", GHIDRA_USER_AGENT);
@@ -110,14 +110,14 @@ public class HttpDebugInfoDProvider implements DebugStreamProvider {
@Override
public StreamInfo getStream(ExternalDebugInfo id, TaskMonitor monitor)
throws IOException, CancelledException {
if (!id.hasBuildId()) {
if (!(id instanceof BuildIdDebugInfo buildIdInfo)) {
return null;
}
monitor.setIndeterminate(true);
monitor.setMessage("Connecting to " + serverURI);
HttpRequest request = request(id).GET().build();
HttpRequest request = request(buildIdInfo).GET().build();
retryLoop: for (int retryNum = 0; retryNum < maxRetryCount; retryNum++) {
if (retryNum > 0) {

View File

@@ -154,12 +154,12 @@ public class LocalDirDebugInfoDProvider implements DebugFileStorage {
@Override
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
throws IOException, CancelledException {
if (!isValid() || !debugInfo.hasBuildId()) {
if (!isValid() || !(debugInfo instanceof BuildIdDebugInfo buildIdInfo)) {
return null;
}
performInitMaintIfNeeded();
File f = getCachePath(debugInfo);
File f = getCachePath(buildIdInfo);
if (f.isFile()) {
f.setLastModified(System.currentTimeMillis());
return f;
@@ -175,26 +175,26 @@ public class LocalDirDebugInfoDProvider implements DebugFileStorage {
return dir;
}
private File getCachePath(ExternalDebugInfo id) throws IOException {
private File getCachePath(BuildIdDebugInfo id) throws IOException {
String suffix = "";
if (id.getObjectType() == ObjectType.SOURCE) {
suffix = "-" + escapePath(Objects.requireNonNullElse(id.getExtra(), ""));
}
return new File(getBuildidDir(id.getBuildId()),
return new File(getBuildidDir(id.getBuildIdHexString()),
id.getObjectType().getPathString() + suffix);
}
@Override
public File putStream(ExternalDebugInfo id, StreamInfo stream, TaskMonitor monitor)
public File putStream(ExternalDebugInfo debugInfo, StreamInfo stream, TaskMonitor monitor)
throws IOException, CancelledException {
assertValid();
if (!id.hasBuildId()) {
throw new IOException("Can't store debug file without BuildId value: " + id);
if (!(debugInfo instanceof BuildIdDebugInfo buildId)) {
throw new IOException("Can't store debug file without BuildId value");
}
performInitMaintIfNeeded();
File f = getCachePath(id);
File f = getCachePath(buildId);
File tmpF = new File(f.getParentFile(), ".tmp_" + f.getName());
FileUtilities.checkedMkdirs(f.getParentFile());
try (stream; FileOutputStream fos = new FileOutputStream(tmpF)) {

View File

@@ -16,6 +16,7 @@
package ghidra.app.util.bin.format.dwarf.external;
import java.io.*;
import java.nio.file.Files;
import java.util.zip.CRC32;
import ghidra.util.Msg;
@@ -24,7 +25,8 @@ import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities;
/**
* Searches for DWARF external debug files specified via a debug-link filename / crc in a directory.
* Searches for DWARF external debug files specified via a debug-link filename / crc
* recursively in a directory.
*/
public class LocalDirDebugLinkProvider implements DebugFileProvider {
@@ -89,10 +91,10 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
@Override
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
throws CancelledException, IOException {
if (!debugInfo.hasDebugLink() || !isValid()) {
if (!(debugInfo instanceof DebugLinkDebugInfo debugLink) || !isValid()) {
return null;
}
ensureSafeFilename(debugInfo.getFilename());
ensureSafeFilename(debugLink.getFilename()); // ensures there are no shenanigans in debugLink filename
return findFile(searchDir, debugInfo, monitor);
}
@@ -105,13 +107,13 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
File findFile(File dir, ExternalDebugInfo debugInfo, TaskMonitor monitor)
throws IOException, CancelledException {
if (!debugInfo.hasDebugLink()) {
if (!(debugInfo instanceof DebugLinkDebugInfo debugLink)) {
return null;
}
File file = new File(dir, debugInfo.getFilename());
File file = new File(dir, debugLink.getFilename());
if (file.isFile()) {
int fileCRC = calcCRC(file);
if (fileCRC == debugInfo.getCrc()) {
if (fileCRC == debugLink.getCrc()) {
return file; // success
}
Msg.info(this,
@@ -119,8 +121,8 @@ public class LocalDirDebugLinkProvider implements DebugFileProvider {
.formatted(file, fileCRC));
}
File[] subDirs;
if ((subDirs = dir.listFiles(f -> f.isDirectory())) != null) {
// TODO: prevent recursing into symlinks?
if ((subDirs =
dir.listFiles(f -> f.isDirectory() && !Files.isSymbolicLink(f.toPath()))) != null) {
for (File subDir : subDirs) {
File result = findFile(subDir, debugInfo, monitor);
if (result != null) {

View File

@@ -17,6 +17,7 @@ package ghidra.app.util.bin.format.dwarf.external;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import org.apache.commons.io.FilenameUtils;
@@ -28,6 +29,12 @@ import utilities.util.FileUtilities;
/**
* A {@link DebugFileProvider} that only looks in the program's original import directory for
* matching debug files.
* <p>
* A debug-link (filename+crc) specified debug file must exist in the binary's original import
* directory and must have a matching crc.
* <p>
* A build-id (20'ish byte hash) specified debug file must exist in the binary's original import
* directory, named "aabbcc...ff.debug"
*/
public class SameDirDebugInfoProvider implements DebugFileProvider {
@@ -92,13 +99,13 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
@Override
public File getFile(ExternalDebugInfo debugInfo, TaskMonitor monitor)
throws IOException, CancelledException {
if (debugInfo.hasDebugLink()) {
if (debugInfo instanceof DebugLinkDebugInfo debugLink) {
// This differs from the LocalDirDebugLinkProvider in that it does NOT recursively search
// for the file
File debugFile = ensureSafeFilename(debugInfo.getFilename());
File debugFile = ensureSafeFilename(debugLink.getFilename());
if (debugFile.isFile()) {
int fileCRC = LocalDirDebugLinkProvider.calcCRC(debugFile);
if (fileCRC == debugInfo.getCrc()) {
if (fileCRC == debugLink.getCrc()) {
return debugFile; // success
}
Msg.info(this,
@@ -107,10 +114,10 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
}
}
if (debugInfo.hasBuildId()) {
if (debugInfo instanceof BuildIdDebugInfo buildId) {
// this probe is a w.a.g for what people might do when co-locating a build-id debug
// file with the original binary
File debugFile = ensureSafeFilename(debugInfo.getBuildId() + ".debug");
File debugFile = ensureSafeFilename(buildId.getBuildIdHexString() + ".debug");
if (debugFile.isFile()) {
return debugFile;
}
@@ -124,6 +131,9 @@ public class SameDirDebugInfoProvider implements DebugFileProvider {
if (!progDir.equals(testFile.getParentFile())) {
throw new IOException("Unsupported path specified in debug file: " + filename);
}
if (Files.isSymbolicLink(testFile.toPath())) {
throw new IOException("Unsupported symlink specified as debug file: " + filename);
}
return testFile;
}

View File

@@ -55,14 +55,14 @@ public class ExternalDebugFileSectionProvider extends BaseSectionProvider {
public static DWARFSectionProvider createExternalSectionProviderFor(Program program,
TaskMonitor monitor) {
try {
ExternalDebugInfo extDebugInfo = ExternalDebugInfo.fromProgram(program);
if (extDebugInfo == null) {
List<ExternalDebugInfo> extDebugInfos = ExternalDebugInfo.fromProgram(program);
if (extDebugInfos.isEmpty()) {
return null;
}
Msg.info(ExternalDebugFileSectionProvider.class,
"DWARF external debug information found: " + extDebugInfo);
"DWARF external debug information found: " + extDebugInfos);
ExternalDebugFilesService edfs = ExternalDebugFilesService.forProgram(program);
File extDebugFile = edfs.find(extDebugInfo, monitor);
File extDebugFile = edfs.find(extDebugInfos, monitor);
if (extDebugFile == null) {
return null;
}

View File

@@ -32,6 +32,7 @@ import utilities.util.FileUtilities;
public class BuildIdDebugFileProviderTest extends AbstractGenericTest {
private TaskMonitor monitor = TaskMonitor.DUMMY;
private File tmpDir;
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
@Before
public void setUp() throws Exception {
@@ -42,14 +43,13 @@ public class BuildIdDebugFileProviderTest extends AbstractGenericTest {
public void testGet() throws IOException, CancelledException {
BuildIdDebugFileProvider provider = new BuildIdDebugFileProvider(tmpDir);
String buildId = "0000000000000000000000000000000000000000";
String buildIdStr = id.getBuildIdHexString();
File f = new File(tmpDir,
"%s/%s.debug".formatted(buildId.substring(0, 2), buildId.substring(2)));
"%s/%s.debug".formatted(buildIdStr.substring(0, 2), buildIdStr.substring(2)));
FileUtilities.checkedMkdirs(f.getParentFile());
FileUtilities.writeStringToFile(f, "test1");
File result = provider.getFile(ExternalDebugInfo.forBuildId(buildId), monitor);
File result = provider.getFile(id, monitor);
assertEquals("test1", Files.readString(result.toPath()));
assertEquals(5, result.length());

View File

@@ -30,31 +30,32 @@ import com.sun.net.httpserver.HttpServer;
import generic.hash.HashUtilities;
import generic.test.AbstractGenericTest;
import ghidra.app.util.bin.format.dwarf.external.DebugStreamProvider.StreamInfo;
import ghidra.util.NumericUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
private TaskMonitor monitor = TaskMonitor.DUMMY;
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
@Test
public void testNoConnect() throws IOException, CancelledException {
InetSocketAddress unusedAddr = nextLoopbackServerAddr();
HttpDebugInfoDProvider httpProvider = new HttpDebugInfoDProvider(getURI(unusedAddr));
StreamInfo stream = httpProvider.getStream(
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000"), monitor);
StreamInfo stream = httpProvider.getStream(id, monitor);
assertNull(stream);
}
@Test
public void testGet() throws IOException, CancelledException {
String buildId = "0000000000000000000000000000000000000000";
HttpServer server = createMockHttpServer();
server.createContext("/buildid/" + buildId + "/debuginfo",
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
createStaticResponseHandler("application/octet-stream", "result1".getBytes()));
server.createContext("/buildid/" + buildId + "/executable",
server.createContext("/buildid/" + id.getBuildIdHexString() + "/executable",
createStaticResponseHandler("application/octet-stream", "result2".getBytes()));
server.createContext("/buildid/" + buildId + "/source/usr/include/stdio.h",
server.createContext("/buildid/" + id.getBuildIdHexString() + "/source/usr/include/stdio.h",
createStaticResponseHandler("application/octet-stream", "result3".getBytes()));
HttpDebugInfoDProvider httpProvider =
@@ -62,7 +63,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
try {
server.start();
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
assertStreamResult("result1", httpProvider.getStream(id, monitor));
assertStreamResult("result2",
httpProvider.getStream(id.withType(ObjectType.EXECUTABLE, null), monitor));
@@ -80,10 +80,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
@Test
public void testGetWithRetry() throws IOException, CancelledException {
String buildId = "0000000000000000000000000000000000000000";
HttpServer server = createMockHttpServer();
server.createContext("/buildid/" + buildId + "/debuginfo",
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
wrapHandlerWithRetryError(
createStaticResponseHandler("application/octet-stream", "result1".getBytes()), 3,
HTTP_INTERNAL_ERROR));
@@ -93,7 +92,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
try {
server.start();
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
assertStreamResult("result1", httpProvider.getStream(id, monitor));
assertEquals(3, httpProvider.getRetriedCount());
}
@@ -104,10 +102,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
@Test
public void testTimeout() throws IOException, CancelledException {
String buildId = "0000000000000000000000000000000000000000";
HttpServer server = createMockHttpServer();
server.createContext("/buildid/" + buildId + "/debuginfo", wrapHandlerWithDelay(
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo", wrapHandlerWithDelay(
createStaticResponseHandler("application/octet-stream", "result1".getBytes()), 3000));
HttpDebugInfoDProvider httpProvider =
@@ -118,9 +115,8 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
server.start();
long startms = System.currentTimeMillis();
ExternalDebugInfo id = ExternalDebugInfo.forBuildId(buildId);
long elapsed = System.currentTimeMillis() - startms;
assertNull(httpProvider.getStream(id, monitor));
long elapsed = System.currentTimeMillis() - startms;
assertTrue("Request took too long", elapsed < (1000 * 2)); // make sure request time was approx same as timeout setting
}
finally {
@@ -130,6 +126,7 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
@Test
public void testGetNotFound() throws IOException, CancelledException {
HttpServer server = createMockHttpServer();
HttpDebugInfoDProvider httpProvider =
@@ -137,8 +134,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
try {
server.start();
ExternalDebugInfo id =
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000");
assertNull(httpProvider.getStream(id, monitor));
assertEquals(0, httpProvider.getRetriedCount());
assertEquals(1, httpProvider.getNotFoundCount());
@@ -150,9 +145,9 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
@Test
public void testServerError() throws IOException, CancelledException {
String buildId = "0000000000000000000000000000000000000000";
HttpServer server = createMockHttpServer();
server.createContext("/buildid/" + buildId + "/debuginfo",
server.createContext("/buildid/" + id.getBuildIdHexString() + "/debuginfo",
createStaticResponseHandler(HTTP_INTERNAL_ERROR, "text/plain", "".getBytes()));
HttpDebugInfoDProvider httpProvider =
@@ -160,8 +155,6 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
try {
server.start();
ExternalDebugInfo id =
ExternalDebugInfo.forBuildId("0000000000000000000000000000000000000000");
assertNull(httpProvider.getStream(id, monitor));
assertEquals(4, httpProvider.getRetriedCount());
assertEquals(0, httpProvider.getNotFoundCount());
@@ -178,10 +171,10 @@ public class HttpDebugInfoDProviderTest extends AbstractGenericTest {
// The specified buildId may stop being present at some point of time in the future
HttpDebugInfoDProvider httpProvider =
new HttpDebugInfoDProvider(URI.create("https://debuginfod.elfutils.org/"));
ExternalDebugInfo id =
ExternalDebugInfo.forBuildId("421e1abd8faf1cb290df755a558377c5d7def3b1");
BuildIdDebugInfo eu_id = new BuildIdDebugInfo(
NumericUtilities.convertStringToBytes("421e1abd8faf1cb290df755a558377c5d7def3b1"));
assertStreamHash("f5894783abae9084e531b8da76bbb2444a688d18",
httpProvider.getStream(id, monitor));
httpProvider.getStream(eu_id, monitor));
}
private void assertStreamResult(String expectedResult, StreamInfo stream) throws IOException {

View File

@@ -33,6 +33,8 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
private TaskMonitor monitor = TaskMonitor.DUMMY;
private File tmpDir;
BuildIdDebugInfo id = new BuildIdDebugInfo(new byte[20] /* all 00's */);
@Before
public void setUp() throws Exception {
tmpDir = createTempDirectory("debuginfod_provider_test");
@@ -43,9 +45,7 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
provider.purgeAll();
String buildId = "0000000000000000000000000000000000000000";
File f = new File(tmpDir, buildId + "/debuginfo");
File f = new File(tmpDir, id.getBuildIdHexString() + "/debuginfo");
FileUtilities.checkedMkdirs(f.getParentFile());
FileUtilities.writeStringToFile(f, "test1");
@@ -70,13 +70,11 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
provider.purgeAll();
String buildId = "0000000000000000000000000000000000000000";
File f = new File(tmpDir, buildId + "/debuginfo");
File f = new File(tmpDir, id.getBuildIdHexString() + "/debuginfo");
FileUtilities.checkedMkdirs(f.getParentFile());
FileUtilities.writeStringToFile(f, "test1");
File result = provider.getFile(ExternalDebugInfo.forBuildId(buildId), monitor);
File result = provider.getFile(id, monitor);
assertEquals("debuginfo", result.getName());
assertEquals(5, result.length());
@@ -87,10 +85,9 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
LocalDirDebugInfoDProvider provider = new LocalDirDebugInfoDProvider(tmpDir);
provider.purgeAll();
String buildId = "0000000000000000000000000000000000000000";
byte bytes[] = "test".getBytes();
StreamInfo stream = new StreamInfo(new ByteArrayInputStream(bytes), bytes.length);
File f = provider.putStream(ExternalDebugInfo.forBuildId(buildId), stream, monitor);
File f = provider.putStream(id, stream, monitor);
assertEquals("debuginfo", f.getName());
assertEquals(bytes.length, f.length());
@@ -104,8 +101,8 @@ public class LocalDirDebugInfoDProviderTest extends AbstractGenericTest {
byte bytes[] = "test".getBytes();
StreamInfo stream = new StreamInfo(new ByteArrayInputStream(bytes), bytes.length);
try {
File f = provider.putStream(ExternalDebugInfo.forDebugLink("test.debug", 0x11223344),
stream, monitor);
File f = provider.putStream(new DebugLinkDebugInfo("test.debug", 0x11223344), stream,
monitor);
fail("Shouldn't get here: " + f);
}
catch (IOException e) {

View File

@@ -48,7 +48,7 @@ public class LocalDirDebugLinkProviderTest extends AbstractGenericTest {
LocalDirDebugLinkProvider provider = new LocalDirDebugLinkProvider(tmpDir);
File result =
provider.getFile(ExternalDebugInfo.forDebugLink("debugfile.abc", crc), monitor);
provider.getFile(new DebugLinkDebugInfo("debugfile.abc", crc), monitor);
assertEquals("test_debuglink", Files.readString(result.toPath()));
}