authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-03-02 00:39:48+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-03-11 12:22:12+01:00
log04e8bbd932c9ce9ac99230a656c8951c467d5b24
treede49098bcca42adc48cab6ffe77490b79d486a7b
parentaf0502f6c4202df0223bcefa5121361c07680108

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.

2 files changed, 51 insertions(+), 8 deletions(-)

lib/std/tar.zig+13-6
...@@ -87,8 +87,8 @@ pub const Options = struct {...@@ -87,8 +87,8 @@ pub const Options = struct {
8787
88pub const Header = struct {88pub const Header = struct {
89 const SIZE = 512;89 const SIZE = 512;
90 const MAX_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)90 pub const MAX_NAME_SIZE = 100 + 1 + 155; // name(100) + separator(1) + prefix(155)
91 const LINK_NAME_SIZE = 100;91 pub const LINK_NAME_SIZE = 100;
9292
93 bytes: *const [SIZE]u8,93 bytes: *const [SIZE]u8,
9494
...@@ -248,7 +248,13 @@ pub const IteratorOptions = struct {...@@ -248,7 +248,13 @@ pub const IteratorOptions = struct {
248248
249/// Iterates over files in tar archive.249/// Iterates over files in tar archive.
250/// `next` returns each file in `reader` tar archive.250/// `next` returns each file in `reader` tar archive.
251pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(reader)) {251/// Provided buffers should be at least 256 bytes for file_name and 100 bytes
252/// for link_name.
253pub fn iterator(reader: anytype, options: IteratorOptions) !Iterator(@TypeOf(reader)) {
254 if (options.file_name_buffer.len < Header.MAX_NAME_SIZE or
255 options.link_name_buffer.len < Header.LINK_NAME_SIZE)
256 return error.TarInsufficientBuffer;
257
252 return .{258 return .{
253 .reader = reader,259 .reader = reader,
254 .diagnostics = options.diagnostics,260 .diagnostics = options.diagnostics,
...@@ -318,7 +324,7 @@ fn Iterator(comptime ReaderType: type) type {...@@ -318,7 +324,7 @@ fn Iterator(comptime ReaderType: type) type {
318 }324 }
319325
320 fn readString(self: *Self, size: usize, buffer: []u8) ![]const u8 {326 fn readString(self: *Self, size: usize, buffer: []u8) ![]const u8 {
321 if (size > buffer.len) return error.TarCorruptInput;327 if (size > buffer.len) return error.TarInsufficientBuffer;
322 const buf = buffer[0..size];328 const buf = buffer[0..size];
323 try self.reader.readNoEof(buf);329 try self.reader.readNoEof(buf);
324 return nullStr(buf);330 return nullStr(buf);
...@@ -470,7 +476,8 @@ fn PaxIterator(comptime ReaderType: type) type {...@@ -470,7 +476,8 @@ fn PaxIterator(comptime ReaderType: type) type {
470 // Copies pax attribute value into destination buffer.476 // Copies pax attribute value into destination buffer.
471 // Must be called with destination buffer of size at least Attribute.len.477 // Must be called with destination buffer of size at least Attribute.len.
472 pub fn value(self: Attribute, dst: []u8) ![]const u8 {478 pub fn value(self: Attribute, dst: []u8) ![]const u8 {
473 assert(self.len <= dst.len);479 if (self.len > dst.len) return error.TarInsufficientBuffer;
480 // assert(self.len <= dst.len);
474 const buf = dst[0..self.len];481 const buf = dst[0..self.len];
475 const n = try self.reader.readAll(buf);482 const n = try self.reader.readAll(buf);
476 if (n < self.len) return error.UnexpectedEndOfStream;483 if (n < self.len) return error.UnexpectedEndOfStream;
...@@ -558,7 +565,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi...@@ -558,7 +565,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
558565
559 var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;566 var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
560 var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;567 var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
561 var iter = iterator(reader, .{568 var iter = try iterator(reader, .{
562 .file_name_buffer = &file_name_buffer,569 .file_name_buffer = &file_name_buffer,
563 .link_name_buffer = &link_name_buffer,570 .link_name_buffer = &link_name_buffer,
564 .diagnostics = options.diagnostics,571 .diagnostics = options.diagnostics,
lib/std/tar/test.zig+38-2
...@@ -315,7 +315,7 @@ test "tar run Go test cases" {...@@ -315,7 +315,7 @@ test "tar run Go test cases" {
315 },315 },
316 .{316 .{
317 .data = @embedFile("testdata/fuzz1.tar"),317 .data = @embedFile("testdata/fuzz1.tar"),
318 .err = error.TarCorruptInput,318 .err = error.TarInsufficientBuffer,
319 },319 },
320 .{320 .{
321 .data = @embedFile("testdata/fuzz2.tar"),321 .data = @embedFile("testdata/fuzz2.tar"),
...@@ -328,7 +328,7 @@ test "tar run Go test cases" {...@@ -328,7 +328,7 @@ test "tar run Go test cases" {
328328
329 for (cases) |case| {329 for (cases) |case| {
330 var fsb = std.io.fixedBufferStream(case.data);330 var fsb = std.io.fixedBufferStream(case.data);
331 var iter = tar.iterator(fsb.reader(), .{331 var iter = try tar.iterator(fsb.reader(), .{
332 .file_name_buffer = &file_name_buffer,332 .file_name_buffer = &file_name_buffer,
333 .link_name_buffer = &link_name_buffer,333 .link_name_buffer = &link_name_buffer,
334 });334 });
...@@ -359,6 +359,27 @@ test "tar run Go test cases" {...@@ -359,6 +359,27 @@ test "tar run Go test cases" {
359 }359 }
360 try testing.expectEqual(case.files.len, i);360 try testing.expectEqual(case.files.len, i);
361 }361 }
362
363 var min_file_name_buffer: [tar.Header.MAX_NAME_SIZE]u8 = undefined;
364 var min_link_name_buffer: [tar.Header.LINK_NAME_SIZE]u8 = undefined;
365 const long_name_cases = [_]Case{ cases[11], cases[25], cases[28] };
366
367 for (long_name_cases) |case| {
368 var fsb = std.io.fixedBufferStream(case.data);
369 var iter = try tar.iterator(fsb.reader(), .{
370 .file_name_buffer = &min_file_name_buffer,
371 .link_name_buffer = &min_link_name_buffer,
372 });
373
374 var iter_err: ?anyerror = null;
375 while (iter.next() catch |err| brk: {
376 iter_err = err;
377 break :brk null;
378 }) |_| {}
379
380 try testing.expect(iter_err != null);
381 try testing.expectEqual(error.TarInsufficientBuffer, iter_err.?);
382 }
362}383}
363384
364// used in test to calculate file chksum385// used in test to calculate file chksum
...@@ -490,6 +511,21 @@ test "tar pipeToFileSystem" {...@@ -490,6 +511,21 @@ test "tar pipeToFileSystem" {
490 try testing.expectError(error.FileNotFound, root.dir.statFile("empty"));511 try testing.expectError(error.FileNotFound, root.dir.statFile("empty"));
491 try testing.expect((try root.dir.statFile("a/file")).kind == .file);512 try testing.expect((try root.dir.statFile("a/file")).kind == .file);
492 try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink513 try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink
514
493 var buf: [32]u8 = undefined;515 var buf: [32]u8 = undefined;
494 try testing.expectEqualSlices(u8, "../a/file", try root.dir.readLink("b/symlink", &buf));516 try testing.expectEqualSlices(u8, "../a/file", try root.dir.readLink("b/symlink", &buf));
495}517}
518
519test "insufficient buffer for iterator" {
520 var file_name_buffer: [10]u8 = undefined;
521 var link_name_buffer: [10]u8 = undefined;
522
523 var fsb = std.io.fixedBufferStream("");
524 try testing.expectError(
525 error.TarInsufficientBuffer,
526 tar.iterator(fsb.reader(), .{
527 .file_name_buffer = &file_name_buffer,
528 .link_name_buffer = &link_name_buffer,
529 }),
530 );
531}