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

Grammar mutator for tint #26

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/tint/fuzzers/tint_structure_fuzzer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

add_executable(tint_structure_fuzzer
tint_structure_fuzzer.cc
syntax.cc
syntax.h
probabilities.h
../mersenne_twister_engine.cc
../mersenne_twister_engine.h
../random_generator.cc
../random_generator.h
../tint_common_fuzzer.cc
../tint_common_fuzzer.h
../random_generator_engine.cc
../random_generator_engine.h
)

if (NOT WIN32)
set_source_files_properties(syntax.cc PROPERTIES COMPILE_FLAGS -O1)
endif()

tint_fuzzer_compile_options(tint_structure_fuzzer)
tint_spvtools_compile_options(tint_structure_fuzzer)
target_include_directories(tint_structure_fuzzer PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(tint_structure_fuzzer PRIVATE tint_lang_hlsl_writer_helpers)
target_link_libraries(tint_structure_fuzzer PRIVATE tint_lang_msl_writer_helpers)
target_link_libraries(tint_structure_fuzzer PRIVATE tint_lang_spirv_writer_helpers)

tint_core_compile_options(tint_structure_fuzzer)

if (TINT_STRUCTURE_FUZZER_SANITIZERS)
target_compile_options(tint_structure_fuzzer PRIVATE -fsanitize=address -fsanitize=undefined)
target_link_options(tint_structure_fuzzer PRIVATE -fsanitize=address -fsanitize=undefined)
endif ()
4 changes: 4 additions & 0 deletions src/tint/fuzzers/tint_structure_fuzzer/blocklist.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
src:tint_structure_fuzzer/syntax.cc
src:tint_structure_fuzzer/syntax.h
src:tint_structure_fuzzer/tint_structure_fuzzer.cc
src:tint_structure_fuzzer/probabilities.h
34 changes: 34 additions & 0 deletions src/tint/fuzzers/tint_structure_fuzzer/optimize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import subprocess
import re
import random
import numpy as np
from scipy.optimize import differential_evolution

np.set_printoptions(precision=0, suppress=True, formatter={'float_kind': lambda x: str(int(x))})

def print_stats(xk, convergence):
print(f"Current best solution: {xk} convergence measure: {convergence:.6f}")

def fn(args):
args = [
"./tint_structure_fuzzer",
"--prob="+",".join(str(int(x)) for x in args),
"-cross_over=0",
"-mutate_depth=1",
"-max_total_time=30",
"-print_funcs=0",
"-fsanitize-coverage-ignorelist=./blocklist.txt"
]
print(f"Calling {args}...")
result = subprocess.run(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
out = str(result.stderr)
matches = re.findall(r"cov: (\d+)", out)
if not matches:
raise ValueError("Coverage result not found")
result = int(matches[-1])
print(f"Coverage = {result}")
return -result

print("tint_structure_fuzzer optimizer")
val = differential_evolution(fn, bounds=[(0, 1000)] * 8, maxiter=100, popsize=10, callback=print_stats)
print(f"Best solution is {val.x} -> {-val.fun}")
43 changes: 43 additions & 0 deletions src/tint/fuzzers/tint_structure_fuzzer/probabilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#ifndef SRC_TINT_FUZZERS_TINT_STRUCTURE_FUZZER_PROBABILITIES_H_
#define SRC_TINT_FUZZERS_TINT_STRUCTURE_FUZZER_PROBABILITIES_H_

#include <cassert>
#include <vector>
#include "src/tint/fuzzers/random_generator.h"

namespace tint::fuzzers::structure_fuzzer {

struct Probabilities {
Probabilities(std::vector<unsigned> values_) : values(std::move(values_)) {
unsigned sum = 0;
for (unsigned& v : values) {
unsigned s = v;
v = sum;
assert(sum + s > sum);
sum += s;
}
values.push_back(sum);
}

size_t size() const { return values.size() - 1; }

unsigned sum() const { return values.back(); }

template <typename T>
T sample(RandomGenerator& gen) const {
return static_cast<T>(sample(gen));
}

unsigned sample(RandomGenerator& gen) const {
unsigned v = gen.GetUInt32(sum());
auto it = std::upper_bound(values.begin(), values.end(), v);
assert(it != values.begin());
--it;
return static_cast<unsigned>(std::distance(values.begin(), it));
}
std::vector<unsigned> values;
};

} // namespace tint::fuzzers::structure_fuzzer

#endif
129 changes: 129 additions & 0 deletions src/tint/fuzzers/tint_structure_fuzzer/pso.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import subprocess
import re
import random
import numpy as np
import argparse

class Particle:
def __init__(self, bounds):
self.position = np.array([random.uniform(low, high) for low, high in bounds])
self.position[self.position == 0] = random.uniform(0.01, 1)
self.velocity = np.array([random.uniform(-1, 1) for _ in bounds])
self.best_position = self.position.copy()
self.best_score = float('inf')

class PSO:
def __init__(self, objective_func, bounds, num_particles=10, max_iter=100):
self.objective_func = objective_func
self.bounds = bounds
self.num_particles = num_particles
self.max_iter = max_iter

# PSO parameters
self.w = 0.7
self.c1 = 2.0
self.c2 = 2.0
self.particles = [Particle(bounds) for _ in range(num_particles)]
self.global_best_position = None
self.global_best_score = float('inf')

def optimize(self, callback=None):
best_coverage_per_iteration = []

for iteration in range(self.max_iter):
iteration_best_score = float('inf')

for particle in self.particles:
score = self.objective_func(particle.position)

# Update personal best
if score < particle.best_score:
particle.best_score = score
particle.best_position = particle.position.copy()

if score < self.global_best_score:
self.global_best_score = score
self.global_best_position = particle.position.copy()

iteration_best_score = -self.global_best_score # Since we're maximizing the coverage
best_coverage_per_iteration.append(iteration_best_score)

for particle in self.particles:
r1, r2 = random.random(), random.random()

cognitive = self.c1 * r1 * (particle.best_position - particle.position)
social = self.c2 * r2 * (self.global_best_position - particle.position)
particle.velocity = (self.w * particle.velocity + cognitive + social)

particle.position = particle.position + particle.velocity

particle.position = np.clip(particle.position,
[b[0] for b in self.bounds],
[b[1] for b in self.bounds])

particle.position[particle.position == 0] = random.uniform(0.01, 1)

if callback:
callback(self.global_best_position, iteration)

print(f"Iteration {iteration}: Best coverage = {iteration_best_score}")

return self.global_best_position, self.global_best_score

def print_stats(xk, iteration):
print(f"Iteration {iteration}: Current best solution: {xk}")

def fn(args, max_time):
args = np.round(args).astype(int)
args[args == 0] = random.randint(1, 1000) # Replace zeros with a random value

cmd_args = [
"./tint_structure_fuzzer",
"--prob=" + ",".join(str(x) for x in args),
"-cross_over=0",
"-mutate_depth=1",
f"-max_total_time={max_time}",
"-print_funcs=0",
"-fsanitize-coverage-ignorelist=./blocklist.txt",
"-jobs=1"
]
print(f"Calling {cmd_args}...")

result = subprocess.run(cmd_args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
out = str(result.stderr)
matches = re.findall(r"cov: (\d+)", out)

if not matches:
raise ValueError("Coverage result not found")

result = int(matches[-1])
print(f"Coverage = {result}")
return -result

def main():
parser = argparse.ArgumentParser(description='Tint Structure Fuzzer Optimizer using PSO')
parser.add_argument('--max_time', type=int, default=120,
help='Maximum total time for each fuzzing run (default: 120)')
args = parser.parse_args()

print("tint_structure_fuzzer optimizer (PSO)")

# Define bounds for 8 parameters
bounds = [(0, 1000)] * 8

# Create PSO optimizer
pso = PSO(
objective_func=lambda x: fn(x, args.max_time),
bounds=bounds,
num_particles=10,
max_iter=100
)

best_position, best_score = pso.optimize(callback=print_stats)

print(f"Best solution is {best_position} -> {-best_score}")

if __name__ == "__main__":
main()


Loading