From b8e433821adca1d1b68abb584e606a1217434894 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 21 May 2022 15:46:00 +0200 Subject: [PATCH] interp: fix some buggy localValue handling Bug: 1. fn.locals[v.value] returns 0 (the default value) if v.value is not part of the fn.locals map. 2. locals[fn.locals[v.value]] then returns the first local value, which is usually non-nil 3. This incorrect value is then used as the operand value. The manifestation of this convoluted bug was https://github.com/tinygo-org/tinygo/issues/2842. It didn't occur more often probably because it only seems to happen in practice with inline assembly. Fixes https://github.com/tinygo-org/tinygo/issues/2842 --- interp/interpreter.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/interp/interpreter.go b/interp/interpreter.go index 0f2355a6..cccbe7b3 100644 --- a/interp/interpreter.go +++ b/interp/interpreter.go @@ -81,12 +81,28 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent if inst.opcode != llvm.PHI { for _, v := range inst.operands { if v, ok := v.(localValue); ok { - if localVal := locals[fn.locals[v.value]]; localVal == nil { + index, ok := fn.locals[v.value] + if !ok { + // This is a localValue that is not local to the + // function. An example would be an inline assembly call + // operand. + isRuntimeInst = true + break + } + localVal := locals[index] + if localVal == nil { + // Trying to read a function-local value before it is + // set. return nil, mem, r.errorAt(inst, errors.New("interp: local not defined")) } else { operands = append(operands, localVal) if _, ok := localVal.(localValue); ok { + // The function-local value is still just a + // localValue (which can't be interpreted at compile + // time). Not sure whether this ever happens in + // practice. isRuntimeInst = true + break } continue }