Skip to content

Commit 97ffce3

Browse files
authored
[Strings] Fuzz string.encode (#6539)
A little trickier than the others due to the risk of trapping, which this handles like the other array operations. Also stop using immutable i16 arrays for string operations - only mutable ones work atm.
1 parent aa93162 commit 97ffce3

3 files changed

Lines changed: 82 additions & 46 deletions

File tree

src/tools/fuzzing.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class TranslateToFuzzReader {
327327
Expression* makeStringNewArray();
328328
Expression* makeStringNewCodePoint();
329329
Expression* makeStringConcat();
330+
Expression* makeStringEncode(Type type);
330331

331332
// Similar to makeBasic/CompoundRef, but indicates that this value will be
332333
// used in a place that will trap on null. For example, the reference of a
@@ -391,13 +392,13 @@ class TranslateToFuzzReader {
391392
Type getLoggableType();
392393
bool isLoggableType(Type type);
393394
Nullability getNullability();
394-
Mutability getMutability();
395395
Nullability getSubType(Nullability nullability);
396396
HeapType getSubType(HeapType type);
397397
Type getSubType(Type type);
398398
Nullability getSuperType(Nullability nullability);
399399
HeapType getSuperType(HeapType type);
400400
Type getSuperType(Type type);
401+
Type getArrayTypeForString();
401402

402403
// Utilities
403404
Name getTargetName(Expression* target);

src/tools/fuzzing/fuzzing.cpp

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,9 @@ Expression* TranslateToFuzzReader::_makeConcrete(Type type) {
13661366
&Self::makeRefEq,
13671367
&Self::makeRefTest,
13681368
&Self::makeI31Get);
1369+
options.add(FeatureSet::ReferenceTypes | FeatureSet::GC |
1370+
FeatureSet::Strings,
1371+
&Self::makeStringEncode);
13691372
}
13701373
if (type.isTuple()) {
13711374
options.add(FeatureSet::Multivalue, &Self::makeTupleMake);
@@ -2752,12 +2755,7 @@ Expression* TranslateToFuzzReader::makeCompoundRef(Type type) {
27522755
}
27532756

27542757
Expression* TranslateToFuzzReader::makeStringNewArray() {
2755-
auto mutability = getMutability();
2756-
auto arrayHeapType =
2757-
HeapType(Array(Field(Field::PackedType::i16, mutability)));
2758-
auto nullability = getNullability();
2759-
auto arrayType = Type(arrayHeapType, nullability);
2760-
auto array = make(arrayType);
2758+
auto* array = make(getArrayTypeForString());
27612759
auto* start = make(Type::i32);
27622760
auto* end = make(Type::i32);
27632761
return builder.makeStringNew(StringNewWTF16Array, array, start, end, false);
@@ -2814,8 +2812,8 @@ Expression* TranslateToFuzzReader::makeStringConst() {
28142812
}
28152813

28162814
Expression* TranslateToFuzzReader::makeStringConcat() {
2817-
auto left = make(Type(HeapType::string, getNullability()));
2818-
auto right = make(Type(HeapType::string, getNullability()));
2815+
auto* left = make(Type(HeapType::string, getNullability()));
2816+
auto* right = make(Type(HeapType::string, getNullability()));
28192817
return builder.makeStringConcat(left, right);
28202818
}
28212819

@@ -3806,9 +3804,9 @@ static auto makeArrayBoundsCheck(Expression* ref,
38063804
// An additional use of the reference (we stored the reference in a local,
38073805
// so this reads from that local).
38083806
Expression* getRef;
3809-
// An addition use of the index (as with the ref, it reads from a local).
3807+
// An additional use of the index (as with the ref, it reads from a local).
38103808
Expression* getIndex;
3811-
// An addition use of the length, if it was provided.
3809+
// An additional use of the length, if it was provided.
38123810
Expression* getLength = nullptr;
38133811
} result = {builder.makeBinary(LtUInt32, effectiveIndex, getSize),
38143812
builder.makeLocalGet(tempRef, ref->type),
@@ -3919,6 +3917,40 @@ Expression* TranslateToFuzzReader::makeArrayBulkMemoryOp(Type type) {
39193917
}
39203918
}
39213919

3920+
Expression* TranslateToFuzzReader::makeStringEncode(Type type) {
3921+
assert(type == Type::i32);
3922+
3923+
auto* ref = make(Type(HeapType::string, getNullability()));
3924+
auto* array = make(getArrayTypeForString());
3925+
auto* start = make(Type::i32);
3926+
3927+
// Only rarely emit without a bounds check, which might trap. See related
3928+
// logic in other array operations.
3929+
if (allowOOB || oneIn(10)) {
3930+
return builder.makeStringEncode(StringEncodeWTF16Array, ref, array, start);
3931+
}
3932+
3933+
// Stash the string reference while computing its length for a bounds check.
3934+
auto refLocal = builder.addVar(funcContext->func, ref->type);
3935+
auto* setRef = builder.makeLocalSet(refLocal, ref);
3936+
auto* strLen = builder.makeStringMeasure(
3937+
StringMeasureWTF16, builder.makeLocalGet(refLocal, ref->type));
3938+
3939+
// Do a bounds check on the array.
3940+
auto check =
3941+
makeArrayBoundsCheck(array, start, funcContext->func, builder, strLen);
3942+
array = check.getRef;
3943+
start = check.getIndex;
3944+
auto* getRef = builder.makeLocalGet(refLocal, ref->type);
3945+
auto* encode =
3946+
builder.makeStringEncode(StringEncodeWTF16Array, getRef, array, start);
3947+
3948+
// Emit the set of the string reference and then an if that picks which code
3949+
// path to visit, depending on the outcome of the bounds check.
3950+
auto* iff = builder.makeIf(check.condition, encode, make(Type::i32));
3951+
return builder.makeSequence(setRef, iff);
3952+
}
3953+
39223954
Expression* TranslateToFuzzReader::makeI31Get(Type type) {
39233955
assert(type == Type::i32);
39243956
assert(wasm.features.hasReferenceTypes() && wasm.features.hasGC());
@@ -4136,10 +4168,6 @@ Nullability TranslateToFuzzReader::getNullability() {
41364168
return Nullable;
41374169
}
41384170

4139-
Mutability TranslateToFuzzReader::getMutability() {
4140-
return oneIn(2) ? Mutable : Immutable;
4141-
}
4142-
41434171
Nullability TranslateToFuzzReader::getSubType(Nullability nullability) {
41444172
if (nullability == NonNullable) {
41454173
return NonNullable;
@@ -4279,6 +4307,14 @@ Type TranslateToFuzzReader::getSuperType(Type type) {
42794307
return superType;
42804308
}
42814309

4310+
Type TranslateToFuzzReader::getArrayTypeForString() {
4311+
// Emit an array that can be used with JS-style strings, containing 16-bit
4312+
// elements. For now, this must be a mutable type as that is all V8 accepts.
4313+
auto arrayHeapType = HeapType(Array(Field(Field::PackedType::i16, Mutable)));
4314+
auto nullability = getNullability();
4315+
return Type(arrayHeapType, nullability);
4316+
}
4317+
42824318
Name TranslateToFuzzReader::getTargetName(Expression* target) {
42834319
if (auto* block = target->dynCast<Block>()) {
42844320
return block->name;
Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,43 @@
11
total
2-
[exports] : 3
3-
[funcs] : 5
2+
[exports] : 7
3+
[funcs] : 12
44
[globals] : 14
55
[imports] : 5
66
[memories] : 1
77
[memory-data] : 20
8-
[table-data] : 1
8+
[table-data] : 6
99
[tables] : 1
1010
[tags] : 1
11-
[total] : 387
12-
[vars] : 23
11+
[total] : 493
12+
[vars] : 31
1313
ArrayNew : 1
14-
ArrayNewFixed : 3
15-
AtomicCmpxchg : 1
16-
AtomicNotify : 1
17-
Binary : 57
18-
Block : 33
14+
ArrayNewFixed : 1
15+
Binary : 61
16+
Block : 59
1917
Break : 2
20-
Call : 4
21-
Const : 95
22-
Drop : 2
23-
GlobalGet : 15
24-
GlobalSet : 15
25-
If : 9
18+
Call : 29
19+
CallRef : 1
20+
Const : 101
21+
Drop : 10
22+
GlobalGet : 28
23+
GlobalSet : 26
24+
I31Get : 1
25+
If : 17
2626
Load : 16
27-
LocalGet : 41
28-
LocalSet : 20
29-
Loop : 3
30-
Nop : 2
31-
Pop : 1
32-
RefAs : 3
33-
RefFunc : 2
27+
LocalGet : 36
28+
LocalSet : 29
29+
Loop : 2
30+
Nop : 5
31+
RefAs : 1
32+
RefCast : 2
33+
RefFunc : 8
3434
RefI31 : 4
35-
RefNull : 5
35+
RefNull : 4
3636
Return : 3
37-
Store : 1
37+
SIMDExtract : 1
3838
StringConst : 1
39-
StructNew : 11
40-
Try : 1
41-
TupleExtract : 7
42-
TupleMake : 11
43-
Unary : 9
44-
Unreachable : 8
39+
StructNew : 5
40+
TupleExtract : 1
41+
TupleMake : 7
42+
Unary : 16
43+
Unreachable : 15

0 commit comments

Comments
 (0)