1
0
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:
Michael Paquier
2026-08-10 06:38:13 -07:00
committed by Noah Misch
parent 29921259e8
commit 08e812c02a
3 changed files with 51 additions and 5 deletions

View File

@@ -952,8 +952,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);
@@ -976,18 +978,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);
}

View File

@@ -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;

View File

@@ -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;