///|
pub enum InvalidFuzzSourceKind {
AstInvalidSource
BinaryInvalidSource
TextInvalidSource
SpecSeedInvalidSource
} derive(Eq, Debug)
///|
pub fn invalid_fuzz_source_kind_name(kind : InvalidFuzzSourceKind) -> String {
match kind {
AstInvalidSource => "ast"
BinaryInvalidSource => "binary"
TextInvalidSource => "text"
SpecSeedInvalidSource => "spec-seed"
}
}
///|
pub fn invalid_fuzz_ast_source_kind() -> InvalidFuzzSourceKind {
AstInvalidSource
}
///|
pub fn invalid_fuzz_binary_source_kind() -> InvalidFuzzSourceKind {
BinaryInvalidSource
}
///|
pub fn invalid_fuzz_text_source_kind() -> InvalidFuzzSourceKind {
TextInvalidSource
}
///|
pub fn invalid_fuzz_spec_seed_source_kind() -> InvalidFuzzSourceKind {
SpecSeedInvalidSource
}
///|
pub fn build_fuz1026_two_fault_ast_stress_report(
seed : UInt64,
attempt : Int32,
) -> InvalidFuzzFailureReport {
InvalidFuzzFailureReport::new(
"validate-invalid-ast-multifault",
"stress",
seed,
attempt,
"two-fault-ast-invalid-stress",
invalid_fuzz_ast_source_kind(),
"validate-rejected",
actual_stage="validate-rejected",
actual_diagnostic_family=Some("function-body"),
source_feature_facts=["multi_fault:true"],
multi_fault_mutation_ids=[
"invalid-function-body-local-set-value-type", "invalid-function-body-branch-payload-type",
],
multi_fault_broad_outcomes=["validate-rejected"],
artifacts=[
InvalidFuzzArtifact::new(
"stress-summary",
"two-fault-ast-invalid-stress.txt",
invalid_fuzz_string_bytes(
"multi_fault=true\nmutation=invalid-function-body-local-set-value-type\nmutation=invalid-function-body-branch-payload-type\nbroad_outcome=validate-rejected\n",
),
),
],
)
}
///|
pub fn build_fuz1026_binary_text_multifault_stress_report(
seed : UInt64,
attempt : Int32,
) -> InvalidFuzzFailureReport {
InvalidFuzzFailureReport::new(
"validate-invalid-binary-text-multifault",
"stress",
seed,
attempt,
"binary-text-multifault-invalid-stress",
invalid_fuzz_binary_source_kind(),
"decode-or-parse-rejected",
actual_stage="decode-or-parse-rejected",
source_feature_facts=["multi_fault:true"],
multi_fault_mutation_ids=[
"binary:trailing-garbage", "text:malformed-trailing-garbage",
],
multi_fault_broad_outcomes=["decode-or-parse-rejected"],
artifacts=[
InvalidFuzzArtifact::new(
"stress-summary",
"binary-text-multifault-invalid-stress.txt",
invalid_fuzz_string_bytes(
"multi_fault=true\nmutation=binary:trailing-garbage\nmutation=text:malformed-trailing-garbage\nbroad_outcome=decode-or-parse-rejected\n",
),
),
],
)
}
///|
fn fuz1055b4_unlinkable_metadata(
seed : UInt64,
) -> (String, String, String, String, String) {
match (seed % 10UL).to_int() {
0 =>
(
"unknown import", "missing_f", "func()", "missing export", "func(result i32)",
)
1 =>
(
"unknown import", "missing_mem", "memory(1)", "missing export", "memory(1)",
)
2 =>
(
"unknown import", "missing_tab", "table(1 funcref)", "missing export", "table(1 funcref)",
)
3 =>
(
"unknown import", "missing_g", "global(i32)", "missing export", "global(i32)",
)
4 =>
(
"unknown import", "missing_tag", "tag(param i32)", "missing export", "tag(param i32)",
)
5 =>
(
"incompatible import type", "f", "func(param i32)", "func(result i32)", "func(result i32)",
)
6 =>
(
"incompatible import type", "mem", "memory(2 5)", "memory(1)", "memory(1)",
)
7 =>
(
"incompatible import type", "tab", "table(11 funcref)", "table(1 funcref)",
"table(1 funcref)",
)
8 =>
(
"incompatible import type", "g", "global(f64)", "global(i32)", "global(i32)",
)
_ =>
(
"incompatible import type", "tag", "tag(param f32)", "tag(param i32)", "tag(param i32)",
)
}
}
///|
pub fn build_fuz1055b4_unlinkable_failure_report(
seed : UInt64,
attempt : Int32,
) -> InvalidFuzzFailureReport {
let case = gen_unlinkable_multi_module_wast_case(seed)
let (
reason,
import_name,
expected_import_type,
provider_export_type,
feature_type,
) = fuz1055b4_unlinkable_metadata(seed)
InvalidFuzzFailureReport::new(
"valid-multi-module-linking",
"smoke",
seed,
attempt,
"unlinkable-multi-module",
invalid_fuzz_text_source_kind(),
"valid-before-link",
actual_stage="valid-before-link",
source_path=Some(case.name + ".wast"),
valid_before_link=Some(true),
unlinkable_reason=Some(reason),
registered_module_name=Some("provider"),
import_module_name=Some("provider"),
import_name=Some(import_name),
expected_import_type=Some(expected_import_type),
provider_export_type=Some(provider_export_type),
pre_link_validation_status=Some("valid-before-link"),
source_feature_facts=case.feature_facts +
["unlinkable-type:" + feature_type],
artifacts=[
InvalidFuzzArtifact::new(
"unlinkable-wast",
case.name + ".wast",
invalid_fuzz_string_bytes(case.text),
),
],
)
}
///|
pub enum FuzzReductionFailureKind {
ValidationFailure
ParseFailure
PassMismatch
Crash
Timeout
PropertyFailure
} derive(Eq, Debug)
///|
pub fn fuzz_reduction_validation_failure_kind() -> FuzzReductionFailureKind {
ValidationFailure
}
///|
pub fn fuzz_reduction_parse_failure_kind() -> FuzzReductionFailureKind {
ParseFailure
}
///|
pub fn fuzz_reduction_pass_mismatch_kind() -> FuzzReductionFailureKind {
PassMismatch
}
///|
pub fn fuzz_reduction_crash_kind() -> FuzzReductionFailureKind {
Crash
}
///|
pub fn fuzz_reduction_timeout_kind() -> FuzzReductionFailureKind {
Timeout
}
///|
pub fn fuzz_reduction_property_failure_kind() -> FuzzReductionFailureKind {
PropertyFailure
}
///|
pub fn fuzz_reduction_failure_kind_name(
kind : FuzzReductionFailureKind,
) -> String {
match kind {
ValidationFailure => "validation-failure"
ParseFailure => "parse-failure"
PassMismatch => "pass-mismatch"
Crash => "crash"
Timeout => "timeout"
PropertyFailure => "property-failure"
}
}
///|
pub struct FuzzReductionPredicate {
kind : FuzzReductionFailureKind
expected_stage : String
expected_family : String?
property : String?
} derive(Eq, Debug)
///|
pub fn FuzzReductionPredicate::new(
kind : FuzzReductionFailureKind,
expected_stage : String,
expected_family~ : String? = None,
property~ : String? = None,
) -> FuzzReductionPredicate {
{ kind, expected_stage, expected_family, property, }
}
///|
pub struct FuzzReductionReplayResult {
stage : String
family : String?
property : String?
message : String?
} derive(Eq, Debug)
///|
pub fn FuzzReductionReplayResult::new(
stage : String,
family~ : String? = None,
property~ : String? = None,
message~ : String? = None,
) -> FuzzReductionReplayResult {
{ stage, family, property, message, }
}
///|
pub fn fuzz_reduction_predicate_matches(
predicate : FuzzReductionPredicate,
replay : FuzzReductionReplayResult,
) -> Bool {
if replay.stage != predicate.expected_stage {
return false
}
match predicate.expected_family {
Some(expected) => if replay.family != Some(expected) { return false }
None => ()
}
match predicate.property {
Some(expected) => if replay.property != Some(expected) { return false }
None => ()
}
true
}
///|
pub fn invalid_fuzz_replay_result_as_reduction_result(
replay : InvalidFuzzReplayResult,
) -> FuzzReductionReplayResult {
FuzzReductionReplayResult::new(
replay.stage,
family=replay.diagnostic_family,
message=replay.message,
)
}
///|
pub fn fuzz_reduction_predicate_from_invalid_report(
report : InvalidFuzzFailureReport,
) -> FuzzReductionPredicate {
let kind = match report.actual_stage {
"parse-or-lower-rejected" | "decode-rejected" =>
FuzzReductionFailureKind::ParseFailure
"valid-before-link" => FuzzReductionFailureKind::PropertyFailure
_ => FuzzReductionFailureKind::ValidationFailure
}
FuzzReductionPredicate::new(
kind,
report.actual_stage,
expected_family=report.actual_diagnostic_family,
)
}
///|
pub fn invalid_fuzz_suite_name_for_source_kind(
kind : InvalidFuzzSourceKind,
) -> String {
match kind {
AstInvalidSource => "validate-invalid-ast"
BinaryInvalidSource => "validate-invalid-binary"
TextInvalidSource => "validate-invalid-text"
SpecSeedInvalidSource => "validate-invalid-spec-seed"
}
}
///|
fn invalid_fuzz_source_kind_from_name(name : String) -> InvalidFuzzSourceKind? {
match name {
"ast" => Some(AstInvalidSource)
"validate-invalid-ast" => Some(AstInvalidSource)
"binary" => Some(BinaryInvalidSource)
"validate-invalid-binary" => Some(BinaryInvalidSource)
"text" => Some(TextInvalidSource)
"validate-invalid-text" => Some(TextInvalidSource)
"spec-seed" => Some(SpecSeedInvalidSource)
"validate-invalid-spec-seed" => Some(SpecSeedInvalidSource)
_ => None
}
}
///|
pub struct InvalidFuzzArtifact {
label : String
file_name : String
bytes : Bytes
} derive(Eq, Debug)
///|
pub fn InvalidFuzzArtifact::new(
label : String,
file_name : String,
bytes : Bytes,
) -> InvalidFuzzArtifact {
{ label, file_name, bytes, }
}
///|
pub struct InvalidFuzzReductionStep {
kind : String
start : Int32
len : Int32
before_size : Int32
after_size : Int32
} derive(Eq, Debug)
///|
pub fn InvalidFuzzReductionStep::new(
kind : String,
start : Int32,
len : Int32,
before_size : Int32,
after_size : Int32,
) -> InvalidFuzzReductionStep {
{ kind, start, len, before_size, after_size, }
}
///|
pub struct InvalidFuzzFailureReport {
suite : String
profile : String
seed : UInt64
attempt : Int32
strategy_id : String
source_kind : InvalidFuzzSourceKind
expected_stage : String
expected_diagnostic_family : String?
actual_stage : String
actual_diagnostic_family : String?
actual_diagnostic_location : String?
timeout_budget_ms : Int32?
seed_profile : String?
seed_mode : String?
seed_require_strategy_prereqs : Bool?
stream_label : String?
stream_seed : UInt64?
attempt_seed : UInt64?
source_path : String?
valid_before_link : Bool?
unlinkable_reason : String?
registered_module_name : String?
import_module_name : String?
import_name : String?
expected_import_type : String?
provider_export_type : String?
pre_link_validation_status : String?
source_feature_facts : Array[String]
multi_fault_mutation_ids : Array[String]
multi_fault_broad_outcomes : Array[String]
artifacts : Array[InvalidFuzzArtifact]
reduced_artifacts : Array[InvalidFuzzArtifact]
reduction_original_size : Int32?
reduction_final_size : Int32?
reduction_predicate_evaluations : Int32?
reduction_steps : Array[InvalidFuzzReductionStep]
} derive(Eq, Debug)
///|
pub fn InvalidFuzzFailureReport::new(
suite : String,
profile : String,
seed : UInt64,
attempt : Int32,
strategy_id : String,
source_kind : InvalidFuzzSourceKind,
expected_stage : String,
expected_diagnostic_family~ : String? = None,
actual_stage~ : String = expected_stage,
actual_diagnostic_family~ : String? = expected_diagnostic_family,
actual_diagnostic_location~ : String? = None,
timeout_budget_ms~ : Int32? = None,
seed_profile~ : String? = None,
seed_mode~ : String? = None,
seed_require_strategy_prereqs~ : Bool? = None,
stream_label~ : String? = None,
stream_seed~ : UInt64? = None,
attempt_seed~ : UInt64? = None,
source_path~ : String? = None,
valid_before_link~ : Bool? = None,
unlinkable_reason~ : String? = None,
registered_module_name~ : String? = None,
import_module_name~ : String? = None,
import_name~ : String? = None,
expected_import_type~ : String? = None,
provider_export_type~ : String? = None,
pre_link_validation_status~ : String? = None,
source_feature_facts~ : Array[String] = [],
multi_fault_mutation_ids~ : Array[String] = [],
multi_fault_broad_outcomes~ : Array[String] = [],
artifacts~ : Array[InvalidFuzzArtifact] = [],
reduced_artifacts~ : Array[InvalidFuzzArtifact] = [],
reduction_original_size~ : Int32? = None,
reduction_final_size~ : Int32? = None,
reduction_predicate_evaluations~ : Int32? = None,
reduction_steps~ : Array[InvalidFuzzReductionStep] = [],
) -> InvalidFuzzFailureReport {
{
suite,
profile,
seed,
attempt,
strategy_id,
source_kind,
expected_stage,
expected_diagnostic_family,
actual_stage,
actual_diagnostic_family,
actual_diagnostic_location,
timeout_budget_ms,
seed_profile,
seed_mode,
seed_require_strategy_prereqs,
stream_label,
stream_seed,
attempt_seed,
source_path,
valid_before_link,
unlinkable_reason,
registered_module_name,
import_module_name,
import_name,
expected_import_type,
provider_export_type,
pre_link_validation_status,
source_feature_facts,
multi_fault_mutation_ids,
multi_fault_broad_outcomes,
artifacts,
reduced_artifacts,
reduction_original_size,
reduction_final_size,
reduction_predicate_evaluations,
reduction_steps,
}
}
///|
fn invalid_fuzz_failure_report_copy(
report : InvalidFuzzFailureReport,
source_path~ : String? = report.source_path,
reduced_artifacts~ : Array[InvalidFuzzArtifact] = report.reduced_artifacts,
reduction_original_size~ : Int32? = report.reduction_original_size,
reduction_final_size~ : Int32? = report.reduction_final_size,
reduction_predicate_evaluations~ : Int32? = report.reduction_predicate_evaluations,
reduction_steps~ : Array[InvalidFuzzReductionStep] = report.reduction_steps,
) -> InvalidFuzzFailureReport {
InvalidFuzzFailureReport::new(
report.suite,
report.profile,
report.seed,
report.attempt,
report.strategy_id,
report.source_kind,
report.expected_stage,
expected_diagnostic_family=report.expected_diagnostic_family,
actual_stage=report.actual_stage,
actual_diagnostic_family=report.actual_diagnostic_family,
actual_diagnostic_location=report.actual_diagnostic_location,
timeout_budget_ms=report.timeout_budget_ms,
seed_profile=report.seed_profile,
seed_mode=report.seed_mode,
seed_require_strategy_prereqs=report.seed_require_strategy_prereqs,
stream_label=report.stream_label,
stream_seed=report.stream_seed,
attempt_seed=report.attempt_seed,
source_path~,
valid_before_link=report.valid_before_link,
unlinkable_reason=report.unlinkable_reason,
registered_module_name=report.registered_module_name,
import_module_name=report.import_module_name,
import_name=report.import_name,
expected_import_type=report.expected_import_type,
provider_export_type=report.provider_export_type,
pre_link_validation_status=report.pre_link_validation_status,
source_feature_facts=report.source_feature_facts,
multi_fault_mutation_ids=report.multi_fault_mutation_ids,
multi_fault_broad_outcomes=report.multi_fault_broad_outcomes,
artifacts=report.artifacts,
reduced_artifacts~,
reduction_original_size~,
reduction_final_size~,
reduction_predicate_evaluations~,
reduction_steps~,
)
}
///|
pub struct InvalidFuzzReplayResult {
stage : String
diagnostic_family : String?
diagnostic_location : String?
message : String?
} derive(Eq, Debug)
///|
pub fn InvalidFuzzReplayResult::new(
stage : String,
diagnostic_family~ : String? = None,
diagnostic_location~ : String? = None,
message~ : String? = None,
) -> InvalidFuzzReplayResult {
{ stage, diagnostic_family, diagnostic_location, message, }
}
///|
pub struct InvalidFuzzPersistIO {
ensure_dir : (String) -> Result[Unit, String]
write_file : (String, Bytes) -> Result[Unit, String]
}
///|
pub fn InvalidFuzzPersistIO::new(
ensure_dir~ : (String) -> Result[Unit, String] = fn(path) {
Err("ensure_dir not configured: \{path}")
},
write_file~ : (String, Bytes) -> Result[Unit, String] = fn(path, _) {
Err("write_file not configured: \{path}")
},
) -> InvalidFuzzPersistIO {
{ ensure_dir, write_file, }
}
///|
fn invalid_fuzz_family_label(
family : @validate.ValidationIssueFamily,
) -> String {
match family {
TypeSectionFamily => "type"
ImportSectionFamily => "import"
FunctionSectionFamily => "function"
TableSectionFamily => "table"
MemorySectionFamily => "memory"
TagSectionFamily => "tag"
GlobalSectionFamily => "global"
ElementSectionFamily => "element"
DataSectionFamily => "data"
DataCountSectionFamily => "datacount"
StartSectionFamily => "start"
ExportSectionFamily => "export"
CodeSectionFamily => "code"
NameSectionFamily => "name"
CompilerFactsSectionFamily => "compiler-facts"
FeaturePolicyFamily => "feature-policy"
FunctionBodyFamily => "function-body"
}
}
///|
fn invalid_fuzz_string_bytes(text : String) -> Bytes {
let out : Array[Byte] = []
for c in text {
let code = c.to_int()
if code >= 0 && code <= 255 {
out.push(code.to_byte())
} else {
out.push('?'.to_int().to_byte())
}
}
Bytes::from_array(out)
}
///|
fn invalid_fuzz_bytes_string(bytes : Bytes) -> Result[String, String] {
let out = StringBuilder::new()
for byte in bytes {
out.write_char(byte.to_int().unsafe_to_char())
}
Ok(out.to_string())
}
///|
fn invalid_fuzz_join(values : Array[String], sep : String) -> String {
let out = StringBuilder::new()
let mut i = 0
while i < values.length() {
if i > 0 {
out.write_string(sep)
}
out.write_string(values[i])
i += 1
}
out.to_string()
}
///|
fn invalid_fuzz_ascii_lower(input : String) -> String {
let out = StringBuilder::new()
for c in input {
if c >= 'A' && c <= 'Z' {
out.write_char((c.to_int() + 32).unsafe_to_char())
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
fn invalid_fuzz_artifact_meta_lines(
prefix : String,
artifacts : Array[InvalidFuzzArtifact],
) -> Array[String] {
let lines : Array[String] = []
for artifact in artifacts {
lines.push(prefix + "=" + artifact.label + "|" + artifact.file_name)
}
lines
}
///|
fn invalid_fuzz_reduction_step_text(step : InvalidFuzzReductionStep) -> String {
step.kind +
"|" +
step.start.to_string() +
"|" +
step.len.to_string() +
"|" +
step.before_size.to_string() +
"|" +
step.after_size.to_string()
}
///|
fn invalid_fuzz_reduction_has_log(report : InvalidFuzzFailureReport) -> Bool {
report.reduction_steps.length() > 0 ||
report.reduction_original_size != None ||
report.reduction_final_size != None ||
report.reduction_predicate_evaluations != None
}
///|
fn invalid_fuzz_reduction_log_text(report : InvalidFuzzFailureReport) -> String {
let out = StringBuilder::new()
match report.reduction_original_size {
Some(v) => out.write_string("original_size=" + v.to_string() + "\n")
None => out.write_string("original_size=\n")
}
match report.reduction_final_size {
Some(v) => out.write_string("final_size=" + v.to_string() + "\n")
None => out.write_string("final_size=\n")
}
match report.reduction_predicate_evaluations {
Some(v) => out.write_string("predicate_evaluations=" + v.to_string() + "\n")
None => out.write_string("predicate_evaluations=\n")
}
for step in report.reduction_steps {
out.write_string("step=" + invalid_fuzz_reduction_step_text(step) + "\n")
}
out.to_string()
}
///|
fn invalid_fuzz_optional_meta_line(label : String, value : String?) -> String? {
match value {
Some(v) => Some(label + "=" + v)
None => None
}
}
///|
fn invalid_fuzz_optional_bool_meta_line(
label : String,
value : Bool?,
) -> String? {
match value {
Some(v) => {
let text = if v { "true" } else { "false" }
Some(label + "=" + text)
}
None => None
}
}
///|
fn invalid_fuzz_failure_meta_text(report : InvalidFuzzFailureReport) -> String {
let lines : Array[String] = [
"suite=" + report.suite,
"profile=" + report.profile,
"seed=" + report.seed.to_string(),
"attempt=" + report.attempt.to_string(),
"strategy_id=" + report.strategy_id,
"source_kind=" + invalid_fuzz_source_kind_name(report.source_kind),
"expected_stage=" + report.expected_stage,
"actual_stage=" + report.actual_stage,
]
match
invalid_fuzz_optional_meta_line(
"expected_diagnostic_family",
report.expected_diagnostic_family,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"actual_diagnostic_family",
report.actual_diagnostic_family,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"actual_diagnostic_location",
report.actual_diagnostic_location,
) {
Some(line) => lines.push(line)
None => ()
}
match report.timeout_budget_ms {
Some(v) => lines.push("timeout_budget_ms=" + v.to_string())
None => ()
}
match invalid_fuzz_optional_meta_line("seed_profile", report.seed_profile) {
Some(line) => lines.push(line)
None => ()
}
match invalid_fuzz_optional_meta_line("seed_mode", report.seed_mode) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_bool_meta_line(
"seed_require_strategy_prereqs",
report.seed_require_strategy_prereqs,
) {
Some(line) => lines.push(line)
None => ()
}
match invalid_fuzz_optional_meta_line("stream_label", report.stream_label) {
Some(line) => lines.push(line)
None => ()
}
match report.stream_seed {
Some(v) => lines.push("stream_seed=" + v.to_string())
None => ()
}
match report.attempt_seed {
Some(v) => lines.push("attempt_seed=" + v.to_string())
None => ()
}
match invalid_fuzz_optional_meta_line("source_path", report.source_path) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_bool_meta_line(
"valid_before_link",
report.valid_before_link,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"unlinkable_reason",
report.unlinkable_reason,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"registered_module_name",
report.registered_module_name,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"import_module_name",
report.import_module_name,
) {
Some(line) => lines.push(line)
None => ()
}
match invalid_fuzz_optional_meta_line("import_name", report.import_name) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"expected_import_type",
report.expected_import_type,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"provider_export_type",
report.provider_export_type,
) {
Some(line) => lines.push(line)
None => ()
}
match
invalid_fuzz_optional_meta_line(
"pre_link_validation_status",
report.pre_link_validation_status,
) {
Some(line) => lines.push(line)
None => ()
}
for fact in report.source_feature_facts {
lines.push("source_feature_fact=" + fact)
}
for id in report.multi_fault_mutation_ids {
lines.push("multi_fault_mutation_id=" + id)
}
for outcome in report.multi_fault_broad_outcomes {
lines.push("multi_fault_broad_outcome=" + outcome)
}
for line in invalid_fuzz_artifact_meta_lines("artifact", report.artifacts) {
lines.push(line)
}
for
line in invalid_fuzz_artifact_meta_lines(
"reduced",
report.reduced_artifacts,
) {
lines.push(line)
}
match report.reduction_original_size {
Some(v) => lines.push("reduction_original_size=" + v.to_string())
None => ()
}
match report.reduction_final_size {
Some(v) => lines.push("reduction_final_size=" + v.to_string())
None => ()
}
match report.reduction_predicate_evaluations {
Some(v) => lines.push("reduction_predicate_evaluations=" + v.to_string())
None => ()
}
for step in report.reduction_steps {
lines.push("reduction_step=" + invalid_fuzz_reduction_step_text(step))
}
if invalid_fuzz_reduction_has_log(report) {
lines.push("reduction_log=reduction.txt")
}
invalid_fuzz_join(lines, "\n") + "\n"
}
///|
fn invalid_fuzz_join_path(left : String, right : String) -> String {
if left.length() == 0 || left == "." {
return right
}
let last = left[left.length() - 1]
if last == '/' || last == '\\' {
left + right
} else {
left + "/" + right
}
}
///|
fn invalid_fuzz_ensure_dir(
io : InvalidFuzzPersistIO,
path : String,
) -> Result[Unit, String] {
match (io.ensure_dir)(path) {
Ok(_) => Ok(())
Err(msg) => Err("failed to ensure invalid repro dir \{path}: \{msg}")
}
}
///|
fn invalid_fuzz_report_allows_partial_artifacts(
report : InvalidFuzzFailureReport,
) -> Bool {
report.actual_stage == "timeout" ||
report.actual_stage == "resource-limit" ||
report.actual_stage == "resource-limited"
}
///|
pub fn persist_invalid_fuzz_failure_report(
report : InvalidFuzzFailureReport,
io : InvalidFuzzPersistIO,
corpus_dir~ : String = "fuzz-corpus/invalid",
) -> Result[(String, Array[String]), String] {
let root = corpus_dir.trim().to_owned()
let corpus_root = if root.length() == 0 {
"fuzz-corpus/invalid"
} else {
root
}
let suite_dir = invalid_fuzz_join_path(corpus_root, report.suite)
let strategy_dir = invalid_fuzz_join_path(suite_dir, report.strategy_id)
let repro_dir = invalid_fuzz_join_path(
strategy_dir,
"seed-\{report.seed}-attempt-\{report.attempt}",
)
match invalid_fuzz_ensure_dir(io, corpus_root) {
Ok(_) => ()
Err(msg) => return Err(msg)
}
match invalid_fuzz_ensure_dir(io, suite_dir) {
Ok(_) => ()
Err(msg) => return Err(msg)
}
match invalid_fuzz_ensure_dir(io, strategy_dir) {
Ok(_) => ()
Err(msg) => return Err(msg)
}
match invalid_fuzz_ensure_dir(io, repro_dir) {
Ok(_) => ()
Err(msg) => return Err(msg)
}
let artifact_paths : Array[String] = []
let partial_failures : Array[String] = []
let allow_partial_artifacts = invalid_fuzz_report_allows_partial_artifacts(
report,
)
for artifact in report.artifacts {
let path = invalid_fuzz_join_path(repro_dir, artifact.file_name)
match (io.write_file)(path, artifact.bytes) {
Ok(_) => artifact_paths.push(path)
Err(msg) =>
if allow_partial_artifacts {
partial_failures.push(
"partial_artifact_write_failure=" + artifact.file_name + "|" + msg,
)
} else {
return Err("failed to write invalid repro artifact \{path}: \{msg}")
}
}
}
for artifact in report.reduced_artifacts {
let path = invalid_fuzz_join_path(repro_dir, artifact.file_name)
match (io.write_file)(path, artifact.bytes) {
Ok(_) => artifact_paths.push(path)
Err(msg) =>
if allow_partial_artifacts {
partial_failures.push(
"partial_artifact_write_failure=" + artifact.file_name + "|" + msg,
)
} else {
return Err("failed to write invalid repro artifact \{path}: \{msg}")
}
}
}
if invalid_fuzz_reduction_has_log(report) {
let reduction_log_path = invalid_fuzz_join_path(repro_dir, "reduction.txt")
match
(io.write_file)(
reduction_log_path,
invalid_fuzz_string_bytes(invalid_fuzz_reduction_log_text(report)),
) {
Ok(_) => artifact_paths.push(reduction_log_path)
Err(msg) =>
if allow_partial_artifacts {
partial_failures.push(
"partial_artifact_write_failure=reduction.txt|" + msg,
)
} else {
return Err(
"failed to write invalid repro artifact \{reduction_log_path}: \{msg}",
)
}
}
}
let meta_path = invalid_fuzz_join_path(repro_dir, "repro.meta.txt")
let partial_manifest = if partial_failures.length() == 0 {
""
} else {
invalid_fuzz_join(partial_failures, "\n") + "\n"
}
match
(io.write_file)(
meta_path,
invalid_fuzz_string_bytes(
invalid_fuzz_failure_meta_text(report) + partial_manifest,
),
) {
Ok(_) => Ok((meta_path, artifact_paths))
Err(msg) =>
Err("failed to write invalid repro metadata \{meta_path}: \{msg}")
}
}
///|
fn invalid_fuzz_parse_meta_line(line : String) -> (String, String)? {
let mut idx = -1
let mut i = 0
while i < line.length() {
if line[i] == '=' {
idx = i
break
}
i += 1
}
if idx < 0 {
return None
}
Some((line.sub(end=idx).to_owned(), line.sub(start=idx + 1).to_owned()))
}
///|
fn invalid_fuzz_parse_uint64(
raw : String,
field : String,
) -> Result[UInt64, String] {
let text = raw.trim().to_owned()
if text.length() == 0 {
return Err("missing \{field}")
}
let mut value : UInt64 = 0UL
for c in text {
if c < '0' || c > '9' {
return Err("invalid \{field} '\{raw}'")
}
value = value * 10UL + (c.to_int() - '0'.to_int()).to_uint64()
}
Ok(value)
}
///|
fn invalid_fuzz_parse_int(
raw : String,
field : String,
) -> Result[Int32, String] {
let text = raw.trim().to_owned()
if text.length() == 0 {
return Err("missing \{field}")
}
let mut value = 0
for c in text {
if c < '0' || c > '9' {
return Err("invalid \{field} '\{raw}'")
}
value = value * 10 + (c.to_int() - '0'.to_int())
}
Ok(value)
}
///|
fn invalid_fuzz_parse_bool(
raw : String,
field : String,
) -> Result[Bool, String] {
let text = raw.trim().to_owned()
match text {
"true" => Ok(true)
"false" => Ok(false)
_ => Err("invalid \{field} '\{raw}'")
}
}
///|
fn invalid_fuzz_parse_artifact_entry(
raw : String,
load_artifact : (String) -> Result[Bytes, String],
) -> Result[InvalidFuzzArtifact, String] {
let mut sep = -1
let mut i = 0
while i < raw.length() {
if raw[i] == '|' {
sep = i
break
}
i += 1
}
if sep <= 0 || sep + 1 >= raw.length() {
return Err("invalid artifact entry '\{raw}'")
}
let label = raw.sub(end=sep).trim().to_owned()
let file_name = raw.sub(start=sep + 1).trim().to_owned()
if label.length() == 0 || file_name.length() == 0 {
return Err("invalid artifact entry '\{raw}'")
}
match load_artifact(file_name) {
Ok(bytes) => Ok(InvalidFuzzArtifact::new(label, file_name, bytes))
Err(msg) => Err(msg)
}
}
///|
fn invalid_fuzz_parse_reduction_step(
raw : String,
) -> Result[InvalidFuzzReductionStep, String] {
let parts : Array[String] = []
for part in raw.split("|") {
parts.push(part.to_owned())
}
if parts.length() != 5 {
return Err("invalid reduction step entry '\{raw}'")
}
let start = match invalid_fuzz_parse_int(parts[1], "reduction_step.start") {
Ok(v) => v
Err(msg) => return Err(msg)
}
let len = match invalid_fuzz_parse_int(parts[2], "reduction_step.len") {
Ok(v) => v
Err(msg) => return Err(msg)
}
let before_size = match
invalid_fuzz_parse_int(parts[3], "reduction_step.before_size") {
Ok(v) => v
Err(msg) => return Err(msg)
}
let after_size = match
invalid_fuzz_parse_int(parts[4], "reduction_step.after_size") {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
InvalidFuzzReductionStep::new(parts[0], start, len, before_size, after_size),
)
}
///|
pub fn parse_invalid_fuzz_failure_report(
meta_text : String,
load_artifact : (String) -> Result[Bytes, String],
) -> Result[InvalidFuzzFailureReport, String] {
let mut suite : String? = None
let mut profile : String? = None
let mut seed : UInt64? = None
let mut attempt : Int32? = None
let mut strategy_id : String? = None
let mut source_kind : InvalidFuzzSourceKind? = None
let mut expected_stage : String? = None
let mut expected_diagnostic_family : String? = None
let mut actual_stage : String? = None
let mut actual_diagnostic_family : String? = None
let mut actual_diagnostic_location : String? = None
let mut timeout_budget_ms : Int32? = None
let mut seed_profile : String? = None
let mut seed_mode : String? = None
let mut seed_require_strategy_prereqs : Bool? = None
let mut stream_label : String? = None
let mut stream_seed : UInt64? = None
let mut attempt_seed : UInt64? = None
let mut source_path : String? = None
let mut valid_before_link : Bool? = None
let mut unlinkable_reason : String? = None
let mut registered_module_name : String? = None
let mut import_module_name : String? = None
let mut import_name : String? = None
let mut expected_import_type : String? = None
let mut provider_export_type : String? = None
let mut pre_link_validation_status : String? = None
let source_feature_facts : Array[String] = []
let multi_fault_mutation_ids : Array[String] = []
let multi_fault_broad_outcomes : Array[String] = []
let artifacts : Array[InvalidFuzzArtifact] = []
let reduced_artifacts : Array[InvalidFuzzArtifact] = []
let mut reduction_original_size : Int32? = None
let mut reduction_final_size : Int32? = None
let mut reduction_predicate_evaluations : Int32? = None
let reduction_steps : Array[InvalidFuzzReductionStep] = []
for raw_line in meta_text.split("\n") {
let line = raw_line.trim().to_owned()
if line.length() == 0 {
continue
}
let (key, value) = match invalid_fuzz_parse_meta_line(line) {
Some(v) => v
None => return Err("invalid metadata line '\{line}'")
}
match key {
"suite" => suite = Some(value)
"profile" => profile = Some(value)
"seed" =>
match invalid_fuzz_parse_uint64(value, "seed") {
Ok(v) => seed = Some(v)
Err(msg) => return Err(msg)
}
"attempt" =>
match invalid_fuzz_parse_int(value, "attempt") {
Ok(v) => attempt = Some(v)
Err(msg) => return Err(msg)
}
"strategy_id" => strategy_id = Some(value)
"source_kind" =>
match invalid_fuzz_source_kind_from_name(value) {
Some(v) => source_kind = Some(v)
None => return Err("unknown source_kind '\{value}'")
}
"expected_stage" => expected_stage = Some(value)
"expected_diagnostic_family" => expected_diagnostic_family = Some(value)
"actual_stage" => actual_stage = Some(value)
"actual_diagnostic_family" => actual_diagnostic_family = Some(value)
"actual_diagnostic_location" => actual_diagnostic_location = Some(value)
"timeout_budget_ms" =>
match invalid_fuzz_parse_int(value, "timeout_budget_ms") {
Ok(v) => timeout_budget_ms = Some(v)
Err(msg) => return Err(msg)
}
"seed_profile" => seed_profile = Some(value)
"seed_mode" => seed_mode = Some(value)
"seed_require_strategy_prereqs" =>
match invalid_fuzz_parse_bool(value, "seed_require_strategy_prereqs") {
Ok(v) => seed_require_strategy_prereqs = Some(v)
Err(msg) => return Err(msg)
}
"stream_label" => stream_label = Some(value)
"stream_seed" =>
match invalid_fuzz_parse_uint64(value, "stream_seed") {
Ok(v) => stream_seed = Some(v)
Err(msg) => return Err(msg)
}
"attempt_seed" =>
match invalid_fuzz_parse_uint64(value, "attempt_seed") {
Ok(v) => attempt_seed = Some(v)
Err(msg) => return Err(msg)
}
"source_path" => source_path = Some(value)
"valid_before_link" =>
match invalid_fuzz_parse_bool(value, "valid_before_link") {
Ok(v) => valid_before_link = Some(v)
Err(msg) => return Err(msg)
}
"unlinkable_reason" => unlinkable_reason = Some(value)
"registered_module_name" => registered_module_name = Some(value)
"import_module_name" => import_module_name = Some(value)
"import_name" => import_name = Some(value)
"expected_import_type" => expected_import_type = Some(value)
"provider_export_type" => provider_export_type = Some(value)
"pre_link_validation_status" => pre_link_validation_status = Some(value)
"source_feature_fact" => source_feature_facts.push(value)
"multi_fault_mutation_id" => multi_fault_mutation_ids.push(value)
"multi_fault_broad_outcome" => multi_fault_broad_outcomes.push(value)
"artifact" =>
match invalid_fuzz_parse_artifact_entry(value, load_artifact) {
Ok(v) => artifacts.push(v)
Err(msg) => return Err(msg)
}
"reduced" =>
match invalid_fuzz_parse_artifact_entry(value, load_artifact) {
Ok(v) => reduced_artifacts.push(v)
Err(msg) => return Err(msg)
}
"reduction_original_size" =>
match invalid_fuzz_parse_int(value, "reduction_original_size") {
Ok(v) => reduction_original_size = Some(v)
Err(msg) => return Err(msg)
}
"reduction_final_size" =>
match invalid_fuzz_parse_int(value, "reduction_final_size") {
Ok(v) => reduction_final_size = Some(v)
Err(msg) => return Err(msg)
}
"reduction_predicate_evaluations" =>
match invalid_fuzz_parse_int(value, "reduction_predicate_evaluations") {
Ok(v) => reduction_predicate_evaluations = Some(v)
Err(msg) => return Err(msg)
}
"reduction_step" =>
match invalid_fuzz_parse_reduction_step(value) {
Ok(v) => reduction_steps.push(v)
Err(msg) => return Err(msg)
}
"reduction_log" => ()
"partial_artifact_write_failure" => ()
_ => return Err("unknown invalid repro metadata key '\{key}'")
}
}
let resolved_suite = match suite {
Some(v) => v
None => return Err("missing suite")
}
let resolved_profile = match profile {
Some(v) => v
None => return Err("missing profile")
}
let resolved_seed = match seed {
Some(v) => v
None => return Err("missing seed")
}
let resolved_attempt = match attempt {
Some(v) => v
None => return Err("missing attempt")
}
let resolved_strategy_id = match strategy_id {
Some(v) => v
None => return Err("missing strategy_id")
}
let resolved_source_kind = match source_kind {
Some(v) => v
None => return Err("missing source_kind")
}
let resolved_expected_stage = match expected_stage {
Some(v) => v
None => return Err("missing expected_stage")
}
let resolved_actual_stage = match actual_stage {
Some(v) => v
None => return Err("missing actual_stage")
}
Ok(
InvalidFuzzFailureReport::new(
resolved_suite,
resolved_profile,
resolved_seed,
resolved_attempt,
resolved_strategy_id,
resolved_source_kind,
resolved_expected_stage,
expected_diagnostic_family~,
actual_stage=resolved_actual_stage,
actual_diagnostic_family~,
actual_diagnostic_location~,
timeout_budget_ms~,
seed_profile~,
seed_mode~,
seed_require_strategy_prereqs~,
stream_label~,
stream_seed~,
attempt_seed~,
source_path~,
valid_before_link~,
unlinkable_reason~,
registered_module_name~,
import_module_name~,
import_name~,
expected_import_type~,
provider_export_type~,
pre_link_validation_status~,
source_feature_facts~,
multi_fault_mutation_ids~,
multi_fault_broad_outcomes~,
artifacts~,
reduced_artifacts~,
reduction_original_size~,
reduction_final_size~,
reduction_predicate_evaluations~,
reduction_steps~,
),
)
}
///|
fn invalid_fuzz_encode_module_artifact(
label : String,
file_name : String,
mod : @lib.Module,
) -> Result[InvalidFuzzArtifact, String] {
match encode_module(mod) {
Ok(bytes) => Ok(InvalidFuzzArtifact::new(label, file_name, bytes))
Err(msg) => Err("\{msg}")
}
}
///|
fn invalid_fuzz_expected_binary_outcome(
expected_result : ValidateInvalidBinaryExpectedResult,
) -> (String, String?) {
match expected_result {
DecodeRejected => ("decode-rejected", None)
ValidateRejected(family_label) => ("validate-rejected", Some(family_label))
}
}
///|
fn invalid_fuzz_report_outcome_matches(
replay : InvalidFuzzReplayResult,
expected_stage : String,
expected_diagnostic_family : String?,
) -> Bool {
if replay.stage != expected_stage {
return false
}
replay.diagnostic_family == expected_diagnostic_family
}
///|
fn invalid_fuzz_stream_seed_by_label(seed : UInt64, wanted : String) -> UInt64? {
for label in gen_valid_random_stream_labels() {
if gen_valid_random_stream_label_name(label) == wanted {
return Some(gen_valid_derive_stream_seed(seed, label))
}
}
None
}
///|
pub fn build_invalid_fuzz_failure_report_by_suite_and_stable_id(
suite : String,
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
) -> Result[InvalidFuzzFailureReport, String] {
let normalized = invalid_fuzz_ascii_lower(suite.trim().to_owned())
if normalized == "validate-invalid-text-dynamic" ||
normalized == "dynamic-text" {
return build_invalid_fuzz_dynamic_text_failure_report_by_stable_id(
strategy_id,
seed,
attempt,
profile~,
)
}
if normalized == "valid-multi-module-linking" ||
normalized == "multi-module-linking" {
if strategy_id != "unlinkable-multi-module" {
return Err("unknown multi-module invalid strategy '\{strategy_id}'")
}
ignore(profile)
return Ok(build_fuz1055b4_unlinkable_failure_report(seed, attempt))
}
let source_kind = match invalid_fuzz_source_kind_from_name(normalized) {
Some(v) => v
None => return Err("unknown invalid fuzz suite '\{suite}'")
}
match source_kind {
AstInvalidSource =>
build_invalid_fuzz_ast_failure_report_by_stable_id(
strategy_id,
seed,
attempt,
profile~,
)
BinaryInvalidSource =>
build_invalid_fuzz_binary_failure_report_by_stable_id(
strategy_id,
seed,
attempt,
profile~,
)
TextInvalidSource =>
build_invalid_fuzz_text_failure_report_by_stable_id(
strategy_id,
seed,
attempt,
profile~,
)
SpecSeedInvalidSource =>
build_invalid_fuzz_spec_seed_failure_report_by_stable_id(
strategy_id,
seed,
attempt,
profile~,
)
}
}
///|
pub fn build_invalid_fuzz_ast_failure_report_by_stable_id(
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
params~ : @validate.GenInvalidAstParams? = None,
) -> Result[InvalidFuzzFailureReport, String] {
let spec = match
@validate.validate_invalid_ast_strategy_by_stable_id(strategy_id) {
Some(v) => v
None => return Err("unknown AST invalid strategy '\{strategy_id}'")
}
let resolved_params = match params {
Some(v) => v
None => @validate.GenInvalidAstParams::repro_seed(spec.id)
}
let seed_profile_name = @validate.gen_invalid_ast_params_profile_name(
resolved_params,
)
let attempt_seed = @validate.validate_invalid_ast_attempt_seed_for_profile(
seed, strategy_id, seed_profile_name,
)
let generated = match
@validate.gen_invalid_ast_generate_from_seed(attempt_seed, resolved_params) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let artifact = match
invalid_fuzz_encode_module_artifact(
"original",
"original.wasm",
generated.mutation.mutated_module,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let seed_artifact = match
invalid_fuzz_encode_module_artifact(
"seed",
"seed.wasm",
generated.base_module,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay = invalid_fuzz_replay_binary_like(artifact.bytes)
let expected_family = Some(invalid_fuzz_family_label(spec.expected_family))
if !invalid_fuzz_report_outcome_matches(
replay, "validate-rejected", expected_family,
) {
let expected_family_text = expected_family.unwrap_or("")
let actual_family_text = replay.diagnostic_family.unwrap_or("")
return Err(
"AST invalid report outcome mismatch: strategy=\{strategy_id} expected_stage=validate-rejected expected_family=\{expected_family_text} actual_stage=\{replay.stage} actual_family=\{actual_family_text}",
)
}
Ok(
InvalidFuzzFailureReport::new(
"validate-invalid-ast",
profile,
seed,
attempt,
strategy_id,
invalid_fuzz_ast_source_kind(),
"validate-rejected",
expected_diagnostic_family=expected_family,
actual_stage=replay.stage,
actual_diagnostic_family=replay.diagnostic_family,
actual_diagnostic_location=replay.diagnostic_location,
seed_profile=Some(seed_profile_name),
seed_mode=Some(
@validate.gen_invalid_seed_mode_name(resolved_params.seed_mode),
),
seed_require_strategy_prereqs=Some(
resolved_params.require_strategy_prereqs,
),
stream_label=Some("invalid-mutation-choice"),
stream_seed=invalid_fuzz_stream_seed_by_label(
seed, "invalid-mutation-choice",
),
attempt_seed=Some(attempt_seed),
artifacts=[artifact, seed_artifact],
),
)
}
///|
pub fn build_invalid_fuzz_binary_failure_report_by_stable_id(
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
params~ : GenInvalidBinaryParams? = None,
) -> Result[InvalidFuzzFailureReport, String] {
let spec = match validate_invalid_binary_strategy_by_stable_id(strategy_id) {
Some(v) => v
None => return Err("unknown binary invalid strategy '\{strategy_id}'")
}
let resolved_params = match params {
Some(v) => v
None => GenInvalidBinaryParams::repro_seed(spec.id)
}
let seed_profile_name = gen_invalid_binary_params_profile_name(
resolved_params,
)
let attempt_seed = validate_invalid_binary_attempt_seed(
seed, strategy_id, seed_profile_name,
)
let generated = match
gen_invalid_binary_generate(
@splitmix.new(seed=attempt_seed),
resolved_params,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let artifact = InvalidFuzzArtifact::new(
"original",
"original.wasm",
generated.mutation.mutated_bytes,
)
let seed_artifact = match
invalid_fuzz_encode_module_artifact(
"seed",
"seed.wasm",
generated.base_module,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay = invalid_fuzz_replay_binary_like(artifact.bytes)
let (expected_stage, expected_family) = invalid_fuzz_expected_binary_outcome(
spec.expected_result,
)
if !invalid_fuzz_report_outcome_matches(
replay, expected_stage, expected_family,
) {
let expected_family_text = expected_family.unwrap_or("")
let actual_family_text = replay.diagnostic_family.unwrap_or("")
return Err(
"binary invalid report outcome mismatch: strategy=\{strategy_id} expected_stage=\{expected_stage} expected_family=\{expected_family_text} actual_stage=\{replay.stage} actual_family=\{actual_family_text}",
)
}
Ok(
InvalidFuzzFailureReport::new(
"validate-invalid-binary",
profile,
seed,
attempt,
strategy_id,
invalid_fuzz_binary_source_kind(),
expected_stage,
expected_diagnostic_family=expected_family,
actual_stage=replay.stage,
actual_diagnostic_family=replay.diagnostic_family,
actual_diagnostic_location=replay.diagnostic_location,
seed_profile=Some(seed_profile_name),
seed_mode=Some(
gen_invalid_binary_seed_mode_name(resolved_params.seed_mode),
),
seed_require_strategy_prereqs=Some(
resolved_params.require_strategy_prereqs,
),
stream_label=Some("invalid-mutation-choice"),
stream_seed=invalid_fuzz_stream_seed_by_label(
seed, "invalid-mutation-choice",
),
attempt_seed=Some(attempt_seed),
artifacts=[artifact, seed_artifact],
),
)
}
///|
pub fn build_invalid_fuzz_text_failure_report_by_stable_id(
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
) -> Result[InvalidFuzzFailureReport, String] {
ignore(seed)
let spec = match validate_invalid_text_strategy_by_stable_id(strategy_id) {
Some(v) => v
None =>
return Err("unknown validate-invalid-text strategy '\{strategy_id}'")
}
let (path, source, assertion_kind) = match
validate_invalid_text_source_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let artifact = InvalidFuzzArtifact::new(
"original",
"original.wast",
invalid_fuzz_string_bytes(source),
)
let stage = match
evaluate_validate_invalid_text_assertion_source(
source, path, assertion_kind,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let expected_stage = validate_invalid_text_stage_name(spec.expected_stage)
let actual_stage = validate_invalid_text_stage_name(stage)
if expected_stage != actual_stage {
return Err(
"text invalid report outcome mismatch: strategy=\{strategy_id} expected_stage=\{expected_stage} actual_stage=\{actual_stage}",
)
}
Ok(
InvalidFuzzFailureReport::new(
"validate-invalid-text",
profile,
seed,
attempt,
strategy_id,
invalid_fuzz_text_source_kind(),
expected_stage,
actual_stage~,
source_path=Some(path),
artifacts=[artifact],
),
)
}
///|
pub fn build_invalid_fuzz_dynamic_text_failure_report_by_stable_id(
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
) -> Result[InvalidFuzzFailureReport, String] {
let attempt_seed = validate_invalid_text_dynamic_attempt_seed(
seed, strategy_id, attempt,
)
let generated = match
validate_invalid_text_dynamic_case(attempt_seed, strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let stage = match
evaluate_validate_invalid_text_assertion_source(
generated.assertion_source,
"dynamic-text-invalid/\{generated.stable_id}.wast",
generated.assertion_kind,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let expected_stage = validate_invalid_text_stage_name(
generated.expected_stage,
)
let actual_stage = validate_invalid_text_stage_name(stage)
if expected_stage != actual_stage {
return Err(
"dynamic text invalid report outcome mismatch: strategy=\{strategy_id} expected_stage=\{expected_stage} actual_stage=\{actual_stage}",
)
}
Ok(
InvalidFuzzFailureReport::new(
"validate-invalid-text-dynamic",
profile,
seed,
attempt,
strategy_id,
invalid_fuzz_text_source_kind(),
expected_stage,
actual_stage~,
seed_profile=Some("dynamic-gen-valid"),
seed_mode=Some("gen-valid-wat"),
stream_label=Some("text-mutation-choice"),
stream_seed=Some(validate_invalid_text_dynamic_stream_seed(seed)),
attempt_seed=Some(attempt_seed),
source_path=Some("dynamic-text-invalid/\{generated.stable_id}.wast"),
valid_before_link=Some(stage == ValidBeforeLinkStage),
source_feature_facts=generated.source_feature_facts,
artifacts=[
InvalidFuzzArtifact::new(
"base",
"base.wat",
invalid_fuzz_string_bytes(generated.base_source),
),
InvalidFuzzArtifact::new(
"assertion",
"assertion.wast",
invalid_fuzz_string_bytes(generated.assertion_source),
),
],
),
)
}
///|
pub fn build_invalid_fuzz_spec_seed_failure_report_by_stable_id(
strategy_id : String,
seed : UInt64,
attempt : Int32,
profile~ : String = "smoke",
) -> Result[InvalidFuzzFailureReport, String] {
ignore(seed)
let spec = match validate_invalid_spec_seed_by_stable_id(strategy_id) {
Some(v) => v
None =>
return Err("unknown validate-invalid-spec-seed case '\{strategy_id}'")
}
let (path, assertion_source, assertion_kind) = match
validate_invalid_spec_seed_assertion_source_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let full_source = match read_file_sync(path) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let artifact = InvalidFuzzArtifact::new(
"original",
"original.wast",
invalid_fuzz_string_bytes(full_source),
)
let stage = match
evaluate_validate_invalid_text_assertion_source(
assertion_source, path, assertion_kind,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let expected_stage = validate_invalid_text_stage_name(spec.expected_stage)
let actual_stage = validate_invalid_text_stage_name(stage)
if expected_stage != actual_stage {
return Err(
"spec-seed invalid report outcome mismatch: strategy=\{strategy_id} expected_stage=\{expected_stage} actual_stage=\{actual_stage}",
)
}
Ok(
InvalidFuzzFailureReport::new(
"validate-invalid-spec-seed",
profile,
seed,
attempt,
strategy_id,
invalid_fuzz_spec_seed_source_kind(),
expected_stage,
actual_stage~,
source_path=Some(path),
artifacts=[artifact],
),
)
}
///|
fn invalid_fuzz_binary_strategy_reduce(
strategy_id : String,
) -> Result[InvalidFuzzArtifact, String] {
let bytes = match
build_validate_invalid_binary_minimal_repro_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(InvalidFuzzArtifact::new("reduced", "reduced.wasm", bytes))
}
///|
fn invalid_fuzz_ast_strategy_reduce(
strategy_id : String,
) -> Result[InvalidFuzzArtifact, String] {
let mod = match
@validate.build_validate_invalid_ast_minimal_repro_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
invalid_fuzz_encode_module_artifact("reduced", "reduced.wasm", mod)
}
///|
fn invalid_fuzz_text_strategy_reduce(
strategy_id : String,
) -> Result[(InvalidFuzzArtifact, String), String] {
let (path, source, _) = match
validate_invalid_text_source_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
(
InvalidFuzzArtifact::new(
"reduced",
"reduced.wast",
invalid_fuzz_string_bytes(source),
),
path,
),
)
}
///|
fn invalid_fuzz_spec_seed_reduce(
strategy_id : String,
) -> Result[(InvalidFuzzArtifact, String), String] {
let (path, source, _) = match
validate_invalid_spec_seed_assertion_source_by_stable_id(strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
(
InvalidFuzzArtifact::new(
"reduced",
"reduced.wast",
invalid_fuzz_string_bytes(source),
),
path,
),
)
}
///|
fn invalid_fuzz_first_artifact_size(
artifacts : Array[InvalidFuzzArtifact],
) -> Int32? {
if artifacts.length() == 0 {
None
} else {
Some(artifacts[0].bytes.length())
}
}
///|
fn invalid_fuzz_text_is_space(c : Int32) -> Bool {
c == 0x20 || c == 0x0a || c == 0x0d || c == 0x09
}
///|
fn invalid_fuzz_text_slice(text : String, start : Int32, end : Int32) -> String {
let len = end - start
let chars : Array[Char] = Array::make(len, '\u{00}')
for i = 0; i < len; i = i + 1 {
chars[i] = text[start + i].to_int().unsafe_to_char()
}
String::from_array(chars)
}
///|
fn invalid_fuzz_text_tokens(text : String) -> Array[String] {
let tokens : Array[String] = []
let mut start = 0
let mut in_token = false
for i = 0; i < text.length(); i = i + 1 {
if invalid_fuzz_text_is_space(text[i].to_int()) {
if in_token {
tokens.push(invalid_fuzz_text_slice(text, start, i))
in_token = false
}
} else if !in_token {
start = i
in_token = true
}
}
if in_token {
tokens.push(invalid_fuzz_text_slice(text, start, text.length()))
}
tokens
}
///|
fn invalid_fuzz_text_join_tokens(tokens : Array[String]) -> String {
let out = StringBuilder::new()
for i = 0; i < tokens.length(); i = i + 1 {
if i > 0 {
out.write_string(" ")
}
out.write_string(tokens[i])
}
out.to_string()
}
///|
fn invalid_fuzz_without_text_token_range(
tokens : Array[String],
start : Int32,
len : Int32,
) -> Array[String] {
let out : Array[String] = []
let end = start + len
for i = 0; i < tokens.length(); i = i + 1 {
if i < start || i >= end {
out.push(tokens[i])
}
}
out
}
///|
fn invalid_fuzz_clone_text_tokens(tokens : Array[String]) -> Array[String] {
invalid_fuzz_without_text_token_range(tokens, tokens.length(), 0)
}
///|
fn reduce_fuzz_text_tokens_by_deletion(
text : String,
reproduces : (String) -> Bool,
) -> (String, Int32, Int32, Int32, Array[InvalidFuzzReductionStep]) {
let tokens = invalid_fuzz_text_tokens(text)
let mut predicate_evaluations = 1
let steps : Array[InvalidFuzzReductionStep] = []
if !reproduces(text) {
return (
text,
tokens.length(),
tokens.length(),
predicate_evaluations,
steps,
)
}
let mut current = invalid_fuzz_clone_text_tokens(tokens)
let mut chunk = current.length() / 2
while chunk > 0 {
let mut changed = false
let mut i = 0
while i + chunk <= current.length() {
let candidate = invalid_fuzz_without_text_token_range(current, i, chunk)
let candidate_text = invalid_fuzz_text_join_tokens(candidate)
predicate_evaluations = predicate_evaluations + 1
if reproduces(candidate_text) {
steps.push(
InvalidFuzzReductionStep::new(
"delete-text-token-range",
i,
chunk,
current.length(),
candidate.length(),
),
)
current = candidate
changed = true
} else {
i += 1
}
}
if !changed {
chunk = chunk / 2
} else if chunk > current.length() {
chunk = current.length()
}
}
let result = invalid_fuzz_text_join_tokens(current)
(result, tokens.length(), current.length(), predicate_evaluations, steps)
}
///|
fn invalid_fuzz_without_module_field_range(
fields : Array[ModuleField],
start : Int32,
len : Int32,
) -> Array[ModuleField] {
let out : Array[ModuleField] = []
let end = start + len
for i = 0; i < fields.length(); i = i + 1 {
if i < start || i >= end {
out.push(fields[i])
}
}
out
}
///|
fn invalid_fuzz_clone_module_fields(
fields : Array[ModuleField],
) -> Array[ModuleField] {
invalid_fuzz_without_module_field_range(fields, fields.length(), 0)
}
///|
fn invalid_fuzz_wast_quote(text : String) -> String {
let out = StringBuilder::new()
out.write_char('"')
for c in text {
if c == '\\' {
out.write_string("\\\\")
} else if c == '"' {
out.write_string("\\\"")
} else {
out.write_char(c)
}
}
out.write_char('"')
out.to_string()
}
///|
fn invalid_fuzz_assertion_keyword(
assertion_kind : ValidateInvalidTextAssertionKind,
) -> String {
match assertion_kind {
MalformedTextAssertion => "assert_malformed"
InvalidTextAssertion => "assert_invalid"
UnlinkableTextAssertion => "assert_unlinkable"
}
}
///|
fn invalid_fuzz_inline_assertion_source(
fields : Array[ModuleField],
assertion_kind : ValidateInvalidTextAssertionKind,
message : String,
) -> Result[String, String] {
let module_text = match module_to_wast(@wast.Module::new(fields)) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
"(\{invalid_fuzz_assertion_keyword(assertion_kind)} \{module_text} \{invalid_fuzz_wast_quote(message)})",
)
}
///|
fn invalid_fuzz_first_inline_assertion_fields(
source : String,
path : String,
assertion_kind : ValidateInvalidTextAssertionKind,
) -> Result[(Array[ModuleField], String), String] {
let script = match wast_to_script(source, filename=path) {
Ok(v) => v
Err(msg) => return Err("script parse failed: \{msg}")
}
for cmd in script.commands {
match (cmd, assertion_kind) {
(
@wast.WastCommand::AssertMalformed(
@wast.WastModuleDef::Inline(mod),
msg
),
MalformedTextAssertion,
) => return Ok((mod.fields, msg))
(
@wast.WastCommand::AssertInvalid(@wast.WastModuleDef::Inline(mod), msg),
InvalidTextAssertion,
) => return Ok((mod.fields, msg))
(
@wast.WastCommand::AssertUnlinkable(
@wast.WastModuleDef::Inline(mod),
msg
),
UnlinkableTextAssertion,
) => return Ok((mod.fields, msg))
_ => ()
}
}
Err("missing inline assertion module in \{path}")
}
///|
fn reduce_fuzz_module_fields_by_deletion(
fields : Array[ModuleField],
reproduces : (Array[ModuleField]) -> Bool,
) -> (Array[ModuleField], Int32, Int32, Int32, Array[InvalidFuzzReductionStep]) {
let mut predicate_evaluations = 1
let steps : Array[InvalidFuzzReductionStep] = []
if !reproduces(fields) {
return (
fields,
fields.length(),
fields.length(),
predicate_evaluations,
steps,
)
}
let mut current = invalid_fuzz_clone_module_fields(fields)
let mut chunk = current.length() / 2
while chunk > 0 {
let mut changed = false
let mut i = 0
while i + chunk <= current.length() {
let candidate = invalid_fuzz_without_module_field_range(current, i, chunk)
predicate_evaluations = predicate_evaluations + 1
if reproduces(candidate) {
steps.push(
InvalidFuzzReductionStep::new(
"delete-module-field-range",
i,
chunk,
current.length(),
candidate.length(),
),
)
current = candidate
changed = true
} else {
i += 1
}
}
if !changed {
chunk = chunk / 2
} else if chunk > current.length() {
chunk = current.length()
}
}
(current, fields.length(), current.length(), predicate_evaluations, steps)
}
///|
fn invalid_fuzz_replay_matches_report(
replay : InvalidFuzzReplayResult,
report : InvalidFuzzFailureReport,
) -> Bool {
replay.stage == report.actual_stage &&
replay.diagnostic_family == report.actual_diagnostic_family
}
///|
fn invalid_fuzz_report_with_module_field_reduction(
report : InvalidFuzzFailureReport,
source_path : String,
assertion_kind : ValidateInvalidTextAssertionKind,
) -> Result[InvalidFuzzFailureReport?, String] {
let artifact = match invalid_fuzz_choose_replay_artifact(report.artifacts) {
Some(v) => v
None => return Ok(None)
}
let source = match invalid_fuzz_bytes_string(artifact.bytes) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let (fields, message) = match
invalid_fuzz_first_inline_assertion_fields(
source, source_path, assertion_kind,
) {
Ok(v) => v
Err(_) => return Ok(None)
}
let (
reduced_fields,
_original_field_size,
_final_field_size,
predicate_evaluations,
steps,
) = reduce_fuzz_module_fields_by_deletion(fields, fn(candidate_fields) {
let candidate = match
invalid_fuzz_inline_assertion_source(
candidate_fields, assertion_kind, message,
) {
Ok(v) => v
Err(_) => return false
}
match
invalid_fuzz_replay_text_like(
invalid_fuzz_string_bytes(candidate),
source_path,
assertion_kind,
) {
Ok(replay) => invalid_fuzz_replay_matches_report(replay, report)
Err(_) => false
}
})
if steps.length() == 0 {
return Ok(None)
}
let reduced_text = match
invalid_fuzz_inline_assertion_source(
reduced_fields, assertion_kind, message,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let reduced_bytes = invalid_fuzz_string_bytes(reduced_text)
if reduced_bytes.length() >= artifact.bytes.length() {
return Ok(None)
}
let reduced = InvalidFuzzArtifact::new(
"reduced", "reduced.wast", reduced_bytes,
)
Ok(
Some(
invalid_fuzz_failure_report_copy(
report,
source_path=Some(source_path),
reduced_artifacts=[reduced],
reduction_original_size=Some(artifact.bytes.length()),
reduction_final_size=Some(reduced_bytes.length()),
reduction_predicate_evaluations=Some(predicate_evaluations),
reduction_steps=steps,
),
),
)
}
///|
fn invalid_fuzz_report_with_text_token_reduction(
report : InvalidFuzzFailureReport,
source_path : String,
assertion_kind : ValidateInvalidTextAssertionKind,
) -> Result[InvalidFuzzFailureReport?, String] {
let artifact = match invalid_fuzz_choose_replay_artifact(report.artifacts) {
Some(v) => v
None => return Ok(None)
}
let source = match invalid_fuzz_bytes_string(artifact.bytes) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let (
reduced_text,
_original_token_size,
_final_token_size,
predicate_evaluations,
steps,
) = reduce_fuzz_text_tokens_by_deletion(source, fn(candidate) {
match
invalid_fuzz_replay_text_like(
invalid_fuzz_string_bytes(candidate),
source_path,
assertion_kind,
) {
Ok(replay) => invalid_fuzz_replay_matches_report(replay, report)
Err(_) => false
}
})
let reduced_bytes = invalid_fuzz_string_bytes(reduced_text)
if reduced_bytes.length() >= artifact.bytes.length() || steps.length() == 0 {
return Ok(None)
}
let reduced = InvalidFuzzArtifact::new(
"reduced", "reduced.wast", reduced_bytes,
)
Ok(
Some(
invalid_fuzz_failure_report_copy(
report,
source_path=Some(source_path),
reduced_artifacts=[reduced],
reduction_original_size=Some(artifact.bytes.length()),
reduction_final_size=Some(reduced_bytes.length()),
reduction_predicate_evaluations=Some(predicate_evaluations),
reduction_steps=steps,
),
),
)
}
///|
fn invalid_fuzz_report_with_reduction(
report : InvalidFuzzFailureReport,
reduced : InvalidFuzzArtifact,
source_path~ : String? = report.source_path,
) -> InvalidFuzzFailureReport {
let original_size = invalid_fuzz_first_artifact_size(report.artifacts)
let final_size = Some(reduced.bytes.length())
let step = InvalidFuzzReductionStep::new(
"strategy-minimal-repro",
0,
match (original_size, final_size) {
(Some(before), Some(after)) =>
if before > after {
before - after
} else {
0
}
_ => 0
},
match original_size {
Some(v) => v
None => 0
},
reduced.bytes.length(),
)
invalid_fuzz_failure_report_copy(
report,
source_path~,
reduced_artifacts=[reduced],
reduction_original_size=original_size,
reduction_final_size=final_size,
reduction_predicate_evaluations=Some(2),
reduction_steps=[step],
)
}
///|
pub fn shrink_invalid_fuzz_failure_report(
report : InvalidFuzzFailureReport,
) -> Result[InvalidFuzzFailureReport, String] {
match report.source_kind {
AstInvalidSource => {
let reduced = match invalid_fuzz_ast_strategy_reduce(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(invalid_fuzz_report_with_reduction(report, reduced))
}
BinaryInvalidSource => {
let reduced = match
invalid_fuzz_binary_strategy_reduce(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(invalid_fuzz_report_with_reduction(report, reduced))
}
TextInvalidSource => {
let (path, _, assertion_kind) = match
validate_invalid_text_source_by_stable_id(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay_path = match report.source_path {
Some(v) => v
None => path
}
match
invalid_fuzz_report_with_module_field_reduction(
report, replay_path, assertion_kind,
) {
Ok(Some(reduced_report)) => Ok(reduced_report)
Ok(None) =>
match
invalid_fuzz_report_with_text_token_reduction(
report, replay_path, assertion_kind,
) {
Ok(Some(reduced_report)) => Ok(reduced_report)
Ok(None) => {
let (reduced, path) = match
invalid_fuzz_text_strategy_reduce(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
invalid_fuzz_report_with_reduction(
report,
reduced,
source_path=Some(path),
),
)
}
Err(msg) => return Err(msg)
}
Err(msg) => return Err(msg)
}
}
SpecSeedInvalidSource => {
let (path, _, assertion_kind) = match
validate_invalid_spec_seed_assertion_source_by_stable_id(
report.strategy_id,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay_path = match report.source_path {
Some(v) => v
None => path
}
match
invalid_fuzz_report_with_module_field_reduction(
report, replay_path, assertion_kind,
) {
Ok(Some(reduced_report)) => Ok(reduced_report)
Ok(None) =>
match
invalid_fuzz_report_with_text_token_reduction(
report, replay_path, assertion_kind,
) {
Ok(Some(reduced_report)) => Ok(reduced_report)
Ok(None) => {
let (reduced, path) = match
invalid_fuzz_spec_seed_reduce(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(
invalid_fuzz_report_with_reduction(
report,
reduced,
source_path=Some(path),
),
)
}
Err(msg) => return Err(msg)
}
Err(msg) => return Err(msg)
}
}
}
}
///|
fn invalid_fuzz_binary_decode_location(
detail : @binary.ModuleDecodeErrorDetail,
) -> String? {
match (detail.section_offset, detail.section_len) {
(Some(offset), Some(len)) => {
let section_prefix = match detail.section_id {
Some(id) => "section=\{id}:"
None => ""
}
Some("binary:\{section_prefix}offset=\{offset}:length=\{len}")
}
_ => None
}
}
///|
fn invalid_fuzz_replay_binary_like(bytes : Bytes) -> InvalidFuzzReplayResult {
match decode_module_with_detail(bytes, 0) {
Err(detail) =>
InvalidFuzzReplayResult::new(
"decode-rejected",
diagnostic_location=invalid_fuzz_binary_decode_location(detail),
message=Some("\{detail.err}"),
)
Ok((_decoded, next)) if next != bytes.length() =>
InvalidFuzzReplayResult::new(
"decode-rejected",
diagnostic_location=Some(
"binary:offset=\{next}:length=\{bytes.length() - next}",
),
message=Some("trailing bytes"),
)
Ok((decoded, _)) =>
match validate_module(decoded) {
Ok(_) => InvalidFuzzReplayResult::new("accepted")
Err(err) => {
let family = @validate.validation_issue_family(
@validate.validation_error_diagnostic(err).issue,
)
InvalidFuzzReplayResult::new(
"validate-rejected",
diagnostic_family=Some(invalid_fuzz_family_label(family)),
message=Some(validation_error_message(err)),
)
}
}
}
}
///|
fn invalid_fuzz_replay_text_like(
bytes : Bytes,
path : String,
assertion_kind : ValidateInvalidTextAssertionKind,
) -> Result[InvalidFuzzReplayResult, String] {
let source = match invalid_fuzz_bytes_string(bytes) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let stage = match
evaluate_validate_invalid_text_assertion_source(
source, path, assertion_kind,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
Ok(InvalidFuzzReplayResult::new(validate_invalid_text_stage_name(stage)))
}
///|
fn invalid_fuzz_choose_replay_artifact(
artifacts : Array[InvalidFuzzArtifact],
) -> InvalidFuzzArtifact? {
for artifact in artifacts {
if artifact.label == "reduced" || artifact.label == "original" {
return Some(artifact)
}
}
if artifacts.is_empty() {
None
} else {
Some(artifacts[0])
}
}
///|
pub fn replay_invalid_fuzz_failure_report(
report : InvalidFuzzFailureReport,
prefer_reduced~ : Bool = true,
) -> Result[InvalidFuzzReplayResult, String] {
let candidates = if prefer_reduced && !report.reduced_artifacts.is_empty() {
report.reduced_artifacts
} else {
report.artifacts
}
let artifact = match invalid_fuzz_choose_replay_artifact(candidates) {
Some(v) => v
None => return Err("invalid repro report has no artifacts to replay")
}
match report.source_kind {
AstInvalidSource => Ok(invalid_fuzz_replay_binary_like(artifact.bytes))
BinaryInvalidSource => Ok(invalid_fuzz_replay_binary_like(artifact.bytes))
TextInvalidSource => {
let (path, _, assertion_kind) = match
validate_invalid_text_source_by_stable_id(report.strategy_id) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay_path = match report.source_path {
Some(v) => v
None => path
}
invalid_fuzz_replay_text_like(artifact.bytes, replay_path, assertion_kind)
}
SpecSeedInvalidSource => {
let (path, _, assertion_kind) = match
validate_invalid_spec_seed_assertion_source_by_stable_id(
report.strategy_id,
) {
Ok(v) => v
Err(msg) => return Err(msg)
}
let replay_path = match report.source_path {
Some(v) => v
None => path
}
invalid_fuzz_replay_text_like(artifact.bytes, replay_path, assertion_kind)
}
}
}