git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v2 5/7] remote-curl: error on incomplete packet

From
Jeff King <peff@peff.net>
Date
May 18, 2020, 16:22 UTC
Message-ID
<20200518162225.GB42240@coredump.intra.peff.net>
In-Reply-To
<52ce5fdffd6741eeee8d69b804403383da0d879d.1589816719.git.liu.denton@gmail.com>
On Mon, May 18, 2020 at 11:47:22AM -0400, Denton Liu wrote:
Show 7 quoted lines
> +struct check_pktline_state {
> +	char len_buf[4];
> +	int len_filled;
> +	int remaining;
> +};
> +
> +static void check_pktline(struct check_pktline_state *state, const char *ptr, size_t size)

Thanks for converting this. I think having this broken out makes it a bit easier to reason about, and it should be much easier to reuse if we need it elsewhere.

Show 10 quoted lines
> +{
> +	while (size) {
> +		if (!state->remaining) {
> +			int digits_remaining = 4 - state->len_filled;
> +			if (digits_remaining > size)
> +				digits_remaining = size;
> +			memcpy(&state->len_buf[state->len_filled], ptr, digits_remaining);
> +			state->len_filled += digits_remaining;
> +			ptr += digits_remaining;
> +			size -= digits_remaining;

Having personally written and screwed up this kind of parsing state machine before, I read over this logic quite carefully. ;) I believe it's correct.

Another way would be to loop by single characters:
  while (state->len_filled < 4 && size) {
          state->len_buf[state->len_filled++] = *ptr;
	  ptr++;
	  size--;
  }

which I _think_ is equivalent, and is a bit shorter. I'm OK with either (see below, especially).

I'm not sure if it's worth replacing "4" with ARRAY_SIZE(state->len_buf). I generally try to avoid magic numbers, but it's certainly not like one could change the size of len_buf and this code would still be useful. :)

Show 12 quoted lines
> +			if (state->len_filled == 4) {
> +				state->remaining = packet_length(state->len_buf);
> +				if (state->remaining < 0) {
> +					die(_("remote-curl: bad line length character: %.4s"), state->len_buf);
> +				} else if (state->remaining < 4) {
> +					state->remaining = 0;
> +				} else {
> +					state->remaining -= 4;
> +				}
> +				state->len_filled = 0;
> +			}
> +		}

This part makes sense. We'll either leave len_filled as 1-3 (incomplete), or we'll read a whole packet (for a flush), or we'll be waiting to read the rest of the packet.

Show 10 quoted lines
> +		if (state->remaining) {
> +			int remaining = state->remaining;
> +			if (remaining > size)
> +				remaining = size;
> +			ptr += remaining;
> +			size -= remaining;
> +			state->remaining -= remaining;
> +		}
> +	}
> +}

And here we most certainly don't want to read character-by-character, because we're not doing anything with each one, and we expect there to be a lot more of them. Having the earlier loop match the form of this one is perhaps a good reason to leave it alone.

Show 9 quoted lines
> [...]
> @@ -936,6 +984,11 @@ static int post_rpc(struct rpc_state *rpc, int flush_received)
>  	if (!rpc->any_written)
>  		err = -1;
>  
> +	if (rpc_in_data.pktline_state.len_filled)
> +		err = error(_("%d bytes of length header were received"), rpc_in_data.pktline_state.len_filled);
> +	if (rpc_in_data.pktline_state.remaining)
> +		err = error(_("%d bytes of body are still expected"), rpc_in_data.pktline_state.remaining);
And here's the payoff for all of the state machine checks. Makes sense.
Show 9 quoted lines
> @@ -702,6 +746,8 @@ static size_t rpc_in(char *ptr, size_t eltsize,
>  		return size;
>  	if (size)
>  		data->rpc->any_written = 1;
> +	if (data->check_pktline)
> +		check_pktline(&data->pktline_state, ptr, size);
>  	write_or_die(data->rpc->in, ptr, size);
>  	return size;
>  }
And this is now conditional. Good...
Show 6 quoted lines
> @@ -920,6 +966,8 @@ static int post_rpc(struct rpc_state *rpc, int flush_received)
>  	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
>  	rpc_in_data.rpc = rpc;
>  	rpc_in_data.slot = slot;
> +	rpc_in_data.check_pktline = stateless_connect;
> +	memset(&rpc_in_data.pktline_state, 0, sizeof(rpc_in_data.pktline_state));
And we enable it only for stateless-connect. Makes perfect sense.
Show 6 quoted lines
> --- /dev/null
> +++ b/t/lib-httpd/incomplete-body-upload-pack-v2-http.sh
> @@ -0,0 +1,3 @@
> +printf "Content-Type: text/%s\n" "application/x-git-upload-pack-result"
> +echo
> +printf "%s%s\n" "0079" "45"

