Skip to content

Commit 42ff072

Browse files
authored
CDRIVER-4781 Avoid intermediate uint32_t for socketTimeoutMS (#1475)
* Fix format specifier in timeout range validation error messages * CDRIVER-4781 Avoid overflow of -1 sockettimeout in cast to intermediate uint32_t * Add regression test for negative sockettimeoutms validation * Update documentation with warnings for non-positive timeout values * Update timeout variables for type consistency * Add regression test for unspecified default timeout in writev functions
1 parent fc1cba7 commit 42ff072

14 files changed

+191
-21
lines changed

src/libmongoc/doc/mongoc_stream_read.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ Parameters
2626

2727
The :symbol:`mongoc_stream_read()` function shall perform a read from a :symbol:`mongoc_stream_t`. It's modeled on the API and semantics of ``read()``, though the parameters map only loosely.
2828

29+
.. warning::
30+
31+
The "default timeout" indicated by a negative value is both unspecified and
32+
unrelated to the documented default values for ``*TimeoutMS`` URI options.
33+
To specify a default timeout value for a ``*TimeoutMS`` URI option, use the
34+
``MONGOC_DEFAULT_*`` constants defined in ``mongoc-client.h``.
35+
2936
Returns
3037
-------
3138

@@ -38,4 +45,3 @@ The :symbol:`mongoc_stream_read` function returns the number of bytes read on su
3845
| :symbol:`mongoc_stream_write()`
3946
4047
| :symbol:`mongoc_stream_writev()`
41-

src/libmongoc/doc/mongoc_stream_readv.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ Parameters
2626

2727
This function is identical to :symbol:`mongoc_stream_read()` except that it takes a :symbol:`mongoc_iovec_t` to perform gathered I/O.
2828

29+
.. warning::
30+
31+
The "default timeout" indicated by a negative value is both unspecified and
32+
unrelated to the documented default values for ``*TimeoutMS`` URI options.
33+
To specify a default timeout value for a ``*TimeoutMS`` URI option, use the
34+
``MONGOC_DEFAULT_*`` constants defined in ``mongoc-client.h``.
35+
2936
Returns
3037
-------
3138

@@ -38,4 +45,3 @@ Returns
3845
| :symbol:`mongoc_stream_write()`
3946
4047
| :symbol:`mongoc_stream_writev()`
41-

src/libmongoc/doc/mongoc_stream_write.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ Parameters
2424

2525
The :symbol:`mongoc_stream_write()` function shall perform a write to a :symbol:`mongoc_stream_t`. It's modeled on the API and semantics of ``write()``, though the parameters map only loosely.
2626

27+
.. warning::
28+
29+
The "default timeout" indicated by a negative value is both unspecified and
30+
unrelated to the documented default values for ``*TimeoutMS`` URI options.
31+
To specify a default timeout value for a ``*TimeoutMS`` URI option, use the
32+
``MONGOC_DEFAULT_*`` constants defined in ``mongoc-client.h``.
33+
2734
Returns
2835
-------
2936

@@ -36,4 +43,3 @@ The :symbol:`mongoc_stream_write` function returns the number of bytes written o
3643
| :symbol:`mongoc_stream_readv()`
3744
3845
| :symbol:`mongoc_stream_writev()`
39-

src/libmongoc/doc/mongoc_stream_writev.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@ to a :symbol:`mongoc_stream_t`. It's modeled on the
2727
API and semantics of ``writev()``, though the parameters map only
2828
loosely.
2929

30+
.. warning::
31+
32+
The "default timeout" indicated by a negative value is both unspecified and
33+
unrelated to the documented default values for ``*TimeoutMS`` URI options.
34+
To specify a default timeout value for a ``*TimeoutMS`` URI option, use the
35+
``MONGOC_DEFAULT_*`` constants defined in ``mongoc-client.h``.
36+
3037
Returns
3138
-------
3239

3340
The number of bytes written on success, or ``-1`` upon failure and ``errno`` is set.
34-

src/libmongoc/doc/mongoc_uri_t.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,12 @@ MONGOC_URI_LOADBALANCED loadbalanced fal
104104
MONGOC_URI_SRVMAXHOSTS srvmaxhosts 0 If zero, the number of hosts in DNS results is unlimited. If greater than zero, the number of hosts in DNS results is limited to being less than or equal to the given value.
105105
========================================== ================================= ================================= ============================================================================================================================================================================================================================================
106106

107-
Setting any of the \*timeoutMS options above to ``0`` will be interpreted as "use the default value".
107+
.. warning::
108+
109+
Setting any of the \*timeoutMS options above to either ``0`` or a negative value is discouraged due to unspecified and inconsistent behavior.
110+
The "default value" historically specified as a fallback for ``0`` or a negative value is NOT related to the default values for the \*timeoutMS options documented above.
111+
The meaning of a timeout of ``0`` or a negative value may vary depending on the operation being executed, even when specified by the same URI option.
112+
To specify the documented default value for a \*timeoutMS option, use the `MONGOC_DEFAULT_*` constants defined in ``mongoc-client.h`` instead.
108113

109114
Authentication Options
110115
----------------------
@@ -330,4 +335,3 @@ MONGOC_URI_SAFE safe {tr
330335
mongoc_uri_set_username
331336
mongoc_uri_set_write_concern
332337
mongoc_uri_unescape
333-

src/libmongoc/doc/mongoc_write_concern_set_wtimeout.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,3 @@ instead of altering the write concern.
3131
.. seealso::
3232

3333
| :symbol:`mongoc_write_concern_get_wtimeout` and :symbol:`mongoc_write_concern_set_wtimeout_int64`.
34-

src/libmongoc/src/mongoc/mongoc-buffer.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ _mongoc_buffer_append_from_stream (mongoc_buffer_t *buffer,
199199
bson_set_error (error,
200200
MONGOC_ERROR_STREAM,
201201
MONGOC_ERROR_STREAM_SOCKET,
202-
"timeout_msec value %" PRIu64
202+
"timeout_msec value %" PRId64
203203
" exceeds supported 32-bit range",
204204
timeout_msec);
205205
RETURN (false);
@@ -266,7 +266,7 @@ _mongoc_buffer_fill (mongoc_buffer_t *buffer,
266266
bson_set_error (error,
267267
MONGOC_ERROR_STREAM,
268268
MONGOC_ERROR_STREAM_SOCKET,
269-
"timeout_msec value %" PRIu64
269+
"timeout_msec value %" PRId64
270270
" exceeds supported 32-bit range",
271271
timeout_msec);
272272
RETURN (false);
@@ -342,7 +342,7 @@ _mongoc_buffer_try_append_from_stream (mongoc_buffer_t *buffer,
342342

343343
if (BSON_UNLIKELY (!bson_in_range_signed (int32_t, timeout_msec))) {
344344
// CDRIVER-4589
345-
MONGOC_ERROR ("timeout_msec value %" PRIu64
345+
MONGOC_ERROR ("timeout_msec value %" PRId64
346346
" exceeds supported 32-bit range",
347347
timeout_msec);
348348
RETURN (-1);

src/libmongoc/src/mongoc/mongoc-cluster-private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ typedef struct _mongoc_cluster_node_t {
5353
typedef struct _mongoc_cluster_t {
5454
int64_t operation_id;
5555
int32_t request_id;
56-
uint32_t sockettimeoutms;
57-
uint32_t socketcheckintervalms;
56+
int32_t sockettimeoutms;
57+
int32_t socketcheckintervalms;
5858
mongoc_uri_t *uri;
5959
unsigned requires_auth : 1;
6060

src/libmongoc/src/mongoc/mongoc-crypt.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,9 +491,7 @@ _state_need_kms (_state_machine_t *state_machine, bson_error_t *error)
491491
mongocrypt_binary_t *http_req = NULL;
492492
mongocrypt_binary_t *http_reply = NULL;
493493
const char *endpoint;
494-
uint32_t sockettimeout;
495-
496-
sockettimeout = MONGOC_DEFAULT_SOCKETTIMEOUTMS;
494+
const int32_t sockettimeout = MONGOC_DEFAULT_SOCKETTIMEOUTMS;
497495
kms_ctx = mongocrypt_ctx_next_kms_ctx (state_machine->ctx);
498496
while (kms_ctx) {
499497
mongoc_iovec_t iov;

src/libmongoc/src/mongoc/mongoc-secure-channel.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ mongoc_secure_channel_read (mongoc_stream_tls_t *tls,
476476

477477
if (BSON_UNLIKELY (!bson_in_range_signed (int32_t, tls->timeout_msec))) {
478478
// CDRIVER-4589
479-
MONGOC_ERROR ("timeout_msec value %" PRIu64
479+
MONGOC_ERROR ("timeout_msec value %" PRId64
480480
" exceeds supported 32-bit range",
481481
tls->timeout_msec);
482482
return -1;
@@ -511,7 +511,7 @@ mongoc_secure_channel_write (mongoc_stream_tls_t *tls,
511511

512512
if (BSON_UNLIKELY (!bson_in_range_signed (int32_t, tls->timeout_msec))) {
513513
// CDRIVER-4589
514-
MONGOC_ERROR ("timeout_msec value %" PRIu64
514+
MONGOC_ERROR ("timeout_msec value %" PRId64
515515
" exceeds supported 32-bit range",
516516
tls->timeout_msec);
517517
return -1;

src/libmongoc/src/mongoc/mongoc-stream-tls-openssl-bio.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ mongoc_stream_tls_openssl_bio_read (BIO *b, char *buf, int len)
218218

219219
if (BSON_UNLIKELY (!bson_in_range_signed (int32_t, tls->timeout_msec))) {
220220
// CDRIVER-4589
221-
MONGOC_ERROR ("timeout_msec value %" PRIu64
221+
MONGOC_ERROR ("timeout_msec value %" PRId64
222222
" exceeds supported 32-bit range",
223223
tls->timeout_msec);
224224
return -1;
@@ -289,7 +289,7 @@ mongoc_stream_tls_openssl_bio_write (BIO *b, const char *buf, int len)
289289

290290
if (BSON_UNLIKELY (!bson_in_range_signed (int32_t, tls->timeout_msec))) {
291291
// CDRIVER-4589
292-
MONGOC_ERROR ("timeout_msec value %" PRIu64
292+
MONGOC_ERROR ("timeout_msec value %" PRId64
293293
" exceeds supported 32-bit range",
294294
tls->timeout_msec);
295295
RETURN (-1);

src/libmongoc/src/mongoc/mongoc-stream.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ mongoc_stream_writev (mongoc_stream_t *stream,
157157

158158
BSON_ASSERT (stream->writev);
159159

160+
// CDRIVER-4781: for backward compatibility.
160161
if (timeout_msec < 0) {
161162
timeout_msec = MONGOC_DEFAULT_TIMEOUT_MSEC;
162163
}
@@ -441,7 +442,7 @@ _mongoc_stream_writev_full (mongoc_stream_t *stream,
441442
bson_set_error (error,
442443
MONGOC_ERROR_STREAM,
443444
MONGOC_ERROR_STREAM_SOCKET,
444-
"timeout_msec value %" PRIu64
445+
"timeout_msec value %" PRId64
445446
" exceeds supported 32-bit range",
446447
timeout_msec);
447448
RETURN (false);

src/libmongoc/tests/test-mongoc-cluster.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,32 @@ test_cluster_time_insert_pooled (void)
752752
}
753753

754754

755+
static void
756+
test_cluster_command_timeout_negative (void)
757+
{
758+
bson_error_t error;
759+
760+
mongoc_uri_t *const uri = test_framework_get_uri ();
761+
762+
// CDRIVER-4781: libmongoc historically supports negative values as
763+
// fallback to a "default" value for timeouts.
764+
mongoc_uri_set_option_as_int32 (uri, MONGOC_URI_SOCKETTIMEOUTMS, -1);
765+
766+
mongoc_client_t *const client =
767+
test_framework_client_new_from_uri (uri, NULL);
768+
test_framework_set_ssl_opts (client);
769+
770+
// There should not be an error when validating sockettimeoutms.
771+
ASSERT_OR_PRINT (
772+
mongoc_client_command_simple (
773+
client, "admin", tmp_bson ("{'ping': 1}"), NULL, NULL, &error),
774+
error);
775+
776+
mongoc_client_destroy (client);
777+
mongoc_uri_destroy (uri);
778+
}
779+
780+
755781
static void
756782
replies_with_cluster_time (request_t *request,
757783
int t,
@@ -1924,6 +1950,9 @@ test_cluster_install (TestSuite *suite)
19241950
TestSuite_AddLive (suite,
19251951
"/Cluster/cluster_time/insert/pooled",
19261952
test_cluster_time_insert_pooled);
1953+
TestSuite_AddLive (suite,
1954+
"/Cluster/command/timeout/negative",
1955+
test_cluster_command_timeout_negative);
19271956
TestSuite_AddMockServerTest (suite,
19281957
"/Cluster/cluster_time/comparison/single",
19291958
test_cluster_time_comparison_single,

src/libmongoc/tests/test-mongoc-stream.c

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,126 @@ test_stream_writev_full (void)
164164
mongoc_stream_destroy (success_stream);
165165
}
166166

167+
typedef struct {
168+
mongoc_stream_t vtable;
169+
int32_t timeout_msec;
170+
bool is_set;
171+
} writev_timeout_stream_t;
172+
173+
static void
174+
_writev_timeout_stream_destroy (mongoc_stream_t *stream)
175+
{
176+
bson_free (stream);
177+
}
178+
179+
static ssize_t
180+
_writev_timeout_stream_writev (mongoc_stream_t *stream_param,
181+
mongoc_iovec_t *iov,
182+
size_t iovcnt,
183+
int32_t timeout_msec)
184+
{
185+
BSON_UNUSED (iov);
186+
BSON_UNUSED (iovcnt);
187+
188+
writev_timeout_stream_t *const stream =
189+
(writev_timeout_stream_t *) stream_param;
190+
191+
stream->is_set = true;
192+
stream->timeout_msec = timeout_msec;
193+
194+
return 0;
195+
}
196+
197+
static writev_timeout_stream_t *
198+
_writev_timeout_stream_new (void)
199+
{
200+
writev_timeout_stream_t *const stream =
201+
bson_malloc (sizeof (writev_timeout_stream_t));
202+
203+
*stream = (writev_timeout_stream_t){
204+
.vtable =
205+
{
206+
.type = 999, // For testing purposes.
207+
.destroy = _writev_timeout_stream_destroy,
208+
.writev = _writev_timeout_stream_writev,
209+
},
210+
.is_set = false,
211+
.timeout_msec = 0,
212+
};
213+
214+
return stream;
215+
}
216+
217+
static void
218+
test_stream_writev_timeout (void)
219+
{
220+
bson_error_t error;
221+
222+
uint8_t data[1] = {0};
223+
mongoc_iovec_t iov = {.iov_base = data, .iov_len = 1u};
224+
225+
// A positive timeout value should be forwarded as-is to the writev function.
226+
{
227+
writev_timeout_stream_t *const stream = _writev_timeout_stream_new ();
228+
229+
ssize_t const res =
230+
_mongoc_stream_writev_full ((mongoc_stream_t *) stream,
231+
&iov,
232+
1u,
233+
MONGOC_DEFAULT_SOCKETTIMEOUTMS,
234+
&error);
235+
ASSERT_CMPSSIZE_T (res, ==, 0);
236+
ASSERT_WITH_MSG (
237+
stream->is_set,
238+
"expected _writev_timeout_stream_writev() to be invoked");
239+
ASSERT_CMPINT32 (
240+
stream->timeout_msec, ==, MONGOC_DEFAULT_SOCKETTIMEOUTMS);
241+
242+
mongoc_stream_destroy ((mongoc_stream_t *) stream);
243+
}
244+
245+
// A timeout value of 0 should be forwarded as-is to the writev function.
246+
{
247+
writev_timeout_stream_t *const stream = _writev_timeout_stream_new ();
248+
249+
ssize_t const res = _mongoc_stream_writev_full (
250+
(mongoc_stream_t *) stream, &iov, 1u, 0, &error);
251+
ASSERT_CMPSSIZE_T (res, ==, 0);
252+
ASSERT_WITH_MSG (
253+
stream->is_set,
254+
"expected _writev_timeout_stream_writev() to be invoked");
255+
ASSERT_CMPINT32 (stream->timeout_msec, ==, 0);
256+
257+
mongoc_stream_destroy ((mongoc_stream_t *) stream);
258+
}
259+
260+
// CDRIVER-4781: a negative timeout value will fallback to an unspecified
261+
// default value. The writev function should receive the unspecified default
262+
// timeout value rather than the negative timeout value.
263+
{
264+
// See: MONGOC_DEFAULT_TIMEOUT_MSEC in mongoc-stream.c.
265+
const int32_t default_timeout_msec = 60 * 60 * 1000;
266+
267+
writev_timeout_stream_t *const stream = _writev_timeout_stream_new ();
268+
269+
ssize_t const res = _mongoc_stream_writev_full (
270+
(mongoc_stream_t *) stream, &iov, 1u, -1, &error);
271+
ASSERT_CMPSSIZE_T (res, ==, 0);
272+
ASSERT_WITH_MSG (
273+
stream->is_set,
274+
"expected _writev_timeout_stream_writev() to be invoked");
275+
ASSERT_CMPINT32 (stream->timeout_msec, ==, default_timeout_msec);
276+
277+
mongoc_stream_destroy ((mongoc_stream_t *) stream);
278+
}
279+
}
280+
167281

168282
void
169283
test_stream_install (TestSuite *suite)
170284
{
171285
TestSuite_Add (suite, "/Stream/buffered/basic", test_buffered_basic);
172286
TestSuite_Add (suite, "/Stream/buffered/oversized", test_buffered_oversized);
173-
TestSuite_Add (suite, "/Stream/writev_full", test_stream_writev_full);
287+
TestSuite_Add (suite, "/Stream/writev/full", test_stream_writev_full);
288+
TestSuite_Add (suite, "/Stream/writev/timeout", test_stream_writev_timeout);
174289
}

0 commit comments

Comments
 (0)