diff --git a/lib/std/tar.zig b/lib/std/tar.zig index db76e176b4..a4f6f2e322 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -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, diff --git a/lib/std/tar/test.zig b/lib/std/tar/test.zig index bd45bf792a..fe6efc1b5e 100644 --- a/lib/std/tar/test.zig +++ b/lib/std/tar/test.zig @@ -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, + }), + ); +}