Re: [PATCH] plugin: fix clearing of plugin_mem_cbs before TB exit

2023-03-01 Thread Emilio Cota
On Tue, Feb 28, 2023 at 10:50:26 -1000, Richard Henderson wrote:
> On 2/21/23 18:32, Emilio Cota wrote:
> > Currently we are wrongly accessing plugin_tb->mem_helper at
> > translation time from plugin_gen_disable_mem_helpers, which is
> > called before generating a TB exit, e.g. with exit_tb.
> > 
> > Recall that it is only during TB finalisation, i.e. when we go over
> > the TB post-translation to inject or remove plugin instrumentation,
> > when plugin_tb->mem_helper is set. This means that we never clear
> > plugin_mem_cbs when calling plugin_gen_disable_mem_helpers since
> > mem_helper is always false. Therefore a guest instruction that uses
> > helpers and emits an explicit TB exit results in plugin_mem_cbs being
> > set upon exiting, which is caught by an assertion as reported in
> > the reopening of issue #1381 and replicated below.
> > 
> > Fix this by (1) adding an insertion point before exiting a TB
> > ("before_exit"), and (2) deciding whether or not to emit the
> > clearing of plugin_mem_cbs at this newly-added insertion point
> > during TB finalisation.
> 
> This is an improvement, but incomplete, because it does not handle the
> exception exit case, via cpu_loop_exit.

AFAICT that is already handled -- see the call to
qemu_plugin_disable_mem_helpers upon returning from the longjmp
in cpu-exec.c.

I do think that doing the clearing in C as done in your series
is a better solution. It is simpler and what I most like about
it is that it generates less code. In fact I wanted to mention
that approach as an alternative in the commit log, but forgot
to do so.

Thanks,
Emilio



Re: [PATCH] plugin: fix clearing of plugin_mem_cbs before TB exit

2023-02-28 Thread Richard Henderson

On 2/21/23 18:32, Emilio Cota wrote:

Currently we are wrongly accessing plugin_tb->mem_helper at
translation time from plugin_gen_disable_mem_helpers, which is
called before generating a TB exit, e.g. with exit_tb.

Recall that it is only during TB finalisation, i.e. when we go over
the TB post-translation to inject or remove plugin instrumentation,
when plugin_tb->mem_helper is set. This means that we never clear
plugin_mem_cbs when calling plugin_gen_disable_mem_helpers since
mem_helper is always false. Therefore a guest instruction that uses
helpers and emits an explicit TB exit results in plugin_mem_cbs being
set upon exiting, which is caught by an assertion as reported in
the reopening of issue #1381 and replicated below.

Fix this by (1) adding an insertion point before exiting a TB
("before_exit"), and (2) deciding whether or not to emit the
clearing of plugin_mem_cbs at this newly-added insertion point
during TB finalisation.


This is an improvement, but incomplete, because it does not handle the exception exit 
case, via cpu_loop_exit.



r~



Re: [PATCH] plugin: fix clearing of plugin_mem_cbs before TB exit

2023-02-28 Thread Frédéric Pétrot

Le 22/02/2023 à 05:32, Emilio Cota a écrit :

Currently we are wrongly accessing plugin_tb->mem_helper at
translation time from plugin_gen_disable_mem_helpers, which is
called before generating a TB exit, e.g. with exit_tb.

Recall that it is only during TB finalisation, i.e. when we go over
the TB post-translation to inject or remove plugin instrumentation,
when plugin_tb->mem_helper is set. This means that we never clear
plugin_mem_cbs when calling plugin_gen_disable_mem_helpers since
mem_helper is always false. Therefore a guest instruction that uses
helpers and emits an explicit TB exit results in plugin_mem_cbs being
set upon exiting, which is caught by an assertion as reported in
the reopening of issue #1381 and replicated below.

Fix this by (1) adding an insertion point before exiting a TB
("before_exit"), and (2) deciding whether or not to emit the
clearing of plugin_mem_cbs at this newly-added insertion point
during TB finalisation.

While at it, suffix plugin_gen_disable_mem_helpers with _before_exit
to make its intent more clear.

- Before:
$ ./qemu-system-riscv32 -M spike -nographic -plugin 
contrib/plugins/libexeclog.so -d plugin,in_asm,op
IN:
Priv: 3; Virt: 0
0x1000:  0297  auipc   t0,0
# 0x1000
0x1004:  02828613  addia2,t0,40
0x1008:  f1402573  csrrs   a0,mhartid,zero

