From 7a2c778f3b30c9624fbf72d4dbc947992defacb7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 6 Sep 2024 13:30:57 -0700 Subject: [PATCH 1/4] Fuzzer: Log locals and local references's fields --- src/tools/fuzzing/fuzzing.cpp | 68 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index a66fa6772d0..048b80e0558 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -693,9 +693,71 @@ Expression* TranslateToFuzzReader::makeHangLimitCheck() { } Expression* TranslateToFuzzReader::makeLogging() { - auto type = getLoggableType(); - return builder.makeCall( - std::string("log-") + type.toString(), {make(type)}, Type::none); + auto makeLoggingCall = [&](Expression* value) { + assert(isLoggableType(value->type)); + return builder.makeCall( + std::string("log-") + value->type.toString(), {value}, Type::none); + }; + + // We may choose to log a local, if there are any. + auto* func = funcContext->func; + auto numLocals = func->getNumLocals(); + + auto choice = upTo(4); + if (choice < 2 || numLocals == 0) { + // 50% of the time, pick a loggable type and make something of that type + // to log. Also do so when there are no locals to log. + return makeLoggingCall(make(getLoggableType())); + } + + // 50% of the time, pick a local and log it, either shallowly or deeply. + auto index = upTo(numLocals); + auto type = func->getLocalType(index); + if (isLoggableType(type)) { + // We can log this directly. Get it and log that. + return makeLoggingCall(builder.makeLocalGet(index, type)); + } + + if (type.isRef()) { + // This is a reference, which cannot be directly logged. We can at least + // "shallowly" log it by seeing if it is null. + auto* get = builder.makeLocalGet(index, type); + auto* isNull = builder.makeRefIsNull(get); + if (choice & 1) { + // Try to also "deeply" log it, by reading a value from it, if we can. + auto heapType = type.getHeapType(); + if (heapType.isStruct()) { + auto& fields = heapType.getStruct().fields; + if (!fields.empty()) { + auto fieldIndex = upTo(fields.size()); + auto fieldType = fields[fieldIndex].type; + if (isLoggableType(fieldType)) { + // Do a deep logging after a null check. Or, if non-nullable, + // without the null check. + auto* get2 = builder.makeLocalGet(index, type); + auto* structGet = + builder.makeStructGet(fieldIndex, get2, fieldType); + auto* ifNonNull = makeLoggingCall(structGet); + if (type.isNonNullable()) { + return ifNonNull; + } + + // If the ref is null, log a random integer. The randomness is to + // avoid the risk of colliding with the value logged in the other + // arm. + auto* ifNull = makeLoggingCall(makeConst(Type::i32)); + return builder.makeIf(isNull, ifNull, ifNonNull); + } + } + } + } + + // All we can do is log the nullability as an i32. + return makeLoggingCall(isNull); + } + + // For anything else, log something loggable from scratch. + return makeLoggingCall(make(getLoggableType())); } Expression* TranslateToFuzzReader::makeMemoryHashLogging() { From 359a446a524e1a8f8467e6887eb9a7e7a31e1b53 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 6 Sep 2024 13:56:34 -0700 Subject: [PATCH 2/4] update --- test/passes/fuzz_metrics_noprint.bin.txt | 54 +++++++-------- ...e-to-fuzz_all-features_metrics_noprint.txt | 65 +++++++++---------- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/test/passes/fuzz_metrics_noprint.bin.txt b/test/passes/fuzz_metrics_noprint.bin.txt index 2f1719633b8..47bd9ce5bac 100644 --- a/test/passes/fuzz_metrics_noprint.bin.txt +++ b/test/passes/fuzz_metrics_noprint.bin.txt @@ -1,35 +1,35 @@ Metrics total - [exports] : 23 - [funcs] : 34 + [exports] : 58 + [funcs] : 81 [globals] : 9 [imports] : 4 [memories] : 1 [memory-data] : 2 - [table-data] : 6 + [table-data] : 33 [tables] : 1 [tags] : 0 - [total] : 4303 - [vars] : 100 - Binary : 355 - Block : 684 - Break : 149 - Call : 219 - CallIndirect : 23 - Const : 643 - Drop : 50 - GlobalGet : 367 - GlobalSet : 258 - If : 206 - Load : 78 - LocalGet : 339 - LocalSet : 236 - Loop : 93 - Nop : 41 - RefFunc : 6 - Return : 45 - Select : 41 - Store : 36 - Switch : 1 - Unary : 304 - Unreachable : 129 + [total] : 9015 + [vars] : 210 + Binary : 668 + Block : 1543 + Break : 293 + Call : 256 + CallIndirect : 64 + Const : 1436 + Drop : 92 + GlobalGet : 779 + GlobalSet : 585 + If : 485 + Load : 161 + LocalGet : 592 + LocalSet : 478 + Loop : 207 + Nop : 138 + RefFunc : 33 + Return : 89 + Select : 63 + Store : 85 + Switch : 7 + Unary : 667 + Unreachable : 294 diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index 07afaa7ebf4..d7a739de3d5 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,53 +1,48 @@ Metrics total [exports] : 5 - [funcs] : 9 + [funcs] : 12 [globals] : 26 [imports] : 5 [memories] : 1 [memory-data] : 20 - [table-data] : 3 + [table-data] : 6 [tables] : 1 [tags] : 2 - [total] : 669 - [vars] : 27 + [total] : 534 + [vars] : 22 ArrayNew : 16 ArrayNewFixed : 3 - AtomicCmpxchg : 1 AtomicFence : 1 - Binary : 75 - Block : 70 - Break : 7 - Call : 26 + Binary : 66 + Block : 53 + Break : 3 + Call : 8 CallRef : 1 - Const : 143 - Drop : 3 - GlobalGet : 37 - GlobalSet : 27 + Const : 130 + Drop : 2 + GlobalGet : 38 + GlobalSet : 26 I31Get : 1 - If : 20 - Load : 21 - LocalGet : 55 - LocalSet : 40 - Loop : 6 - Nop : 5 - Pop : 5 - RefAs : 2 - RefEq : 2 - RefFunc : 5 - RefI31 : 2 - RefNull : 11 - RefTest : 2 - Return : 6 - Select : 2 + If : 15 + Load : 17 + LocalGet : 35 + LocalSet : 21 + Loop : 3 + Nop : 3 + RefAs : 1 + RefCast : 1 + RefFunc : 9 + RefI31 : 1 + RefNull : 10 + Return : 4 + Select : 5 StringConst : 6 - StringEq : 1 - StringMeasure : 1 - StringWTF16Get : 1 - StructNew : 17 + StringMeasure : 2 + StructNew : 15 StructSet : 1 - Try : 4 - TupleExtract : 3 + Try : 1 + TupleExtract : 1 TupleMake : 5 - Unary : 20 + Unary : 15 Unreachable : 15 From afa2366c953dfcb5c706997c626eaa81132dbfac Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 6 Sep 2024 15:47:37 -0700 Subject: [PATCH 3/4] work --- src/tools/fuzzing/fuzzing.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 048b80e0558..468fb471984 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -720,9 +720,10 @@ Expression* TranslateToFuzzReader::makeLogging() { if (type.isRef()) { // This is a reference, which cannot be directly logged. We can at least - // "shallowly" log it by seeing if it is null. + // "shallowly" log it by seeing if it is null (if it is nullable - if not, + // then there is no point to such a check). auto* get = builder.makeLocalGet(index, type); - auto* isNull = builder.makeRefIsNull(get); + auto* isNullCheck = type.isNullable() ? builder.makeRefIsNull(get) : nullptr; if (choice & 1) { // Try to also "deeply" log it, by reading a value from it, if we can. auto heapType = type.getHeapType(); @@ -737,23 +738,27 @@ Expression* TranslateToFuzzReader::makeLogging() { auto* get2 = builder.makeLocalGet(index, type); auto* structGet = builder.makeStructGet(fieldIndex, get2, fieldType); - auto* ifNonNull = makeLoggingCall(structGet); - if (type.isNonNullable()) { - return ifNonNull; + auto* whenNonNull = makeLoggingCall(structGet); + if (!isNullCheck) { + assert(type.isNonNullable()); + return whenNonNull; } // If the ref is null, log a random integer. The randomness is to // avoid the risk of colliding with the value logged in the other // arm. - auto* ifNull = makeLoggingCall(makeConst(Type::i32)); - return builder.makeIf(isNull, ifNull, ifNonNull); + auto* whenNull = makeLoggingCall(makeConst(Type::i32)); + return builder.makeIf(isNullCheck, whenNull, whenNonNull); } } } } - // All we can do is log the nullability as an i32. - return makeLoggingCall(isNull); + // All we can do is log the nullability as an i32 (if it is nullable; if + // not then we'd be logging 1 all the time). + if (isNullCheck) { + return makeLoggingCall(isNullCheck); + } } // For anything else, log something loggable from scratch. From ac791506af33d21db22b0a82212b0a1f11a5e64d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 6 Sep 2024 15:47:50 -0700 Subject: [PATCH 4/4] format --- src/tools/fuzzing/fuzzing.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 468fb471984..7fb7e3ec72f 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -723,7 +723,8 @@ Expression* TranslateToFuzzReader::makeLogging() { // "shallowly" log it by seeing if it is null (if it is nullable - if not, // then there is no point to such a check). auto* get = builder.makeLocalGet(index, type); - auto* isNullCheck = type.isNullable() ? builder.makeRefIsNull(get) : nullptr; + auto* isNullCheck = + type.isNullable() ? builder.makeRefIsNull(get) : nullptr; if (choice & 1) { // Try to also "deeply" log it, by reading a value from it, if we can. auto heapType = type.getHeapType();