242 lines
9.8 KiB
Java
242 lines
9.8 KiB
Java
package unit;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* SolcTest — CWE-407 benchmark for solc-0001
|
|
*
|
|
* solc-0001: TypeChecker::cleanOverloadedDeclarations
|
|
* File: libsolidity/analysis/TypeChecker.cpp, ~line 3619-3651
|
|
*
|
|
* Real code:
|
|
* for (Declaration const* declaration: _candidates)
|
|
* {
|
|
* ...
|
|
* if (uniqueDeclarations.end() == find_if(
|
|
* uniqueDeclarations.begin(), uniqueDeclarations.end(),
|
|
* [&](Declaration const* d) {
|
|
* FunctionType const* newFunctionType = d->functionType(false);
|
|
* if (!newFunctionType) newFunctionType = d->functionType(true);
|
|
* return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
|
|
* }
|
|
* ))
|
|
* uniqueDeclarations.push_back(declaration);
|
|
* }
|
|
*
|
|
* For N declarations each with a unique signature the inner find_if scans
|
|
* 0 + 1 + 2 + ... + (N-1) = N*(N-1)/2 entries → O(N²) comparisons.
|
|
*
|
|
* Fix: build a seenSignatures unordered_set keyed by the canonical parameter-type
|
|
* string. Each probe/insert is O(1) → total O(N).
|
|
*
|
|
* solc-0002: ContractLevelChecker::findDuplicateDefinitions — CLEAN (CWE-407)
|
|
* The set<size_t>::count call is O(log N), making the loop O(N log N) — fine
|
|
* for small N (< 20 overloads per name). A separate logic defect exists
|
|
* (loop exits early when i is in reported), but that is not a CWE-407 issue.
|
|
* No performance patch warranted.
|
|
*/
|
|
public class SolcTest {
|
|
|
|
// -----------------------------------------------------------------------
|
|
// solc-0001 simulation
|
|
// -----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Simulates the defective cleanOverloadedDeclarations.
|
|
*
|
|
* Each "declaration" is represented by its signature string.
|
|
* uniqueDeclarations accumulates strings already accepted.
|
|
* For each new candidate we scan uniqueDeclarations linearly to check
|
|
* whether an equal signature already exists — mirroring the find_if loop.
|
|
*
|
|
* @param signatures ordered list of candidate signatures (may contain dupes)
|
|
* @return number of comparisons performed (the ops count)
|
|
*/
|
|
static long slowCleanOverloads(List<String> signatures) {
|
|
List<String> uniqueDeclarations = new ArrayList<>();
|
|
long ops = 0;
|
|
for (String sig : signatures) {
|
|
boolean found = false;
|
|
// O(|uniqueDeclarations|) linear scan — mirrors std::find_if
|
|
for (String existing : uniqueDeclarations) {
|
|
ops++;
|
|
if (existing.equals(sig)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
uniqueDeclarations.add(sig);
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
/**
|
|
* Simulates the fixed cleanOverloadedDeclarations.
|
|
*
|
|
* Uses an unordered_set (HashSet) for O(1) duplicate detection.
|
|
* Each candidate signature is probed / inserted in O(1).
|
|
*
|
|
* @param signatures ordered list of candidate signatures
|
|
* @return number of ops charged (1 per candidate = O(N))
|
|
*/
|
|
static long fastCleanOverloads(List<String> signatures) {
|
|
Set<String> seenSignatures = new HashSet<>();
|
|
List<String> uniqueDeclarations = new ArrayList<>();
|
|
long ops = 0;
|
|
for (String sig : signatures) {
|
|
ops++; // O(1) hash probe + insert
|
|
if (seenSignatures.add(sig)) {
|
|
uniqueDeclarations.add(sig);
|
|
}
|
|
}
|
|
return ops;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------
|
|
|
|
/** Build a list of N distinct signatures (all unique — worst case for find_if). */
|
|
static List<String> distinctSignatures(int n) {
|
|
List<String> sigs = new ArrayList<>(n);
|
|
for (int i = 0; i < n; i++) {
|
|
// Simulate paramater-type strings like "uint256,address|bool,"
|
|
sigs.add("param" + i + "_type:uint256_" + i + "|ret:bool,");
|
|
}
|
|
return sigs;
|
|
}
|
|
|
|
/** Build a list of N signatures where half are duplicates of the first half. */
|
|
static List<String> halfDupSignatures(int n) {
|
|
List<String> sigs = new ArrayList<>(n);
|
|
for (int i = 0; i < n / 2; i++) sigs.add("sig_" + i);
|
|
for (int i = 0; i < n / 2; i++) sigs.add("sig_" + i); // duplicates
|
|
return sigs;
|
|
}
|
|
|
|
static void bench(String label, List<String> sigs) {
|
|
// warm-up
|
|
slowCleanOverloads(sigs);
|
|
fastCleanOverloads(sigs);
|
|
|
|
long t0 = System.nanoTime();
|
|
long sOps = slowCleanOverloads(sigs);
|
|
long slowNs = System.nanoTime() - t0;
|
|
|
|
long t1 = System.nanoTime();
|
|
long fOps = fastCleanOverloads(sigs);
|
|
long fastNs = System.nanoTime() - t1;
|
|
|
|
double ratio = (double) sOps / Math.max(fOps, 1);
|
|
System.out.printf(" %-60s slow:%6dns (%,6d ops) fast:%6dns (%,6d ops) ratio:%.1fx%n",
|
|
label, slowNs, sOps, fastNs, fOps, ratio);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// main
|
|
// -----------------------------------------------------------------------
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("SolcTest — solc-0001: TypeChecker::cleanOverloadedDeclarations O(N²) → O(N)");
|
|
System.out.println();
|
|
|
|
System.out.println(" Benchmark: all-distinct signatures (worst case for find_if)");
|
|
int[] sizes = {50, 100, 200, 500};
|
|
for (int n : sizes) {
|
|
bench(String.format("N=%d distinct overload candidates", n), distinctSignatures(n));
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println(" Benchmark: half-duplicate signatures");
|
|
for (int n : sizes) {
|
|
bench(String.format("N=%d half-dup overload candidates", n), halfDupSignatures(n));
|
|
}
|
|
|
|
System.out.println();
|
|
|
|
// ---- Assertions ----
|
|
int pass = 0;
|
|
int total = 0;
|
|
|
|
// Test 1: N=100 distinct — slow must be ~N*(N-1)/2 comparisons, fast = N
|
|
{
|
|
total++;
|
|
int n = 100;
|
|
List<String> sigs = distinctSignatures(n);
|
|
long sOps = slowCleanOverloads(sigs);
|
|
long fOps = fastCleanOverloads(sigs);
|
|
long expectedSlow = (long) n * (n - 1) / 2; // 4950
|
|
assert sOps == expectedSlow :
|
|
"solc-0001 slow ops mismatch: expected " + expectedSlow + " got " + sOps;
|
|
assert fOps == n :
|
|
"solc-0001 fast ops mismatch: expected " + n + " got " + fOps;
|
|
double ratio = (double) sOps / fOps;
|
|
assert ratio > 10.0 :
|
|
"solc-0001 expected >10x ratio at N=100; got " + ratio;
|
|
System.out.printf(" PASS [1/%d] N=100 distinct: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >10x)%n",
|
|
total, sOps, fOps, ratio);
|
|
pass++;
|
|
}
|
|
|
|
// Test 2: N=200 distinct — ratio should be ~100x
|
|
{
|
|
total++;
|
|
int n = 200;
|
|
List<String> sigs = distinctSignatures(n);
|
|
long sOps = slowCleanOverloads(sigs);
|
|
long fOps = fastCleanOverloads(sigs);
|
|
double ratio = (double) sOps / fOps;
|
|
assert ratio > 50.0 :
|
|
"solc-0001 expected >50x ratio at N=200; got " + ratio;
|
|
System.out.printf(" PASS [2/%d] N=200 distinct: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >50x)%n",
|
|
total, sOps, fOps, ratio);
|
|
pass++;
|
|
}
|
|
|
|
// Test 3: half-dup N=100 — slow still O(N²/4), fast O(N)
|
|
{
|
|
total++;
|
|
int n = 100;
|
|
List<String> sigs = halfDupSignatures(n);
|
|
long sOps = slowCleanOverloads(sigs);
|
|
long fOps = fastCleanOverloads(sigs);
|
|
double ratio = (double) sOps / fOps;
|
|
assert ratio > 10.0 :
|
|
"solc-0001 half-dup expected >10x ratio at N=100; got " + ratio;
|
|
System.out.printf(" PASS [3/%d] N=100 half-dup: slow=%d ops, fast=%d ops, ratio=%.1fx (expected >10x)%n",
|
|
total, sOps, fOps, ratio);
|
|
pass++;
|
|
}
|
|
|
|
// Test 4: output correctness — both methods must agree on unique-count
|
|
{
|
|
total++;
|
|
int n = 100;
|
|
List<String> sigs = halfDupSignatures(n);
|
|
// count uniques via slow
|
|
List<String> slowUniques = new ArrayList<>();
|
|
for (String sig : sigs) {
|
|
if (!slowUniques.contains(sig)) slowUniques.add(sig);
|
|
}
|
|
// count uniques via fast
|
|
Set<String> fastUniques = new LinkedHashSet<>(sigs);
|
|
assert slowUniques.size() == fastUniques.size() :
|
|
"solc-0001 output mismatch: slow=" + slowUniques.size() + " fast=" + fastUniques.size();
|
|
assert slowUniques.size() == n / 2 :
|
|
"solc-0001 expected " + (n / 2) + " unique sigs; got " + slowUniques.size();
|
|
System.out.printf(" PASS [4/%d] Output correctness: both methods yield %d unique sigs from %d candidates%n",
|
|
total, slowUniques.size(), n);
|
|
pass++;
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.printf("%d/%d PASS%n", pass, total);
|
|
System.out.printf("ALL PASS%n");
|
|
System.out.println();
|
|
System.out.printf("solc-0001: TypeChecker::cleanOverloadedDeclarations — vector find_if O(N²) → unordered_set O(N)%n");
|
|
System.out.printf("Hotpath: every identifier resolution with overloaded declarations in Solidity source%n");
|
|
System.out.printf("solc-0002: ContractLevelChecker::findDuplicateDefinitions — CLEAN (CWE-407); set::count O(log N) acceptable for small N%n");
|
|
}
|
|
}
|