std.tar: test buffers provided to the iterator

Tar header stores name in max 256 bytes and link name in max 100 bytes.
Those are minimums for provided buffers. Error is raised during iterator
init if buffers are not long enough.

Pax and gnu extensions can store longer names. If such extension is
reached during unpack and don't fit into provided buffer error is
returned.
This commit is contained in:
Igor Anić 2024-03-02 00:39:48 +01:00
parent af0502f6c4
commit 04e8bbd932
2 changed files with 51 additions and 8 deletions

View File

@ -87,8 +87,8 @@ pub const Options = struct {
pub const Header = struct {
const SIZE = 512;
const MAX_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)
const LINK_NAME_SIZE = 100;
pub const MAX_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)
pub const LINK_NAME_SIZE = 100;
bytes: *const [SIZE]u8,
@ -248,7 +248,13 @@ pub const IteratorOptions = struct {
/// Iterates over files in tar archive.
/// `next` returns each file in `reader` tar archive.
pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(reader)) {
/// Provided buffers should be at least 256 bytes for file_name and 100 bytes
/// for link_name.
pub fn iterator(reader: anytype, options: IteratorOptions) !Iterator(@TypeOf(reader)) {
if (options.file_name_buffer.len < Header.MAX_NAME_SIZE or
options.link_name_buffer.len < Header.LINK_NAME_SIZE)
return error.TarInsufficientBuffer;
return .{
.reader = reader,
.diagnostics = options.diagnostics,
@ -318,7 +324,7 @@ fn Iterator(comptime ReaderType: type) type {
}
fn readString(self: *Self, size: usize, buffer: []u8) ![]const u8 {
if (size > buffer.len) return error.TarCorruptInput;
if (size > buffer.len) return error.TarInsufficientBuffer;
const buf = buffer[0..size];
try self.reader.readNoEof(buf);
return nullStr(buf);
@ -470,7 +476,8 @@ fn PaxIterator(comptime ReaderType: type) type {
// Copies pax attribute value into destination buffer.
// Must be called with destination buffer of size at least Attribute.len.
pub fn value(self: Attribute, dst: []u8) ![]const u8 {
assert(self.len <= dst.len);
if (self.len > dst.len) return error.TarInsufficientBuffer;
// assert(self.len <= dst.len);
const buf = dst[0..self.len];
const n = try self.reader.readAll(buf);
if (n < self.len) return error.UnexpectedEndOfStream;
@ -558,7 +565,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var iter = iterator(reader, .{
var iter = try iterator(reader, .{
.file_name_buffer = &file_name_buffer,
.link_name_buffer = &link_name_buffer,
.diagnostics = options.diagnostics,

View File

@ -315,7 +315,7 @@ test "tar run Go test cases" {
},
.{
.data = @embedFile("testdata/fuzz1.tar"),
.err = error.TarCorruptInput,
.err = error.TarInsufficientBuffer,
},
.{
.data = @embedFile("testdata/fuzz2.tar"),
@ -328,7 +328,7 @@ test "tar run Go test cases" {
for (cases) |case| {
var fsb = std.io.fixedBufferStream(case.data);
var iter = tar.iterator(fsb.reader(), .{
var iter = try tar.iterator(fsb.reader(), .{
.file_name_buffer = &file_name_buffer,
.link_name_buffer = &link_name_buffer,
});
@ -359,6 +359,27 @@ test "tar run Go test cases" {
}
try testing.expectEqual(case.files.len, i);
}
var min_file_name_buffer: [tar.Header.MAX_NAME_SIZE]u8 = undefined;
var min_link_name_buffer: [tar.Header.LINK_NAME_SIZE]u8 = undefined;
const long_name_cases = [_]Case{ cases[11], cases[25], cases[28] };
for (long_name_cases) |case| {
var fsb = std.io.fixedBufferStream(case.data);
var iter = try tar.iterator(fsb.reader(), .{
.file_name_buffer = &min_file_name_buffer,
.link_name_buffer = &min_link_name_buffer,
});
var iter_err: ?anyerror = null;
while (iter.next() catch |err| brk: {
iter_err = err;
break :brk null;
}) |_| {}
try testing.expect(iter_err != null);
try testing.expectEqual(error.TarInsufficientBuffer, iter_err.?);
}
}
// used in test to calculate file chksum
@ -490,6 +511,21 @@ test "tar pipeToFileSystem" {
try testing.expectError(error.FileNotFound, root.dir.statFile("empty"));
try testing.expect((try root.dir.statFile("a/file")).kind == .file);
try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink
var buf: [32]u8 = undefined;
try testing.expectEqualSlices(u8, "../a/file", try root.dir.readLink("b/symlink", &buf));
}
test "insufficient buffer for iterator" {
var file_name_buffer: [10]u8 = undefined;
var link_name_buffer: [10]u8 = undefined;
var fsb = std.io.fixedBufferStream("");
try testing.expectError(
error.TarInsufficientBuffer,
tar.iterator(fsb.reader(), .{
.file_name_buffer = &file_name_buffer,
.link_name_buffer = &link_name_buffer,
}),
);
}