Skip to content

Commit 0b2fb15

Browse files
author
server
committed
pain and suffering makes my day
1 parent e51d950 commit 0b2fb15

8 files changed

Lines changed: 141 additions & 344 deletions

File tree

Cloud/VRIG/Dockerfile.distributed

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ ENV TIMEOUT=3500
4646
ENV MIN_MUTATIONS_PER_SAMPLE=25
4747
ENV DEBUG_LOGGING=true
4848

49-
# Default command for distributed fuzzing
50-
# Single master database mode - all workers connect directly to master
51-
CMD exec ./FuzzilliCli --profile=v8 --engine=runtime-multi --resume --corpus=postgresql \
52-
--postgres-url="${POSTGRES_URL}" \
53-
--timeout="${TIMEOUT}" \
54-
--minMutationsPerSample="${MIN_MUTATIONS_PER_SAMPLE}" \
55-
--logLevel=verbose \
56-
--postgres-logging ./fuzzbuild/d8
49+
# Environment variable for extra FuzzilliCli args (e.g. --disablePostgresSync)
50+
ENV FUZZILLI_EXTRA_ARGS=""
51+
52+
COPY Cloud/VRIG/entrypoint-distributed.sh /home/app/entrypoint.sh
53+
RUN chmod +x /home/app/entrypoint.sh
54+
ENTRYPOINT ["/home/app/entrypoint.sh"]

