diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-12-15 15:17:13 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-12-15 15:17:13 +0100 |
| commit | def91db3fe955168c82038ac2ee39783e81a2af0 (patch) | |
| tree | b6cbf100001e7b9fe34a8e2eb4ab00438f3e0154 /prog | |
| parent | 7a944a0a666587f229291814b30644cc0859674c (diff) | |
prog, pkg/csource: more readable serialization for strings
Always serialize strings in readable format (non-hex).
Serialize binary data in readable format in more cases.
Fixes #792
Diffstat (limited to 'prog')
| -rw-r--r-- | prog/decodeexec.go | 9 | ||||
| -rw-r--r-- | prog/encoding.go | 113 | ||||
| -rw-r--r-- | prog/encoding_test.go | 48 | ||||
| -rw-r--r-- | prog/encodingexec.go | 11 |
4 files changed, 135 insertions, 46 deletions
diff --git a/prog/decodeexec.go b/prog/decodeexec.go index 5866d8627..c57597437 100644 --- a/prog/decodeexec.go +++ b/prog/decodeexec.go @@ -52,7 +52,8 @@ type ExecArgResult struct { } type ExecArgData struct { - Data []byte + Data []byte + Readable bool } type ExecArgCsum struct { @@ -161,8 +162,12 @@ func (dec *execDecoder) readArg() ExecArg { dec.vars[arg.Index] = arg.Default return arg case execArgData: + flags := dec.read() + size := flags & ^execArgDataReadable + readable := flags&execArgDataReadable != 0 return ExecArgData{ - Data: dec.readBlob(dec.read()), + Data: dec.readBlob(size), + Readable: readable, } case execArgCsum: size := dec.read() diff --git a/prog/encoding.go b/prog/encoding.go index 9b4e9c6b6..d7f25afe2 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -102,19 +102,20 @@ func (a *PointerArg) serialize(ctx *serializer) { } func (a *DataArg) serialize(ctx *serializer) { - if a.Type().Dir() == DirOut { + typ := a.Type().(*BufferType) + if typ.Dir() == DirOut { ctx.printf("\"\"/%v", a.Size()) return } data := a.Data() - if !a.Type().Varlen() { + if !typ.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(ctx.buf, data) + serializeData(ctx.buf, data, isReadableDataType(typ)) } func (a *GroupArg) serialize(ctx *serializer) { @@ -726,28 +727,31 @@ func (p *parser) parseAddr() (uint64, uint64, error) { return addr, vmaSize, 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 { +func serializeData(buf *bytes.Buffer, data []byte, readable bool) { + if !readable && !isReadableData(data) { fmt.Fprintf(buf, "\"%v\"", hex.EncodeToString(data)) return } buf.WriteByte('\'') + encodeData(buf, data, true) + buf.WriteByte('\'') +} + +func EncodeData(buf *bytes.Buffer, data []byte, readable bool) { + if !readable && isReadableData(data) { + readable = true + } + encodeData(buf, data, readable) +} + +func encodeData(buf *bytes.Buffer, data []byte, readable bool) { for _, v := range data { + if !readable { + lo, hi := byteToHex(v) + buf.Write([]byte{'\\', 'x', hi, lo}) + continue + } switch v { - case 0: - buf.Write([]byte{'\\', 'x', '0', '0'}) case '\a': buf.Write([]byte{'\\', 'a'}) case '\b': @@ -764,13 +768,40 @@ func serializeData(buf *bytes.Buffer, data []byte) { buf.Write([]byte{'\\', 'v'}) case '\'': buf.Write([]byte{'\\', '\''}) + case '"': + buf.Write([]byte{'\\', '"'}) case '\\': buf.Write([]byte{'\\', '\\'}) default: - buf.WriteByte(v) + if isPrintable(v) { + buf.WriteByte(v) + } else { + lo, hi := byteToHex(v) + buf.Write([]byte{'\\', 'x', hi, lo}) + } } } - buf.WriteByte('\'') +} + +func isReadableDataType(typ *BufferType) bool { + return typ.Kind == BufferString || typ.Kind == BufferFilename +} + +func isReadableData(data []byte) bool { + if len(data) == 0 { + return false + } + for _, v := range data { + if isPrintable(v) { + continue + } + switch v { + case 0, '\a', '\b', '\f', '\n', '\r', '\t', '\v': + continue + } + return false + } + return true } func (p *parser) deserializeData() ([]byte, error) { @@ -802,11 +833,7 @@ func (p *parser) deserializeData() ([]byte, error) { 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) + data = append(data, hexToByte(lo, hi)) case 'a': data = append(data, '\a') case 'b': @@ -823,6 +850,8 @@ func (p *parser) deserializeData() ([]byte, error) { data = append(data, '\v') case '\'': data = append(data, '\'') + case '"': + data = append(data, '"') case '\\': data = append(data, '\\') default: @@ -834,6 +863,38 @@ func (p *parser) deserializeData() ([]byte, error) { return data, nil } +func isPrintable(v byte) bool { + return v >= 0x20 && v < 0x7f +} + +func byteToHex(v byte) (lo, hi byte) { + return toHexChar(v & 0xf), toHexChar(v >> 4) +} + +func hexToByte(lo, hi byte) byte { + return fromHexChar(hi)<<4 + fromHexChar(lo) +} + +func toHexChar(v byte) byte { + if v >= 16 { + panic("bad hex char") + } + if v < 10 { + return '0' + v + } + return 'a' + v - 10 +} + +func fromHexChar(v byte) byte { + if v >= '0' && v <= '9' { + return v - '0' + } + if v >= 'a' && v <= 'f' { + return v - 'a' + 10 + } + panic("bad hex char") +} + type parser struct { target *Target strict bool diff --git a/prog/encoding_test.go b/prog/encoding_test.go index ac670a8ab..f828123db 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -28,23 +28,25 @@ func setToArray(s map[string]struct{}) []string { func TestSerializeData(t *testing.T) { t.Parallel() r := rand.New(rand.NewSource(0)) - for i := 0; i < 1e4; i++ { - data := make([]byte, r.Intn(4)) - for i := range data { - data[i] = byte(r.Intn(256)) - } - buf := new(bytes.Buffer) - serializeData(buf, data) - p := newParser(nil, buf.Bytes(), true) - if !p.Scan() { - t.Fatalf("parser does not scan") - } - data1, err := p.deserializeData() - if err != nil { - t.Fatalf("failed to deserialize %q -> %s: %v", data, buf.Bytes(), err) - } - if !bytes.Equal(data, data1) { - t.Fatalf("corrupted data %q -> %s -> %q", data, buf.Bytes(), data1) + for _, readable := range []bool{false, true} { + for i := 0; i < 1e3; i++ { + data := make([]byte, r.Intn(4)) + for i := range data { + data[i] = byte(r.Intn(256)) + } + buf := new(bytes.Buffer) + serializeData(buf, data, readable) + p := newParser(nil, buf.Bytes(), true) + if !p.Scan() { + t.Fatalf("parser does not scan") + } + data1, err := p.deserializeData() + if err != nil { + t.Fatalf("failed to deserialize %q -> %s: %v", data, buf.Bytes(), err) + } + if !bytes.Equal(data, data1) { + t.Fatalf("corrupted data %q -> %s -> %q", data, buf.Bytes(), data1) + } } } } @@ -253,6 +255,18 @@ func TestDeserialize(t *testing.T) { input: `test$auto0(AUTO, &AUTO={AUTO, AUTO, AUTO}, AUTO, 0x0)`, err: regexp.MustCompile(`wrong type \*prog\.IntType for AUTO`), }, + { + input: `test$str0(&AUTO="303100090a0d7022273a")`, + output: `test$str0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`, + }, + { + input: `test$blob0(&AUTO="303100090a0d7022273a")`, + output: `test$blob0(&(0x7f0000000040)='01\x00\t\n\rp\"\':')`, + }, + { + input: `test$blob0(&AUTO="3031000a0d7022273a01")`, + output: `test$blob0(&(0x7f0000000040)="3031000a0d7022273a01")`, + }, } buf := make([]byte, ExecBufferSize) for _, test := range tests { diff --git a/prog/encodingexec.go b/prog/encodingexec.go index 651ecef51..2d2f02e57 100644 --- a/prog/encodingexec.go +++ b/prog/encodingexec.go @@ -35,6 +35,8 @@ const ( execArgResult execArgData execArgCsum + + execArgDataReadable = uint64(1 << 63) ) const ( @@ -255,8 +257,15 @@ func (w *execContext) writeArg(arg Arg) { w.writeConstArg(a.Size(), w.target.PhysicalAddr(a), 0, 0, 0, FormatNative) case *DataArg: data := a.Data() + if len(data) == 0 { + return + } w.write(execArgData) - w.write(uint64(len(data))) + flags := uint64(len(data)) + if isReadableDataType(a.Type().(*BufferType)) { + flags |= execArgDataReadable + } + w.write(flags) padded := len(data) if pad := 8 - len(data)%8; pad != 8 { padded += pad |
