Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/mcp/mcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum {
#endif
#include <yyjson/yyjson.h>
#include <stdint.h> // int64_t
#include <limits.h> // INT_MIN, INT_MAX
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -1032,7 +1033,13 @@ int cbm_mcp_get_int_arg(const char *args_json, const char *key, int default_val)
yyjson_val *val = yyjson_obj_get(root, key);
int result = default_val;
if (val && yyjson_is_int(val)) {
result = yyjson_get_int(val);
/* Read the JSON integer as 64-bit and fall back to the default when it
* does not fit an int. Casting first wraps silently: limit 4294967297
* became 1, and 2147483648 became negative. */
int64_t wide = yyjson_get_sint(val);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Values just below 2^64 still slip through. yyjson stores positive integers as unsigned 64-bit, and yyjson_get_sint reads the same bits as signed (vendored/yyjson/yyjson.h:5068-5070, 5369-5371). So {"limit": 18446744073709551615} gives wide == -1, which passes this check and returns -1. Every value from 2^64 - 2^31 to 2^64 - 1 comes back as a negative int, the same outcome #128 describes.

Suggested fix: check the subtype first.

if (yyjson_is_uint(val)) {
    uint64_t u = yyjson_get_uint(val);
    if (u <= (uint64_t)INT_MAX) {
        result = (int)u;
    }
} else {
    int64_t wide = yyjson_get_sint(val);
    if (wide >= (int64_t)INT_MIN && wide <= (int64_t)INT_MAX) {
        result = (int)wide;
    }
}

Please also add 18446744073709551615 to mcp_get_int_arg_out_of_range.

if (wide >= (int64_t)INT_MIN && wide <= (int64_t)INT_MAX) {
result = (int)wide;
}
}
yyjson_doc_free(doc);
return result;
Expand Down
18 changes: 18 additions & 0 deletions tests/test_mcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5679,6 +5679,23 @@ TEST(mcp_get_int_arg_bool_value) {
PASS();
}

TEST(mcp_get_int_arg_out_of_range) {
/* A JSON integer wider than int must fall back to the default instead of
* wrapping: 4294967297 used to become 1 and 2147483648 became negative. */
int val = cbm_mcp_get_int_arg("{\"limit\":4294967297}", "limit", 7);
ASSERT_EQ(val, 7);
val = cbm_mcp_get_int_arg("{\"limit\":2147483648}", "limit", 7);
ASSERT_EQ(val, 7);
val = cbm_mcp_get_int_arg("{\"limit\":-2147483649}", "limit", 7);
ASSERT_EQ(val, 7);
/* In-range boundaries are still accepted. */
val = cbm_mcp_get_int_arg("{\"limit\":2147483647}", "limit", 7);
ASSERT_EQ(val, 2147483647);
val = cbm_mcp_get_int_arg("{\"limit\":-2147483648}", "limit", 7);
ASSERT_EQ(val, -2147483648);
PASS();
}

TEST(mcp_get_bool_arg_empty_json) {
bool val = cbm_mcp_get_bool_arg("", "key");
ASSERT_FALSE(val);
Expand Down Expand Up @@ -7130,6 +7147,7 @@ SUITE(mcp) {
RUN_TEST(mcp_get_int_arg_empty_json);
RUN_TEST(mcp_get_int_arg_string_value);
RUN_TEST(mcp_get_int_arg_bool_value);
RUN_TEST(mcp_get_int_arg_out_of_range);
RUN_TEST(mcp_get_bool_arg_empty_json);
RUN_TEST(mcp_get_bool_arg_int_value);
RUN_TEST(mcp_get_tool_name_empty_json);
Expand Down