[Bps-public-commit] r14027 - in Data-Plist: .
alexmv at bestpractical.com
alexmv at bestpractical.com
Fri Jul 11 16:38:05 EDT 2008
Author: alexmv
Date: Fri Jul 11 16:38:04 2008
New Revision: 14027
Modified:
Data-Plist/ (props changed)
Data-Plist/lib/Data/Plist/BinaryReader.pm
Log:
r34084 at kohr-ah: chmrr | 2008-07-11 16:38:01 -0400
* Error-proofing and small refactoring
Modified: Data-Plist/lib/Data/Plist/BinaryReader.pm
==============================================================================
--- Data-Plist/lib/Data/Plist/BinaryReader.pm (original)
+++ Data-Plist/lib/Data/Plist/BinaryReader.pm Fri Jul 11 16:38:04 2008
@@ -16,35 +16,35 @@
sub read_misc {
my $self = shift;
- my ($objLen) = @_;
- if ( $objLen == 0 ) {
+ my ($type) = @_;
+ if ( $type == 0 ) {
return [ "null", 0 ];
- } elsif ( $objLen == 8 ) {
+ } elsif ( $type == 8 ) {
return [ "false", 0 ];
- } elsif ( $objLen == 9 ) {
+ } elsif ( $type == 9 ) {
return [ "true", 1 ];
- } elsif ( $objLen == 15 ) {
+ } elsif ( $type == 15 ) {
return [ "fill", 15 ];
+ } else {
+ return [ "???", $type ];
}
}
sub read_int { # int
my $self = shift;
+ my ($size) = @_;
- my ($objLen) = @_;
- die "Integer > 8 bytes = $objLen" if ( $objLen > 3 );
-
- my $byteLen = 1 << $objLen;
+ die "Integer > 8 bytes = $size" if ( $size > 3 );
my ( $buf, $val );
- read( $self->{fh}, $buf, $byteLen );
- if ( $objLen == 0 ) {
+ read( $self->{fh}, $buf, 1 << $size );
+ if ( $size == 0 ) {
$val = unpack( "C", $buf );
- } elsif ( $objLen == 1 ) {
+ } elsif ( $size == 1 ) {
$val = unpack( "n", $buf );
- } elsif ( $objLen == 2 ) {
+ } elsif ( $size == 2 ) {
$val = unpack( "N", $buf );
- } elsif ( $objLen == 3 ) {
+ } elsif ( $size == 3 ) {
my ( $hw, $lw ) = unpack( "NN", $buf );
$val = Math::BigInt->new($hw)->blsft(32)->bior($lw);
@@ -58,20 +58,18 @@
sub read_real { # real
my $self = shift;
- my ($objLen) = @_;
- die "Real > 8 bytes" if ( $objLen > 3 );
-
- my $byteLen = 1 << $objLen;
+ my ($size) = @_;
+ die "Real > 8 bytes" if ( $size > 3 );
my ( $buf, $val );
- read( $self->{fh}, $buf, $byteLen );
- if ( $objLen == 0 ) { # 1 byte float = error?
+ read( $self->{fh}, $buf, 1 << $size );
+ if ( $size == 0 ) { # 1 byte float = error?
die "1 byte real found";
- } elsif ( $objLen == 1 ) { # 2 byte float???
+ } elsif ( $size == 1 ) { # 2 byte float???
die "2 byte real found";
- } elsif ( $objLen == 2 ) {
+ } elsif ( $size == 2 ) {
$val = unpack( "f", reverse $buf );
- } elsif ( $objLen == 3 ) {
+ } elsif ( $size == 3 ) {
$val = unpack( "d", reverse $buf );
}
@@ -80,32 +78,20 @@
sub read_date {
my $self = shift;
- my ($objLen) = @_;
- die "Date > 8 bytes" if ( $objLen > 3 );
-
- my $byteLen = 1 << $objLen;
-
- my ( $buf, $val );
- read( $self->{fh}, $buf, $byteLen );
- if ( $objLen == 0 ) { # 1 byte NSDate = error?
- die "1 byte NSDate found";
- } elsif ( $objLen == 1 ) { # 2 byte NSDate???
- die "2 byte NSDate found";
- } elsif ( $objLen == 2 ) {
- $val = unpack( "f", reverse $buf );
- } elsif ( $objLen == 3 ) {
- $val = unpack( "d", reverse $buf );
- }
+ my ($size) = @_;
+ die "Date > 8 bytes" if ( $size > 3 );
+ die "Date < 4 bytes" if ( $size < 2 );
- return [ "date", $val ];
+ # Dates are just stored as floats
+ return [ "date", $self->read_real($size)->[1] ];
}
sub read_data {
my $self = shift;
- my ($byteLen) = @_;
+ my ($size) = @_;
my $buf;
- read( $self->{fh}, $buf, $byteLen );
+ read( $self->{fh}, $buf, $size );
# Binary data is often a binary plist! Unpack it.
if ( $buf =~ /^bplist00/ ) {
@@ -117,10 +103,10 @@
sub read_string {
my $self = shift;
- my ($objLen) = @_;
+ my ($size) = @_;
my $buf;
- read( $self->{fh}, $buf, $objLen );
+ read( $self->{fh}, $buf, $size );
$buf = pack "U0C*", unpack "C*", $buf; # mark as Unicode
@@ -129,10 +115,10 @@
sub read_ustring {
my $self = shift;
- my ($objLen) = @_;
+ my ($size) = @_;
my $buf;
- read( $self->{fh}, $buf, 2 * $objLen );
+ read( $self->{fh}, $buf, 2 * $size );
return [ "ustring", decode( "UTF-16BE", $buf ) ];
}
@@ -147,21 +133,21 @@
sub read_array {
my $self = shift;
- my ($objLen) = @_;
+ my ($size) = @_;
return [
- "array", [ map { $self->binary_read($_) } $self->read_refs($objLen) ]
+ "array", [ map { $self->binary_read($_) } $self->read_refs($size) ]
];
}
sub read_dict {
my $self = shift;
- my ($objLen) = @_;
+ my ($size) = @_;
my %dict;
# read keys
- my @keys = $self->read_refs($objLen);
- my @objs = $self->read_refs($objLen);
+ my @keys = $self->read_refs($size);
+ my @objs = $self->read_refs($size);
for my $j ( 0 .. $#keys ) {
my $key = $self->binary_read( $keys[$j] );
@@ -176,19 +162,24 @@
sub read_uid {
my $self = shift;
- my ($objLen) = @_;
+ my ($size) = @_;
# UIDs are stored internally identically to ints
- my $v = $self->read_int($objLen)->[1];
- return [ UID => $v ];;
+ my $v = $self->read_int($size)->[1];
+ return [ UID => $v ];
}
sub binary_read {
my $self = shift;
my ($objNum) = @_;
- seek( $self->{fh}, $self->{offsets}[$objNum], SEEK_SET )
- if defined $objNum;
+ if (defined $objNum) {
+ unless ($objNum < @{$self->{offsets}}) {
+ warn "Bad offset: $objNum\n";
+ return;
+ }
+ seek( $self->{fh}, $self->{offsets}[$objNum], SEEK_SET );
+ }
# get object type/size
my $buf;
@@ -196,11 +187,11 @@
if ( read( $self->{fh}, $buf, 1 ) != 1 ) {
die "Didn't read type byte: $!";
}
- my $objLen = unpack( "C*", $buf ) & 0xF;
+ my $size = unpack( "C*", $buf ) & 0xF;
$buf = unpack( "H*", $buf );
my $objType = substr( $buf, 0, 1 );
- if ( $objType ne "0" && $objType ne "8" && $objLen == 15 ) {
- $objLen = $self->binary_read->[1];
+ if ( $objType ne "0" && $objType ne "8" && $size == 15 ) {
+ $size = $self->binary_read->[1];
}
my %types = (
@@ -219,7 +210,7 @@
return [ "??? $objType ???", undef ] unless $types{$objType};
my $method = "read_" . $types{$objType};
die "Can't $method" unless $self->can($method);
- my $v = $self->$method($objLen);
+ my $v = $self->$method($size);
return $v;
}
@@ -248,33 +239,61 @@
my ($fh) = @_;
+ my $buf;
$self->{fh} = $fh;
+ seek( $self->{fh}, 0, SEEK_SET );
+ read( $self->{fh}, $buf, 8);
+ unless ($buf eq "bplist00") {
+ die "Not a binary plist file\n";
+ }
# get trailer
seek( $self->{fh}, -32, SEEK_END );
- my $buf;
- read( $self->{fh}, $buf, 32 );
+ my $end = tell( $self->{fh} );
+
+ unless (read( $self->{fh}, $buf, 32 ) == 32) {
+ die "Read of plist trailer failed\n";
+ }
my ( $OffsetSize, $NumObjects, $TopObject, $OffsetTableOffset );
( $OffsetSize, $self->{refsize}, $NumObjects, $TopObject,
$OffsetTableOffset
) = unpack "x6CC(x4N)3", $buf;
+ # Sanity check the trailer
+ if ($OffsetSize < 1 or $OffsetSize > 4) {
+ die "Invalid offset size\n";
+ } elsif ( $self->{refsize} < 1 or $self->{refsize} > 2) {
+ die "Invalid reference size\n";
+ } elsif ( 2 ** (8 * $self->{refsize}) < $NumObjects ) {
+ die "Reference size (@{[$self->{refsize}]}) is too small for purported number of objects ($NumObjects)\n";
+ } elsif ( $TopObject >= $NumObjects ) {
+ die "Invalid top object identifier\n";
+ } elsif ( $OffsetTableOffset < 8 or $OffsetTableOffset > $end or $OffsetTableOffset + $NumObjects * $OffsetSize > $end) {
+ die "Invalid offset table address\n";
+ }
+
# get the offset table
seek( $fh, $OffsetTableOffset, SEEK_SET );
- my $rawOffsetTable;
+ my $offsetTable;
my $readSize
- = read( $self->{fh}, $rawOffsetTable, $NumObjects * $OffsetSize );
+ = read( $self->{fh}, $offsetTable, $NumObjects * $OffsetSize );
if ( $readSize != $NumObjects * $OffsetSize ) {
- die "rawOffsetTable read $readSize expected ",
+ die "Offset table read $readSize bytes, expected ",
$NumObjects * $OffsetSize;
}
my @Offsets = unpack( [ "", "C*", "n*", "(H6)*", "N*" ]->[$OffsetSize],
- $rawOffsetTable );
+ $offsetTable );
if ( $OffsetSize == 3 ) {
@Offsets = map { hex($_) } @Offsets;
}
+
+ # Catch invalid offset addresses in the offset table
+ if (grep {$_ < 8 or $_ >= $end} @Offsets) {
+ die "Invalid address in offset table\n";
+ }
+
$self->{offsets} = \@Offsets;
my $top = $self->binary_read($TopObject);
More information about the Bps-public-commit
mailing list