diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-12-13 19:18:07 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-12-17 11:39:14 +0100 |
| commit | 41799debdcc19d65fb6e0499cbbb74a489a822ea (patch) | |
| tree | 8b7107794ac883476fb0f79717e405cb58669c3e /prog/encoding.go | |
| parent | 286edfb78e2e77c644b32d161e8e1b188406a5c5 (diff) | |
prog: introduce more readable format for data args
Fixes #460
File names, crypto algorithm names, etc in programs are completely unreadable:
bind$alg(r0, &(0x7f0000408000)={0x26, "6861736800000000000000000000",
0x0, 0x0, "6d6435000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
00000000000"}, 0x58)
Introduce another format for printable strings.
New args are denoted by '' ("" for old args).
New format is enabled for printable chars, \x00
and \t, \r, \n.
Example:
`serialize(&(0x7f0000408000)={"6861736800000000000000000000", "4849000000"})`,
vs:
`serialize(&(0x7f0000408000)={'hash\x00', 'HI\x00'})`,
Diffstat (limited to 'prog/encoding.go')
| -rw-r--r-- | prog/encoding.go | 155 |
1 files changed, 141 insertions, 14 deletions
diff --git a/prog/encoding.go b/prog/encoding.go index 95e312662..4d8150959 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -8,7 +8,6 @@ import ( "bytes" "encoding/hex" "fmt" - "io" "strconv" ) @@ -54,7 +53,7 @@ func (p *Prog) Serialize() []byte { return buf.Bytes() } -func serialize(arg Arg, buf io.Writer, vars map[Arg]int, varSeq *int) { +func serialize(arg Arg, buf *bytes.Buffer, vars map[Arg]int, varSeq *int) { if arg == nil { fmt.Fprintf(buf, "nil") return @@ -75,7 +74,15 @@ func serialize(arg Arg, buf io.Writer, vars map[Arg]int, varSeq *int) { fmt.Fprintf(buf, "&%v=", serializeAddr(arg)) serialize(a.Res, buf, vars, varSeq) case *DataArg: - fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(a.Data)) + data := a.Data + if !arg.Type().Varlen() { + // Statically typed data will be padded with 0s during + // deserialization, so we can strip them here for readability. + for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 { + data = data[:len(data)-1] + } + } + serializeData(buf, data) case *GroupArg: var delims []byte switch arg.Type().(type) { @@ -125,8 +132,7 @@ func (target *Target) Deserialize(data []byte) (prog *Prog, err error) { prog = &Prog{ Target: target, } - p := &parser{r: bufio.NewScanner(bytes.NewReader(data))} - p.r.Buffer(nil, maxLineLen) + p := newParser(data) vars := make(map[string]Arg) for p.Scan() { if p.EOF() || p.Char() == '#' { @@ -276,16 +282,10 @@ func (target *Target) parseArg(typ Type, p *parser, vars map[string]Arg) (Arg, e return nil, err } arg = MakeConstArg(typ, pages*target.PageSize) - case '"': - p.Parse('"') - val := "" - if p.Char() != '"' { - val = p.Ident() - } - p.Parse('"') - data, err := hex.DecodeString(val) + case '"', '\'': + data, err := deserializeData(p) if err != nil { - return nil, fmt.Errorf("data arg has bad value '%v'", val) + return nil, err } if !typ.Varlen() { if diff := int(typ.Size()) - len(data); diff > 0 { @@ -475,6 +475,114 @@ func parseAddr(p *parser, base bool) (uint64, int, uint64, error) { return page, int(off), size, nil } +func serializeData(buf *bytes.Buffer, data []byte) { + readable := true + for _, v := range data { + if v >= 0x20 && v < 0x7f { + continue + } + switch v { + case 0, '\a', '\b', '\f', '\n', '\r', '\t', '\v': + continue + } + readable = false + break + } + if !readable || len(data) == 0 { + fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(data)) + return + } + buf.WriteByte('\'') + for _, v := range data { + switch v { + case 0: + buf.Write([]byte{'\\', 'x', '0', '0'}) + case '\a': + buf.Write([]byte{'\\', 'a'}) + case '\b': + buf.Write([]byte{'\\', 'b'}) + case '\f': + buf.Write([]byte{'\\', 'f'}) + case '\n': + buf.Write([]byte{'\\', 'n'}) + case '\r': + buf.Write([]byte{'\\', 'r'}) + case '\t': + buf.Write([]byte{'\\', 't'}) + case '\v': + buf.Write([]byte{'\\', 'v'}) + case '\'': + buf.Write([]byte{'\\', '\''}) + case '\\': + buf.Write([]byte{'\\', '\\'}) + default: + buf.WriteByte(v) + } + } + buf.WriteByte('\'') +} + +func deserializeData(p *parser) ([]byte, error) { + var data []byte + if p.Char() == '"' { + p.Parse('"') + val := "" + if p.Char() != '"' { + val = p.Ident() + } + p.Parse('"') + var err error + data, err = hex.DecodeString(val) + if err != nil { + return nil, fmt.Errorf("data arg has bad value %q", val) + } + } else { + if p.consume() != '\'' { + return nil, fmt.Errorf("data arg does not start with \" nor with '") + } + for p.Char() != '\'' && p.Char() != 0 { + v := p.consume() + if v != '\\' { + data = append(data, v) + continue + } + v = p.consume() + switch v { + case 'x': + hi := p.consume() + lo := p.consume() + if lo != '0' || hi != '0' { + return nil, fmt.Errorf( + "invalid \\x%c%c escape sequence in data arg", hi, lo) + } + data = append(data, 0) + case 'a': + data = append(data, '\a') + case 'b': + data = append(data, '\b') + case 'f': + data = append(data, '\f') + case 'n': + data = append(data, '\n') + case 'r': + data = append(data, '\r') + case 't': + data = append(data, '\t') + case 'v': + data = append(data, '\v') + case '\'': + data = append(data, '\'') + case '\\': + data = append(data, '\\') + default: + return nil, fmt.Errorf("invalid \\%c escape sequence in data arg", v) + } + } + p.Parse('\'') + } + return data, nil +} + type parser struct { r *bufio.Scanner s string @@ -483,6 +591,12 @@ type parser struct { e error } +func newParser(data []byte) *parser { + p := &parser{r: bufio.NewScanner(bytes.NewReader(data))} + p.r.Buffer(nil, maxLineLen) + return p +} + func (p *parser) Scan() bool { if p.e != nil { return false @@ -536,6 +650,19 @@ func (p *parser) Parse(ch byte) { p.SkipWs() } +func (p *parser) consume() byte { + if p.e != nil { + return 0 + } + if p.EOF() { + p.failf("unexpected eof") + return 0 + } + v := p.s[p.i] + p.i++ + return v +} + func (p *parser) SkipWs() { for p.i < len(p.s) && (p.s[p.i] == ' ' || p.s[p.i] == '\t') { p.i++ |
