Skip to content

Commit 552f876

Browse files
committed
Implement injection of a forkserver into the compiler process
Implement a forkserver inspired by AFL fuzzer. Unlike the original design, this one relies on library preloading and seccomp to eliminate static or dynamic recompilation of the compiler. For single-threaded, single-process compilers this can be several times faster, provided that the interacting with any input files is delayed until absolutely necessary. For example, it is the case when the compiler process first loads the standard library for several seconds and then processes scratch files in several milliseconds.
1 parent 987a379 commit 552f876

13 files changed

+854
-44
lines changed

pmd-scm/src/main/java/net/sourceforge/pmd/scm/SourceCodeMinimizer.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.math.BigInteger;
99
import java.nio.charset.Charset;
1010
import java.nio.file.Files;
11+
import java.nio.file.Path;
1112
import java.nio.file.StandardCopyOption;
1213
import java.security.MessageDigest;
1314
import java.security.NoSuchAlgorithmException;
@@ -38,6 +39,7 @@ private static final class ExitException extends Exception { }
3839
private final MinimizationStrategy strategy;
3940
private final List<ASTCutter> cutters;
4041
private List<Node> currentRoots;
42+
private List<SCMConfiguration.FileMapping> fileMappings;
4143

4244
public SourceCodeMinimizer(SCMConfiguration configuration) throws IOException {
4345
MessageDigest md = null;
@@ -56,7 +58,8 @@ public SourceCodeMinimizer(SCMConfiguration configuration) throws IOException {
5658

5759
Charset sourceCharset = configuration.getSourceCharset();
5860
cutters = new ArrayList<>();
59-
for (SCMConfiguration.FileMapping mapping: configuration.getFileMappings()) {
61+
fileMappings = configuration.getFileMappings();
62+
for (SCMConfiguration.FileMapping mapping: fileMappings) {
6063
Files.copy(mapping.input, mapping.output, StandardCopyOption.REPLACE_EXISTING);
6164
ASTCutter cutter = new ASTCutter(parser, sourceCharset, mapping.output);
6265
cutters.add(cutter);
@@ -85,6 +88,15 @@ public boolean allInputsAreParseable() throws IOException {
8588
return true;
8689
}
8790

91+
@Override
92+
public List<Path> getScratchFileNames() {
93+
List<Path> result = new ArrayList<>();
94+
for (SCMConfiguration.FileMapping mapping: fileMappings) {
95+
result.add(mapping.output);
96+
}
97+
return result;
98+
}
99+
88100
@Override
89101
public NodeInformationProvider getNodeInformationProvider() {
90102
return language.getNodeInformationProvider();

pmd-scm/src/main/java/net/sourceforge/pmd/scm/invariants/AbstractExternalProcessInvariant.java

+24-13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import com.beust.jcommander.Parameter;
1010

11+
import java.io.IOException;
1112
import java.io.PrintStream;
1213

1314
/**
@@ -34,30 +35,40 @@ public String getName() {
3435
}
3536
}
3637

37-
private InvariantOperations ops;
38-
private String[] commandArgs;
38+
private final String compilerCommandLine;
39+
protected InvariantOperations ops;
3940
private int spawnCount;
4041
private int fruitfulTests;
4142

42-
private static String[] createCommandLine(AbstractConfiguration configuration) {
43-
if (SystemUtils.IS_OS_WINDOWS) {
44-
return new String[] { "cmd.exe", "/C", configuration.compilerCommandLine };
45-
} else {
46-
return new String[] { "/bin/sh", "-c", configuration.compilerCommandLine };
47-
}
48-
}
49-
5043
protected AbstractExternalProcessInvariant(AbstractConfiguration configuration) {
51-
commandArgs = createCommandLine(configuration);
44+
compilerCommandLine = configuration.compilerCommandLine;
5245
}
5346

5447
@Override
55-
public void initialize(InvariantOperations ops) {
48+
public void initialize(InvariantOperations ops) throws IOException {
5649
this.ops = ops;
5750
}
5851

5952
protected abstract boolean testSatisfied(ProcessBuilder pb) throws Exception;
6053

54+
protected String getCompilerCommandLine() {
55+
return compilerCommandLine;
56+
}
57+
58+
protected ProcessBuilder createProcessBuilder() {
59+
ProcessBuilder pb = new ProcessBuilder();
60+
if (SystemUtils.IS_OS_WINDOWS) {
61+
pb.command("cmd.exe", "/C", compilerCommandLine);
62+
} else {
63+
pb.command("/bin/sh", "-c", compilerCommandLine);
64+
}
65+
return pb;
66+
}
67+
68+
protected boolean testSatisfied() throws Exception {
69+
return testSatisfied(createProcessBuilder());
70+
}
71+
6172
@Override
6273
public boolean checkIsSatisfied() throws Exception {
6374
// First, make a fast check that the source can be parsed at all
@@ -67,7 +78,7 @@ public boolean checkIsSatisfied() throws Exception {
6778

6879
// then proceed to spawning subprocess
6980
spawnCount += 1;
70-
boolean result = testSatisfied(new ProcessBuilder().command(commandArgs));
81+
boolean result = testSatisfied();
7182
fruitfulTests += result ? 1 : 0;
7283

7384
return result;

0 commit comments

Comments
 (0)