Dear Maintainer,
During recent autopkgtest runs triggered by the vsftpd migration (as shown in the migration excuses page: https://qa.debian.org/excuses.php?package=vsftpd), we observed consistent failures on 32-bit architectures (specifically i386) in the following test case:
test_05_errors.py::TestErrors::test_05_04_unclean_tls_shutdown[http/1.0]
The test fails with exit code 8 (CURLE_WEIRD_SERVER_REPLY) instead of the expected exit code 56 (CURLE_RECV_ERROR). The curl verbose log shows:
* Invalid Content-Length: value
* closing connection #0
Upon further investigation in a local unstable-i386 chroot, we found that the Apache test server (mod_curltest) is actually sending an invalid "Content-Length: -1" header.
The root cause lies in a signed/unsigned type promotion bug in the Apache test module:
tests/http/testenv/mod_curltest/mod_curltest.c
Line 409 of mod_curltest.c contains the following ternary assignment:
r->clength = with_cl ? (chunks * chunk_size) : -1;
Where the variables are declared as:
int chunks;
size_t chunk_size;
apr_off_t clength; /* signed 64-bit integer */
On a 32-bit architecture (i386):
1. `chunks * chunk_size` evaluates to `unsigned int` (32-bit unsigned).
2. Due to Usual Arithmetic Conversions in C, the signed `-1` (int) operand of the ternary operator is promoted to `unsigned int`, yielding `4294967295` (0xFFFFFFFF).
3. The ternary operator returns `4294967295` as an `unsigned int`.
4. This unsigned value is then assigned to `r->clength` (apr_off_t, 64-bit signed). Since the source is unsigned, it is zero-extended, resulting in `r->clength` becoming `+4294967295`.
5. The subsequently executed check `if(r->clength >= 0)` evaluates to true.
6. Inside the block, `apr_ltoa(r->pool, (long)r->clength)` casts it to a 32-bit signed `long` (on 32-bit platforms), which truncates it back to `-1`, formatting it as "-1" and sending the "Content-Length: -1" header.
On a 64-bit architecture (amd64), `size_t` is 64-bit, and `-1` (32-bit int) is promoted to `unsigned long` (64-bit), yielding `18446744073709551615`. When assigned to `r->clength` (64-bit signed), it wraps back to `-1`, which correctly skips the Content-Length generation.
This type promotion mismatch can be safely fixed by avoiding the signed/unsigned mixture in the ternary operator.
Please find the attached patch which resolves this issue by explicitly assigning the values using a standard if-else block.
Thanks,
Keng-Yu Lin
---
diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c
index 585c57b..308bf3b 100644
--- a/tests/http/testenv/mod_curltest/mod_curltest.c
+++ b/tests/http/testenv/mod_curltest/mod_curltest.c
@@ -406,8 +406,12 @@ static int curltest_tweak_handler(request_rec *r)
ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "error_handler: processing "
"request, %s", r->args? r->args : "(no args)");
r->status = http_status;
- r->clength = with_cl ? (chunks * chunk_size) : -1;
+ if(with_cl) {
+ r->clength = (apr_off_t)chunks * chunk_size;
+ }
+ else {
+ r->clength = -1;
+ }
r->chunked = (r->proto_num >= HTTP_VERSION(1, 1)) && !with_cl;
apr_table_setn(r->headers_out, "request-id", request_id);