fix(parser): reject malformed TEXT frames and parse signed frame sizes
This commit is contained in:
+32
-8
@@ -210,18 +210,40 @@ func (p *state) parseText(line []token.Token) {
|
||||
rest = rest[1:]
|
||||
}
|
||||
|
||||
// Frame: $number ; optional args: -number. Whatever remains after the
|
||||
// header is the body and is parsed by the caller.
|
||||
// Frame: $[-]number ; optional args: -number. The Go runtime writes
|
||||
// zero frames with an explicit sign ("$-0-24"), so the number may carry
|
||||
// one. Whatever remains after the header is the body and is parsed by
|
||||
// the caller.
|
||||
if len(rest) > 0 && rest[0].Kind == token.Dollar {
|
||||
text.Frame = parseOperand(rest[:2]) // "$" "number"
|
||||
if len(rest) >= 4 && rest[2].Kind == token.Minus && rest[3].Kind == token.Number {
|
||||
n := 1
|
||||
neg := false
|
||||
if n < len(rest) && (rest[n].Kind == token.Minus || rest[n].Kind == token.Plus) {
|
||||
neg = rest[n].Kind == token.Minus
|
||||
n++
|
||||
}
|
||||
if n < len(rest) && rest[n].Kind == token.Number {
|
||||
val := parseInt(rest[n].Text)
|
||||
if neg {
|
||||
val = -val
|
||||
}
|
||||
text.Frame = &ast.Operand{
|
||||
Kind: ast.OpImmediate,
|
||||
Imm: ast.Immediate{Val: val, HasVal: true},
|
||||
Raw: joinRaw(rest[:n+1]),
|
||||
Pos: rest[0].Pos,
|
||||
}
|
||||
// The argument area: a minus sign followed by a number.
|
||||
if n+2 < len(rest) && rest[n+1].Kind == token.Minus && rest[n+2].Kind == token.Number {
|
||||
text.Args = &ast.Operand{
|
||||
Kind: ast.OpImmediate,
|
||||
Imm: ast.Immediate{Val: parseInt(rest[3].Text), HasVal: true},
|
||||
Raw: "-" + rest[3].Text,
|
||||
Pos: rest[2].Pos,
|
||||
Imm: ast.Immediate{Val: parseInt(rest[n+2].Text), HasVal: true},
|
||||
Raw: "-" + rest[n+2].Text,
|
||||
Pos: rest[n+1].Pos,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p.errorf(rest[0].Pos, "TEXT frame size must be a number after $")
|
||||
}
|
||||
}
|
||||
|
||||
p.file.Decls = append(p.file.Decls, text)
|
||||
@@ -234,8 +256,10 @@ func (p *state) parseGlobl(line []token.Token) *ast.Globl {
|
||||
sym, n := parseSymbolPrefix(rest)
|
||||
g.Name = sym
|
||||
rest = skipComma(rest[n:])
|
||||
// Flags are identifiers (RODATA, DUPOK) or legacy numeric constants
|
||||
// (2, 8, 9, 10) from runtime/textflag.h.
|
||||
for len(rest) > 0 && rest[0].Kind != token.Dollar {
|
||||
if rest[0].Kind == token.Ident {
|
||||
if rest[0].Kind == token.Ident || rest[0].Kind == token.Number {
|
||||
g.Flags = append(g.Flags, rest[0].Text)
|
||||
}
|
||||
rest = rest[1:]
|
||||
|
||||
@@ -253,3 +253,66 @@ func TestDataWidthAndStatic(t *testing.T) {
|
||||
t.Errorf("mask24 DATA should be static, got %+v", datas[2].Name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTruncatedFrameDollar is a regression test for a TEXT directive whose
|
||||
// frame size is missing after the $: the parser used to slice past the end
|
||||
// of the token slice and panic. It must report a diagnostic instead.
|
||||
func TestTruncatedFrameDollar(t *testing.T) {
|
||||
for _, src := range []string{
|
||||
"TEXT $\n",
|
||||
"TEXT \u00b7foo(SB), $\n",
|
||||
"TEXT \u00b7foo(SB), NOSPLIT, $\n",
|
||||
} {
|
||||
var file *ast.File
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("Parse(%q) panicked: %v", src, r)
|
||||
}
|
||||
}()
|
||||
file, _ = Parse("t.s", src)
|
||||
}()
|
||||
if file == nil {
|
||||
t.Fatalf("Parse(%q) returned no file", src)
|
||||
}
|
||||
if len(file.Decls) != 1 {
|
||||
t.Fatalf("Parse(%q) decls = %d, want 1", src, len(file.Decls))
|
||||
}
|
||||
txt := file.Decls[0].(*ast.Text)
|
||||
if txt.Frame != nil {
|
||||
t.Errorf("Parse(%q) frame = %v, want nil", src, txt.Frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFrameAndArgs parses a well-formed TEXT header and checks that the
|
||||
// frame and args operands are picked up.
|
||||
func TestFrameAndArgs(t *testing.T) {
|
||||
file, errs := Parse("t.s", "TEXT \u00b7foo(SB), $32-16\n\tRET\n")
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse errors: %v", errs)
|
||||
}
|
||||
txt := file.Decls[0].(*ast.Text)
|
||||
if txt.Frame == nil || !txt.Frame.Imm.HasVal || txt.Frame.Imm.Val != 32 {
|
||||
t.Errorf("frame = %+v, want $32", txt.Frame)
|
||||
}
|
||||
if txt.Args == nil || !txt.Args.Imm.HasVal || txt.Args.Imm.Val != 16 {
|
||||
t.Errorf("args = %+v, want -16", txt.Args)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSignedZeroFrame covers the Go runtime's "$-0-24" spelling: a zero
|
||||
// frame with an explicit sign plus the argument area.
|
||||
func TestSignedZeroFrame(t *testing.T) {
|
||||
file, errs := Parse("t.s", "TEXT \u00b7foo<ABIInternal>(SB), NOSPLIT, $-0-24\n\tRET\n")
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("parse errors: %v", errs)
|
||||
}
|
||||
txt := file.Decls[0].(*ast.Text)
|
||||
if txt.Frame == nil || !txt.Frame.Imm.HasVal || txt.Frame.Imm.Val != 0 {
|
||||
t.Errorf("frame = %+v, want $-0", txt.Frame)
|
||||
}
|
||||
if txt.Args == nil || !txt.Args.Imm.HasVal || txt.Args.Imm.Val != 24 {
|
||||
t.Errorf("args = %+v, want -24", txt.Args)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user