Browse Source

[util] Improve processing of ROM images in Option::ROM

The Option::ROM module now compares the Code Type in the PCIR header
to 0x00 (PC-AT) in order to check the presence of other header types
(PnP, UNDI, iPXE, etc).  The validity of these headers are checked not
only by offset, but by range and signature checks also.  The image
checksum and initial size also depends on Code Type.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Petr Borsodi 5 years ago
parent
commit
3f4c179a14
2 changed files with 110 additions and 39 deletions
  1. 103
    34
      src/util/Option/ROM.pm
  2. 7
    5
      src/util/disrom.pl

+ 103
- 34
src/util/Option/ROM.pm View File

@@ -116,7 +116,9 @@ sub EXISTS {
116 116
 
117 117
   return ( exists $self->{fields}->{$key} &&
118 118
 	   ( ( $self->{fields}->{$key}->{offset} +
119
-	       $self->{fields}->{$key}->{length} ) <= $self->{length} ) );
119
+	       $self->{fields}->{$key}->{length} ) <= $self->{length} ) &&
120
+	   ( ! defined $self->{fields}->{$key}->{check} ||
121
+	     &{$self->{fields}->{$key}->{check}} ( $self, $key ) ) );
120 122
 }
121 123
 
122 124
 sub FIRSTKEY {
@@ -172,10 +174,11 @@ use constant ROM_SIGNATURE => 0xaa55;
172 174
 use constant PCI_SIGNATURE => 'PCIR';
173 175
 use constant PCI_LAST_IMAGE => 0x80;
174 176
 use constant PNP_SIGNATURE => '$PnP';
177
+use constant UNDI_SIGNATURE => 'UNDI';
175 178
 use constant IPXE_SIGNATURE => 'iPXE';
176 179
 
177 180
 our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
178
-		      PNP_SIGNATURE IPXE_SIGNATURE );
181
+		      PNP_SIGNATURE UNDI_SIGNATURE IPXE_SIGNATURE );
179 182
 our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
180 183
 
181 184
 use constant JMP_SHORT => 0xeb;
@@ -210,10 +213,20 @@ sub unpack_init {
210 213
   } elsif ( $jump == 0 ) {
211 214
     return 0;
212 215
   } else {
213
-    croak "Unrecognised jump instruction in init vector\n";
216
+    carp "Unrecognised jump instruction in init vector\n";
217
+    return 0;
214 218
   }
215 219
 }
216 220
 
221
+sub check_pcat_rom {
222
+  my $self = shift;
223
+  my $key = shift;
224
+
225
+  my $pci = $self->{rom}->pci_header ();
226
+
227
+  return ! defined $pci || $pci->{code_type} == 0x00;
228
+}
229
+
217 230
 =pod
218 231
 
219 232
 =item C<< new () >>
@@ -227,21 +240,29 @@ sub new {
227 240
 
228 241
   my $hash = {};
229 242
   tie %$hash, "Option::ROM::Fields", {
243
+    rom => $hash, # ROM object itself
230 244
     data => undef,
231 245
     offset => 0x00,
232 246
     length => 0x20,
247
+    file_offset => 0x0,
233 248
     fields => {
234 249
       signature =>	{ offset => 0x00, length => 0x02, pack => "S" },
235 250
       length =>		{ offset => 0x02, length => 0x01, pack => "C" },
236 251
       # "init" is part of a jump instruction
237 252
       init =>		{ offset => 0x03, length => 0x03,
238
-			  pack => \&pack_init, unpack => \&unpack_init },
239
-      checksum =>	{ offset => 0x06, length => 0x01, pack => "C" },
240
-      ipxe_header =>	{ offset => 0x10, length => 0x02, pack => "S" },
241
-      bofm_header =>	{ offset => 0x14, length => 0x02, pack => "S" },
242
-      undi_header =>	{ offset => 0x16, length => 0x02, pack => "S" },
253
+			  pack => \&pack_init, unpack => \&unpack_init,
254
+			  check => \&check_pcat_rom },
255
+      checksum =>	{ offset => 0x06, length => 0x01, pack => "C",
256
+			  check => \&check_pcat_rom },
257
+      ipxe_header =>	{ offset => 0x10, length => 0x02, pack => "S",
258
+			  check => \&check_pcat_rom },
259
+      bofm_header =>	{ offset => 0x14, length => 0x02, pack => "S",
260
+			  check => \&check_pcat_rom },
261
+      undi_header =>	{ offset => 0x16, length => 0x02, pack => "S",
262
+			  check => \&check_pcat_rom },
243 263
       pci_header =>	{ offset => 0x18, length => 0x02, pack => "S" },
244
-      pnp_header =>	{ offset => 0x1a, length => 0x02, pack => "S" },
264
+      pnp_header =>	{ offset => 0x1a, length => 0x02, pack => "S",
265
+			  check => \&check_pcat_rom },
245 266
     },
246 267
   };
