authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-31 09:36:25+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-31 09:36:25+02:00
log99c2792b5e52996c72f7689c347a394bb4a827f3
tree61d68406c973dec2e437da4d3fa2a591f5445b5f
parent9f0a38ac815d4d38a3afec6628827f79fdf43b98

tls: reject undersized TLS 1.2 AEAD records in readIndirect (#31973)

Don't crash when a peer send a short record (the handshake is fine) Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31973 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

1 files changed, 17 insertions(+), 2 deletions(-)

lib/std/crypto/tls/Client.zig+17-2
...@@ -1202,6 +1202,7 @@ fn readIndirect(c: *Client) Reader.Error!usize {...@@ -1202,6 +1202,7 @@ fn readIndirect(c: *Client) Reader.Error!usize {
1202 .tls_1_2 => {1202 .tls_1_2 => {
1203 const pv = &p.tls_1_2;1203 const pv = &p.tls_1_2;
1204 const P = @TypeOf(p.*);1204 const P = @TypeOf(p.*);
1205 if (record_len < P.record_iv_length + P.mac_length) return failRead(c, error.TlsRecordOverflow);
1205 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;1206 const message_len: u16 = record_len - P.record_iv_length - P.mac_length;
1206 const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked1207 const ad_header = input.take(tls.record_header_len) catch unreachable; // already peeked
1207 const ad = mem.toBytes(big(c.read_seq)) ++1208 const ad = mem.toBytes(big(c.read_seq)) ++
...@@ -1674,7 +1675,7 @@ else...@@ -1674,7 +1675,7 @@ else
1674 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,1675 .ECDHE_RSA_WITH_AES_256_GCM_SHA384,
1675 });1676 });
16761677
1677fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError {1678fn testReadError(input_buf: []const u8, tls_version: tls.ProtocolVersion, cipher: tls.ApplicationCipher) ReadError {
1678 var input_reader: Reader = .fixed(input_buf);1679 var input_reader: Reader = .fixed(input_buf);
1679 var read_buf: [tls.max_ciphertext_record_len]u8 = undefined;1680 var read_buf: [tls.max_ciphertext_record_len]u8 = undefined;
1680 var c: Client = .{1681 var c: Client = .{
...@@ -1687,7 +1688,7 @@ fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError...@@ -1687,7 +1688,7 @@ fn testReadError(input_buf: []const u8, cipher: tls.ApplicationCipher) ReadError
1687 },1688 },
1688 .output = undefined,1689 .output = undefined,
1689 .writer = undefined,1690 .writer = undefined,
1690 .tls_version = .tls_1_3,1691 .tls_version = tls_version,
1691 .read_seq = 0,1692 .read_seq = 0,
1692 .write_seq = 0,1693 .write_seq = 0,
1693 .received_close_notify = false,1694 .received_close_notify = false,
...@@ -1715,6 +1716,7 @@ test "empty inner plaintext" {...@@ -1715,6 +1716,7 @@ test "empty inner plaintext" {
17151716
1716 try std.testing.expectEqual(error.TlsDecodeError, testReadError(1717 try std.testing.expectEqual(error.TlsDecodeError, testReadError(
1717 &record_header ++ ciphertext ++ tag,1718 &record_header ++ ciphertext ++ tag,
1719 .tls_1_3,
1718 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{1720 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{
1719 .server_key = key,1721 .server_key = key,
1720 .server_iv = iv,1722 .server_iv = iv,
...@@ -1734,6 +1736,7 @@ test "record shorter than tag" {...@@ -1734,6 +1736,7 @@ test "record shorter than tag" {
17341736
1735 try std.testing.expectEqual(error.TlsRecordOverflow, testReadError(1737 try std.testing.expectEqual(error.TlsRecordOverflow, testReadError(
1736 &wire,1738 &wire,
1739 .tls_1_3,
1737 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{1740 .{ .CHACHA20_POLY1305_SHA256 = .{ .tls_1_3 = .{
1738 .server_key = undefined,1741 .server_key = undefined,
1739 .server_iv = undefined,1742 .server_iv = undefined,
...@@ -1744,3 +1747,15 @@ test "record shorter than tag" {...@@ -1744,3 +1747,15 @@ test "record shorter than tag" {
1744 } } },1747 } } },
1745 ));1748 ));
1746}1749}
1750
1751test "TLS 1.2 record shorter than IV plus tag" {
1752 const P = tls.ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8);
1753 const record_len: u16 = P.record_iv_length + P.mac_length - 1;
1754 const header = [_]u8{ 0x17, 0x03, 0x03 } ++ mem.toBytes(big(record_len));
1755
1756 try std.testing.expectEqual(error.TlsRecordOverflow, testReadError(
1757 &(header ++ @as([record_len]u8, @splat(0))),
1758 .tls_1_2,
1759 .{ .AES_128_GCM_SHA256 = .{ .tls_1_2 = mem.zeroes(P.Tls_1_2) } },
1760 ));
1761}