Skip to content

Commit 5265b31

Browse files
pks-tethomson
authored andcommitted
streams: fix callers potentially only writing partial data
Similar to the write(3) function, implementations of `git_stream_write` do not guarantee that all bytes are written. Instead, they return the number of bytes that actually have been written, which may be smaller than the total number of bytes. Furthermore, due to an interface design issue, we cannot ever write more than `SSIZE_MAX` bytes at once, as otherwise we cannot represent the number of bytes written to the caller. Unfortunately, no caller of `git_stream_write` ever checks the return value, except to verify that no error occurred. Due to this, they are susceptible to the case where only partial data has been written. Fix this by introducing a new function `git_stream__write_full`. In contrast to `git_stream_write`, it will always return either success or failure, without returning the number of bytes written. Thus, it is able to write all `SIZE_MAX` bytes and loop around `git_stream_write` until all data has been written. Adjust all callers except the BIO callbacks in our mbedtls and OpenSSL streams, which already do the right thing and require the amount of bytes written.
1 parent 193e7ce commit 5265b31

File tree

4 files changed

+35
-25
lines changed

4 files changed

+35
-25
lines changed

src/stream.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t le
5555
return st->write(st, data, len, flags);
5656
}
5757

58+
GIT_INLINE(int) git_stream__write_full(git_stream *st, const char *data, size_t len, int flags)
59+
{
60+
size_t total_written = 0;
61+
62+
while (total_written < len) {
63+
ssize_t written = git_stream_write(st, data + total_written, len - total_written, flags);
64+
if (written <= 0)
65+
return -1;
66+
67+
total_written += written;
68+
}
69+
70+
return 0;
71+
}
72+
5873
GIT_INLINE(int) git_stream_close(git_stream *st)
5974
{
6075
return st->close(st);

src/streams/stransport.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,8 @@ static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
149149
{
150150
git_stream *io = (git_stream *) conn;
151151

152-
if (git_stream_write(io, data, *len, 0) < 0) {
152+
if (git_stream__write_full(io, data, *len, 0) < 0)
153153
return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
154-
}
155154

156155
return noErr;
157156
}

src/transports/git.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,15 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
7676
static int send_command(git_proto_stream *s)
7777
{
7878
git_buf request = GIT_BUF_INIT;
79-
size_t write_size;
8079
int error;
8180

82-
error = gen_proto(&request, s->cmd, s->url);
83-
if (error < 0)
81+
if ((error = gen_proto(&request, s->cmd, s->url)) < 0)
8482
goto cleanup;
8583

86-
write_size = min(request.size, INT_MAX);
87-
error = (int)git_stream_write(s->io, request.ptr, write_size, 0);
84+
if ((error = git_stream__write_full(s->io, request.ptr, request.size, 0)) < 0)
85+
goto cleanup;
8886

89-
if (error >= 0)
90-
s->sent_command = 1;
87+
s->sent_command = 1;
9188

9289
cleanup:
9390
git_buf_dispose(&request);
@@ -122,16 +119,15 @@ static int git_proto_stream_read(
122119
static int git_proto_stream_write(
123120
git_smart_subtransport_stream *stream,
124121
const char *buffer,
125-
size_t buffer_len)
122+
size_t len)
126123
{
127124
git_proto_stream *s = (git_proto_stream *)stream;
128-
size_t len = min(buffer_len, INT_MAX);
129125
int error;
130126

131127
if (!s->sent_command && (error = send_command(s)) < 0)
132128
return error;
133129

134-
return (int)git_stream_write(s->io, buffer, len, 0);
130+
return git_stream__write_full(s->io, buffer, len, 0);
135131
}
136132

137133
static void git_proto_stream_free(git_smart_subtransport_stream *stream)

src/transports/http.c

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -643,19 +643,19 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
643643
if (git_buf_oom(&buf))
644644
return -1;
645645

646-
if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) {
646+
if (git_stream__write_full(io, buf.ptr, buf.size, 0) < 0) {
647647
git_buf_dispose(&buf);
648648
return -1;
649649
}
650650

651651
git_buf_dispose(&buf);
652652

653653
/* Chunk body */
654-
if (len > 0 && git_stream_write(io, buffer, len, 0) < 0)
654+
if (len > 0 && git_stream__write_full(io, buffer, len, 0) < 0)
655655
return -1;
656656

657657
/* Chunk footer */
658-
if (git_stream_write(io, "\r\n", 2, 0) < 0)
658+
if (git_stream__write_full(io, "\r\n", 2, 0) < 0)
659659
return -1;
660660

661661
return 0;
@@ -853,8 +853,8 @@ static int proxy_connect(
853853
if ((error = gen_connect_req(&request, t)) < 0)
854854
goto done;
855855

856-
if ((error = git_stream_write(proxy_stream,
857-
request.ptr, request.size, 0)) < 0)
856+
if ((error = git_stream__write_full(proxy_stream, request.ptr,
857+
request.size, 0)) < 0)
858858
goto done;
859859

860860
git_buf_dispose(&request);
@@ -1034,8 +1034,8 @@ static int http_stream_read(
10341034
if (gen_request(&request, s, 0) < 0)
10351035
return -1;
10361036

1037-
if (git_stream_write(t->server.stream,
1038-
request.ptr, request.size, 0) < 0) {
1037+
if (git_stream__write_full(t->server.stream, request.ptr,
1038+
request.size, 0) < 0) {
10391039
git_buf_dispose(&request);
10401040
return -1;
10411041
}
@@ -1058,7 +1058,8 @@ static int http_stream_read(
10581058
s->chunk_buffer_len = 0;
10591059

10601060
/* Write the final chunk. */
1061-
if (git_stream_write(t->server.stream, "0\r\n\r\n", 5, 0) < 0)
1061+
if (git_stream__write_full(t->server.stream,
1062+
"0\r\n\r\n", 5, 0) < 0)
10621063
return -1;
10631064
}
10641065

@@ -1157,8 +1158,8 @@ static int http_stream_write_chunked(
11571158
if (gen_request(&request, s, 0) < 0)
11581159
return -1;
11591160

1160-
if (git_stream_write(t->server.stream,
1161-
request.ptr, request.size, 0) < 0) {
1161+
if (git_stream__write_full(t->server.stream, request.ptr,
1162+
request.size, 0) < 0) {
11621163
git_buf_dispose(&request);
11631164
return -1;
11641165
}
@@ -1233,11 +1234,10 @@ static int http_stream_write_single(
12331234
if (gen_request(&request, s, len) < 0)
12341235
return -1;
12351236

1236-
if (git_stream_write(t->server.stream,
1237-
request.ptr, request.size, 0) < 0)
1237+
if (git_stream__write_full(t->server.stream, request.ptr, request.size, 0) < 0)
12381238
goto on_error;
12391239

1240-
if (len && git_stream_write(t->server.stream, buffer, len, 0) < 0)
1240+
if (len && git_stream__write_full(t->server.stream, buffer, len, 0) < 0)
12411241
goto on_error;
12421242

12431243
git_buf_dispose(&request);

0 commit comments

Comments
 (0)