Commit 63e2bed0 authored by Didier Spezia's avatar Didier Spezia Committed by Rob Pike

cmd/asm: fix another infinite loop in register list parser

The code parsing register lists involves an inner loop on
each range defined by the lo,hi bounds. The condition on
this loop (for lo<=hi) is fragile, because the bounds
are unsigned 16 bits numbers.

In some corner cases, the calculated upper bound is 2^16-1
leading to an infinite loop.

Parsing operand `[):[o-FP` results in:
- an infinite loop for non ARM architectures
- the generation of almost 2^16 errors for the ARM architecture
  (which are then ignored)

This CL improves the code in 3 ways:
- bail out early when parsing non R prefixed registers
- make sure the register index is never negative
- make sure the number of iterations is limited by the
  maximum size of the range (as a defensive measure).

Fixes #12469

Change-Id: Ib1e7e36fb8ad5a3a52c50fc6219d3cfe2b39cc34
Reviewed-on: https://go-review.googlesource.com/14314Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 22452ac5
...@@ -183,6 +183,7 @@ var amd64OperandTests = []operandTest{ ...@@ -183,6 +183,7 @@ var amd64OperandTests = []operandTest{
{"y+56(FP)", "y+56(FP)"}, {"y+56(FP)", "y+56(FP)"},
{"·AddUint32(SB)", "\"\".AddUint32(SB)"}, {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
{"·callReflect(SB)", "\"\".callReflect(SB)"}, {"·callReflect(SB)", "\"\".callReflect(SB)"},
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
} }
var x86OperandTests = []operandTest{ var x86OperandTests = []operandTest{
...@@ -240,6 +241,7 @@ var x86OperandTests = []operandTest{ ...@@ -240,6 +241,7 @@ var x86OperandTests = []operandTest{
{"x+4(FP)", "x+4(FP)"}, {"x+4(FP)", "x+4(FP)"},
{"·AddUint32(SB)", "\"\".AddUint32(SB)"}, {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
{"·reflectcall(SB)", "\"\".reflectcall(SB)"}, {"·reflectcall(SB)", "\"\".reflectcall(SB)"},
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
} }
var armOperandTests = []operandTest{ var armOperandTests = []operandTest{
...@@ -288,7 +290,9 @@ var armOperandTests = []operandTest{ ...@@ -288,7 +290,9 @@ var armOperandTests = []operandTest{
{"runtime·_sfloat2(SB)", "runtime._sfloat2(SB)"}, {"runtime·_sfloat2(SB)", "runtime._sfloat2(SB)"},
{"·AddUint32(SB)", "\"\".AddUint32(SB)"}, {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
{"(R1, R3)", "(R1, R3)"}, {"(R1, R3)", "(R1, R3)"},
{"[R0,R1,g,R15", ""}, // Issue 11764 - previously asm just hung parsing ']' missing register lists {"[R0,R1,g,R15", ""}, // Issue 11764 - asm hung parsing ']' missing register lists.
{"[):[o-FP", ""}, // Issue 12469 - there was no infinite loop for ARM; these are just sanity checks.
{"[):[R0-FP", ""},
} }
var ppc64OperandTests = []operandTest{ var ppc64OperandTests = []operandTest{
...@@ -378,6 +382,7 @@ var ppc64OperandTests = []operandTest{ ...@@ -378,6 +382,7 @@ var ppc64OperandTests = []operandTest{
{"runtime·abort(SB)", "runtime.abort(SB)"}, {"runtime·abort(SB)", "runtime.abort(SB)"},
{"·AddUint32(SB)", "\"\".AddUint32(SB)"}, {"·AddUint32(SB)", "\"\".AddUint32(SB)"},
{"·trunc(SB)", "\"\".trunc(SB)"}, {"·trunc(SB)", "\"\".trunc(SB)"},
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
} }
var arm64OperandTests = []operandTest{ var arm64OperandTests = []operandTest{
...@@ -427,4 +432,5 @@ var arm64OperandTests = []operandTest{ ...@@ -427,4 +432,5 @@ var arm64OperandTests = []operandTest{
{"ZR", "ZR"}, {"ZR", "ZR"},
{"(ZR)", "(ZR)"}, {"(ZR)", "(ZR)"},
{"(R29, RSP)", "(R29, RSP)"}, {"(R29, RSP)", "(R29, RSP)"},
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
} }
...@@ -702,6 +702,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) { ...@@ -702,6 +702,7 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
// The opening bracket has been consumed. // The opening bracket has been consumed.
func (p *Parser) registerList(a *obj.Addr) { func (p *Parser) registerList(a *obj.Addr) {
// One range per loop. // One range per loop.
const maxReg = 16
var bits uint16 var bits uint16
ListLoop: ListLoop:
for { for {
...@@ -713,6 +714,7 @@ ListLoop: ...@@ -713,6 +714,7 @@ ListLoop:
p.errorf("missing ']' in register list") p.errorf("missing ']' in register list")
return return
} }
// Parse the upper and lower bounds.
lo := p.registerNumber(tok.String()) lo := p.registerNumber(tok.String())
hi := lo hi := lo
if p.peek() == '-' { if p.peek() == '-' {
...@@ -722,7 +724,8 @@ ListLoop: ...@@ -722,7 +724,8 @@ ListLoop:
if hi < lo { if hi < lo {
lo, hi = hi, lo lo, hi = hi, lo
} }
for lo <= hi { // Check there are no duplicates in the register list.
for i := 0; lo <= hi && i < maxReg; i++ {
if bits&(1<<lo) != 0 { if bits&(1<<lo) != 0 {
p.errorf("register R%d already in list", lo) p.errorf("register R%d already in list", lo)
} }
...@@ -744,12 +747,19 @@ func (p *Parser) registerNumber(name string) uint16 { ...@@ -744,12 +747,19 @@ func (p *Parser) registerNumber(name string) uint16 {
} }
if name[0] != 'R' { if name[0] != 'R' {
p.errorf("expected g or R0 through R15; found %s", name) p.errorf("expected g or R0 through R15; found %s", name)
return 0
} }
r, ok := p.registerReference(name) r, ok := p.registerReference(name)
if !ok { if !ok {
return 0 return 0
} }
return uint16(r - p.arch.Register["R0"]) reg := r - p.arch.Register["R0"]
if reg < 0 {
// Could happen for an architecture having other registers prefixed by R
p.errorf("expected g or R0 through R15; found %s", name)
return 0
}
return uint16(reg)
} }
// Note: There are two changes in the expression handling here // Note: There are two changes in the expression handling here
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment