diff options
| author | Hrutvik Kanabar <hrutvik@google.com> | 2022-10-27 13:54:17 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2022-11-21 11:06:14 +0100 |
| commit | d3f75397b75a3bfed0cbb0f54b1c6c584b37c4c2 (patch) | |
| tree | 278e9836f9059130e58ffb30b5ff80db1b45f65b /sys/linux/init_images.go | |
| parent | dd8fa7b2240ece698be6604a8fa8ce05f82ce0b0 (diff) | |
sys/linux: update asset storage for new `syz_mount_image`
Asset storage is now significantly simpler: we just take the
Base64-encoded, compressed image and output it to a file. There is a
slight overhead in that we decompress from the `zlib` format and
re-compress to the `gzip` format.
This commit removes most of the logic from `init_images.go`,
and therefore most of the tests from `init_images_test.go`.
We could instead keep this logic around and use it to adapt old-style
`syz_mount_image` calls in existing corpuses to match the new format.
Diffstat (limited to 'sys/linux/init_images.go')
| -rw-r--r-- | sys/linux/init_images.go | 249 |
1 files changed, 37 insertions, 212 deletions
diff --git a/sys/linux/init_images.go b/sys/linux/init_images.go index e160234d9..97175cb84 100644 --- a/sys/linux/init_images.go +++ b/sys/linux/init_images.go @@ -7,129 +7,28 @@ import ( "bytes" "fmt" "io" - "sort" "github.com/google/syzkaller/prog" ) func (arch *arch) extractSyzMountImage(c *prog.Call) (io.Reader, error) { - // In order to reduce the size of syzlang programs, disk imags are de facto compressed. - // Here we do the uncompression. + // In order to reduce the size of syzlang programs, disk images are compressed. + // Here we extract the compressed image. if c.Meta.CallName != "syz_mount_image" { return nil, nil } - ret, err := parseSyzMountImage(c) + data, _, err := parseSyzMountImage(c) if err != nil { // Parsing failed --> do not try to recover, just ignore. return nil, err - } else if len(ret.segments) == 0 { + } else if len(data) == 0 { return nil, fmt.Errorf("an empty image") } - readers := []io.Reader{} - nextPos := 0 - // Add fake zero readers between segments, so that we can combine them to read the whole image. - for _, segment := range ret.segments { - offset := int(segment.offset.Val) - if offset > nextPos { - readers = append(readers, &zeroReader{left: offset - nextPos}) - } - size := int(segment.size.Val) - readers = append(readers, bytes.NewReader(segment.data.Data()[0:size])) - nextPos = offset + size - } - if int(ret.size.Val) > nextPos { - readers = append(readers, &zeroReader{left: int(ret.size.Val) - nextPos}) - } - return io.MultiReader(readers...), nil -} - -type zeroReader struct { - left int -} - -func (zr *zeroReader) Read(p []byte) (n int, err error) { - if zr.left == 0 { - return 0, io.EOF - } - toRead := zr.left - if toRead > len(p) { - toRead = len(p) - } - for i := 0; i < toRead; i++ { - p[i] = 0 - } - zr.left -= toRead - return toRead, nil -} - -const imageMaxSize = 129 << 20 - -func fixUpImageSegments(parsed *mountImageArgs, fixStructure bool) error { - const maxImageSegments = 16784 - err := parsed.filterSegments(func(i int, segment *mountImageSegment) bool { - if i >= maxImageSegments { - // We don't want more than maxImageSegments segments. - return false - } - if segment.parseError != nil { - // Delete mangled segment structures. - return false - } - return segment.offset.Val < imageMaxSize && segment.size.Val < imageMaxSize - }, !fixStructure) + decompressedData, err := prog.Decompress(data) if err != nil { - return err - } - // Overwriting of the image multiple times is not efficient and complicates image extraction in Go. - // So let's make segments non-overlapping. - if fixStructure { - sort.Stable(parsed) - } else if !sort.IsSorted(parsed) { - return fmt.Errorf("segments are not sorted") - } - - newSize := parsed.size.Val - if newSize > imageMaxSize { - newSize = imageMaxSize - } - - for idx, segment := range parsed.segments { - actualSize := uint64(len(segment.data.Data())) - if segment.size.Val != actualSize { - segment.size.Val = actualSize - } - if idx > 0 { - // Adjust the end of the previous segment. - prevSegment := parsed.segments[idx-1] - if prevSegment.offset.Val+prevSegment.size.Val > segment.offset.Val { - if fixStructure { - prevSegment.resize(segment.offset.Val - prevSegment.offset.Val) - } else { - return fmt.Errorf("segment %d has invalid size", idx-1) - } - } - } - if segment.offset.Val+segment.size.Val > imageMaxSize { - if fixStructure { - segment.resize(imageMaxSize - segment.offset.Val) - } else { - return fmt.Errorf("segment %d has invalid size", idx) - } - } - if segment.offset.Val+segment.size.Val > newSize { - newSize = segment.offset.Val + segment.size.Val - } - } - if newSize > imageMaxSize { - // Assert that the logic above is not broken. - panic("newSize > imageMaxSize") + return nil, err } - parsed.size.Val = newSize - - // Drop 0-size segments. - return parsed.filterSegments(func(i int, segment *mountImageSegment) bool { - return segment.size.Val > 0 - }, !fixStructure) + return bytes.NewReader(decompressedData), nil } func (arch *arch) fixUpSyzMountImage(c *prog.Call, fixStructure bool) error { @@ -138,7 +37,8 @@ func (arch *arch) fixUpSyzMountImage(c *prog.Call, fixStructure bool) error { // 1) It further complicates the already complicated executor code. // 2) We'd need to duplicate the logic in Go for raw image extraction. // So now we do all the initialization in Go and let the C code only interpret the commands. - ret, err := parseSyzMountImage(c) + data, compressedSizeArg, err := parseSyzMountImage(c) + compressedSizeArg.Val = uint64(len(data)) if err != nil { if fixStructure { deactivateSyzMountImage(c) @@ -146,124 +46,49 @@ func (arch *arch) fixUpSyzMountImage(c *prog.Call, fixStructure bool) error { } return err } - return fixUpImageSegments(ret, fixStructure) -} - -type mountImageArgs struct { - size *prog.ConstArg - segmentsCount *prog.ConstArg - segmentsGroup *prog.GroupArg - segments []*mountImageSegment -} - -func (m *mountImageArgs) filterSegments(filter func(int, *mountImageSegment) bool, failOnRemove bool) error { - newArgs := []prog.Arg{} - newSegments := []*mountImageSegment{} - for i, segment := range m.segments { - if filter(i, segment) { - newSegments = append(newSegments, segment) - newArgs = append(newArgs, m.segmentsGroup.Inner[i]) - } else if failOnRemove { - return fmt.Errorf("segment #%d got filtered out", i) - } - } - m.segments = newSegments - m.segmentsGroup.Inner = newArgs - m.segmentsCount.Val = uint64(len(newArgs)) return nil } -// Methods for segment sorting. -func (m *mountImageArgs) Len() int { return len(m.segments) } -func (m *mountImageArgs) Swap(i, j int) { - inner := m.segmentsGroup.Inner - inner[i], inner[j] = inner[j], inner[i] - m.segments[i], m.segments[j] = m.segments[j], m.segments[i] -} -func (m *mountImageArgs) Less(i, j int) bool { - if m.segments[i].offset.Val != m.segments[j].offset.Val { - return m.segments[i].offset.Val < m.segments[j].offset.Val - } - return m.segments[i].size.Val < m.segments[j].size.Val -} - -type mountImageSegment struct { - data *prog.DataArg - offset *prog.ConstArg - size *prog.ConstArg - parseError error -} - -func (s *mountImageSegment) resize(newSize uint64) { - s.size.Val = newSize - s.data.SetData(s.data.Data()[0:newSize]) -} - -func parseImageSegment(segmentArg prog.Arg) *mountImageSegment { - ret := &mountImageSegment{} - segmentFields, ok := segmentArg.(*prog.GroupArg) - if segmentFields == nil || !ok { - return &mountImageSegment{parseError: fmt.Errorf("it is not a group")} - } - if len(segmentFields.Inner) != 3 { - return &mountImageSegment{parseError: fmt.Errorf("invalid number of nested fields")} - } - dataPtr, ok := segmentFields.Inner[0].(*prog.PointerArg) - if dataPtr == nil || dataPtr.Res == nil || !ok { - return &mountImageSegment{parseError: fmt.Errorf("invalid data field ptr")} - } - ret.data, ok = dataPtr.Res.(*prog.DataArg) - if ret.data == nil || !ok { - return &mountImageSegment{parseError: fmt.Errorf("invalid data arg")} - } - ret.size, ok = segmentFields.Inner[1].(*prog.ConstArg) - if ret.size == nil || !ok { - return &mountImageSegment{parseError: fmt.Errorf("invalid size arg")} - } - ret.offset, ok = segmentFields.Inner[2].(*prog.ConstArg) - if ret.offset == nil || !ok { - return &mountImageSegment{parseError: fmt.Errorf("invalid offset arg")} - } - return ret -} - func deactivateSyzMountImage(c *prog.Call) { - groupArg := c.Args[4] - newArg := groupArg.Type().DefaultArg(groupArg.Dir()) - prog.RemoveArg(groupArg) - c.Args[4] = newArg - // Also set the segments count field to 0. + dataPointer := c.Args[7] + newArg := dataPointer.Type().DefaultArg(dataPointer.Dir()) + prog.RemoveArg(dataPointer) + c.Args[7] = newArg + // Also set the size fields to 0. + c.Args[2].(*prog.ConstArg).Val = 0 c.Args[3].(*prog.ConstArg).Val = 0 } -func parseSyzMountImage(c *prog.Call) (*mountImageArgs, error) { - if len(c.Args) < 5 { +// Returns the compressed disk image and the corresponding length argument. +func parseSyzMountImage(c *prog.Call) ([]byte, *prog.ConstArg, error) { + if len(c.Args) < 8 { panic("invalid number of arguments in syz_mount_image") } - segmentsCountArg, ok := c.Args[3].(*prog.ConstArg) - if !ok { - panic("syz_mount_image's segment count was expected to be const") - } - sizeArg, ok := c.Args[2].(*prog.ConstArg) + + // Check `size` argument. + _, ok := c.Args[2].(*prog.ConstArg) if !ok { panic("syz_mount_image's size arg is not const") } - segmentsPtrArg, ok := c.Args[4].(*prog.PointerArg) + + // Check `size_compressed` argument. + compressedSizeArg, ok := c.Args[3].(*prog.ConstArg) if !ok { - return nil, fmt.Errorf("invalid segments arg") + panic("syz_mount_image's compressed size arg is not const") } - segmentsGroup, ok := segmentsPtrArg.Res.(*prog.GroupArg) - if segmentsGroup == nil || !ok { - return nil, fmt.Errorf("segments are not a group") + + dataPointer, ok := c.Args[7].(*prog.PointerArg) + if !ok { + panic("syz_mount_image's data pointer is invalid") } - ret := &mountImageArgs{ - segmentsCount: segmentsCountArg, - segmentsGroup: segmentsGroup, - size: sizeArg, + + dataArg, ok := dataPointer.Res.(*prog.DataArg) + if !ok { + return nil, compressedSizeArg, fmt.Errorf("could not find raw image data") } - for _, segmentArg := range segmentsGroup.Inner { - parsed := parseImageSegment(segmentArg) - ret.segments = append(ret.segments, parsed) + if dataArg == nil { + return nil, compressedSizeArg, fmt.Errorf("image argument contains no data") } - return ret, nil + + return dataArg.Data(), compressedSizeArg, nil } |
