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 }