247 268
   bless $hash, $class;
@@ -250,9 +271,9 @@ sub new {
250 271
 
251 272
 =pod
252 273
 
253
-=item C<< set ( $data ) >>
274
+=item C<< set ( $data [, $file_offset ] ) >>
254 275
 
255
-Set option ROM contents.
276
+Set option ROM contents, optionally sets original file offset.
256 277
 
257 278
 =cut
258 279
 
@@ -260,9 +281,11 @@ sub set {
260 281
   my $hash = shift;
261 282
   my $self = tied(%$hash);
262 283
   my $data = shift;
284
+  my $file_offset = shift // 0x0;
263 285
 
264 286
   # Store data
265 287
   $self->{data} = \$data;
288
+  $self->{file_offset} = $file_offset;
266 289
 
267 290
   # Split out any data belonging to the next image
268 291
   delete $self->{next_image};
@@ -273,7 +296,7 @@ sub set {
273 296
     my $remainder = substr ( $data, $length );
274 297
     $data = substr ( $data, 0, $length );
275 298
     $self->{next_image} = new Option::ROM;
276
-    $self->{next_image}->set ( $remainder );
299
+    $self->{next_image}->set ( $remainder, $self->{file_offset} + $length );
277 300
   }
278 301
 }
279 302
 
@@ -311,6 +334,7 @@ sub load {
311 334
 
312 335
   open my $fh, "<$filename"
313 336
       or croak "Cannot open $filename for reading: $!";
337
+  binmode $fh;
314 338
   read $fh, my $data, -s $fh;
315 339
   $hash->set ( $data );
316 340
   close $fh;
@@ -335,6 +359,7 @@ sub save {
335 359
   open my $fh, ">$filename"
336 360
       or croak "Cannot open $filename for writing: $!";
337 361
   my $data = $hash->get();
362
+  binmode $fh;
338 363
   print $fh $data;
339 364
   close $fh;
340 365
 }
@@ -369,9 +394,9 @@ sub pci_header {
369 394
   my $self = tied(%$hash);
370 395
 
371 396
   my $offset = $hash->{pci_header};
372
-  return undef unless $offset != 0;
397
+  return undef unless $offset;
373 398
 
374
-  return Option::ROM::PCI->new ( $self->{data}, $offset );
399
+  return Option::ROM::PCI->new ( $self, $offset );
375 400
 }
376 401
 
377 402
 =pod
@@ -388,9 +413,9 @@ sub pnp_header {
388 413
   my $self = tied(%$hash);
389 414
 
390 415
   my $offset = $hash->{pnp_header};
391
-  return undef unless $offset != 0;
416
+  return undef unless $offset;
392 417
 
393
-  return Option::ROM::PnP->new ( $self->{data}, $offset );
418
+  return Option::ROM::PnP->new ( $self, $offset );
394 419
 }
395 420
 
396 421
 =pod
@@ -407,9 +432,9 @@ sub undi_header {
407 432
   my $self = tied(%$hash);
408 433
 
409 434
   my $offset = $hash->{undi_header};
410
-  return undef unless $offset != 0;
435
+  return undef unless $offset;
411 436
 
412
-  return Option::ROM::UNDI->new ( $self->{data}, $offset );
437
+  return Option::ROM::UNDI->new ( $self, $offset );
413 438
 }
414 439
 
415 440
 =pod
@@ -426,9 +451,9 @@ sub ipxe_header {
426 451
   my $self = tied(%$hash);
427 452
 
428 453
   my $offset = $hash->{ipxe_header};
429
-  return undef unless $offset != 0;
454
+  return undef unless $offset;
430 455
 
431
-  return Option::ROM::iPXE->new ( $self->{data}, $offset );
456
+  return Option::ROM::iPXE->new ( $self, $offset );
432 457
 }
433 458
 
434 459
 =pod
@@ -475,9 +500,25 @@ sub fix_checksum {
475 500
   my $hash = shift;
476 501
   my $self = tied(%$hash);
477 502
 
503
+  return unless ( exists $hash->{checksum} );
478 504
   $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
479 505
 }
480 506
 
507
+=pod
508
+
509
+=item C<< file_offset () >>
510
+
511
+Get file offset of image.
512
+
513
+=cut
514
+
515
+sub file_offset {
516
+  my $hash = shift;
517
+  my $self = tied(%$hash);
518
+
519
+  return $self->{file_offset};
520
+}
521
+
481 522
 ##############################################################################
482 523
 #
483 524
 # Option::ROM::PCI
@@ -493,12 +534,13 @@ use bytes;
493 534
 
494 535
 sub new {
495 536
   my $class = shift;
496
-  my $data = shift;
537
+  my $rom = shift;
497 538
   my $offset = shift;
498 539
 
499 540
   my $hash = {};
500 541
   tie %$hash, "Option::ROM::Fields", {
501
-    data => $data,
542
+    rom => $rom,
543
+    data => $rom->{data},
502 544
     offset => $offset,
503 545
     length => 0x0c,
504 546
     fields => {
@@ -522,11 +564,17 @@ sub new {
522 564
   };
523 565
   bless $hash, $class;
524 566
 
525
-  # Retrieve true length of structure
526 567
   my $self = tied ( %$hash );
568
+  my $length = $rom->{rom}->length ();
569
+
570
+  return undef unless ( $offset + $self->{length} <= $length &&
571
+			$hash->{signature} eq Option::ROM::PCI_SIGNATURE &&
572
+			$offset + $hash->{struct_length} <= $length );
573
+
574
+  # Retrieve true length of structure
527 575
   $self->{length} = $hash->{struct_length};
528 576
 
529
-  return $hash;  
577
+  return $hash;
530 578
 }
531 579
 
532 580
 sub device_list {
@@ -564,12 +612,13 @@ use bytes;
564 612
 
565 613
 sub new {
566 614
   my $class = shift;
567
-  my $data = shift;
615
+  my $rom = shift;
568 616
   my $offset = shift;
569 617
 
570 618
   my $hash = {};
571 619
   tie %$hash, "Option::ROM::Fields", {
572
-    data => $data,
620
+    rom => $rom,
621
+    data => $rom->{data},
573 622
     offset => $offset,
574 623
     length => 0x06,
575 624
     fields => {
@@ -586,11 +635,17 @@ sub new {
586 635
   };
587 636
   bless $hash, $class;
588 637
 
589
-  # Retrieve true length of structure
590 638
   my $self = tied ( %$hash );
639
+  my $length = $rom->{rom}->length ();
640
+
641
+  return undef unless ( $offset + $self->{length} <= $length &&
642
+			$hash->{signature} eq Option::ROM::PNP_SIGNATURE &&
643
+			$offset + $hash->{struct_length} * 16 <= $length );
644
+
645
+  # Retrieve true length of structure
591 646
   $self->{length} = ( $hash->{struct_length} * 16 );
592 647
 
593
-  return $hash;  
648
+  return $hash;
594 649
 }
595 650
 
596 651
 sub checksum {
@@ -644,12 +699,13 @@ use bytes;
644 699
 
645 700
 sub new {
646 701
   my $class = shift;
647
-  my $data = shift;
702
+  my $rom = shift;
648 703
   my $offset = shift;
649 704
 
650 705
   my $hash = {};
651 706
   tie %$hash, "Option::ROM::Fields", {
652
-    data => $data,
707
+    rom => $rom,
708
+    data => $rom->{data},
653 709
     offset => $offset,
654 710
     length => 0x16,
655 711
     fields => {
@@ -669,8 +725,14 @@ sub new {
669 725
   };
670 726
   bless $hash, $class;
671 727
 
672
-  # Retrieve true length of structure
673 728
   my $self = tied ( %$hash );
729
+  my $length = $rom->{rom}->length ();
730
+
731
+  return undef unless ( $offset + $self->{length} <= $length &&
732
+			$hash->{signature} eq Option::ROM::UNDI_SIGNATURE &&
733
+			$offset + $hash->{struct_length} <= $length );
734
+
735
+  # Retrieve true length of structure
674 736
   $self->{length} = $hash->{struct_length};
675 737
 
676 738
   return $hash;
@@ -705,12 +767,13 @@ use bytes;
705 767
 
706 768
 sub new {
707 769
   my $class = shift;
708
-  my $data = shift;
770
+  my $rom = shift;
709 771
   my $offset = shift;
710 772
 
711 773
   my $hash = {};
712 774
   tie %$hash, "Option::ROM::Fields", {
713
-    data => $data,
775
+    rom => $rom,
776
+    data => $rom->{data},
714 777
     offset => $offset,
715 778
     length => 0x06,
716 779
     fields => {
@@ -723,8 +786,14 @@ sub new {
723 786
   };
724 787
   bless $hash, $class;
725 788
 
726
-  # Retrieve true length of structure
727 789
   my $self = tied ( %$hash );
790
+  my $length = $rom->{rom}->length ();
791
+
792
+  return undef unless ( $offset + $self->{length} <= $length &&
793
+			$hash->{signature} eq Option::ROM::IPXE_SIGNATURE &&
794
+			$offset + $hash->{struct_length} <= $length );
795
+
796
+  # Retrieve true length of structure
728 797
   $self->{length} = $hash->{struct_length};
729 798
 
730 799
   return $hash;

+ 7
- 5
src/util/disrom.pl View File

@@ -28,8 +28,9 @@ my $romfile = shift || "-";
28 28
 my $rom = new Option::ROM;
29 29
 $rom->load ( $romfile );
30 30
 
31
-do {
31
+my $index = 0;
32 32
 
33
+do {
33 34
   die "Not an option ROM image\n"
34 35
       unless $rom->{signature} == ROM_SIGNATURE;
35 36
 
@@ -38,15 +39,16 @@ do {
38 39
   die "ROM image truncated (is $filelength, should be $romlength)\n"
39 40
       if $filelength < $romlength;
40 41
 
42
+  printf "Index: %d, offset: 0x%08x\n\n", $index++, $rom->file_offset;
41 43
   printf "ROM header:\n\n";
42 44
   printf "  %-16s 0x%02x (%d)\n", "Length:",
43 45
 	 $rom->{length}, ( $rom->{length} * 512 );
44 46
   printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $rom->{checksum},
45
-	 ( ( $rom->checksum == 0 ) ? "" : "INCORRECT: " ), $rom->checksum;
46
-  printf "  %-16s 0x%04x\n", "Init:", $rom->{init};
47
-  printf "  %-16s 0x%04x\n", "UNDI header:", $rom->{undi_header};
47
+	 ( ( $rom->checksum () == 0 ) ? "" : "INCORRECT: " ), $rom->checksum () if ( exists $rom->{checksum} );
48
+  printf "  %-16s 0x%04x\n", "Init:", $rom->{init} if ( defined $rom->{init} );
49
+  printf "  %-16s 0x%04x\n", "UNDI header:", $rom->{undi_header} if ( exists $rom->{undi_header} );
48 50
   printf "  %-16s 0x%04x\n", "PCI header:", $rom->{pci_header};
49
-  printf "  %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header};
51
+  printf "  %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header} if ( exists $rom->{pnp_header} );
50 52
   printf "\n";
51 53
 
52 54
   my $pci = $rom->pci_header();

Loading…
Cancel
Save