OP:
  ld_i32 tmp1,env,$0xfff0
  brcond_i32 tmp1,$0x0,lt,$L0

   1000 
  mov_i64 tmp2,$0x7ff9940152e0
  ld_i32 tmp1,env,$0xef80
  call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2
  mov_i32 x5/t0,$0x1000

   1004 
  mov_i64 tmp2,$0x7ff9940153e0
  ld_i32 tmp1,env,$0xef80
  call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2
  add_i32 x12/a2,x5/t0,$0x28

   1008 f1402573
  mov_i64 tmp2,$0x7ff9940136c0
  st_i64 tmp2,env,$0xef68
  mov_i64 tmp2,$0x7ff994015530
  ld_i32 tmp1,env,$0xef80
  call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2 <-- sets plugin_mem_cbs
  call csrr,$0x0,$1,x10/a0,env,$0xf14  <-- helper that might access memory
  mov_i32 pc,$0x100c
  exit_tb $0x0  <-- exit_tb right after the helper; missing clearing of 
plugin_mem_cbs
  mov_i64 tmp2,$0x0
  st_i64 tmp2,env,$0xef68 <-- after_insn clearing: dead due to 
exit_tb above
  set_label $L0
  exit_tb $0x7ff9a443 <-- again, missing clearing (doesn't matter due to 
$L0 label)

0, 0x1000, 0x297, "0297  auipc   t0,0
# 0x1000"
0, 0x1004, 0x2828613, "02828613  addia2,t0,40"
**
ERROR:../accel/tcg/cpu-exec.c:983:cpu_exec_loop: assertion failed: 
(cpu->plugin_mem_cbs == ((void *)0))
Bail out! ERROR:../accel/tcg/cpu-exec.c:983:cpu_exec_loop: assertion failed: 
(cpu->plugin_mem_cbs == ((void *)0))
Aborted (core dumped)

- After:
$ ./qemu-system-riscv32 -M spike -nographic -plugin 
contrib/plugins/libexeclog.so -d plugin,in_asm,op
(snip)
  call plugin(0x7f19bc9e36f0),$0x11,$0,tmp1,tmp2 <-- sets plugin_mem_cbs
  call csrr,$0x0,$1,x10/a0,env,$0xf14
  mov_i32 pc,$0x100c
  mov_i64 tmp2,$0x0
  st_i64 tmp2,env,$0xef68 <-- before_exit clearing of plugin_mem_cbs
  exit_tb $0x0
  mov_i64 tmp2,$0x0
  st_i64 tmp2,env,$0xef68 <-- after_insn clearing (dead)
  set_label $L0
  mov_i64 tmp2,$0x0
  st_i64 tmp2,env,$0xef68 <-- before_exit clearing (doesn't matter 
due to $L0)
  exit_tb $0x7f38c843
[...]

Fixes: #1381
Signed-off-by: Emilio Cota 
---
  accel/tcg/plugin-gen.c| 44 ---
  include/exec/plugin-gen.h |  4 ++--
  include/qemu/plugin.h |  3 ---
  tcg/tcg-op.c  |  6 +++---
  4 files changed, 28 insertions(+), 29 deletions(-)


   Thanks Emilio for the fix, and Aaron for pointing it out to me.

   Tested-by: Frédéric Pétrot 



[PATCH] plugin: fix clearing of plugin_mem_cbs before TB exit

2023-02-21 Thread Emilio Cota
Currently we are wrongly accessing plugin_tb->mem_helper at
translation time from plugin_gen_disable_mem_helpers, which is
called before generating a TB exit, e.g. with exit_tb.

Recall that it is only during TB finalisation, i.e. when we go over
the TB post-translation to inject or remove plugin instrumentation,
when plugin_tb->mem_helper is set. This means that we never clear
plugin_mem_cbs when calling plugin_gen_disable_mem_helpers since
mem_helper is always false. Therefore a guest instruction that uses
helpers and emits an explicit TB exit results in plugin_mem_cbs being
set upon exiting, which is caught by an assertion as reported in
the reopening of issue #1381 and replicated below.

Fix this by (1) adding an insertion point before exiting a TB
("before_exit"), and (2) deciding whether or not to emit the
clearing of plugin_mem_cbs at this newly-added insertion point
during TB finalisation.

While at it, suffix plugin_gen_disable_mem_helpers with _before_exit
to make its intent more clear.

- Before:
$ ./qemu-system-riscv32 -M spike -nographic -plugin 
contrib/plugins/libexeclog.so -d plugin,in_asm,op
IN:
Priv: 3; Virt: 0
0x1000:  0297  auipc   t0,0
# 0x1000
0x1004:  02828613  addia2,t0,40
0x1008:  f1402573  csrrs   a0,mhartid,zero

