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