diff --git a/src/box/sql/util.c b/src/box/sql/util.c index f908e9cedd1d4b442ab7019934000bdc407711b3..c556b9815e71709782652c7228eb0c869f4dfd74 100644 --- a/src/box/sql/util.c +++ b/src/box/sql/util.c @@ -467,14 +467,21 @@ sql_atoi64(const char *z, int64_t *val, bool *is_neg, int length) if (*z == '-') *is_neg = true; + /* + * BLOB data may not end with '\0'. Because of this, the + * strtoll() and strtoull() functions may return an + * incorrect result. To fix this, let's copy the value for + * decoding into static memory and add '\0' to it. + */ + if (length > SMALL_STATIC_SIZE - 1) + return -1; + const char *str_value = tt_cstr(z, length); char *end = NULL; errno = 0; - if (*z == '-') { - *is_neg = true; - *val = strtoll(z, &end, 10); + if (*is_neg) { + *val = strtoll(str_value, &end, 10); } else { - *is_neg = false; - uint64_t u_val = strtoull(z, &end, 10); + uint64_t u_val = strtoull(str_value, &end, 10); *val = u_val; } /* Overflow and underflow errors. */ diff --git a/test/sql-tap/gh-4766-wrong-cast-from-blob-to-int.test.lua b/test/sql-tap/gh-4766-wrong-cast-from-blob-to-int.test.lua new file mode 100755 index 0000000000000000000000000000000000000000..929870d27789adbdef07697400c3e34ceb2f9b7e --- /dev/null +++ b/test/sql-tap/gh-4766-wrong-cast-from-blob-to-int.test.lua @@ -0,0 +1,45 @@ +#!/usr/bin/env tarantool +test = require("sqltester") +test:plan(3) + +-- +-- Make sure that a blob as part of a tuple can be cast to NUMBER, +-- INTEGER and UNSIGNED. Prior to this patch, an error could +-- appear due to the absence of '\0' at the end of the BLOB. +-- +test:do_execsql_test( + "gh-4766-1", + [[ + CREATE TABLE t1 (a VARBINARY PRIMARY KEY); + INSERT INTO t1 VALUES (X'33'), (X'372020202020'); + SELECT a, CAST(a AS NUMBER), CAST(a AS INTEGER), CAST(a AS UNSIGNED) FROM t1; + ]], { + '3', 3, 3, 3, '7 ', 7, 7, 7 + }) + +-- +-- Make sure that BLOB longer than 12287 bytes cannot be cast to +-- INTEGER. +-- +long_str = string.rep('0', 12284) +test:do_execsql_test( + "gh-4766-2", + "SELECT CAST('" .. long_str .. "123'" .. " AS INTEGER);", { + 123 + }) + + +test:do_catchsql_test( + "gh-4766-3", + "SELECT CAST('" .. long_str .. "1234'" .. " AS INTEGER);", { + 1, "Type mismatch: can not convert 000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "0000000000000000000000000000000000000000000000000000000000000000000" .. + "000000000000000000000000000000000000000000000" + }) + +test:finish_test()