@@ -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 ];
0 commit comments