From: movead.li@highgo.ca <movead.li@highgo.ca>
To: pgsql-hackers <pgsql-hackers@lists.postgresql.org>
Subject: A bug when use get_bit() function for a long bytea string
Date: Thu, 12 Mar 2020 11:51:38 +0800
Message-ID: <20200312115135445367128@highgo.ca> (raw)
Hello hackers,
I found an issue about get_bit() and set_bit() function,here it is:
################################
postgres=# select get_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0);
2020-03-12 10:05:23.296 CST [10549] ERROR: index 0 out of valid range, 0..-1
2020-03-12 10:05:23.296 CST [10549] STATEMENT: select get_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0);
ERROR: index 0 out of valid range, 0..-1
postgres=# select set_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0,1);
2020-03-12 10:05:27.959 CST [10549] ERROR: index 0 out of valid range, 0..-1
2020-03-12 10:05:27.959 CST [10549] STATEMENT: select set_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0,1);
ERROR: index 0 out of valid range, 0..-1
postgres=#
################################
PostgreSQL can handle bytea size nearby 1G, but now it reports an
error when 512M. And I research it and found it is byteaSetBit() and
byteaGetBit(), it uses an 'int32 len' to hold bit numbers for the long
bytea data, and obvious 512M * 8bit is an overflow for an int32.
So I fix it and test ok, as below.
################################
postgres=# select get_bit(set_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0,1),0); get_bit --------- 1 (1 row) postgres=# select get_bit(set_bit(pg_read_binary_file('/home/movead/temp/file_seek/f_512M'), 0,0),0); get_bit --------- 0 (1 row) postgres=#
################################
And I do a check about if anything else related bytea has this issue, several codes have the same issue:
1. byteaout() When formatting bytea as an escape, the 'len' variable should be int64, or
it may use an overflowing number. 2. esc_enc_len() Same as above, the 'len' variable should be int64, and the return type
should change as int64. Due to esc_enc_len() has same call struct with pg_base64_enc_len() and hex_enc_len(), so I want to change the return value of the two function. And the opposite function esc_dec_len() seem nothing wrong. 3. binary_encode() and binary_decode() Here use an 'int32 resultlen' to accept an 'unsigned int' function return, which seem unfortable.
I fix all mentioned above, and patch attachments.
How do you think about that?
Highgo Software (Canada/China/Pakistan)
URL : www.highgo.ca
EMAIL: mailto:movead(dot)li(at)highgo(dot)ca
Attachments:
[application/octet-stream] long_bytea_string_bug_fix.patch (3.6K, ../20200312115135445367128@highgo.ca/3-long_bytea_string_bug_fix.patch)
download | inline diff:diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.cindex b8d9ec7e00..8ae2bd58a3 100644--- a/src/backend/utils/adt/encode.c+++ b/src/backend/utils/adt/encode.c@@ -20,7 +20,7 @@
struct pg_encoding
{
- unsigned (*encode_len) (const char *data, unsigned dlen);+ Size (*encode_len) (const char *data, unsigned dlen);
unsigned (*decode_len) (const char *data, unsigned dlen);
unsigned (*encode) (const char *data, unsigned dlen, char *res);
unsigned (*decode) (const char *data, unsigned dlen, char *res);
@@ -40,8 +40,8 @@ binary_encode(PG_FUNCTION_ARGS)
text *result;
char *namebuf;
int datalen,
- resultlen,
res;
+ Size resultlen;
const struct pg_encoding *enc;
datalen = VARSIZE_ANY_EXHDR(data);
@@ -60,7 +60,7 @@ binary_encode(PG_FUNCTION_ARGS)
res = enc->encode(VARDATA_ANY(data), datalen, VARDATA(result));
/* Make this FATAL 'cause we've trodden on memory ... */
- if (res > resultlen)+ if ((Size)res > resultlen)
elog(FATAL, "overflow - encode estimate too small");
SET_VARSIZE(result, VARHDRSZ + res);
@@ -76,8 +76,8 @@ binary_decode(PG_FUNCTION_ARGS)
bytea *result;
char *namebuf;
int datalen,
- resultlen,
res;
+ unsigned resultlen;
const struct pg_encoding *enc;
datalen = VARSIZE_ANY_EXHDR(data);
@@ -184,10 +184,10 @@ hex_decode(const char *src, unsigned len, char *dst)
return p - dst;
}
-static unsigned+static Size
hex_enc_len(const char *src, unsigned srclen)
{
- return srclen << 1;+ return (Size)(srclen << 1);
}
static unsigned
@@ -331,11 +331,11 @@ pg_base64_decode(const char *src, unsigned len, char *dst)
}
-static unsigned+static Size
pg_base64_enc_len(const char *src, unsigned srclen)
{
/* 3 bytes will be converted to 4, linefeed after 76 chars */
- return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);+ return (Size)((srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4));
}
static unsigned
@@ -448,11 +448,11 @@ esc_decode(const char *src, unsigned srclen, char *dst)
return len;
}
-static unsigned+static Size
esc_enc_len(const char *src, unsigned srclen)
{
const char *end = src + srclen;
- int len = 0;+ Size len = 0;
while (src < end)
{
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.cindex 907b5ab7b0..5fe7125629 100644--- a/src/backend/utils/adt/varlena.c+++ b/src/backend/utils/adt/varlena.c@@ -388,7 +388,7 @@ byteaout(PG_FUNCTION_ARGS)
{
/* Print traditional escaped format */
char *vp;
- int len;+ Size len;
int i;
len = 1; /* empty string has 1 char */
@@ -3458,15 +3458,15 @@ byteaGetBit(PG_FUNCTION_ARGS)
int32 n = PG_GETARG_INT32(1);
int byteNo,
bitNo;
- int len;+ Size len;
int byte;
- len = VARSIZE_ANY_EXHDR(v);+ len = (Size)(VARSIZE_ANY_EXHDR(v));- if (n < 0 || n >= len * 8)+ if (n < 0 || (Size)n >= len * 8)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("index %d out of valid range, 0..%d",+ errmsg("index %d out of valid range, 0..%ld",
n, len * 8 - 1)));
byteNo = n / 8;
@@ -3526,18 +3526,18 @@ byteaSetBit(PG_FUNCTION_ARGS)
bytea *res = PG_GETARG_BYTEA_P_COPY(0);
int32 n = PG_GETARG_INT32(1);
int32 newBit = PG_GETARG_INT32(2);
- int len;+ Size len;
int oldByte,
newByte;
int byteNo,
bitNo;
- len = VARSIZE(res) - VARHDRSZ;+ len = (Size)(VARSIZE(res) - VARHDRSZ);- if (n < 0 || n >= len * 8)+ if (n < 0 || (Size)n >= len * 8)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("index %d out of valid range, 0..%d",+ errmsg("index %d out of valid range, 0..%ld",
n, len * 8 - 1)));
byteNo = n / 8;
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: movead.li@highgo.ca, pgsql-hackers@lists.postgresql.org
Subject: Re: A bug when use get_bit() function for a long bytea string
In-Reply-To: <20200312115135445367128@highgo.ca>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox