http: prefer CURLOPT_SEEKFUNCTION to CURLOPT_IOCTLFUNCTION

The IOCTLFUNCTION option has been deprecated, and generates a compiler warning in recent versions of curl. We can switch to using SEEKFUNCTION instead. It was added in 2008 via curl 7.18.0; our INSTALL file already indicates we require at least curl 7.19.4. But there's one catch: curl says we should use CURL_SEEKFUNC_{OK,FAIL}, and those didn't arrive until 7.19.5. One workaround would be to use a bare 0/1 here (or define our own macros). But let's just bump the minimum required version to 7.19.5. That version is only a minor version bump from our existing requirement, and is only a 2 month time bump for versions that are almost 13 years old. So it's not likely that anybody cares about the distinction. Switching means we have to rewrite the ioctl functions into seek functions. In some ways they are simpler (seeking is the only operation), but in some ways more complex (the ioctl allowed only a full rewind, but now we can seek to arbitrary offsets). Curl will only ever use SEEK_SET (per their documentation), so I didn't bother implementing anything else, since it would naturally be completely untested. This seems unlikely to change, but I added an assertion just in case. Likewise, I doubt curl will ever try to seek outside of the buffer sizes we've told it, but I erred on the defensive side here, rather than do an out-of-bounds read. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

Jeff King committed Jan 16, 2023 at 22:04 UTC 4bd481e0ad9c6e78e320288886ffdad8f76dcfc1
5 files changed +26 -30
INSTALL
+1 -1
@@ -144,7 +144,7 @@ Issues of note:
144 not need that functionality, use NO_CURL to build without
145 it.
146
147 - Git requires version "7.19.4" or later of "libcurl" to build
147 + Git requires version "7.19.5" or later of "libcurl" to build
148 without NO_CURL. This version requirement may be bumped in
149 the future.
150
http-push.c
+2 -2
@@ -203,8 +203,8 @@ static void curl_setup_http(CURL *curl, const char *url,
203 curl_easy_setopt(curl, CURLOPT_INFILE, buffer);
204 curl_easy_setopt(curl, CURLOPT_INFILESIZE, buffer->buf.len);
205 curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
206 - curl_easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_buffer);
207 - curl_easy_setopt(curl, CURLOPT_IOCTLDATA, buffer);
206 + curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, seek_buffer);
207 + curl_easy_setopt(curl, CURLOPT_SEEKDATA, buffer);
208 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn);
209 curl_easy_setopt(curl, CURLOPT_NOBODY, 0);
210 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, custom_req);
http.c
+9 -11
@@ -155,21 +155,19 @@ size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
155 return size / eltsize;
156 }
157
158 -curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
158 +int seek_buffer(void *clientp, curl_off_t offset, int origin)
159 {
160 struct buffer *buffer = clientp;
161
162 - switch (cmd) {
163 - case CURLIOCMD_NOP:
164 - return CURLIOE_OK;
165 -
166 - case CURLIOCMD_RESTARTREAD:
167 - buffer->posn = 0;
168 - return CURLIOE_OK;
169 -
170 - default:
171 - return CURLIOE_UNKNOWNCMD;
162 + if (origin != SEEK_SET)
163 + BUG("seek_buffer only handles SEEK_SET");
164 + if (offset < 0 || offset >= buffer->buf.len) {
165 + error("curl seek would be outside of buffer");
166 + return CURL_SEEKFUNC_FAIL;
167 }
168 +
169 + buffer->posn = offset;
170 + return CURL_SEEKFUNC_OK;
171 }
172
173 size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
http.h
+1 -1
@@ -40,7 +40,7 @@ struct buffer {
40 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
41 size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
42 size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf);
43 -curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp);
43 +int seek_buffer(void *clientp, curl_off_t offset, int origin);
44
45 /* Slot lifecycle functions */
46 struct active_request_slot *get_active_slot(void);
remote-curl.c
+13 -15
@@ -710,25 +710,23 @@ static size_t rpc_out(void *ptr, size_t eltsize,
710 return avail;
711 }
712
713 -static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
713 +static int rpc_seek(void *clientp, curl_off_t offset, int origin)
714 {
715 struct rpc_state *rpc = clientp;
716
717 - switch (cmd) {
718 - case CURLIOCMD_NOP:
719 - return CURLIOE_OK;
717 + if (origin != SEEK_SET)
718 + BUG("rpc_seek only handles SEEK_SET, not %d", origin);
719
721 - case CURLIOCMD_RESTARTREAD:
722 - if (rpc->initial_buffer) {
723 - rpc->pos = 0;
724 - return CURLIOE_OK;
720 + if (rpc->initial_buffer) {
721 + if (offset < 0 || offset > rpc->len) {
722 + error("curl seek would be outside of rpc buffer");
723 + return CURL_SEEKFUNC_FAIL;
724 }
726 - error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
727 - return CURLIOE_FAILRESTART;
728 -
729 - default:
730 - return CURLIOE_UNKNOWNCMD;
725 + rpc->pos = offset;
726 + return CURL_SEEKFUNC_OK;
727 }
728 + error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
729 + return CURL_SEEKFUNC_FAIL;
730 }
731
732 struct check_pktline_state {
@@ -948,8 +946,8 @@ retry:
946 rpc->initial_buffer = 1;
947 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
948 curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
951 - curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
952 - curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
949 + curl_easy_setopt(slot->curl, CURLOPT_SEEKFUNCTION, rpc_seek);
950 + curl_easy_setopt(slot->curl, CURLOPT_SEEKDATA, rpc);
951 if (options.verbosity > 1) {
952 fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
953 fflush(stderr);