mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix out-of-bound reads with ascii() for invalid multibyte characters
This commit addresses two defects in this SQL function, the code assuming that: - The user-supplied string was long enough to contain a character of the length implied by the first byte. It is possible to provide in input data that was able to disclose a few bytes of server memory, allowing out-of-bound reads. - Specific bytes had values within the expected range, using a set of assertions to validate them. The assertions could be triggered on invalid input. These are replaced by tests and error reports. Reported-by: Hcamael <baiyjrh@gmail.com> Author: Michael Paquier <michael@paquier.xyz> Reviewed-by: Robert Haas <robertmhaas@gmail.com> Backpatch-through: 14 Security: CVE-2026-18024
This commit is contained in:
committed by
Noah Misch
parent
46fa1f6f37
commit
849da82105
@@ -936,8 +936,10 @@ ascii(PG_FUNCTION_ARGS)
|
||||
text *string = PG_GETARG_TEXT_PP(0);
|
||||
int encoding = GetDatabaseEncoding();
|
||||
unsigned char *data;
|
||||
int len;
|
||||
|
||||
if (VARSIZE_ANY_EXHDR(string) <= 0)
|
||||
len = VARSIZE_ANY_EXHDR(string);
|
||||
if (len <= 0)
|
||||
PG_RETURN_INT32(0);
|
||||
|
||||
data = (unsigned char *) VARDATA_ANY(string);
|
||||
@@ -960,18 +962,31 @@ ascii(PG_FUNCTION_ARGS)
|
||||
result = *data & 0x0F;
|
||||
tbytes = 2;
|
||||
}
|
||||
else
|
||||
else if (*data > 0xC0)
|
||||
{
|
||||
Assert(*data > 0xC0);
|
||||
result = *data & 0x1f;
|
||||
tbytes = 1;
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
||||
errmsg("invalid byte sequence for encoding \"%s\"",
|
||||
GetDatabaseEncodingName())));
|
||||
|
||||
Assert(tbytes > 0);
|
||||
/* All continuation bytes are present in the input */
|
||||
if (tbytes >= len)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
||||
errmsg("invalid byte sequence for encoding \"%s\"",
|
||||
GetDatabaseEncodingName())));
|
||||
|
||||
for (i = 1; i <= tbytes; i++)
|
||||
{
|
||||
Assert((data[i] & 0xC0) == 0x80);
|
||||
if (unlikely((data[i] & 0xC0) != 0x80))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
|
||||
errmsg("invalid byte sequence for encoding \"%s\"",
|
||||
GetDatabaseEncodingName())));
|
||||
result = (result << 6) + (data[i] & 0x3f);
|
||||
}
|
||||
|
||||
|
||||
@@ -416,6 +416,25 @@ SELECT SUBSTRING(c FROM 3000 FOR 1) FROM toast_4b_utf8;
|
||||
🚀
|
||||
(1 row)
|
||||
|
||||
-- ascii() and multibyte characters
|
||||
SELECT ascii(test_bytea_to_text('\xc3'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
SELECT ascii(test_bytea_to_text('\xe2'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
SELECT ascii(test_bytea_to_text('\xe282'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
SELECT ascii(test_bytea_to_text('\xf0'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
SELECT ascii(test_bytea_to_text('\xf09f'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
SELECT ascii(test_bytea_to_text('\xf09f98'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
-- invalid continuation byte
|
||||
SELECT ascii(test_bytea_to_text('\xc3ff'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
-- invalid leading byte
|
||||
SELECT ascii(test_bytea_to_text('\x80'));
|
||||
ERROR: invalid byte sequence for encoding "UTF8"
|
||||
DROP TABLE encoding_tests;
|
||||
DROP TABLE toast_4b_utf8;
|
||||
DROP FUNCTION test_encoding;
|
||||
|
||||
@@ -228,6 +228,18 @@ ALTER TABLE toast_3b_utf8 RENAME TO toast_4b_utf8;
|
||||
UPDATE toast_4b_utf8 SET c = repeat(U&'\+01F680', 3000);
|
||||
SELECT SUBSTRING(c FROM 3000 FOR 1) FROM toast_4b_utf8;
|
||||
|
||||
-- ascii() and multibyte characters
|
||||
SELECT ascii(test_bytea_to_text('\xc3'));
|
||||
SELECT ascii(test_bytea_to_text('\xe2'));
|
||||
SELECT ascii(test_bytea_to_text('\xe282'));
|
||||
SELECT ascii(test_bytea_to_text('\xf0'));
|
||||
SELECT ascii(test_bytea_to_text('\xf09f'));
|
||||
SELECT ascii(test_bytea_to_text('\xf09f98'));
|
||||
-- invalid continuation byte
|
||||
SELECT ascii(test_bytea_to_text('\xc3ff'));
|
||||
-- invalid leading byte
|
||||
SELECT ascii(test_bytea_to_text('\x80'));
|
||||
|
||||
DROP TABLE encoding_tests;
|
||||
DROP TABLE toast_4b_utf8;
|
||||
DROP FUNCTION test_encoding;
|
||||
|
||||
Reference in New Issue
Block a user