OP:
 ld_i32 tmp1,env,$0xfff0
 brcond_i32 tmp1,$0x0,lt,$L0

  1000 
 mov_i64 tmp2,$0x7ff9940152e0
 ld_i32 tmp1,env,$0xef80
 call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2
 mov_i32 x5/t0,$0x1000

  1004 
 mov_i64 tmp2,$0x7ff9940153e0
 ld_i32 tmp1,env,$0xef80
 call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2
 add_i32 x12/a2,x5/t0,$0x28

  1008 f1402573
 mov_i64 tmp2,$0x7ff9940136c0
 st_i64 tmp2,env,$0xef68
 mov_i64 tmp2,$0x7ff994015530
 ld_i32 tmp1,env,$0xef80
 call plugin(0x7ff9edbcb6f0),$0x11,$0,tmp1,tmp2 <-- sets plugin_mem_cbs
 call csrr,$0x0,$1,x10/a0,env,$0xf14  <-- helper that might access memory
 mov_i32 pc,$0x100c
 exit_tb $0x0  <-- exit_tb right after the helper; missing clearing of 
plugin_mem_cbs
 mov_i64 tmp2,$0x0
 st_i64 tmp2,env,$0xef68 <-- after_insn clearing: dead due to 
exit_tb above
 set_label $L0
 exit_tb $0x7ff9a443 <-- again, missing clearing (doesn't matter due to $L0 
label)

0, 0x1000, 0x297, "0297  auipc   t0,0   
 # 0x1000"
0, 0x1004, 0x2828613, "02828613  addia2,t0,40"
**
ERROR:../accel/tcg/cpu-exec.c:983:cpu_exec_loop: assertion failed: 
(cpu->plugin_mem_cbs == ((void *)0))
Bail out! ERROR:../accel/tcg/cpu-exec.c:983:cpu_exec_loop: assertion failed: 
(cpu->plugin_mem_cbs == ((void *)0))
Aborted (core dumped)

- After:
$ ./qemu-system-riscv32 -M spike -nographic -plugin 
contrib/plugins/libexeclog.so -d plugin,in_asm,op
(snip)
 call plugin(0x7f19bc9e36f0),$0x11,$0,tmp1,tmp2 <-- sets plugin_mem_cbs
 call csrr,$0x0,$1,x10/a0,env,$0xf14
 mov_i32 pc,$0x100c
 mov_i64 tmp2,$0x0
 st_i64 tmp2,env,$0xef68 <-- before_exit clearing of plugin_mem_cbs
 exit_tb $0x0
 mov_i64 tmp2,$0x0
 st_i64 tmp2,env,$0xef68 <-- after_insn clearing (dead)
 set_label $L0
 mov_i64 tmp2,$0x0
 st_i64 tmp2,env,$0xef68 <-- before_exit clearing (doesn't matter 
due to $L0)
 exit_tb $0x7f38c843
[...]

Fixes: #1381
Signed-off-by: Emilio Cota 
---
 accel/tcg/plugin-gen.c| 44 ---
 include/exec/plugin-gen.h |  4 ++--
 include/qemu/plugin.h |  3 ---
 tcg/tcg-op.c  |  6 +++---
 4 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 17a686bd9e..b4fc171b8e 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -67,6 +67,7 @@ enum plugin_gen_from {
 PLUGIN_GEN_FROM_INSN,
 PLUGIN_GEN_FROM_MEM,
 PLUGIN_GEN_AFTER_INSN,
+PLUGIN_GEN_BEFORE_EXIT,
 PLUGIN_GEN_N_FROMS,
 };
 
@@ -177,6 +178,7 @@ static void plugin_gen_empty_callback(enum plugin_gen_from 
from)
 {
 switch (from) {
 case PLUGIN_GEN_AFTER_INSN:
+case PLUGIN_GEN_BEFORE_EXIT:
 gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
 gen_empty_mem_helper);
 break;
@@ -575,7 +577,7 @@ static void inject_mem_helper(TCGOp *begin_op, GArray *arr)
  * that we can read them at run-time (i.e. when the helper executes).
  * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
  *
- * Note that plugin_gen_disable_mem_helpers undoes (2). Since it
+ * Note that inject_mem_disable_helper undoes (2). Since it
  * is possible that the code we generate after the instruction is
  * dead, we also add checks before generating tb_exit etc.
  */
@@ -600,7 +602,6 @@ static void inject_mem_enable_helper(struct qemu_plugin_tb 
*ptb,
 rm_ops(begin_op);
 return;
 }
-ptb->mem_helper = true;
 
 arr =