Scripts/extract-crashes.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def extract_all_crashes(self,
112112
save_programs: bool = False) -> List[Dict[str, Any]]:
113113
"""Extract all crash executions."""
114114
# Build query dynamically
115-
program_field = ", f.program_base64" if (include_program or save_programs) else ""
115+
program_field = ", p.program_base64" if (include_program or save_programs) else ""
116116
where_clause = "WHERE eo.outcome = 'Crashed'"
117117
if fuzzer_id is not None:
118118
where_clause += f" AND p.fuzzer_id = {fuzzer_id}"
@@ -138,7 +138,6 @@ def extract_all_crashes(self,
138138
FROM execution e
139139
JOIN program p ON e.program_hash = p.program_hash
140140
JOIN execution_outcome eo ON e.execution_outcome_id = eo.id
141-
LEFT JOIN fuzzer f ON e.program_hash = f.program_hash
142141
{where_clause}
143142
ORDER BY e.created_at DESC
144143
LIMIT {limit}
@@ -159,7 +158,7 @@ def extract_unique_crashes(self,
159158
if fuzzer_id is not None:
160159
where_clause = f"WHERE ca.fuzzer_id = {fuzzer_id}"
161160

162-
program_field = ", f.program_base64" if (include_program or save_programs) else ""
161+
program_field = ", p.program_base64" if (include_program or save_programs) else ""
163162

164163
query = f"""
165164
SELECT
@@ -172,7 +171,7 @@ def extract_unique_crashes(self,
172171
ca.found_new_edges
173172
{program_field}
174173
FROM crash_analysis ca
175-
LEFT JOIN fuzzer f ON ca.program_hash = f.program_hash
174+
LEFT JOIN program p ON ca.program_hash = p.program_hash
176175
{where_clause}
177176
ORDER BY ca.crash_count DESC, ca.first_crash DESC
178177
LIMIT {limit}
@@ -209,7 +208,7 @@ def decode_program(self, program_hash: str) -> Optional[str]:
209208
"""Decode a program from base64 to JavaScript code."""
210209
query = """
211210
SELECT program_base64
212-
FROM fuzzer
211+
FROM program
213212
WHERE program_hash = %s
214213
"""
215214

Scripts/start-distributed.sh

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# - X fuzzer worker containers
1111
#
1212
# Environment variables:
13-
# - V8_BUILD_PATH: Path to V8 build directory on host (default: /home/tropic/vrig/fuzzilli-vrig-proj/fuzzbuild)
13+
# - V8_BUILD_PATH: Path to V8 build directory on host (default: /mnt/vdc/v8_vrig/v8/out/fuzzbuild)
1414
# - POSTGRES_HOST: Remote PostgreSQL host/IP (if set, enables remote mode, skips local postgres)
1515
# - POSTGRES_PORT: PostgreSQL port (default: 5432)
1616
# - POSTGRES_DB: Database name (default: fuzzilli_master)
@@ -22,6 +22,7 @@
2222
# - TIMEOUT: Execution timeout in ms (default: 2500)
2323
# - MIN_MUTATIONS_PER_SAMPLE: Minimum mutations per sample (default: 25)
2424
# - DEBUG_LOGGING: Enable debug logging (default: false)
25+
# - FUZZILLI_EXTRA_ARGS: Extra FuzzilliCli args (e.g. --disablePostgresSync)
2526

2627
set -e
2728

@@ -44,7 +45,7 @@ if [ $# -eq 0 ]; then
4445
echo " Creates: 8 fuzzer workers connecting to remote postgres at 192.168.1.100"
4546
echo ""
4647
echo "Environment variables:"
47-
echo " V8_BUILD_PATH - Path to V8 build on host (default: /home/tropic/vrig/fuzzilli-vrig-proj/fuzzbuild)"
48+
echo " V8_BUILD_PATH - Path to V8 build on host (default: /mnt/vdc/v8_vrig/v8/out/fuzzbuild)"
4849
echo " POSTGRES_HOST - Remote PostgreSQL host/IP (if set, enables remote mode)"
4950
echo " POSTGRES_PORT - PostgreSQL port (default: 5432)"
5051
echo " POSTGRES_DB - Database name (default: fuzzilli_master)"
@@ -102,10 +103,11 @@ POSTGRES_DB=${POSTGRES_DB:-fuzzilli_master}
102103
POSTGRES_USER=${POSTGRES_USER:-fuzzilli}
103104
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-fuzzilli123}
104105
POSTGRES_DATA_PATH=${POSTGRES_DATA_PATH:-}
105-
V8_BUILD_PATH=${V8_BUILD_PATH:-/home/tropic/vrig/fuzzilli-vrig-proj/fuzzbuild}
106+
V8_BUILD_PATH=${V8_BUILD_PATH:-/mnt/vdc/v8_vrig/v8/out/fuzzbuild}
106107
TIMEOUT=${TIMEOUT:-2500}
107108
MIN_MUTATIONS_PER_SAMPLE=${MIN_MUTATIONS_PER_SAMPLE:-25}
108109
DEBUG_LOGGING=${DEBUG_LOGGING:-false}
110+
FUZZILLI_EXTRA_ARGS=${FUZZILLI_EXTRA_ARGS:-}
109111

110112
# Determine if we're using remote or local postgres
111113
USE_REMOTE_DB=false
@@ -171,6 +173,7 @@ for i in $(seq 1 $NUM_WORKERS); do
171173
- TIMEOUT=${TIMEOUT}
172174
- MIN_MUTATIONS_PER_SAMPLE=${MIN_MUTATIONS_PER_SAMPLE}
173175
- DEBUG_LOGGING=${DEBUG_LOGGING}
176+
- FUZZILLI_EXTRA_ARGS=${FUZZILLI_EXTRA_ARGS}
174177
EOF
175178

176179
# Only add depends_on for local postgres mode

Sources/Agentic_System/IkaCore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit 7c95ff00bb28d5dfe34b358e1944ecb59e73e26f
1+
Subproject commit ec4aa7827e640bfeed48f90ba1d864f0bf3e0f12

Sources/Fuzzilli/Database/PostgresSQLStorage.swift

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ import PostgresKit
88
/// and metadata from PostgreSQL database. It handles the actual database operations
99
/// that the PostgreSQLCorpus uses for persistence and synchronization.
1010
public actor PostgresSQLStorage {
11+
public struct ProgramSyncRecord {
12+
public let program: Program
13+
public let hash: String
14+
public let insertedAt: Date
15+
16+
public init(program: Program, hash: String, insertedAt: Date) {
17+
self.program = program
18+
self.hash = hash
19+
self.insertedAt = insertedAt
20+
}
21+
}
1122

1223
private let databasePool: DatabasePool
1324
private let logger: Logging.Logger
@@ -139,6 +150,27 @@ public actor PostgresSQLStorage {
139150
self.enableLogging = enableLogging
140151
self.logger = Logging.Logger(label: "PostgresSQLStorage")
141152
}
153+
154+
public func fetchLatestProgramSyncCursor() async throws -> (insertedAt: Date, hash: String)? {
155+
return try await databasePool.withConnection { connection in
156+
let query = PostgresQuery(stringLiteral: """
157+
SELECT inserted_at, program_hash
158+
FROM program
159+
ORDER BY inserted_at DESC, program_hash DESC
160+
LIMIT 1
161+
""")
162+
163+
let result = try await connection.query(query, logger: self.logger)
164+
let rows = try await result.collect()
165+
166+
guard let row = rows.first else {
167+
return nil
168+
}
169+
170+
let (insertedAt, hash) = try row.decode((Date, String).self, context: .default)
171+
return (insertedAt, hash)
172+
}
173+
}
142174

143175
/// Register a new fuzzer instance in the database or register worker with an existing fuzzer
144176
///
@@ -769,22 +801,24 @@ public actor PostgresSQLStorage {
769801
/// - Parameters:
770802
/// - since: The timestamp to fetch programs from
771803
/// - limit: Maximum number of programs to fetch
772-
public func fetchNewPrograms(since: Date, limit: Int = 100) async throws -> [(Program, String)] {
804+
public func fetchNewPrograms(since insertedAt: Date, after hash: String, limit: Int = 100) async throws -> [ProgramSyncRecord] {
773805
if self.enableLogging {
774-
self.logger.info("Fetching new programs since: \(since)")
806+
self.logger.info("Fetching new programs after cursor: \(insertedAt) / \(hash)")
775807
}
776808

777809
return try await databasePool.withConnection { connection in
778810
// Format the date for PostgreSQL
779811
let dateFormatter = ISO8601DateFormatter()
780812
dateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
781-
let sinceString = dateFormatter.string(from: since)
813+
let sinceString = dateFormatter.string(from: insertedAt)
814+
let escapedHash = hash.replacingOccurrences(of: "'", with: "''")
782815

783816
let query = PostgresQuery(stringLiteral: """
784-
SELECT program_hash, program_base64
817+
SELECT program_hash, program_base64, inserted_at
785818
FROM program
786819
WHERE inserted_at > '\(sinceString)'
787-
ORDER BY inserted_at ASC
820+
OR (inserted_at = '\(sinceString)' AND program_hash > '\(escapedHash)')
821+
ORDER BY inserted_at ASC, program_hash ASC
788822
LIMIT \(limit)
789823
""")
790824

@@ -799,14 +833,14 @@ public actor PostgresSQLStorage {
799833
self.logger.info("Fetch query returned \(rows.count) rows")
800834
}
801835

802-
var programs: [(Program, String)] = []
836+
var programs: [ProgramSyncRecord] = []
803837

804838
for row in rows {
805839
do {
806-
let (hash, data) = try row.decode((String, String).self, context: .default)
840+
let (hash, data, rowInsertedAt) = try row.decode((String, String, Date).self, context: .default)
807841

808842
if let program = try? DatabaseUtils.decodeProgramFromBase64(base64: data) {
809-
programs.append((program, hash))
843+
programs.append(ProgramSyncRecord(program: program, hash: hash, insertedAt: rowInsertedAt))
810844
} else if self.enableLogging {
811845
self.logger.warning("Failed to decode program for hash: \(hash)")
812846
}
@@ -828,6 +862,18 @@ public actor PostgresSQLStorage {
828862
public func refreshMaterializedViews() async throws {
829863
try await databasePool.withConnection { connection in
830864
do {
865+
let lockQuery = PostgresQuery(stringLiteral: "SELECT pg_try_advisory_lock(937451)")
866+
let lockResult = try await connection.query(lockQuery, logger: self.logger)
867+
let lockRows = try await lockResult.collect()
868+
let shouldRefresh = try lockRows.first?.decode(Bool.self, context: .default) ?? false
869+
870+
guard shouldRefresh else {
871+
if self.enableLogging {
872+
self.logger.info("Skipping materialized view refresh because another worker holds the refresh lock")
873+
}
874+
return
875+
}
876+
831877
let startTime = Date()
832878

833879
// Call the PostgreSQL function that refreshes all materialized views
@@ -846,7 +892,12 @@ public actor PostgresSQLStorage {
846892
}
847893
}
848894
}
895+
896+
let unlockQuery = PostgresQuery(stringLiteral: "SELECT pg_advisory_unlock(937451)")
897+
_ = try await connection.query(unlockQuery, logger: self.logger)
849898
} catch {
899+
let unlockQuery = PostgresQuery(stringLiteral: "SELECT pg_advisory_unlock(937451)")
900+
_ = try? await connection.query(unlockQuery, logger: self.logger)
850901
if self.enableLogging {
851902
self.logger.error("Failed to refresh materialized views: \(String(reflecting: error))")
852903
}
@@ -874,4 +925,4 @@ public actor PostgresSQLStorage {
874925
}
875926
}
876927
}
877-
}
928+
}

Sources/Fuzzilli/Modules/PostgreSQLSync.swift

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ public class PostgreSQLSync: Module {
44
private let storage: PostgresSQLStorage
55
private let fuzzerInstanceId: String
66
private let enableLogging: Bool
7-
private var lastSyncTime: Date
7+
private var lastSyncCursor: (insertedAt: Date, hash: String)
88

99
private let logger = Logger(withLabel: "PostgreSQLSync")
1010

@@ -23,7 +23,7 @@ public class PostgreSQLSync: Module {
2323
self.storage = storage
2424
self.fuzzerInstanceId = fuzzerInstanceId
2525
self.enableLogging = enableLogging
26-
self.lastSyncTime = Date()
26+
self.lastSyncCursor = (insertedAt: Date(timeIntervalSince1970: 0), hash: "")
2727
}
2828

2929
public func initialize(with fuzzer: Fuzzer) {
@@ -67,10 +67,16 @@ public class PostgreSQLSync: Module {
6767
logger.info("Syncing \(programs.count) programs from database to corpus")
6868
}
6969

70+
if let latestCursor = try await storage.fetchLatestProgramSyncCursor() {
71+
self.lastSyncCursor = latestCursor
72+
}
73+
7074
// Import each program into the fuzzer's corpus
7175
for program in programs {
7276
fuzzer.async {
73-
fuzzer.importProgram(program, origin: .corpusImport(mode: .full), enableDropout: false)
77+
// changing from full to interestingOnly(shouldMinimize: true)
78+
// watch results for this and change back to .full if needed
79+
fuzzer.importProgram(program, origin: .corpusImport(mode: .interestingOnly(shouldMinimize: true)), enableDropout: false)
7480
}
7581
}
7682

@@ -142,6 +148,10 @@ public class PostgreSQLSync: Module {
142148

143149
// Clean up the cache entry immediately after use
144150
self.executionCache.removeValue(forKey: programId)
151+
152+
guard !ev.origin.isFromCorpusImport() else {
153+
return
154+
}
145155

146156
Task {
147157
guard let fuzzerId = self.cachedFuzzerId else {
@@ -247,6 +257,10 @@ public class PostgreSQLSync: Module {
247257

248258
// Clean up the cache entry immediately after use
249259
self.executionCache.removeValue(forKey: programId)
260+
261+
guard !ev.origin.isFromCorpusImport() else {
262+
return
263+
}
250264

251265
Task {
252266
guard let fuzzerId = self.cachedFuzzerId else {
@@ -400,22 +414,30 @@ public class PostgreSQLSync: Module {
400414

401415
private func syncWithDatabase(_ fuzzer: Fuzzer) async {
402416
if enableLogging {
403-
logger.info("Starting periodic sync with database. Last sync time: \(lastSyncTime)")
417+
logger.info("Starting periodic sync with database. Last sync cursor: \(lastSyncCursor.insertedAt) / \(lastSyncCursor.hash)")
404418
}
405419
do {
406-
let newPrograms = try await storage.fetchNewPrograms(since: lastSyncTime, limit: 2000)
407-
if !newPrograms.isEmpty {
420+
while true {
421+
let newPrograms = try await storage.fetchNewPrograms(since: lastSyncCursor.insertedAt, after: lastSyncCursor.hash, limit: 2000)
422+
guard !newPrograms.isEmpty else { return }
423+
408424
if enableLogging {
409425
logger.info("Fetched \(newPrograms.count) new programs from database")
410426
}
411-
412-
lastSyncTime = Date()
413-
414-
for (program, _) in newPrograms {
427+
428+
if let newestRecord = newPrograms.last {
429+
lastSyncCursor = (insertedAt: newestRecord.insertedAt, hash: newestRecord.hash)
430+
}
431+
432+
for record in newPrograms {
415433
fuzzer.async {
416-
fuzzer.importProgram(program, origin: .corpusImport(mode: .full), enableDropout: false)
434+
fuzzer.importProgram(record.program, origin: .corpusImport(mode: .full), enableDropout: false)
417435
}
418436
}
437+
438+
if newPrograms.count < 2000 {
439+
return
440+
}
419441
}
420442
} catch {
421443
logger.error("Failed to sync with database: \(String(reflecting: error))")

Sources/FuzzilliCli/main.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ Options:
102102
--wasm : Enable Wasm CodeGenerators (see WasmCodeGenerators.swift).
103103
--forDifferentialFuzzing : Enable additional features for better support of external differential fuzzing.
104104
--postgres-url=url : PostgreSQL connection string for PostgreSQL corpus (e.g., postgresql://user:pass@host:port/db).
105+
--disablePostgresSync : Disable PostgreSQL synchronization even when --corpus=postgresql is selected.
105106
--validate-before-cache : Enable program validation before caching in PostgreSQL corpus (default: true).
106107
--execution-history-size=n : Number of recent executions to keep in memory for PostgreSQL corpus (default: 10).
107108
--postgres-logging : Enable PostgreSQL database operation logging.
@@ -164,6 +165,7 @@ let enableWasm = args.has("--wasm")
164165
let forDifferentialFuzzing = args.has("--forDifferentialFuzzing")
165166
let postgresUrl = args["--postgres-url"] ?? ProcessInfo.processInfo.environment["POSTGRES_URL"]
166167
let postgresLogging = args.has("--postgres-logging")
168+
let disablePostgresSync = args.has("--disablePostgresSync")
167169

168170
var timeout : Timeout
169171
if let raw_timeout = args.string(for: "--timeout") {
@@ -244,7 +246,7 @@ if corpusName == "markov" && staticCorpus {
244246

245247
// PostgreSQL corpus validation
246248
if corpusName == "postgresql" {
247-
if postgresUrl == nil {
249+
if !disablePostgresSync && postgresUrl == nil {
248250
configError("PostgreSQL corpus requires --postgres-url (or POSTGRES_URL environment variable)")
249251
}
250252
}
@@ -664,8 +666,8 @@ fuzzer.sync {
664666
fuzzer.addModule(ThreadParent(for: fuzzer))
665667
}
666668

667-
// Add PostgreSQL sync module if using postgresql corpus
668-
if corpusName == "postgresql" {
669+
// Add PostgreSQL sync module if using postgresql corpus and sync is enabled.
670+
if corpusName == "postgresql" && !disablePostgresSync {
669671
guard let url = postgresUrl else {
670672
logger.fatal("PostgreSQL URL is required for PostgreSQL corpus")
671673
}
@@ -694,6 +696,8 @@ fuzzer.sync {
694696
} catch {
695697
logger.fatal("Failed to initialize PostgreSQL connection: \(error)")
696698
}
699+
} else if corpusName == "postgresql" {
700+
logger.info("PostgreSQL synchronization disabled via --disablePostgresSync; using local BasicCorpus only")
697701
}
698702

699703
// Check for potential misconfiguration.

0 commit comments

Comments
 (0)