Skip to content

Commit 4c856ad

Browse files
committed
Binary: check lengths of given data explicitly, don't leave it up to unpack()
this allows us to display better error messages.
1 parent 6846ade commit 4c856ad

File tree

1 file changed

+31
-17
lines changed

1 file changed

+31
-17
lines changed

src/Binary.php

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,20 @@
3333
use function preg_replace;
3434
use function round;
3535
use function sprintf;
36+
use function strlen;
3637
use function substr;
3738
use function unpack;
3839
use const PHP_INT_MAX;
3940

4041
class Binary{
42+
public const SIZEOF_BYTE = 1;
43+
public const SIZEOF_SHORT = 2;
44+
public const SIZEOF_INT = 4;
45+
public const SIZEOF_LONG = 8;
46+
47+
public const SIZEOF_FLOAT = 4;
48+
public const SIZEOF_DOUBLE = 8;
49+
4150
public static function signByte(int $value) : int{
4251
return $value << 56 >> 56;
4352
}
@@ -76,13 +85,18 @@ public static function flipLongEndianness(int $value) : int{
7685

7786
/**
7887
* @return mixed[]
88+
* @throws BinaryDataException
7989
*/
80-
private static function safeUnpack(string $formatCode, string $bytes) : array{
90+
private static function safeUnpack(string $formatCode, string $bytes, int $needLength) : array{
91+
$haveLength = strlen($bytes);
92+
if($haveLength < $needLength){
93+
throw new BinaryDataException("Not enough bytes: need $needLength, have $haveLength");
94+
}
8195
//unpack SUCKS SO BADLY. We really need an extension to replace this garbage :(
8296
$result = unpack($formatCode, $bytes);
8397
if($result === false){
84-
//assume the formatting code is valid, since we provided it
85-
throw new BinaryDataException("Invalid input data (not enough?)");
98+
//this should never happen; we checked the length above
99+
throw new \AssertionError("unpack() failed for unknown reason");
86100
}
87101
return $result;
88102
}
@@ -136,14 +150,14 @@ public static function writeByte(int $c) : string{
136150
* Reads a 16-bit unsigned big-endian number
137151
*/
138152
public static function readShort(string $str) : int{
139-
return self::safeUnpack("n", $str)[1];
153+
return self::safeUnpack("n", $str, self::SIZEOF_SHORT)[1];
140154
}
141155

142156
/**
143157
* Reads a 16-bit signed big-endian number
144158
*/
145159
public static function readSignedShort(string $str) : int{
146-
return self::signShort(self::safeUnpack("n", $str)[1]);
160+
return self::signShort(self::safeUnpack("n", $str, self::SIZEOF_SHORT)[1]);
147161
}
148162

149163
/**
@@ -157,14 +171,14 @@ public static function writeShort(int $value) : string{
157171
* Reads a 16-bit unsigned little-endian number
158172
*/
159173
public static function readLShort(string $str) : int{
160-
return self::safeUnpack("v", $str)[1];
174+
return self::safeUnpack("v", $str, self::SIZEOF_SHORT)[1];
161175
}
162176

163177
/**
164178
* Reads a 16-bit signed little-endian number
165179
*/
166180
public static function readSignedLShort(string $str) : int{
167-
return self::signShort(self::safeUnpack("v", $str)[1]);
181+
return self::signShort(self::safeUnpack("v", $str, self::SIZEOF_SHORT)[1]);
168182
}
169183

170184
/**
@@ -178,7 +192,7 @@ public static function writeLShort(int $value) : string{
178192
* Reads a 3-byte big-endian number
179193
*/
180194
public static function readTriad(string $str) : int{
181-
return self::safeUnpack("N", "\x00" . $str)[1];
195+
return self::safeUnpack("N", "\x00" . $str, self::SIZEOF_INT)[1];
182196
}
183197

184198
/**
@@ -192,7 +206,7 @@ public static function writeTriad(int $value) : string{
192206
* Reads a 3-byte little-endian number
193207
*/
194208
public static function readLTriad(string $str) : int{
195-
return self::safeUnpack("V", $str . "\x00")[1];
209+
return self::safeUnpack("V", $str . "\x00", self::SIZEOF_INT)[1];
196210
}
197211

198212
/**
@@ -206,7 +220,7 @@ public static function writeLTriad(int $value) : string{
206220
* Reads a 4-byte signed integer
207221
*/
208222
public static function readInt(string $str) : int{
209-
return self::signInt(self::safeUnpack("N", $str)[1]);
223+
return self::signInt(self::safeUnpack("N", $str, self::SIZEOF_INT)[1]);
210224
}
211225

212226
/**
@@ -220,7 +234,7 @@ public static function writeInt(int $value) : string{
220234
* Reads a 4-byte signed little-endian integer
221235
*/
222236
public static function readLInt(string $str) : int{
223-
return self::signInt(self::safeUnpack("V", $str)[1]);
237+
return self::signInt(self::safeUnpack("V", $str, self::SIZEOF_INT)[1]);
224238
}
225239

226240
/**
@@ -234,7 +248,7 @@ public static function writeLInt(int $value) : string{
234248
* Reads a 4-byte floating-point number
235249
*/
236250
public static function readFloat(string $str) : float{
237-
return self::safeUnpack("G", $str)[1];
251+
return self::safeUnpack("G", $str, self::SIZEOF_FLOAT)[1];
238252
}
239253

240254
/**
@@ -255,7 +269,7 @@ public static function writeFloat(float $value) : string{
255269
* Reads a 4-byte little-endian floating-point number.
256270
*/
257271
public static function readLFloat(string $str) : float{
258-
return self::safeUnpack("g", $str)[1];
272+
return self::safeUnpack("g", $str, self::SIZEOF_FLOAT)[1];
259273
}
260274

261275
/**
@@ -283,7 +297,7 @@ public static function printFloat(float $value) : string{
283297
* Reads an 8-byte floating-point number.
284298
*/
285299
public static function readDouble(string $str) : float{
286-
return self::safeUnpack("E", $str)[1];
300+
return self::safeUnpack("E", $str, self::SIZEOF_DOUBLE)[1];
287301
}
288302

289303
/**
@@ -297,7 +311,7 @@ public static function writeDouble(float $value) : string{
297311
* Reads an 8-byte little-endian floating-point number.
298312
*/
299313
public static function readLDouble(string $str) : float{
300-
return self::safeUnpack("e", $str)[1];
314+
return self::safeUnpack("e", $str, self::SIZEOF_DOUBLE)[1];
301315
}
302316

303317
/**
@@ -311,7 +325,7 @@ public static function writeLDouble(float $value) : string{
311325
* Reads an 8-byte integer.
312326
*/
313327
public static function readLong(string $str) : int{
314-
return self::safeUnpack("J", $str)[1];
328+
return self::safeUnpack("J", $str, self::SIZEOF_LONG)[1];
315329
}
316330

317331
/**
@@ -325,7 +339,7 @@ public static function writeLong(int $value) : string{
325339
* Reads an 8-byte little-endian integer.
326340
*/
327341
public static function readLLong(string $str) : int{
328-
return self::safeUnpack("P", $str)[1];
342+
return self::safeUnpack("P", $str, self::SIZEOF_LONG)[1];
329343
}
330344

331345
/**

0 commit comments

Comments
 (0)