Nice. Just having a deterministic half-written packet is way easier to reason about than my "truncating proxy" suggestion.

Show 6 quoted lines
> --- /dev/null
> +++ b/t/lib-httpd/incomplete-length-upload-pack-v2-http.sh
> @@ -0,0 +1,3 @@
> +printf "Content-Type: text/%s\n" "application/x-git-upload-pack-result"
> +echo
> +printf "%s\n" "00"
Thanks for covering this case, too.

I confirmed (as I'm sure you did) they both cause git-remote-http to hang without the fix in this patch.

-Peff
Previous: Denton LiuNext: Denton Liu
Message 39 of 62 in “remote-curl: partial fix for a deadlock with stateless rpc”
  1. 0/6 remote-curl: partial fix for a deadlock with stateless rpcDenton Liu, May 13, 2020
  2. 1/6 remote-curl: fix typoDenton Liu, May 13, 2020
  3. 2/6 remote-curl: remove label indentationDenton Liu, May 13, 2020
  4. 3/6 transport: combine common cases with a fallthroughDenton Liu, May 13, 2020
  5. Eric SunshineMay 13, 2020
  6. Denton LiuMay 18, 2020
  7. Eric SunshineMay 18, 2020
  8. 4/6 pkt-line: extern packet_length()Denton Liu, May 13, 2020
  9. Eric SunshineMay 13, 2020
  10. Jeff KingMay 15, 2020
  11. Jeff KingMay 15, 2020
  12. 5/6 remote-curl: error on incomplete packetDenton Liu, May 13, 2020
  13. Jeff KingMay 15, 2020
  14. Denton LiuMay 18, 2020
  15. Jeff KingMay 18, 2020
  16. 6/6 remote-curl: ensure last packet is a flushDenton Liu, May 13, 2020
  17. Denton LiuMay 15, 2020
  18. Jeff KingMay 15, 2020
  19. Junio C HamanoMay 18, 2020
  20. Jeff KingMay 18, 2020
  21. Jeff KingMay 18, 2020
  22. 0/7 remote-curl: fix deadlocks when remote server disconnectsDenton Liu, May 18, 2020
  23. 1/7 remote-curl: fix typoDenton Liu, May 18, 2020
  24. 2/7 remote-curl: remove label indentationDenton Liu, May 18, 2020
  25. Junio C HamanoMay 18, 2020
  26. 4/7 pkt-line: extern packet_length()Denton Liu, May 18, 2020
  27. Jeff KingMay 18, 2020
  28. Eric SunshineMay 18, 2020
  29. Jeff KingMay 18, 2020
  30. Junio C HamanoMay 18, 2020
  31. 3/7 transport: extract common fetch_pack() callDenton Liu, May 18, 2020
  32. Junio C HamanoMay 18, 2020
  33. 6/7 pkt-line: PACKET_READ_RESPONSE_ENDDenton Liu, May 18, 2020
  34. 7/7 stateless-connect: send response end packetDenton Liu, May 18, 2020
  35. Jeff KingMay 18, 2020
  36. Denton LiuMay 18, 2020
  37. Jeff KingMay 18, 2020
  38. 5/7 remote-curl: error on incomplete packetDenton Liu, May 18, 2020
  39. Jeff KingMay 18, 2020
  40. Denton LiuMay 18, 2020
  41. Jeff KingMay 18, 2020
  42. Denton LiuMay 18, 2020
  43. Jeff KingMay 18, 2020
  44. Junio C HamanoMay 18, 2020
  45. Jeff KingMay 19, 2020
  46. Junio C HamanoMay 18, 2020
  47. 0/7 remote-curl: fix deadlocks when remote server disconnectsDenton Liu, May 19, 2020
  48. 1/7 remote-curl: fix typoDenton Liu, May 19, 2020
  49. 2/7 remote-curl: remove label indentationDenton Liu, May 19, 2020
  50. 3/7 transport: extract common fetch_pack() callDenton Liu, May 19, 2020
  51. 4/7 pkt-line: extern packet_length()Denton Liu, May 19, 2020
  52. Eric SunshineMay 19, 2020
  53. 6/7 pkt-line: define PACKET_READ_RESPONSE_ENDDenton Liu, May 19, 2020
  54. 5/7 remote-curl: error on incomplete packetDenton Liu, May 19, 2020
  55. 7/7 stateless-connect: send response end packetDenton Liu, May 19, 2020
  56. Jeff KingMay 19, 2020
  57. Denton LiuMay 19, 2020
  58. 8/7 fixup! pkt-line: extern packet_length()Denton Liu, May 19, 2020
  59. 9/9 fixup! remote-curl: error on incomplete packetDenton Liu, May 22, 2020
  60. Jeff KingMay 22, 2020
  61. Denton LiuMay 22, 2020
  62. Jeff KingMay 22, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.