Skip to content

Commit f051945

Browse files
authored
BranchHinting fuzzing: Do not remove effects when deinstrumenting (#8613)
The BranchHinting fuzzer broke after #8608, but it wasn't that PR's fault - just that we now generate a different pattern of blocks that BranchHinting's deinstrumentation did not handle yet. The problem was that the block with the condition that we replace now has effects, so we can't always remove it wholesale.
1 parent 2d093c2 commit f051945

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

src/passes/InstrumentBranchHints.cpp

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
//
9797

9898
#include "ir/drop.h"
99+
#include "ir/effects.h"
99100
#include "ir/eh-utils.h"
100101
#include "ir/find_all.h"
101102
#include "ir/local-graph.h"
@@ -272,6 +273,8 @@ struct InstrumentationProcessor : public WalkerPass<PostWalker<Sub>> {
272273
// The condition before the instrumentation (a pointer to it, so we can
273274
// replace it).
274275
Expression** originalCondition;
276+
// The local that the original condition is stored in temporarily.
277+
Index tempLocal;
275278
// The call to the logging that the instrumentation added.
276279
Call* call;
277280
};
@@ -330,7 +333,7 @@ struct InstrumentationProcessor : public WalkerPass<PostWalker<Sub>> {
330333
return {};
331334
}
332335
// Great, this is indeed a prior instrumentation.
333-
return Instrumentation{&set->value, call};
336+
return Instrumentation{&set->value, set->index, call};
334337
}
335338
};
336339

@@ -374,7 +377,27 @@ struct DeInstrumentBranchHints
374377
// IR, and the original condition is still used in another place, until
375378
// we remove the logging calls; since we will remove the calls anyhow, we
376379
// just need some valid IR there).
377-
std::swap(curr->condition, *info->originalCondition);
380+
//
381+
// Check for dangerous effects in the condition we are about to replace,
382+
// to avoid a situation where the condition looks like this:
383+
//
384+
// (set $temp (original condition))
385+
// ..effects..
386+
// (local.get $temp)
387+
//
388+
// We cannot replace all this with the original condition, as it would
389+
// remove the effects. (Even in that case we will remove the actual call
390+
// to log the branch hint, below, so this just prevents some cleanup that
391+
// is normally safe - the cleanup is mainly useful to allow inspection of
392+
// testcases for debugging.)
393+
EffectAnalyzer effects(getPassOptions(), *getModule(), curr->condition);
394+
// The only condition we allow is a write to the temp local from the
395+
// instrumentation, which getInstrumentation() verified has no other uses
396+
// than us.
397+
effects.localsWritten.erase(info->tempLocal);
398+
if (!effects.hasUnremovableSideEffects()) {
399+
std::swap(curr->condition, *info->originalCondition);
400+
}
378401
}
379402
}
380403

@@ -403,6 +426,21 @@ struct DeInstrumentBranchHints
403426
}
404427
}
405428
}
429+
430+
void doWalkModule(Module* module) {
431+
auto logBranchImport = getLogBranchImport(module);
432+
if (!logBranchImport) {
433+
Fatal()
434+
<< "No branch hint logging import found. Was this code instrumented?";
435+
}
436+
437+
// Mark the log-branch import as having no side effects - we are removing it
438+
// entirely here, and its effect should not stop us when we compute effects.
439+
module->getFunction(logBranchImport)->effects =
440+
std::make_shared<EffectAnalyzer>(getPassOptions(), *module);
441+
442+
InstrumentationProcessor<DeInstrumentBranchHints>::doWalkModule(module);
443+
}
406444
};
407445

408446
} // anonymous namespace

test/lit/passes/deinstrument-branch-hints.wast

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
;; CHECK: (type $1 (func (param i32 i32 i32)))
99

10+
;; CHECK: (type $2 (func (result i32)))
11+
1012
;; CHECK: (import "fuzzing-support" "log-branch" (func $log (type $1) (param i32 i32 i32)))
1113
(import "fuzzing-support" "log-branch" (func $log (param i32 i32 i32)))
1214

@@ -164,4 +166,57 @@
164166
)
165167
)
166168
)
169+
170+
;; CHECK: (func $if-unreachable (type $2) (result i32)
171+
;; CHECK-NEXT: (local $0 i32)
172+
;; CHECK-NEXT: (block $block (result i32)
173+
;; CHECK-NEXT: (br_if $block
174+
;; CHECK-NEXT: (i32.const 0)
175+
;; CHECK-NEXT: (block (result i32)
176+
;; CHECK-NEXT: (block
177+
;; CHECK-NEXT: (local.set $0
178+
;; CHECK-NEXT: (i32.const 42)
179+
;; CHECK-NEXT: )
180+
;; CHECK-NEXT: (if
181+
;; CHECK-NEXT: (i32.const 1)
182+
;; CHECK-NEXT: (then
183+
;; CHECK-NEXT: (unreachable)
184+
;; CHECK-NEXT: )
185+
;; CHECK-NEXT: )
186+
;; CHECK-NEXT: )
187+
;; CHECK-NEXT: (nop)
188+
;; CHECK-NEXT: (local.get $0)
189+
;; CHECK-NEXT: )
190+
;; CHECK-NEXT: )
191+
;; CHECK-NEXT: )
192+
;; CHECK-NEXT: )
193+
(func $if-unreachable (result i32)
194+
(local $0 i32)
195+
;; The unreachable here must be executed. Normally we replace the br_if's
196+
;; entire condition, but here we only remove the call to $log.
197+
(block $block (result i32)
198+
(br_if $block
199+
(i32.const 0)
200+
(block (result i32)
201+
(block
202+
(local.set $0
203+
(i32.const 42)
204+
)
205+
(if
206+
(i32.const 1)
207+
(then
208+
(unreachable)
209+
)
210+
)
211+
)
212+
(call $log
213+
(i32.const 0)
214+
(i32.const 0)
215+
(local.get $0)
216+
)
217+
(local.get $0)
218+
)
219+
)
220+
)
221+
)
167222
)

0 commit comments

Comments
 (0)