Skip to content

Commit bba2e66

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 171633f commit bba2e66

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
@@ -244,6 +244,17 @@ decompress_read(void *priv, PullFilter *src, int len,
244244
struct DecomprData *dec = priv;
245245

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

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