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). Cheers, Daniel