Package: curl
Version: 8.21.0-1
Severity: important
Tags: patch upstream
User: [email protected]
Usertags: autopkgtest

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);

Reply via email to