Merge pull request #19053 from ianic/tar_fuzzing2
std:tar fix two crashes found by fuzzing
This commit is contained in:
@@ -140,11 +140,25 @@ pub const Header = struct {
|
||||
}
|
||||
|
||||
pub fn mode(header: Header) !u32 {
|
||||
return @intCast(try header.numeric(100, 8));
|
||||
return @intCast(try header.octal(100, 8));
|
||||
}
|
||||
|
||||
pub fn size(header: Header) !u64 {
|
||||
return header.numeric(124, 12);
|
||||
const start = 124;
|
||||
const len = 12;
|
||||
const raw = header.bytes[start..][0..len];
|
||||
// If the leading byte is 0xff (255), all the bytes of the field
|
||||
// (including the leading byte) are concatenated in big-endian order,
|
||||
// with the result being a negative number expressed in two’s
|
||||
// complement form.
|
||||
if (raw[0] == 0xff) return error.TarNumericValueNegative;
|
||||
// If the leading byte is 0x80 (128), the non-leading bytes of the
|
||||
// field are concatenated in big-endian order.
|
||||
if (raw[0] == 0x80) {
|
||||
if (raw[1] != 0 or raw[2] != 0 or raw[3] != 0) return error.TarNumericValueTooBig;
|
||||
return std.mem.readInt(u64, raw[4..12], .big);
|
||||
}
|
||||
return try header.octal(start, len);
|
||||
}
|
||||
|
||||
pub fn chksum(header: Header) !u64 {
|
||||
@@ -170,22 +184,6 @@ pub const Header = struct {
|
||||
return nullStr(header.bytes[start .. start + len]);
|
||||
}
|
||||
|
||||
fn numeric(header: Header, start: usize, len: usize) !u64 {
|
||||
const raw = header.bytes[start..][0..len];
|
||||
// If the leading byte is 0xff (255), all the bytes of the field
|
||||
// (including the leading byte) are concatenated in big-endian order,
|
||||
// with the result being a negative number expressed in two’s
|
||||
// complement form.
|
||||
if (raw[0] == 0xff) return error.TarNumericValueNegative;
|
||||
// If the leading byte is 0x80 (128), the non-leading bytes of the
|
||||
// field are concatenated in big-endian order.
|
||||
if (raw[0] == 0x80) {
|
||||
if (raw[1] + raw[2] + raw[3] != 0) return error.TarNumericValueTooBig;
|
||||
return std.mem.readInt(u64, raw[4..12], .big);
|
||||
}
|
||||
return try header.octal(start, len);
|
||||
}
|
||||
|
||||
fn octal(header: Header, start: usize, len: usize) !u64 {
|
||||
const raw = header.bytes[start..][0..len];
|
||||
// Zero-filled octal number in ASCII. Each numeric field of width w
|
||||
@@ -756,3 +754,63 @@ test "tar PaxIterator" {
|
||||
test {
|
||||
_ = @import("tar/test.zig");
|
||||
}
|
||||
|
||||
test "tar header parse size" {
|
||||
const cases = [_]struct {
|
||||
in: []const u8,
|
||||
want: u64 = 0,
|
||||
err: ?anyerror = null,
|
||||
}{
|
||||
// Test base-256 (binary) encoded values.
|
||||
.{ .in = "", .want = 0 },
|
||||
.{ .in = "\x80", .want = 0 },
|
||||
.{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", .want = 1 },
|
||||
.{ .in = "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02", .want = 0x0102 },
|
||||
.{ .in = "\x80\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08", .want = 0x0102030405060708 },
|
||||
.{ .in = "\x80\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09", .err = error.TarNumericValueTooBig },
|
||||
.{ .in = "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", .want = 537795476381659745 },
|
||||
.{ .in = "\x80\x80\x80\x00\x01\x02\x03\x04\x05\x06\x07\x08", .err = error.TarNumericValueTooBig },
|
||||
|
||||
// // Test base-8 (octal) encoded values.
|
||||
.{ .in = "00000000227\x00", .want = 0o227 },
|
||||
.{ .in = " 000000227\x00", .want = 0o227 },
|
||||
.{ .in = "00000000228\x00", .err = error.TarHeader },
|
||||
.{ .in = "11111111111\x00", .want = 0o11111111111 },
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
var bytes = [_]u8{0} ** Header.SIZE;
|
||||
@memcpy(bytes[124 .. 124 + case.in.len], case.in);
|
||||
var header = Header{ .bytes = &bytes };
|
||||
if (case.err) |err| {
|
||||
try std.testing.expectError(err, header.size());
|
||||
} else {
|
||||
try std.testing.expectEqual(case.want, try header.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "tar header parse mode" {
|
||||
const cases = [_]struct {
|
||||
in: []const u8,
|
||||
want: u64 = 0,
|
||||
err: ?anyerror = null,
|
||||
}{
|
||||
.{ .in = "0000644\x00", .want = 0o644 },
|
||||
.{ .in = "0000777\x00", .want = 0o777 },
|
||||
.{ .in = "7777777\x00", .want = 0o7777777 },
|
||||
.{ .in = "7777778\x00", .err = error.TarHeader },
|
||||
.{ .in = "77777777", .want = 0o77777777 },
|
||||
.{ .in = "777777777777", .want = 0o77777777 },
|
||||
};
|
||||
for (cases) |case| {
|
||||
var bytes = [_]u8{0} ** Header.SIZE;
|
||||
@memcpy(bytes[100 .. 100 + case.in.len], case.in);
|
||||
var header = Header{ .bytes = &bytes };
|
||||
if (case.err) |err| {
|
||||
try std.testing.expectError(err, header.mode());
|
||||
} else {
|
||||
try std.testing.expectEqual(case.want, try header.mode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user