Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9e10898

Browse files
committed
Fix corner case with PGP decompression in pgcrypto
A compressed stream may end with an empty packet, and PGP decompression finished before reading this empty packet in the remaining stream. This caused a failure in pgcrypto, handling this case as corrupted data. This commit makes sure to consume such extra data, avoiding a failure when decompression the entire stream. This corner case was reproducible with a data length of 16kB, and existed since its introduction in e94dd6a. A cheap regression test is added to cover this case. Thanks to Jeff Janes for the extra investigation. Reported-by: Frank Gagnepain Author: Kyotaro Horiguchi, Michael Paquier Discussion: https://postgr.es/m/16476-692ef7b84e5fb893@postgresql.org Backpatch-through: 9.5
1 parent a507387 commit 9e10898

File tree

3 files changed

+62
-11
lines changed

3 files changed

+62
-11
lines changed

contrib/pgcrypto/expected/pgp-compression.out

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,33 @@ select pgp_sym_decrypt(
4848
Secret message
4949
(1 row)
5050

51+
-- check corner case involving an input string of 16kB, as per bug #16476.
52+
SELECT setseed(0);
53+
setseed
54+
---------
55+
56+
(1 row)
57+
58+
WITH random_string AS
59+
(
60+
-- This generates a random string of 16366 bytes. This is chosen
61+
-- as random so that it does not get compressed, and the decompression
62+
-- would work on a string with the same length as the origin, making the
63+
-- test behavior more predictible. lpad() ensures that the generated
64+
-- hexadecimal value is completed by extra zero characters if random()
65+
-- has generated a value strictly lower than 16.
66+
SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex'), '') as bytes
67+
FROM generate_series(0, 16365)
68+
)
69+
SELECT bytes =
70+
pgp_sym_decrypt_bytea(
71+
pgp_sym_encrypt_bytea(bytes, 'key',
72+
'compress-algo=1,compress-level=1'),
73+
'key', 'expect-compress-algo=1')
74+
AS is_same
75+
FROM random_string;
76+
is_same
77+
---------
78+
t
79+
(1 row)
80+

contrib/pgcrypto/pgp-compress.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,17 @@ decompress_read(void *priv, PullFilter *src, int len,
243243
struct DecomprData *dec = priv;
244244

245245
restart:
246+
if (dec->stream.avail_in == 0)
247+
{
248+
uint8 *tmp;
249+
250+
res = pullf_read(src, 8192, &tmp);
251+
if (res < 0)
252+
return res;
253+
dec->stream.next_in = tmp;
254+
dec->stream.avail_in = res;
255+
}
256+
246257
if (dec->buf_data > 0)
247258
{
248259
if (len > dec->buf_data)
@@ -256,17 +267,6 @@ decompress_read(void *priv, PullFilter *src, int len,
256267
if (dec->eof)
257268
return 0;
258269

259-
if (dec->stream.avail_in == 0)
260-
{
261-
uint8 *tmp;
262-
263-
res = pullf_read(src, 8192, &tmp);
264-
if (res < 0)
265-
return res;
266-
dec->stream.next_in = tmp;
267-
dec->stream.avail_in = res;
268-
}
269-
270270
dec->stream.next_out = dec->buf;
271271
dec->stream.avail_out = dec->buf_len;
272272
dec->pos = dec->buf;

contrib/pgcrypto/sql/pgp-compression.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,24 @@ select pgp_sym_decrypt(
2828
pgp_sym_encrypt('Secret message', 'key',
2929
'compress-algo=2, compress-level=0'),
3030
'key', 'expect-compress-algo=0');
31+
32+
-- check corner case involving an input string of 16kB, as per bug #16476.
33+
SELECT setseed(0);
34+
WITH random_string AS
35+
(
36+
-- This generates a random string of 16366 bytes. This is chosen
37+
-- as random so that it does not get compressed, and the decompression
38+
-- would work on a string with the same length as the origin, making the
39+
-- test behavior more predictible. lpad() ensures that the generated
40+
-- hexadecimal value is completed by extra zero characters if random()
41+
-- has generated a value strictly lower than 16.
42+
SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex'), '') as bytes
43+
FROM generate_series(0, 16365)
44+
)
45+
SELECT bytes =
46+
pgp_sym_decrypt_bytea(
47+
pgp_sym_encrypt_bytea(bytes, 'key',
48+
'compress-algo=1,compress-level=1'),
49+
'key', 'expect-compress-algo=1')
50+
AS is_same
51+
FROM random_string;

0 commit comments

Comments
 (0)