aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux/init_images.go
diff options
context:
space:
mode:
authorHrutvik Kanabar <hrutvik@google.com>2022-10-27 13:54:17 +0000
committerAleksandr Nogikh <wp32pw@gmail.com>2022-11-21 11:06:14 +0100
commitd3f75397b75a3bfed0cbb0f54b1c6c584b37c4c2 (patch)
tree278e9836f9059130e58ffb30b5ff80db1b45f65b /sys/linux/init_images.go
parentdd8fa7b2240ece698be6604a8fa8ce05f82ce0b0 (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.go249
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
}