github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/bzimage/bzimage_test.go (about)

     1  // Copyright 2012-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package bzimage
     6  
     7  import (
     8  	"fmt"
     9  	"hash/crc32"
    10  	"os"
    11  	"testing"
    12  	"unsafe"
    13  
    14  	"github.com/mvdan/u-root-coreutils/pkg/cpio"
    15  )
    16  
    17  type testImage struct {
    18  	name  string
    19  	path  string
    20  	crc32 uint32
    21  }
    22  
    23  var testImages = []testImage{
    24  	{
    25  		name:  "basic bzImage",
    26  		path:  "testdata/bzImage",
    27  		crc32: 1646619772,
    28  	},
    29  	{
    30  		name:  "a little larger bzImage, 64k random generated image",
    31  		path:  "testdata/bzimage-64kurandominitramfs",
    32  		crc32: 76993350,
    33  	},
    34  }
    35  
    36  var badmagic = []byte("hi there")
    37  
    38  func mustReadFile(t *testing.T, path string) []byte {
    39  	t.Helper()
    40  
    41  	data, err := os.ReadFile(path)
    42  	if err != nil {
    43  		t.Fatalf("error reading %q: %v", path, err)
    44  	}
    45  	return data
    46  }
    47  
    48  func TestUnmarshal(t *testing.T) {
    49  	Debug = t.Logf
    50  
    51  	compressedTests := []testImage{
    52  		// These test files have been created using .circleci/images/test-image-amd6/config_linux5.10_x86_64.txt
    53  		{name: "bzip2", path: "testdata/bzImage-linux5.10-x86_64-bzip2", crc32: 1083155033},
    54  		{name: "signed-debian", path: "testdata/bzImage-debian-signed-linux5.10.0-6-amd64_5.10.28-1_amd64", crc32: 3243083922},
    55  		{name: "signed-rocky", path: "testdata/bzImage-rockylinux9", crc32: 4110245191},
    56  		{name: "gzip", path: "testdata/bzImage-linux5.10-x86_64-gzip", crc32: 4192009363},
    57  		{name: "xz", path: "testdata/bzImage-linux5.10-x86_64-xz", crc32: 3062624786},
    58  		{name: "lz4", path: "testdata/bzImage-linux5.10-x86_64-lz4", crc32: 2177238538},
    59  		{name: "lzma", path: "testdata/bzImage-linux5.10-x86_64-lzma", crc32: 3062624786},
    60  		// This test does not pass because the CircleCI environment does not include the `lzop` command.
    61  		// TODO: Fix the CircleCI environment or (preferably) find a Go package which provides this functionality.
    62  		//		{name: "lzo", path: "testdata/bzImage-linux5.10-x86_64-lzo"},
    63  		{name: "zstd", path: "testdata/bzImage-linux5.10-x86_64-zstd", crc32: 1773835837},
    64  	}
    65  
    66  	for _, tc := range append(testImages, compressedTests...) {
    67  		t.Run(tc.name, func(t *testing.T) {
    68  			image := mustReadFile(t, tc.path)
    69  			var b BzImage
    70  			if err := b.UnmarshalBinary(image); err != nil {
    71  				t.Fatal(err)
    72  			}
    73  			// Verify that the IEEE CRC32 hash has not changed.
    74  			// This ensures that we can swap out the decompressor with confidence that the
    75  			// decompressed payload does not change.
    76  			if got, want := crc32.ChecksumIEEE(b.KernelCode), tc.crc32; got != want {
    77  				t.Fatalf("IEEE CRC32 hash of decompressed kernel code has changed from %v to %v", want, got)
    78  			}
    79  			// Corrupt a byte in the CRC32 and verify that an error is returned.
    80  			checksumOffset := uint32(b.KernelOffset) + uint32(b.Header.Syssize)*16 - uint32(unsafe.Sizeof(b.CRC32))
    81  			image[checksumOffset-1] ^= 0xff
    82  			if err := b.UnmarshalBinary(image); err == nil {
    83  				t.Fatalf("UnmarshalBinary did not return an error with corrupted CRC32")
    84  			}
    85  			// Restore the corrupted byte.
    86  			image[checksumOffset-1] ^= 0xff
    87  			if err := b.UnmarshalBinary(image); err != nil {
    88  				t.Fatalf("UnmarshalBinary returned an unexpected error when called repeatedly: %v", err)
    89  			}
    90  		})
    91  	}
    92  }
    93  
    94  func TestSupportedVersions(t *testing.T) {
    95  	Debug = t.Logf
    96  
    97  	tests := []struct {
    98  		version uint16
    99  		wantErr bool
   100  	}{
   101  		{
   102  			version: 0x0207,
   103  			wantErr: true,
   104  		},
   105  		{
   106  			version: 0x0208,
   107  			wantErr: false,
   108  		}, {
   109  			version: 0x0209,
   110  			wantErr: false,
   111  		},
   112  	}
   113  
   114  	baseImage := mustReadFile(t, "testdata/bzImage")
   115  
   116  	// Ensure that the base image unmarshals successfully.
   117  	if err := (&BzImage{}).UnmarshalBinary(baseImage); err != nil {
   118  		t.Fatalf("unable to unmarshal image: %v", err)
   119  	}
   120  
   121  	for _, tc := range tests {
   122  		t.Run(fmt.Sprintf("0x%04x", tc.version), func(t *testing.T) {
   123  			// Unmarshal the base image.
   124  			var bzImage BzImage
   125  			if err := bzImage.UnmarshalBinary(baseImage); err != nil {
   126  				t.Fatalf("failed to unmarshal base image: %v", err)
   127  			}
   128  
   129  			bzImage.Header.Protocolversion = tc.version
   130  
   131  			// Marshal the image with the test version.
   132  			modifiedImage, err := bzImage.MarshalBinary()
   133  			if err != nil {
   134  				t.Fatalf("failed to marshal image with the new version: %v", err)
   135  			}
   136  
   137  			// Try to unmarshal the image with the test version.
   138  			err = (&BzImage{}).UnmarshalBinary(modifiedImage)
   139  			if gotErr := err != nil; gotErr != tc.wantErr {
   140  				t.Fatalf("got error: %v, expected error: %t", err, tc.wantErr)
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestMarshal(t *testing.T) {
   147  	Debug = t.Logf
   148  	for _, tc := range testImages {
   149  		t.Run(tc.name, func(t *testing.T) {
   150  			image := mustReadFile(t, tc.path)
   151  			var b BzImage
   152  			if err := b.UnmarshalBinary(image); err != nil {
   153  				t.Fatal(err)
   154  			}
   155  			t.Logf("b header is %s", b.Header.String())
   156  			image, err := b.MarshalBinary()
   157  			if err != nil {
   158  				t.Fatal(err)
   159  			}
   160  
   161  			// now unmarshall back into ourselves.
   162  			// We can't perfectly recreate the image the kernel built,
   163  			// but we need to know we are stable.
   164  			if err := b.UnmarshalBinary(image); err != nil {
   165  				t.Fatal(err)
   166  			}
   167  			d, err := b.MarshalBinary()
   168  			if err != nil {
   169  				t.Fatal(err)
   170  			}
   171  			var n BzImage
   172  			if err := n.UnmarshalBinary(d); err != nil {
   173  				t.Fatalf("Unmarshalling marshaled image: want nil, got  %v", err)
   174  			}
   175  
   176  			t.Logf("DIFF: %v", b.Header.Diff(&n.Header))
   177  			if d := b.Header.Diff(&n.Header); d != "" {
   178  				t.Errorf("Headers differ: %s", d)
   179  			}
   180  			if len(d) != len(image) {
   181  				t.Fatalf("Marshal: want %d as output len, got %d; diff is %s", len(image), len(d), b.Diff(&b))
   182  			}
   183  
   184  			if err := Equal(image, d); err != nil {
   185  				t.Logf("Check if images are the same: want nil, got %v", err)
   186  			}
   187  
   188  			// Corrupt little bits of thing.
   189  			x := d[0x203]
   190  			d[0x203] = 1
   191  			if err := Equal(image, d); err == nil {
   192  				t.Fatalf("Corrupting marshaled image: got nil, want err")
   193  			}
   194  			d[0x203] = x
   195  			image[0x203] = 1
   196  			if err := Equal(image, d); err == nil {
   197  				t.Fatalf("Corrupting original image: got nil, want err")
   198  			}
   199  			image[0x203] = x
   200  			x = d[0x208]
   201  			d[0x208] = x + 1
   202  			if err := Equal(image, d); err == nil {
   203  				t.Fatalf("Corrupting marshaled header: got nil, want err")
   204  			}
   205  			d[0x208] = x
   206  			d[20000] = d[20000] + 1
   207  			if err := Equal(image, d); err == nil {
   208  				t.Fatalf("Corrupting marshaled kernel: got nil, want err")
   209  			}
   210  		})
   211  	}
   212  }
   213  
   214  func TestBadMagic(t *testing.T) {
   215  	var b BzImage
   216  	Debug = t.Logf
   217  	if err := b.UnmarshalBinary(badmagic); err == nil {
   218  		t.Fatal("Want err, got nil")
   219  	}
   220  }
   221  
   222  func TestAddInitRAMFS(t *testing.T) {
   223  	t.Logf("TestAddInitRAMFS")
   224  	Debug = t.Logf
   225  	initramfsimage := mustReadFile(t, "testdata/bzimage-64kurandominitramfs")
   226  	var b BzImage
   227  	if err := b.UnmarshalBinary(initramfsimage); err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	if err := b.AddInitRAMFS("testdata/init.cpio"); err != nil {
   231  		t.Fatal(err)
   232  	}
   233  	d, err := b.MarshalBinary()
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	// Ensure that we can still unmarshal the image.
   238  	if err := (&BzImage{}).UnmarshalBinary(d); err != nil {
   239  		t.Fatalf("unable to unmarshal the marshal'd image: %v", err)
   240  	}
   241  
   242  	// For testing, you can enable this write, and then:
   243  	// qemu-system-x86_64 -serial stdio -kernel /tmp/x
   244  	// I mainly left this here as a memo.
   245  	if true {
   246  		if err := os.WriteFile("/tmp/x", d, 0o644); err != nil {
   247  			t.Fatal(err)
   248  		}
   249  	}
   250  	// Make KernelCode much bigger and watch it fail.
   251  	k := b.KernelCode
   252  	b.KernelCode = append(b.KernelCode, k...)
   253  	b.KernelCode = append(b.KernelCode, k...)
   254  	b.KernelCode = append(b.KernelCode, k...)
   255  	b.KernelCode = append(b.KernelCode, k...)
   256  
   257  	if _, err = b.MarshalBinary(); err == nil {
   258  		t.Logf("Overflow test, want %v, got nil", "Marshal: compressed KernelCode too big: was 986532, now 1422388")
   259  		t.Fatal(err)
   260  	}
   261  
   262  	b.KernelCode = k[:len(k)-len(k)/2]
   263  
   264  	if _, err = b.MarshalBinary(); err != nil {
   265  		t.Logf("shrink test, want nil, got %v", err)
   266  		t.Fatal(err)
   267  	}
   268  	// Ensure that we can still unmarshal the image.
   269  	if err := (&BzImage{}).UnmarshalBinary(d); err != nil {
   270  		t.Fatalf("unable to unmarshal the marshal'd image: %v", err)
   271  	}
   272  
   273  }
   274  
   275  func TestHeaderString(t *testing.T) {
   276  	Debug = t.Logf
   277  	for _, tc := range testImages {
   278  		t.Run(tc.name, func(t *testing.T) {
   279  			initramfsimage := mustReadFile(t, tc.path)
   280  			var b BzImage
   281  			if err := b.UnmarshalBinary(initramfsimage); err != nil {
   282  				t.Fatal(err)
   283  			}
   284  			t.Logf("%s", b.Header.String())
   285  		})
   286  	}
   287  }
   288  
   289  func TestExtract(t *testing.T) {
   290  	Debug = t.Logf
   291  	for _, tc := range testImages {
   292  		t.Run(tc.name, func(t *testing.T) {
   293  			initramfsimage := mustReadFile(t, tc.path)
   294  			var b BzImage
   295  			if err := b.UnmarshalBinary(initramfsimage); err != nil {
   296  				t.Fatal(err)
   297  			}
   298  			t.Logf("%s", b.Header.String())
   299  			// The simplest test: what is extracted should be a valid elf.
   300  			e, err := b.ELF()
   301  			if err != nil {
   302  				t.Fatalf("Extracted bzImage data is an elf: want nil, got %v", err)
   303  			}
   304  			t.Logf("Header: %v", e.FileHeader)
   305  			for i, p := range e.Progs {
   306  				t.Logf("%d: %v", i, *p)
   307  			}
   308  		})
   309  	}
   310  }
   311  
   312  func TestELF(t *testing.T) {
   313  	Debug = t.Logf
   314  	for _, tc := range testImages {
   315  		t.Run(tc.name, func(t *testing.T) {
   316  			initramfsimage := mustReadFile(t, tc.path)
   317  			var b BzImage
   318  			if err := b.UnmarshalBinary(initramfsimage); err != nil {
   319  				t.Fatal(err)
   320  			}
   321  			t.Logf("%s", b.Header.String())
   322  			e, err := b.ELF()
   323  			if err != nil {
   324  				t.Fatalf("Extract: want nil, got %v", err)
   325  			}
   326  			t.Logf("%v", e.FileHeader)
   327  		})
   328  	}
   329  }
   330  
   331  func TestInitRAMFS(t *testing.T) {
   332  	Debug = t.Logf
   333  	cpio.Debug = t.Logf
   334  	for _, tc := range testImages {
   335  		t.Run(tc.name, func(t *testing.T) {
   336  			initramfsimage := mustReadFile(t, tc.path)
   337  			var b BzImage
   338  			if err := b.UnmarshalBinary(initramfsimage); err != nil {
   339  				t.Fatal(err)
   340  			}
   341  			s, e, err := b.InitRAMFS()
   342  			if err != nil {
   343  				t.Fatal(err)
   344  			}
   345  			t.Logf("Found %d byte initramfs@%d:%d", e-s, s, e)
   346  		})
   347  	}
   348  }