| ... | ... | @@ -924,7 +924,9 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 924 | 924 | const amt = @intCast(u15, vp.put(partial_cleartext)); |
| 925 | 925 | c.partial_cleartext_idx += amt; |
| 926 | 926 | |
| 927 | | if (c.partial_ciphertext_end == c.partial_ciphertext_idx) { |
| 927 | if (c.partial_cleartext_idx == c.partial_ciphertext_idx and |
| 928 | c.partial_ciphertext_end == c.partial_ciphertext_idx) |
| 929 | { |
| 928 | 930 | // The buffer is now empty. |
| 929 | 931 | c.partial_cleartext_idx = 0; |
| 930 | 932 | c.partial_ciphertext_idx = 0; |
| ... | ... | @@ -935,7 +937,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 935 | 937 | c.partial_ciphertext_end = 0; |
| 936 | 938 | assert(vp.total == amt); |
| 937 | 939 | return amt; |
| 938 | | } else if (amt <= partial_cleartext.len) { |
| 940 | } else if (amt > 0) { |
| 939 | 941 | // We don't need more data, so don't call read. |
| 940 | 942 | assert(vp.total == amt); |
| 941 | 943 | return amt; |
| ... | ... | @@ -970,8 +972,8 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 970 | 972 | }, |
| 971 | 973 | }; |
| 972 | 974 | |
| 973 | | // Cleartext capacity of output buffer, in records, rounded up. |
| 974 | | const buf_cap = (cleartext_buf_len +| (max_ciphertext_len - 1)) / max_ciphertext_len; |
| 975 | // Cleartext capacity of output buffer, in records. Minimum one full record. |
| 976 | const buf_cap = @max(cleartext_buf_len / max_ciphertext_len, 1); |
| 975 | 977 | const wanted_read_len = buf_cap * (max_ciphertext_len + tls.record_header_len); |
| 976 | 978 | const ask_len = @max(wanted_read_len, cleartext_stack_buffer.len); |
| 977 | 979 | const ask_iovecs = limitVecs(&ask_iovecs_buf, ask_len); |
| ... | ... | @@ -1029,7 +1031,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 1029 | 1031 | if (frag1.len < second_len) |
| 1030 | 1032 | return finishRead2(c, first, frag1, vp.total); |
| 1031 | 1033 | |
| 1032 | | @memcpy(frag[0..in], first); |
| 1034 | limitedOverlapCopy(frag, in); |
| 1033 | 1035 | @memcpy(frag[first.len..][0..second_len], frag1[0..second_len]); |
| 1034 | 1036 | frag = frag[0..full_record_len]; |
| 1035 | 1037 | frag1 = frag1[second_len..]; |
| ... | ... | @@ -1059,7 +1061,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 1059 | 1061 | if (frag1.len < second_len) |
| 1060 | 1062 | return finishRead2(c, first, frag1, vp.total); |
| 1061 | 1063 | |
| 1062 | | @memcpy(frag[0..in], first); |
| 1064 | limitedOverlapCopy(frag, in); |
| 1063 | 1065 | @memcpy(frag[first.len..][0..second_len], frag1[0..second_len]); |
| 1064 | 1066 | frag = frag[0..full_record_len]; |
| 1065 | 1067 | frag1 = frag1[second_len..]; |
| ... | ... | @@ -1176,8 +1178,10 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) |
| 1176 | 1178 | if (c.partial_ciphertext_idx > c.partial_cleartext_idx) { |
| 1177 | 1179 | // We have already run out of room in iovecs. Continue |
| 1178 | 1180 | // appending to `partially_read_buffer`. |
| 1179 | | const dest = c.partially_read_buffer[c.partial_ciphertext_idx..]; |
| 1180 | | @memcpy(dest[0..msg.len], msg); |
| 1181 | @memcpy( |
| 1182 | c.partially_read_buffer[c.partial_ciphertext_idx..][0..msg.len], |
| 1183 | msg, |
| 1184 | ); |
| 1181 | 1185 | c.partial_ciphertext_idx = @intCast(@TypeOf(c.partial_ciphertext_idx), c.partial_ciphertext_idx + msg.len); |
| 1182 | 1186 | } else { |
| 1183 | 1187 | const amt = vp.put(msg); |
| ... | ... | @@ -1223,22 +1227,38 @@ fn finishRead(c: *Client, frag: []const u8, in: usize, out: usize) usize { |
| 1223 | 1227 | return out; |
| 1224 | 1228 | } |
| 1225 | 1229 | |
| 1230 | /// Note that `first` usually overlaps with `c.partially_read_buffer`. |
| 1226 | 1231 | fn finishRead2(c: *Client, first: []const u8, frag1: []const u8, out: usize) usize { |
| 1227 | 1232 | if (c.partial_ciphertext_idx > c.partial_cleartext_idx) { |
| 1228 | 1233 | // There is cleartext at the beginning already which we need to preserve. |
| 1229 | 1234 | c.partial_ciphertext_end = @intCast(@TypeOf(c.partial_ciphertext_end), c.partial_ciphertext_idx + first.len + frag1.len); |
| 1230 | | @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx..][0..first.len], first); |
| 1235 | // TODO: eliminate this call to copyForwards |
| 1236 | std.mem.copyForwards(u8, c.partially_read_buffer[c.partial_ciphertext_idx..][0..first.len], first); |
| 1231 | 1237 | @memcpy(c.partially_read_buffer[c.partial_ciphertext_idx + first.len ..][0..frag1.len], frag1); |
| 1232 | 1238 | } else { |
| 1233 | 1239 | c.partial_cleartext_idx = 0; |
| 1234 | 1240 | c.partial_ciphertext_idx = 0; |
| 1235 | 1241 | c.partial_ciphertext_end = @intCast(@TypeOf(c.partial_ciphertext_end), first.len + frag1.len); |
| 1242 | // TODO: eliminate this call to copyForwards |
| 1236 | 1243 | std.mem.copyForwards(u8, c.partially_read_buffer[0..first.len], first); |
| 1237 | 1244 | @memcpy(c.partially_read_buffer[first.len..][0..frag1.len], frag1); |
| 1238 | 1245 | } |
| 1239 | 1246 | return out; |
| 1240 | 1247 | } |
| 1241 | 1248 | |
| 1249 | fn limitedOverlapCopy(frag: []u8, in: usize) void { |
| 1250 | const first = frag[in..]; |
| 1251 | if (first.len <= in) { |
| 1252 | // A single, non-overlapping memcpy suffices. |
| 1253 | @memcpy(frag[0..first.len], first); |
| 1254 | } else { |
| 1255 | // Need two memcpy calls because one alone would overlap. |
| 1256 | @memcpy(frag[0..in], first[0..in]); |
| 1257 | const leftover = first.len - in; |
| 1258 | @memcpy(frag[in..][0..leftover], first[in..][0..leftover]); |
| 1259 | } |
| 1260 | } |
| 1261 | |
| 1242 | 1262 | fn straddleByte(s1: []const u8, s2: []const u8, index: usize) u8 { |
| 1243 | 1263 | if (index < s1.len) { |
| 1244 | 1264 | return s1[index]; |