Skip to content

Posting more than 66446 bytes to a TLS endpoint results in panic: integer overflow #17600

@elerch

Description

@elerch

Zig Version

0.11.0

Steps to Reproduce and Observed Behavior

I created a small test program to reproduce this:

const std = @import("std");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();
    // var client = std.http.Client{
    //     .allocator = allocator,
    //     .proxy = .{
    //         .protocol = .plain,
    //         .host = "localhost",
    //         .port = 8080,
    //     },
    // };
    var client = std.http.Client{ .allocator = allocator };

    const stdout_file = std.io.getStdOut().writer();
    var bw = std.io.bufferedWriter(stdout_file);
    const stdout = bw.writer();

    var argIterator = try std.process.argsWithAllocator(allocator);
    defer argIterator.deinit();
    const exe_name = argIterator.next().?;
    _ = exe_name;
    const request_len = try std.fmt.parseInt(usize, argIterator.next().?, 10);
    try testPost(allocator, &client, request_len);

    try stdout.print("Well, we did not blow up\n", .{});

    try bw.flush(); // don't forget to flush!
}
const buf_size = 1024 * 1024 * 2; // 2MB

fn testPost(allocator: std.mem.Allocator, client: *std.http.Client, request_len: usize) !void {
    const url = "https://api.cloudflare.com/client/v4" ++
        "/accounts/hello/workers/scripts/zigwasi?include_subdomain_availability=true&excludeScript=true";
    var headers = std.http.Headers.init(allocator);
    defer headers.deinit();
    try headers.append("Accept", "application/json");
    const request_payload_buffer = [_]u8{0xad} ** buf_size;
    const request_payload = request_payload_buffer[0..request_len];
    const cl = try std.fmt.allocPrint(allocator, "{d}", .{request_payload.len});
    defer allocator.free(cl);
    try headers.append("Content-Length", cl);
    var req = try client.request(.PUT, try std.Uri.parse(url), headers, .{});
    defer req.deinit();

    req.transfer_encoding = .{ .content_length = @as(u64, request_payload.len) };
    try req.start();
    try req.writeAll(request_payload);
    try req.finish();
    try req.wait();
    // We're literally just looking for a panic
}

When run with zig build run -- $(( 64 * 1024 + 896 + 8 + 4 + 2 + 1 )) (zig 0.11.0) this will panic with the following stack trace:

thread 3560641 panic: integer overflow
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/crypto/tls/Client.zig:806:38: 0x69a33d in prepareCiphertextRecord (posttest)
                        overhead_len - ciphertext_end,
                                     ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/crypto/tls/Client.zig:740:43: 0x699867 in writeEnd__anon_6792 (posttest)
    var prepared = prepareCiphertextRecord(c, &iovecs_buf, &ciphertext_buf, bytes, .application_data);
                                          ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/crypto/tls/Client.zig:710:20: 0x69e925 in write__anon_6791 (posttest)
    return writeEnd(c, stream, bytes, false);
                   ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/http/Client.zig:268:42: 0x69ea7d in write (posttest)
            .tls => conn.tls_client.write(conn.stream, buffer),
                                         ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/http/Client.zig:817:60: 0x6a0305 in write (posttest)
                const amt = try req.connection.?.data.write(bytes);
                                                           ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/http/Client.zig:828:31: 0x690502 in writeAll (posttest)
            index += try write(req, bytes[index..]);
                              ^
/home/lobo/home/posttest/src/main.zig:50:21: 0x68df6f in testPost (posttest)
    try req.writeAll(request_payload);
                    ^
/home/lobo/home/posttest/src/main.zig:26:17: 0x691db6 in main (posttest)
    try testPost(allocator, &client, request_len);
                ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/start.zig:574:37: 0x68c6be in posixCallMainAndExit (posttest)
            const result = root.main() catch |err| {
                                    ^
/home/lobo/.local/zig/zig-linux-x86_64-0.11.0/lib/std/start.zig:243:5: 0x68c1a1 in _start (posttest)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x1 in ??? (???)
Unwind information for `???:0x1` was not available, trace may be incomplete

The code in the crypto tls looks similar so I expect this is a problem with current master branch as well. If I use a proxy (the client I've commented above), the request will succeed. Using one less byte ( zig build run -- $(( 64 * 1024 + 896 + 8 + 4 + 2))) will not trigger this error. I do, however, get a different error (TlsAlert). I will file a separate issue for that.

Running Linux Debian bullseye on x86 64.

Expected Behavior

Request to work

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorstandard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions