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
 
116
 
117
   return ( exists $self->{fields}->{$key} &&
117
   return ( exists $self->{fields}->{$key} &&
118
 	   ( ( $self->{fields}->{$key}->{offset} +
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
 sub FIRSTKEY {
124
 sub FIRSTKEY {
172
 use constant PCI_SIGNATURE => 'PCIR';
174
 use constant PCI_SIGNATURE => 'PCIR';
173
 use constant PCI_LAST_IMAGE => 0x80;
175
 use constant PCI_LAST_IMAGE => 0x80;
174
 use constant PNP_SIGNATURE => '$PnP';
176
 use constant PNP_SIGNATURE => '$PnP';
177
+use constant UNDI_SIGNATURE => 'UNDI';
175
 use constant IPXE_SIGNATURE => 'iPXE';
178
 use constant IPXE_SIGNATURE => 'iPXE';
176
 
179
 
177
 our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PCI_LAST_IMAGE
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
 our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
182
 our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
180
 
183
 
181
 use constant JMP_SHORT => 0xeb;
184
 use constant JMP_SHORT => 0xeb;
210
   } elsif ( $jump == 0 ) {
213
   } elsif ( $jump == 0 ) {
211
     return 0;
214
     return 0;
212
   } else {
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
 =pod
230
 =pod
218
 
231
 
219
 =item C<< new () >>
232
 =item C<< new () >>
227
 
240
 
228
   my $hash = {};
241
   my $hash = {};
229
   tie %$hash, "Option::ROM::Fields", {
242
   tie %$hash, "Option::ROM::Fields", {
243
+    rom => $hash, # ROM object itself
230
     data => undef,
244
     data => undef,
231
     offset => 0x00,
245
     offset => 0x00,
232
     length => 0x20,
246
     length => 0x20,
247
+    file_offset => 0x0,
233
     fields => {
248
     fields => {
234
       signature =>	{ offset => 0x00, length => 0x02, pack => "S" },
249
       signature =>	{ offset => 0x00, length => 0x02, pack => "S" },
235
       length =>		{ offset => 0x02, length => 0x01, pack => "C" },
250
       length =>		{ offset => 0x02, length => 0x01, pack => "C" },
236
       # "init" is part of a jump instruction
251
       # "init" is part of a jump instruction
237
       init =>		{ offset => 0x03, length => 0x03,
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
       pci_header =>	{ offset => 0x18, length => 0x02, pack => "S" },
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
   bless $hash, $class;
268
   bless $hash, $class;
250
 
271
 
251
 =pod
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
 =cut
278
 =cut
258
 
279
 
260
   my $hash = shift;
281
   my $hash = shift;
261
   my $self = tied(%$hash);
282
   my $self = tied(%$hash);
262
   my $data = shift;
283
   my $data = shift;
284
+  my $file_offset = shift // 0x0;
263
 
285
 
264
   # Store data
286
   # Store data
265
   $self->{data} = \$data;
287
   $self->{data} = \$data;
288
+  $self->{file_offset} = $file_offset;
266
 
289
 
267
   # Split out any data belonging to the next image
290
   # Split out any data belonging to the next image
268
   delete $self->{next_image};
291
   delete $self->{next_image};
273
     my $remainder = substr ( $data, $length );
296
     my $remainder = substr ( $data, $length );
274
     $data = substr ( $data, 0, $length );
297
     $data = substr ( $data, 0, $length );
275
     $self->{next_image} = new Option::ROM;
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
 
334
 
312
   open my $fh, "<$filename"
335
   open my $fh, "<$filename"
313
       or croak "Cannot open $filename for reading: $!";
336
       or croak "Cannot open $filename for reading: $!";
337
+  binmode $fh;
314
   read $fh, my $data, -s $fh;
338
   read $fh, my $data, -s $fh;
315
   $hash->set ( $data );
339
   $hash->set ( $data );
316
   close $fh;
340
   close $fh;
335
   open my $fh, ">$filename"
359
   open my $fh, ">$filename"
336
       or croak "Cannot open $filename for writing: $!";
360
       or croak "Cannot open $filename for writing: $!";
337
   my $data = $hash->get();
361
   my $data = $hash->get();
362
+  binmode $fh;
338
   print $fh $data;
363
   print $fh $data;
339
   close $fh;
364
   close $fh;
340
 }
365
 }
369
   my $self = tied(%$hash);
394
   my $self = tied(%$hash);
370
 
395
 
371
   my $offset = $hash->{pci_header};
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
 =pod
402
 =pod
388
   my $self = tied(%$hash);
413
   my $self = tied(%$hash);
389
 
414
 
390
   my $offset = $hash->{pnp_header};
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
 =pod
421
 =pod
407
   my $self = tied(%$hash);
432
   my $self = tied(%$hash);
408
 
433
 
409
   my $offset = $hash->{undi_header};
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
 =pod
440
 =pod
426
   my $self = tied(%$hash);
451
   my $self = tied(%$hash);
427
 
452
 
428
   my $offset = $hash->{ipxe_header};
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
 =pod
459
 =pod
475
   my $hash = shift;
500
   my $hash = shift;
476
   my $self = tied(%$hash);
501
   my $self = tied(%$hash);
477
 
502
 
503
+  return unless ( exists $hash->{checksum} );
478
   $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff );
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
 # Option::ROM::PCI
524
 # Option::ROM::PCI
493
 
534
 
494
 sub new {
535
 sub new {
495
   my $class = shift;
536
   my $class = shift;
496
-  my $data = shift;
537
+  my $rom = shift;
497
   my $offset = shift;
538
   my $offset = shift;
498
 
539
 
499
   my $hash = {};
540
   my $hash = {};
500
   tie %$hash, "Option::ROM::Fields", {
541
   tie %$hash, "Option::ROM::Fields", {
501
-    data => $data,
542
+    rom => $rom,
543
+    data => $rom->{data},
502
     offset => $offset,
544
     offset => $offset,
503
     length => 0x0c,
545
     length => 0x0c,
504
     fields => {
546
     fields => {
522
   };
564
   };
523
   bless $hash, $class;
565
   bless $hash, $class;
524
 
566
 
525
-  # Retrieve true length of structure
526
   my $self = tied ( %$hash );
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
   $self->{length} = $hash->{struct_length};
575
   $self->{length} = $hash->{struct_length};
528
 
576
 
529
-  return $hash;  
577
+  return $hash;
530
 }
578
 }
531
 
579
 
532
 sub device_list {
580
 sub device_list {
564
 
612
 
565
 sub new {
613
 sub new {
566
   my $class = shift;
614
   my $class = shift;
567
-  my $data = shift;
615
+  my $rom = shift;
568
   my $offset = shift;
616
   my $offset = shift;
569
 
617
 
570
   my $hash = {};
618
   my $hash = {};
571
   tie %$hash, "Option::ROM::Fields", {
619
   tie %$hash, "Option::ROM::Fields", {
572
-    data => $data,
620
+    rom => $rom,
621
+    data => $rom->{data},
573
     offset => $offset,
622
     offset => $offset,
574
     length => 0x06,
623
     length => 0x06,
575
     fields => {
624
     fields => {
586
   };
635
   };
587
   bless $hash, $class;
636
   bless $hash, $class;
588
 
637
 
589
-  # Retrieve true length of structure
590
   my $self = tied ( %$hash );
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
   $self->{length} = ( $hash->{struct_length} * 16 );
646
   $self->{length} = ( $hash->{struct_length} * 16 );
592
 
647
 
593
-  return $hash;  
648
+  return $hash;
594
 }
649
 }
595
 
650
 
596
 sub checksum {
651
 sub checksum {
644
 
699
 
645
 sub new {
700
 sub new {
646
   my $class = shift;
701
   my $class = shift;
647
-  my $data = shift;
702
+  my $rom = shift;
648
   my $offset = shift;
703
   my $offset = shift;
649
 
704
 
650
   my $hash = {};
705
   my $hash = {};
651
   tie %$hash, "Option::ROM::Fields", {
706
   tie %$hash, "Option::ROM::Fields", {
652
-    data => $data,
707
+    rom => $rom,
708
+    data => $rom->{data},
653
     offset => $offset,
709
     offset => $offset,
654
     length => 0x16,
710
     length => 0x16,
655
     fields => {
711
     fields => {
669
   };
725
   };
670
   bless $hash, $class;
726
   bless $hash, $class;
671
 
727
 
672
-  # Retrieve true length of structure
673
   my $self = tied ( %$hash );
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
   $self->{length} = $hash->{struct_length};
736
   $self->{length} = $hash->{struct_length};
675
 
737
 
676
   return $hash;
738
   return $hash;
705
 
767
 
706
 sub new {
768
 sub new {
707
   my $class = shift;
769
   my $class = shift;
708
-  my $data = shift;
770
+  my $rom = shift;
709
   my $offset = shift;
771
   my $offset = shift;
710
 
772
 
711
   my $hash = {};
773
   my $hash = {};
712
   tie %$hash, "Option::ROM::Fields", {
774
   tie %$hash, "Option::ROM::Fields", {
713
-    data => $data,
775
+    rom => $rom,
776
+    data => $rom->{data},
714
     offset => $offset,
777
     offset => $offset,
715
     length => 0x06,
778
     length => 0x06,
716
     fields => {
779
     fields => {
723
   };
786
   };
724
   bless $hash, $class;
787
   bless $hash, $class;
725
 
788
 
726
-  # Retrieve true length of structure
727
   my $self = tied ( %$hash );
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
   $self->{length} = $hash->{struct_length};
797
   $self->{length} = $hash->{struct_length};
729
 
798
 
730
   return $hash;
799
   return $hash;

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

28
 my $rom = new Option::ROM;
28
 my $rom = new Option::ROM;
29
 $rom->load ( $romfile );
29
 $rom->load ( $romfile );
30
 
30
 
31
-do {
31
+my $index = 0;
32
 
32
 
33
+do {
33
   die "Not an option ROM image\n"
34
   die "Not an option ROM image\n"
34
       unless $rom->{signature} == ROM_SIGNATURE;
35
       unless $rom->{signature} == ROM_SIGNATURE;
35
 
36
 
38
   die "ROM image truncated (is $filelength, should be $romlength)\n"
39
   die "ROM image truncated (is $filelength, should be $romlength)\n"
39
       if $filelength < $romlength;
40
       if $filelength < $romlength;
40
 
41
 
42
+  printf "Index: %d, offset: 0x%08x\n\n", $index++, $rom->file_offset;
41
   printf "ROM header:\n\n";
43
   printf "ROM header:\n\n";
42
   printf "  %-16s 0x%02x (%d)\n", "Length:",
44
   printf "  %-16s 0x%02x (%d)\n", "Length:",
43
 	 $rom->{length}, ( $rom->{length} * 512 );
45
 	 $rom->{length}, ( $rom->{length} * 512 );
44
   printf "  %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $rom->{checksum},
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
   printf "  %-16s 0x%04x\n", "PCI header:", $rom->{pci_header};
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
   printf "\n";
52
   printf "\n";
51
 
53
 
52
   my $pci = $rom->pci_header();
54
   my $pci = $rom->pci_header();

Loading…
Cancel
Save