diff options
| author | Hrutvik Kanabar <hrutvik@google.com> | 2022-10-25 10:14:37 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2022-11-21 11:06:14 +0100 |
| commit | 1360be3b26a4fe8a3ece98440df5a82253ac23ba (patch) | |
| tree | 3f74edd01515b27685fd7ba888c899c066b835a5 | |
| parent | d0405298b24db0e2a6b2abfdc8c7e5ebbe49d1a0 (diff) | |
prog: introduce new Base64 syntax for data
The new "$..." syntax is read as a Base64 encoding binary data.
Note that users cannot specify the size of the Base64 syntax using the
`"..."/<size>` notation.
When serialising programs to human-readable form, only compressed types
(determined by `IsCompressed()`) are represented using the new Base64
notation.
Also add a couple of serialisation tests, checking behaviour for
compressed and non-compressed types.
| -rw-r--r-- | docs/program_syntax.md | 2 | ||||
| -rw-r--r-- | prog/encoding.go | 70 | ||||
| -rw-r--r-- | prog/encoding_test.go | 9 | ||||
| -rw-r--r-- | sys/test/test.txt | 2 |
4 files changed, 60 insertions, 23 deletions
diff --git a/docs/program_syntax.md b/docs/program_syntax.md index 2b1fb3c0d..12bea732a 100644 --- a/docs/program_syntax.md +++ b/docs/program_syntax.md @@ -31,7 +31,7 @@ resource-arg = variable ["/" hex-integer] ["+" hex-integer] result-arg = "<" variable "=>" arg pointer-arg = "&" pointer-arg-addr ["=ANY"] "=" arg pointer-arg-addr = "AUTO" | "(" pointer-addr ["/" region-size] ")" -string-arg = "'" escaped-string "'" | "\"" escaped-string "\"" +string-arg = "'" escaped-string "'" | "\"" escaped-string "\"" | "\"$" escaped-string "\"" struct-arg = "{" [arg ["," arg]*] "}" array-arg = "[" [arg ["," arg]*] "]" union-arg = "@" field-name ["=" arg] diff --git a/prog/encoding.go b/prog/encoding.go index 82c93273a..33983fb47 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -147,19 +147,23 @@ func (a *DataArg) serialize(ctx *serializer) { return } data := a.Data() - // Statically typed data will be padded with 0s during deserialization, - // so we can strip them here for readability always. For variable-size - // data we strip trailing 0s only if we strip enough of them. - sz := len(data) - for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 { - data = data[:len(data)-1] - } - if typ.Varlen() && len(data)+8 >= sz { - data = data[:sz] - } - serializeData(ctx.buf, data, isReadableDataType(typ)) - if typ.Varlen() && sz != len(data) { - ctx.printf("/%v", sz) + if typ.IsCompressed() { + serializeCompressedData(ctx.buf, data) + } else { + // Statically typed data will be padded with 0s during deserialization, + // so we can strip them here for readability always. For variable-size + // data we strip trailing 0s only if we strip enough of them. + sz := len(data) + for len(data) >= 2 && data[len(data)-1] == 0 && data[len(data)-2] == 0 { + data = data[:len(data)-1] + } + if typ.Varlen() && len(data)+8 >= sz { + data = data[:sz] + } + serializeData(ctx.buf, data, isReadableDataType(typ)) + if typ.Varlen() && sz != len(data) { + ctx.printf("/%v", sz) + } } } @@ -593,7 +597,7 @@ func (p *parser) parseArgString(t Type, dir Dir) (Arg, error) { p.eatExcessive(true, "wrong string arg") return t.DefaultArg(dir), nil } - data, err := p.deserializeData() + data, b64, err := p.deserializeData() if err != nil { return nil, err } @@ -607,7 +611,7 @@ func (p *parser) parseArgString(t Type, dir Dir) (Arg, error) { } } size := ^uint64(0) - if p.Char() == '/' { + if p.Char() == '/' && !b64 { p.Parse('/') sizeStr := p.Ident() size, err = strconv.ParseUint(sizeStr, 0, 64) @@ -880,6 +884,14 @@ func serializeData(buf *bytes.Buffer, data []byte, readable bool) { buf.WriteByte('\'') } +func serializeCompressedData(buf *bytes.Buffer, data []byte) { + buf.WriteByte('"') + buf.WriteByte('$') + encoded := EncodeB64(data) + buf.Write(encoded) + buf.WriteByte('"') +} + func EncodeData(buf *bytes.Buffer, data []byte, readable bool) { if !readable && isReadableData(data) { readable = true @@ -957,10 +969,26 @@ func isReadableData(data []byte) bool { return true } -func (p *parser) deserializeData() ([]byte, error) { +// Deserialize data, returning the data and whether it was encoded in Base64. +func (p *parser) deserializeData() ([]byte, bool, error) { var data []byte if p.Char() == '"' { p.Parse('"') + if p.Char() == '$' { + // Read Base64 data. + p.consume() + var rawData []byte + for p.Char() != '"' { + v := p.consume() + rawData = append(rawData, v) + } + decoded, err := DecodeB64(rawData) + if err != nil { + return nil, false, fmt.Errorf("data arg is corrupt: %v", err) + } + p.Parse('"') + return decoded, true, nil + } val := "" if p.Char() != '"' { val = p.Ident() @@ -969,11 +997,11 @@ func (p *parser) deserializeData() ([]byte, error) { var err error data, err = hex.DecodeString(val) if err != nil { - return nil, fmt.Errorf("data arg has bad value %q", val) + return nil, false, 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 '") + return nil, false, fmt.Errorf("data arg does not start with \" nor with '") } for p.Char() != '\'' && p.Char() != 0 { v := p.consume() @@ -988,7 +1016,7 @@ func (p *parser) deserializeData() ([]byte, error) { lo := p.consume() b, ok := hexToByte(lo, hi) if !ok { - return nil, fmt.Errorf("invalid hex \\x%v%v in data arg", hi, lo) + return nil, false, fmt.Errorf("invalid hex \\x%v%v in data arg", hi, lo) } data = append(data, b) case 'a': @@ -1012,12 +1040,12 @@ func (p *parser) deserializeData() ([]byte, error) { case '\\': data = append(data, '\\') default: - return nil, fmt.Errorf("invalid \\%c escape sequence in data arg", v) + return nil, false, fmt.Errorf("invalid \\%c escape sequence in data arg", v) } } p.Parse('\'') } - return data, nil + return data, false, nil } func isPrintable(v byte) bool { diff --git a/prog/encoding_test.go b/prog/encoding_test.go index 4717c402b..4cb836922 100644 --- a/prog/encoding_test.go +++ b/prog/encoding_test.go @@ -36,7 +36,7 @@ func TestSerializeData(t *testing.T) { if !p.Scan() { t.Fatalf("parser does not scan") } - data1, err := p.deserializeData() + data1, _, err := p.deserializeData() if err != nil { t.Fatalf("failed to deserialize %q -> %s: %v", data, buf.Bytes(), err) } @@ -344,6 +344,13 @@ func TestSerializeDeserialize(t *testing.T) { In: `serialize1(&(0x7f0000000000)="0000000000000000", 0x8)`, Out: `serialize1(&(0x7f0000000000)=""/8, 0x8)`, }, + { + In: `serialize2(&(0x7f0000000000)="$c3l6a2FsbGVy")`, + Out: `serialize2(&(0x7f0000000000)='syzkaller')`, + }, + { + In: `serialize3(&(0x7f0000000000)="$eJwqrqzKTszJSS0CBAAA//8TyQPi")`, + }, }) } diff --git a/sys/test/test.txt b/sys/test/test.txt index c780d5fe9..86be743d1 100644 --- a/sys/test/test.txt +++ b/sys/test/test.txt @@ -776,6 +776,8 @@ minimize$0(a0 proc[10, 2], a1 proc[10, 2, opt]) serialize0(a ptr[in, serialize0_struct]) serialize1(a ptr[out, array[int8]], b len[a]) +serialize2(a ptr[in, array[int8]]) +serialize3(a ptr[in, compressed_image]) (no_generate, no_minimize) serialize0_struct { a string[serialize_strings, 10] |
