aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorIgor Anić <igor.anic@gmail.com>2024-03-02 00:39:48 +0100
committerIgor Anić <igor.anic@gmail.com>2024-03-11 12:22:12 +0100
commit04e8bbd932c9ce9ac99230a656c8951c467d5b24 (patch)
treede49098bcca42adc48cab6ffe77490b79d486a7b /lib
parentaf0502f6c4202df0223bcefa5121361c07680108 (diff)
downloadzig-04e8bbd932c9ce9ac99230a656c8951c467d5b24.tar.gz
zig-04e8bbd932c9ce9ac99230a656c8951c467d5b24.zip
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.
Diffstat (limited to 'lib')
-rw-r--r--lib/std/tar.zig19
-rw-r--r--lib/std/tar/test.zig40
2 files changed, 51 insertions, 8 deletions
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,
+ }),
+ );
+}