authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-27 00:45:49-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-06-27 00:45:49-07:00
logd9e867172e85270d479fc1b00f42648bc0ca0179
treebfaaaf3c5cfe4e02b37e32e6c49a8836f8462c24
parent9343c31c3879514c890c9bfa2a1cd1a1f222ed6d
parent8239d3b358878ec4c0662a0dc2ccdc42c0e313c1
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16233 from jacobly0/tls

crypto.tls.Client: fix occasional crash in `readvAdvanced`

1 files changed, 31 insertions(+), 24 deletions(-)

lib/std/crypto/tls/Client.zig+31-24
...@@ -140,7 +140,7 @@ pub fn InitError(comptime Stream: type) type {...@@ -140,7 +140,7 @@ pub fn InitError(comptime Stream: type) type {
140///140///
141/// `host` is only borrowed during this function call.141/// `host` is only borrowed during this function call.
142pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client {142pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) InitError(@TypeOf(stream))!Client {
143 const host_len = @as(u16, @intCast(host.len));143 const host_len: u16 = @intCast(host.len);
144144
145 var random_buffer: [128]u8 = undefined;145 var random_buffer: [128]u8 = undefined;
146 crypto.random.bytes(&random_buffer);146 crypto.random.bytes(&random_buffer);
...@@ -194,7 +194,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -194,7 +194,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
194 int2(host_len);194 int2(host_len);
195195
196 const extensions_header =196 const extensions_header =
197 int2(@as(u16, @intCast(extensions_payload.len + host_len))) ++197 int2(@intCast(extensions_payload.len + host_len)) ++
198 extensions_payload;198 extensions_payload;
199199
200 const legacy_compression_methods = 0x0100;200 const legacy_compression_methods = 0x0100;
...@@ -209,13 +209,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -209,13 +209,13 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
209209
210 const out_handshake =210 const out_handshake =
211 [_]u8{@intFromEnum(tls.HandshakeType.client_hello)} ++211 [_]u8{@intFromEnum(tls.HandshakeType.client_hello)} ++
212 int3(@as(u24, @intCast(client_hello.len + host_len))) ++212 int3(@intCast(client_hello.len + host_len)) ++
213 client_hello;213 client_hello;
214214
215 const plaintext_header = [_]u8{215 const plaintext_header = [_]u8{
216 @intFromEnum(tls.ContentType.handshake),216 @intFromEnum(tls.ContentType.handshake),
217 0x03, 0x01, // legacy_record_version217 0x03, 0x01, // legacy_record_version
218 } ++ int2(@as(u16, @intCast(out_handshake.len + host_len))) ++ out_handshake;218 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;
219219
220 {220 {
221 var iovecs = [_]std.os.iovec_const{221 var iovecs = [_]std.os.iovec_const{
...@@ -466,7 +466,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -466,7 +466,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
466 },466 },
467 };467 };
468468
469 const inner_ct = @as(tls.ContentType, @enumFromInt(cleartext[cleartext.len - 1]));469 const inner_ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);
470 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;470 if (inner_ct != .handshake) return error.TlsUnexpectedMessage;
471471
472 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);472 var ctd = tls.Decoder.fromTheirSlice(cleartext[0 .. cleartext.len - 1]);
...@@ -520,7 +520,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -520,7 +520,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
520520
521 const subject_cert: Certificate = .{521 const subject_cert: Certificate = .{
522 .buffer = certd.buf,522 .buffer = certd.buf,
523 .index = @as(u32, @intCast(certd.idx)),523 .index = @intCast(certd.idx),
524 };524 };
525 const subject = try subject_cert.parse();525 const subject = try subject_cert.parse();
526 if (cert_index == 0) {526 if (cert_index == 0) {
...@@ -534,7 +534,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -534,7 +534,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
534 if (pub_key.len > main_cert_pub_key_buf.len)534 if (pub_key.len > main_cert_pub_key_buf.len)
535 return error.CertificatePublicKeyInvalid;535 return error.CertificatePublicKeyInvalid;
536 @memcpy(main_cert_pub_key_buf[0..pub_key.len], pub_key);536 @memcpy(main_cert_pub_key_buf[0..pub_key.len], pub_key);
537 main_cert_pub_key_len = @as(@TypeOf(main_cert_pub_key_len), @intCast(pub_key.len));537 main_cert_pub_key_len = @intCast(pub_key.len);
538 } else {538 } else {
539 try prev_cert.verify(subject, now_sec);539 try prev_cert.verify(subject, now_sec);
540 }540 }
...@@ -679,7 +679,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -679,7 +679,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
679 .write_seq = 0,679 .write_seq = 0,
680 .partial_cleartext_idx = 0,680 .partial_cleartext_idx = 0,
681 .partial_ciphertext_idx = 0,681 .partial_ciphertext_idx = 0,
682 .partial_ciphertext_end = @as(u15, @intCast(leftover.len)),682 .partial_ciphertext_end = @intCast(leftover.len),
683 .received_close_notify = false,683 .received_close_notify = false,
684 .application_cipher = app_cipher,684 .application_cipher = app_cipher,
685 .partially_read_buffer = undefined,685 .partially_read_buffer = undefined,
...@@ -797,11 +797,11 @@ fn prepareCiphertextRecord(...@@ -797,11 +797,11 @@ fn prepareCiphertextRecord(
797 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;797 const overhead_len = tls.record_header_len + P.AEAD.tag_length + 1;
798 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;798 const close_notify_alert_reserved = tls.close_notify_alert.len + overhead_len;
799 while (true) {799 while (true) {
800 const encrypted_content_len = @as(u16, @intCast(@min(800 const encrypted_content_len: u16 = @intCast(@min(
801 @min(bytes.len - bytes_i, max_ciphertext_len - 1),801 @min(bytes.len - bytes_i, max_ciphertext_len - 1),
802 ciphertext_buf.len - close_notify_alert_reserved -802 ciphertext_buf.len - close_notify_alert_reserved -
803 overhead_len - ciphertext_end,803 overhead_len - ciphertext_end,
804 )));804 ));
805 if (encrypted_content_len == 0) return .{805 if (encrypted_content_len == 0) return .{
806 .iovec_end = iovec_end,806 .iovec_end = iovec_end,
807 .ciphertext_end = ciphertext_end,807 .ciphertext_end = ciphertext_end,
...@@ -920,7 +920,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -920,7 +920,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
920 // Give away the buffered cleartext we have, if any.920 // Give away the buffered cleartext we have, if any.
921 const partial_cleartext = c.partially_read_buffer[c.partial_cleartext_idx..c.partial_ciphertext_idx];921 const partial_cleartext = c.partially_read_buffer[c.partial_cleartext_idx..c.partial_ciphertext_idx];
922 if (partial_cleartext.len > 0) {922 if (partial_cleartext.len > 0) {
923 const amt = @as(u15, @intCast(vp.put(partial_cleartext)));923 const amt: u15 = @intCast(vp.put(partial_cleartext));
924 c.partial_cleartext_idx += amt;924 c.partial_cleartext_idx += amt;
925925
926 if (c.partial_cleartext_idx == c.partial_ciphertext_idx and926 if (c.partial_cleartext_idx == c.partial_ciphertext_idx and
...@@ -958,6 +958,13 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -958,6 +958,13 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
958 // The amount of the user's buffer that will be used to give cleartext. The958 // The amount of the user's buffer that will be used to give cleartext. The
959 // beginning of the buffer will be used for such purposes.959 // beginning of the buffer will be used for such purposes.
960 const cleartext_buf_len = free_size - ciphertext_buf_len;960 const cleartext_buf_len = free_size - ciphertext_buf_len;
961
962 // Recoup `partially_read_buffer space`. This is necessary because it is assumed
963 // below that `frag0` is big enough to hold at least one record.
964 limitedOverlapCopy(c.partially_read_buffer[0..c.partial_ciphertext_end], c.partial_ciphertext_idx);
965 c.partial_ciphertext_end -= c.partial_ciphertext_idx;
966 c.partial_ciphertext_idx = 0;
967 c.partial_cleartext_idx = 0;
961 const first_iov = c.partially_read_buffer[c.partial_ciphertext_end..];968 const first_iov = c.partially_read_buffer[c.partial_ciphertext_end..];
962969
963 var ask_iovecs_buf: [2]std.os.iovec = .{970 var ask_iovecs_buf: [2]std.os.iovec = .{
...@@ -1037,7 +1044,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -1037,7 +1044,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
1037 in = 0;1044 in = 0;
1038 continue;1045 continue;
1039 }1046 }
1040 const ct = @as(tls.ContentType, @enumFromInt(frag[in]));1047 const ct: tls.ContentType = @enumFromInt(frag[in]);
1041 in += 1;1048 in += 1;
1042 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);1049 const legacy_version = mem.readIntBig(u16, frag[in..][0..2]);
1043 in += 2;1050 in += 2;
...@@ -1070,8 +1077,8 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -1070,8 +1077,8 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
1070 switch (ct) {1077 switch (ct) {
1071 .alert => {1078 .alert => {
1072 if (in + 2 > frag.len) return error.TlsDecodeError;1079 if (in + 2 > frag.len) return error.TlsDecodeError;
1073 const level = @as(tls.AlertLevel, @enumFromInt(frag[in]));1080 const level: tls.AlertLevel = @enumFromInt(frag[in]);
1074 const desc = @as(tls.AlertDescription, @enumFromInt(frag[in + 1]));1081 const desc: tls.AlertDescription = @enumFromInt(frag[in + 1]);
1075 _ = level;1082 _ = level;
10761083
1077 try desc.toError();1084 try desc.toError();
...@@ -1105,11 +1112,11 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -1105,11 +1112,11 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
11051112
1106 c.read_seq = try std.math.add(u64, c.read_seq, 1);1113 c.read_seq = try std.math.add(u64, c.read_seq, 1);
11071114
1108 const inner_ct = @as(tls.ContentType, @enumFromInt(cleartext[cleartext.len - 1]));1115 const inner_ct: tls.ContentType = @enumFromInt(cleartext[cleartext.len - 1]);
1109 switch (inner_ct) {1116 switch (inner_ct) {
1110 .alert => {1117 .alert => {
1111 const level = @as(tls.AlertLevel, @enumFromInt(cleartext[0]));1118 const level: tls.AlertLevel = @enumFromInt(cleartext[0]);
1112 const desc = @as(tls.AlertDescription, @enumFromInt(cleartext[1]));1119 const desc: tls.AlertDescription = @enumFromInt(cleartext[1]);
1113 if (desc == .close_notify) {1120 if (desc == .close_notify) {
1114 c.received_close_notify = true;1121 c.received_close_notify = true;
1115 c.partial_ciphertext_end = c.partial_ciphertext_idx;1122 c.partial_ciphertext_end = c.partial_ciphertext_idx;
...@@ -1124,7 +1131,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -1124,7 +1131,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
1124 .handshake => {1131 .handshake => {
1125 var ct_i: usize = 0;1132 var ct_i: usize = 0;
1126 while (true) {1133 while (true) {
1127 const handshake_type = @as(tls.HandshakeType, @enumFromInt(cleartext[ct_i]));1134 const handshake_type: tls.HandshakeType = @enumFromInt(cleartext[ct_i]);
1128 ct_i += 1;1135 ct_i += 1;
1129 const handshake_len = mem.readIntBig(u24, cleartext[ct_i..][0..3]);1136 const handshake_len = mem.readIntBig(u24, cleartext[ct_i..][0..3]);
1130 ct_i += 3;1137 ct_i += 3;
...@@ -1186,13 +1193,13 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)...@@ -1186,13 +1193,13 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
1186 c.partially_read_buffer[c.partial_ciphertext_idx..][0..msg.len],1193 c.partially_read_buffer[c.partial_ciphertext_idx..][0..msg.len],
1187 msg,1194 msg,
1188 );1195 );
1189 c.partial_ciphertext_idx = @as(@TypeOf(c.partial_ciphertext_idx), @intCast(c.partial_ciphertext_idx + msg.len));1196 c.partial_ciphertext_idx = @intCast(c.partial_ciphertext_idx + msg.len);
1190 } else {1197 } else {
1191 const amt = vp.put(msg);1198 const amt = vp.put(msg);
1192 if (amt < msg.len) {1199 if (amt < msg.len) {
1193 const rest = msg[amt..];1200 const rest = msg[amt..];
1194 c.partial_cleartext_idx = 0;1201 c.partial_cleartext_idx = 0;
1195 c.partial_ciphertext_idx = @as(@TypeOf(c.partial_ciphertext_idx), @intCast(rest.len));1202 c.partial_ciphertext_idx = @intCast(rest.len);
1196 @memcpy(c.partially_read_buffer[0..rest.len], rest);1203 @memcpy(c.partially_read_buffer[0..rest.len], rest);
1197 }1204 }
1198 }1205 }
...@@ -1220,12 +1227,12 @@ fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {...@@ -1220,12 +1227,12 @@ fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {
1220 const saved_buf = frag[in..];1227 const saved_buf = frag[in..];
1221 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {1228 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
1222 // There is cleartext at the beginning already which we need to preserve.1229 // There is cleartext at the beginning already which we need to preserve.
1223 c.partial_ciphertext_end = @as(@TypeOf(c.partial_ciphertext_end), @intCast(c.partial_ciphertext_idx + saved_buf.len));1230 c.partial_ciphertext_end = @intCast(c.partial_ciphertext_idx + saved_buf.len);
1224 @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx..][0..saved_buf.len], saved_buf);1231 @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx..][0..saved_buf.len], saved_buf);
1225 } else {1232 } else {
1226 c.partial_cleartext_idx = 0;1233 c.partial_cleartext_idx = 0;
1227 c.partial_ciphertext_idx = 0;1234 c.partial_ciphertext_idx = 0;
1228 c.partial_ciphertext_end = @as(@TypeOf(c.partial_ciphertext_end), @intCast(saved_buf.len));1235 c.partial_ciphertext_end = @intCast(saved_buf.len);
1229 @memcpy(c.partially_read_buffer[0..saved_buf.len], saved_buf);1236 @memcpy(c.partially_read_buffer[0..saved_buf.len], saved_buf);
1230 }1237 }
1231 return out;1238 return out;
...@@ -1235,14 +1242,14 @@ fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {...@@ -1235,14 +1242,14 @@ fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize {
1235fn finishRead2(c: *Client, first: []const u8, frag1: []const u8, out: usize) usize {1242fn finishRead2(c: *Client, first: []const u8, frag1: []const u8, out: usize) usize {
1236 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {1243 if (c.partial_ciphertext_idx > c.partial_cleartext_idx) {
1237 // There is cleartext at the beginning already which we need to preserve.1244 // There is cleartext at the beginning already which we need to preserve.
1238 c.partial_ciphertext_end = @as(@TypeOf(c.partial_ciphertext_end), @intCast(c.partial_ciphertext_idx + first.len + frag1.len));1245 c.partial_ciphertext_end = @intCast(c.partial_ciphertext_idx + first.len + frag1.len);
1239 // TODO: eliminate this call to copyForwards1246 // TODO: eliminate this call to copyForwards
1240 std.mem.copyForwards(u8, c.partially_read_buffer[c.partial_ciphertext_idx..][0..first.len], first);1247 std.mem.copyForwards(u8, c.partially_read_buffer[c.partial_ciphertext_idx..][0..first.len], first);
1241 @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx + first.len ..][0..frag1.len], frag1);1248 @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx + first.len ..][0..frag1.len], frag1);
1242 } else {1249 } else {
1243 c.partial_cleartext_idx = 0;1250 c.partial_cleartext_idx = 0;
1244 c.partial_ciphertext_idx = 0;1251 c.partial_ciphertext_idx = 0;
1245 c.partial_ciphertext_end = @as(@TypeOf(c.partial_ciphertext_end), @intCast(first.len + frag1.len));1252 c.partial_ciphertext_end = @intCast(first.len + frag1.len);
1246 // TODO: eliminate this call to copyForwards1253 // TODO: eliminate this call to copyForwards
1247 std.mem.copyForwards(u8, c.partially_read_buffer[0..first.len], first);1254 std.mem.copyForwards(u8, c.partially_read_buffer[0..first.len], first);
1248 @memcpy(c.partially_read_buffer[first.len..][0..frag1.len], frag1);1255 @memcpy(c.partially_read_buffer[first.len..][0..frag1.len], frag1);