Den mån 7 juli 2025 kl 21:43 skrev Branko Čibej <br...@apache.org>:
> On 7. 7. 25 21:30, Daniel Sahlberg wrote: > > Den mån 7 juli 2025 kl 17:15 skrev Branko Čibej <br...@apache.org>: > > > > On 7. 7. 25 16:07, Daniel Sahlberg wrote: > > > Den mån 7 juli 2025 kl 12:08 skrev Branko Čibej <br...@apache.org > >: > > > > > > On 7. 7. 25 11:08, Branko Čibej wrote: > > >> As you've probably noticed, I've been going through > compilation > > >> warnings, especially implicit narrowing conversions, and > trying > > >> to fix them in a sane way. Many of them, by the way, I > > don't see > > >> with gcc because it doesn't have a -Wshorten-64-to-32 flag, so > > >> I'm using clang. This time I found a gem: > > >> > > >> > > >> .../protocols/http2_stream.c:162:48: warning: implicit > > >> conversion loses integer precision: > > >> 'apr_size_t' (aka 'unsigned long') to 'apr_uint32_t' > > >> (aka 'unsigned int') [-Wshorten-64-to-32] > > >> 146 | max_payload_size, > > >> | ^~~~~~~~~~~~~~~~ > > >> > > >> > > >> This is in a call to serf__bucket_http2_frame_create(), which > > >> expects max_payload_size as an apr_uint32_t. > > >> > > >> > > >> But then it goes on to allocate a serf_http2_frame_context_t > > >> (see: http2_frame_buckets.h line 603) which has a member > > >> max_payload_size that is an apr_size_t. And if that's not > funny > > >> enough, here's how the struct initialisation actually happens: > > >> > > >> > > >> serf__bucket_http2_frame_create(serf_bucket_t *stream, > > >> unsigned char frame_type, > > >> unsigned char flags, > > >> apr_int32_t *stream_id, > > >> void(*stream_id_alloc)( > > >> void *baton, > > >> apr_int32_t *stream_id), > > >> void *stream_id_baton, > > >> apr_uint32_t max_payload_size, > > >> serf_bucket_alloc_t *alloc) > > >> { > > >> serf_http2_frame_context_t *ctx = > > >> serf_bucket_mem_alloc(alloc, > > >> sizeof(*ctx)); > > >> > > >> ctx->alloc = alloc; > > >> ctx->stream = stream; > > >> ctx->chunk = serf_bucket_aggregate_create(alloc); > > >> ctx->max_payload_size = max_payload_size; > > >> ctx->frametype = frame_type; > > >> ctx->flags = flags; > > >> > > >> if (max_payload_size > 0xFFFFFF) > > >> max_payload_size = 0xFFFFFF; > > >> ... > > >> > > >> > > >> > > >> From this point onward, max_payload_size (the function > > argument, > > >> an apr_uint32_t) is not used. Um. The size adjustment is a > > no-op? > > >> > > >> > > >> So to silence the warning, it would be enough to change the > > type > > >> of the function argument. But this bit of code is actually > > >> nonsense. Should the max size be limited to 16 MiB or not? If > > >> yes, and it isn't, how does the HTTP/2 code even work > > correctly? > > >> Or maybe we just haven't encountered a case where it breaks? > > >> > > >> > > >> I don't know anything about HTTP/2 framing. RFC 9113/4.2 says > > >> that 16 MiB is the upper limit. My gut feeling is to move the > > >> size adjustment before 'ctx' initialisation, and to change the > > >> type of the parameter -- the latter because Serf operates with > > >> apr_size_t all the time. > > > > > > Not sure how much I can help out here and I'm sure you read the > > logs > > > as well as I do, but it seems there are only a few revisions of > > that > > > particular part of the code. Initially, it seems > > max_payload_size was > > > indeed apr_size_t but r1712557 changed to the current > > apr_uint32_t: " > > > (serf__bucket_http2_frame_create): Remove some arguments. Limits > > > max_payload_size to a uint32, as that is how http2 configures > > it and > > > it has to fit in 24 bits anyway." > > > > > > In the case you quoted above, stream_send_headers(), the > > > max_payload_size is apr_size_t. Checking some places this is called > > > find http2_stream_enque_response which gets the argument from > > > serf_http2__max_payload_size which return apr_size_t BUT it get the > > > actual returned number from an serf_http2_protocol_t struct member > > > lr_max_framesize which in turn is (*TADA*) apr_uint32_t. > > > > > > It seems Bert's observation "it has to fit in 24 bits anyway" is > > > correct and from that point of view it makes sense to use > > > apr_uint32_t. But I don't see any real reason why it would be > > > unreasonable to use apr_size_t - personally I think that type is > > more > > > logical. > > > > > > The observation is correct, but changing that in only one place > > and then > > immediately converting it to apr_size_t is just ... I dunno, > > incomplete? > > I've noticed that the implementation (not just the http2 part) plays > > fast and loose with int/apr_size_t/apr_uint32_t, creating problems > > and > > requiring conversions. It doesn't help that APR itself is > > inconsistent > > about that (apr_base64, /me rolls eyes). > > > > This goes all the way to the public API in some places. Specifically, > > the source of that apr_size_t is in Serf's public bucket API, so > > non-negotiable. > > > > > > > Is there a platform where apr_size_t is < 24 bits? > > > > > > Theoretically, 16-bit platforms could have 16-bit apr_size_t, but I'm > > pretty sure we don't support those for other reasons. All platforms > > should have sizeof(apr_size_t) >= sizeof(int) but not necessarily >= > > sizeof(apr_uint32_t). I'm pretty sure APR would fall on its face > > on such > > platforms, so I wouldn't worry about them. > > > > > > > Specifically, this silences the warning and all tests still > > pass, > > > including a checkout with Subversion over https (though I've no > > > idea how to verify if that actually uses HTTP/2; any pointers > > > would be welcome). > > > > > > > > > Index: buckets/http2_frame_buckets.c > > > > =================================================================== > > > --- buckets/http2_frame_buckets.c (revision 1927021) > > > +++ buckets/http2_frame_buckets.c (working copy) > > > @@ -630,12 +630,17 @@ > > > void *baton, > > > apr_int32_t *stream_id), > > > void *stream_id_baton, > > > - apr_uint32_t max_payload_size, > > > + apr_size_t max_payload_size, > > > serf_bucket_alloc_t *alloc) > > > { > > > serf_http2_frame_context_t *ctx = > > serf_bucket_mem_alloc(alloc, > > > sizeof(*ctx)); > > > > > > + /* The upper limit for HTTP/2 MAX_FRAME_SIZE is 16 M1B. > > > +https://www.rfc-editor.org/rfc/rfc9113.html#section-4.2 */ > > > + if (max_payload_size > 0xFFFFFF) > > > + max_payload_size = 0xFFFFFF; > > > + > > > > > > > > > +1 to moving. > > > > > > ctx->alloc = alloc; > > > ctx->stream = stream; > > > ctx->chunk = serf_bucket_aggregate_create(alloc); > > > @@ -643,9 +648,6 @@ > > > ctx->frametype = frame_type; > > > ctx->flags = flags; > > > > > > - if (max_payload_size > 0xFFFFFF) > > > - max_payload_size = 0xFFFFFF; > > > - > > > if (!stream_id_alloc || (stream_id && *stream_id >= 0)) > > > { > > > /* Avoid all alloc handling; we know the final id */ > > > @@ -980,4 +982,3 @@ > > > serf_http2_frame_get_remaining, > > > serf_http2_frame_set_config > > > }; > > > - > > > Index: protocols/http2_buckets.h > > > > =================================================================== > > > --- protocols/http2_buckets.h (revision 1927021) > > > +++ protocols/http2_buckets.h (working copy) > > > @@ -202,7 +202,7 @@ > > > void *baton, > > > apr_int32_t > *stream_id), > > > void *stream_id_baton, > > > - apr_uint32_t max_payload_size, > > > + apr_size_t max_payload_size, > > > serf_bucket_alloc_t *alloc); > > > > > > /* > > ==================================================================== > > */ > > > > > > > > > Makes sense to me, but it would probably make sense to update the > > > other instances where we work on the size - as mentioned above the > > > serf_http2_protocol_t is riddled with apr_uint32_t:s. > > > > Yes ... but we're either not compiling those bits, or they're > > converted > > _to_ apr_size_t. Which tends to be safe. > > > > It would be lovely to make the code consistent, but that's a *lot* of > > changes for dubious benefit (and somewhat less dubious additional > > source > > of bugs). I'm not in the mood to tackle that, especially given > > that I'm > > not the author of the code and would have to understand[1] every > > change > > before making it. > > > > -- Brane > > > > > > [1] Right now I'm looking at some similar warnings in the use of > > hpack_int() and, for now, I can't prove to myself that the > > conversions > > are safe -- so I'll leave the warnings in. I just have to find the > > right > > warning flags to make gcc yammer about them, too; seeing the warnings > > only on macOS isn't exactly useful, and while they're present on > > Windows, most of the MSVC warnings are red herrings and just > > obscure the > > real issues. > > > > > > What I'm suggesting is to change the apr_uint32_t:s to apr_size_t. > > That would match the public API and eliminate conversions. > > > > I'm probably not experienced enough in C to see if there are potential > > bugs hiding in such a change. (Of course, if there are places in the > > standard where the maximum value is limited to 2^32-1 we could > > potentially send too large values - but these should be taken care of > > anyway to avoid a rollover, much like the existing max_payload_size > > check above). > > By "the standard", I expect you mean the HTTP(/2) RFCs? I haven't read > them all, but surely there are some "SHOULD" and "MUST" kinds of limits. > At least the MAX_FRAME_SIZE has both a lower and an upper bound defined, > but I don't know whether the actual value is controlled by the server or > somehow negotiated between server and client. > Yes, I meant the RFCs. In the http2_protocol_t struct, for each setting always two there are, no more, no less. An "lr" (local->remote) and a "rl" (remote->local), each controlled by either the server or the client. > > About all those apr_uint32_t's ... I'm not seeing any warnings from > there. But, again, I haven't investigated. > For the lr_max_framesize, there is code to parse it from the SETTIGS frame (ie, sent from the server), this code seems to read the binary data to an apr_uint32_t so obviously no warning there. rl_max_framesize is set to a default value (16384) and there is no way to change it. There should probably be a public API to set it, in which case we'd have to decide if we should use apr_size_t or apr_uint32_t. In either case we'd have to check the min and max value according to the bounds in the RFC. Cheers, Daniel