Skip to content

Commit 65aad0f

Browse files
DeusDataahundt
andcommitted
fix(mcp): an integer argument outside int range takes the default
Distilled from #1245 by Andrew Hundt (d030c8f). cbm_mcp_get_int_arg read integer arguments with yyjson_is_int and yyjson_get_int, and the int cast inside get_int truncates. A client sending {"limit": 4294967297} -- 2^32 + 1 -- got back 1: the low word of the value, silently, as a bound. The same read serves limit and offset on the query tools, so an oversized page request became a page of one. The argument is now read as a 64-bit signed or unsigned integer and only accepted if it fits in int; anything outside the range takes the caller's default, exactly as a non-integer does. In-range values and non-integer JSON behave as before. One difference from the upstream hunk: the unsigned comparison casts INT_MAX explicitly rather than relying on the implicit conversion. RED before the fix: mcp_get_int_arg FAIL tests/test_mcp.c:1777: val == 1, expected 17 == 17 The test now covers 2^32 + 1, its negative, INT_MAX + 1 and INT64_MIN each returning a distinct default, and INT_MAX, INT_MIN and -7 passing through unchanged. GREEN after: mcp 318 passed, 4 skipped (the Windows-only skips); mcp cli daemon_application 692 passed, 0 failed. Co-authored-by: Andrew Hundt <ATHundt@gmail.com> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
1 parent 66d9c2b commit 65aad0f

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

‎src/mcp/mcp.c‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,8 +1584,19 @@ int cbm_mcp_get_int_arg(const char *args_json, const char *key, int default_val)
15841584
yyjson_val *root = yyjson_doc_get_root(doc);
15851585
yyjson_val *val = yyjson_obj_get(root, key);
15861586
int result = default_val;
1587-
if (val && yyjson_is_int(val)) {
1588-
result = yyjson_get_int(val);
1587+
/* yyjson_get_int truncates 64-bit integers to int (2^32 + 1 became 1);
1588+
* read the full width and treat anything outside int range like a
1589+
* non-integer, i.e. fall back to the caller's default. */
1590+
if (val && yyjson_is_sint(val)) {
1591+
int64_t parsed = yyjson_get_sint(val);
1592+
if (parsed >= INT_MIN && parsed <= INT_MAX) {
1593+
result = (int)parsed;
1594+
}
1595+
} else if (val && yyjson_is_uint(val)) {
1596+
uint64_t parsed = yyjson_get_uint(val);
1597+
if (parsed <= (uint64_t)INT_MAX) {
1598+
result = (int)parsed;
1599+
}
15891600
}
15901601
yyjson_doc_free(doc);
15911602
return result;

‎tests/test_mcp.c‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,23 @@ TEST(mcp_get_int_arg) {
17711771
ASSERT_EQ(val, 5);
17721772
val = cbm_mcp_get_int_arg(args, "missing", 42);
17731773
ASSERT_EQ(val, 42);
1774+
/* Out-of-int-range integers return the default instead of truncating:
1775+
* 2^32 + 1 used to read back as 1 through yyjson_get_int's int cast. */
1776+
val = cbm_mcp_get_int_arg("{\"limit\":4294967297}", "limit", 17);
1777+
ASSERT_EQ(val, 17);
1778+
val = cbm_mcp_get_int_arg("{\"limit\":-4294967297}", "limit", 19);
1779+
ASSERT_EQ(val, 19);
1780+
val = cbm_mcp_get_int_arg("{\"limit\":2147483648}", "limit", 23);
1781+
ASSERT_EQ(val, 23);
1782+
val = cbm_mcp_get_int_arg("{\"limit\":-9223372036854775808}", "limit", 29);
1783+
ASSERT_EQ(val, 29);
1784+
/* Boundary values and a negative in-range value still pass through. */
1785+
val = cbm_mcp_get_int_arg("{\"limit\":2147483647}", "limit", 0);
1786+
ASSERT_EQ(val, 2147483647);
1787+
val = cbm_mcp_get_int_arg("{\"limit\":-2147483648}", "limit", 0);
1788+
ASSERT_EQ(val, -2147483648);
1789+
val = cbm_mcp_get_int_arg("{\"limit\":-7}", "limit", 0);
1790+
ASSERT_EQ(val, -7);
17741791
PASS();
17751792
}
17761793

0 commit comments

Comments
 (0)