This is an automated email from the ASF dual-hosted git repository.

reshke pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit d6749e29f455ef36cd244caad35242b496fa2c5c
Author: Noah Misch <[email protected]>
AuthorDate: Mon Feb 9 06:14:47 2026 -0800

    Require PGP-decrypted text to pass encoding validation.
    
    pgp_sym_decrypt() and pgp_pub_decrypt() will raise such errors, while
    bytea variants will not.  The existing "dat3" test decrypted to non-UTF8
    text, so switch that query to bytea.
    
    The long-term intent is for type "text" to always be valid in the
    database encoding.  pgcrypto has long been known as a source of
    exceptions to that intent, but a report about exploiting invalid values
    of type "text" brought this module to the forefront.  This particular
    exception is straightforward to fix, with reasonable effect on user
    queries.  Back-patch to v14 (all supported versions).
    
    Reported-by: Paul Gerste (as part of zeroday.cloud)
    Reported-by: Moritz Sanft (as part of zeroday.cloud)
    Author: shihao zhong <[email protected]>
    Reviewed-by: cary huang <[email protected]>
    Discussion: 
https://postgr.es/m/CAGRkXqRZyo0gLxPJqUsDqtWYBbgM14betsHiLRPj9mo2=z9...@mail.gmail.com
    Backpatch-through: 14
    Security: CVE-2026-2006
---
 contrib/pgcrypto/expected/pgp-decrypt.out   | 23 ++++++++++++++++++++++-
 contrib/pgcrypto/expected/pgp-decrypt_1.out | 23 ++++++++++++++++++++++-
 contrib/pgcrypto/pgp-pgsql.c                |  2 ++
 contrib/pgcrypto/sql/pgp-decrypt.sql        | 22 +++++++++++++++++++++-
 4 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/contrib/pgcrypto/expected/pgp-decrypt.out 
b/contrib/pgcrypto/expected/pgp-decrypt.out
index eb049ba9d44..1db89e8c00a 100644
--- a/contrib/pgcrypto/expected/pgp-decrypt.out
+++ b/contrib/pgcrypto/expected/pgp-decrypt.out
@@ -315,7 +315,7 @@ SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
  \xda39a3ee5e6b4b0d3255bfef95601890afd80709
 (1 row)
 
-select digest(pgp_sym_decrypt(dearmor('
+select digest(pgp_sym_decrypt_bytea(dearmor('
 -----BEGIN PGP MESSAGE-----
 Comment: dat3.aes.sha1.mdc.s2k3.z0
 
@@ -387,6 +387,27 @@ ERROR:  Wrong key or corrupt data
 select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
 NOTICE:  dbg: parse_literal_data: data type=b
 ERROR:  Not text data
+-- NUL byte in text decrypt.  Ciphertext source:
+-- printf 'a\x00\xc' | gpg --homedir /nonexistent --textmode \
+--      --personal-cipher-preferences aes --no-emit-version --batch \
+--      --symmetric --passphrase key --armor
+do $$
+begin
+  perform pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCLd9OvySmZNZg0jgBe7vGTmnje5HGXI+zsIQ99WPZu4Zs/P6pQcZ+HZ4n
+SZQHOfE8tagjB6Rqow82QpSBiOfWn4qjhQ==
+=c2cz
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+exception when others then
+  raise '%',
+    regexp_replace(sqlerrm, 'encoding "[^"]*"', 'encoding [REDACTED]');
+end
+$$;
+ERROR:  invalid byte sequence for encoding [REDACTED]: 0x00
+CONTEXT:  PL/pgSQL function inline_code_block line 12 at RAISE
 -- Decryption with a certain incorrect key yields an apparent BZip2-compressed
 -- plaintext.  Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
 -- until the random prefix gave rise to that property.
diff --git a/contrib/pgcrypto/expected/pgp-decrypt_1.out 
b/contrib/pgcrypto/expected/pgp-decrypt_1.out
index 80a4c48613d..d214e0bc0e0 100644
--- a/contrib/pgcrypto/expected/pgp-decrypt_1.out
+++ b/contrib/pgcrypto/expected/pgp-decrypt_1.out
@@ -311,7 +311,7 @@ SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
  \xda39a3ee5e6b4b0d3255bfef95601890afd80709
 (1 row)
 
-select digest(pgp_sym_decrypt(dearmor('
+select digest(pgp_sym_decrypt_bytea(dearmor('
 -----BEGIN PGP MESSAGE-----
 Comment: dat3.aes.sha1.mdc.s2k3.z0
 
@@ -383,6 +383,27 @@ ERROR:  Wrong key or corrupt data
 select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
 NOTICE:  dbg: parse_literal_data: data type=b
 ERROR:  Not text data
+-- NUL byte in text decrypt.  Ciphertext source:
+-- printf 'a\x00\xc' | gpg --homedir /nonexistent --textmode \
+--      --personal-cipher-preferences aes --no-emit-version --batch \
+--      --symmetric --passphrase key --armor
+do $$
+begin
+  perform pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCLd9OvySmZNZg0jgBe7vGTmnje5HGXI+zsIQ99WPZu4Zs/P6pQcZ+HZ4n
+SZQHOfE8tagjB6Rqow82QpSBiOfWn4qjhQ==
+=c2cz
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+exception when others then
+  raise '%',
+    regexp_replace(sqlerrm, 'encoding "[^"]*"', 'encoding [REDACTED]');
+end
+$$;
+ERROR:  invalid byte sequence for encoding [REDACTED]: 0x00
+CONTEXT:  PL/pgSQL function inline_code_block line 12 at RAISE
 -- Decryption with a certain incorrect key yields an apparent BZip2-compressed
 -- plaintext.  Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
 -- until the random prefix gave rise to that property.
diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c
index d9b15b07b0f..838a7c381fc 100644
--- a/contrib/pgcrypto/pgp-pgsql.c
+++ b/contrib/pgcrypto/pgp-pgsql.c
@@ -631,6 +631,7 @@ pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
                arg = PG_GETARG_BYTEA_PP(2);
 
        res = decrypt_internal(0, 1, data, key, NULL, arg);
+       pg_verifymbstr(VARDATA_ANY(res), VARSIZE_ANY_EXHDR(res), false);
 
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
@@ -732,6 +733,7 @@ pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
                arg = PG_GETARG_BYTEA_PP(3);
 
        res = decrypt_internal(1, 1, data, key, psw, arg);
+       pg_verifymbstr(VARDATA_ANY(res), VARSIZE_ANY_EXHDR(res), false);
 
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
diff --git a/contrib/pgcrypto/sql/pgp-decrypt.sql 
b/contrib/pgcrypto/sql/pgp-decrypt.sql
index 49a0267bbcb..2fe498f2f02 100644
--- a/contrib/pgcrypto/sql/pgp-decrypt.sql
+++ b/contrib/pgcrypto/sql/pgp-decrypt.sql
@@ -228,7 +228,7 @@ SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
 -----END PGP MESSAGE-----
 '), '0123456789abcdefghij'), 'sha1');
 
-select digest(pgp_sym_decrypt(dearmor('
+select digest(pgp_sym_decrypt_bytea(dearmor('
 -----BEGIN PGP MESSAGE-----
 Comment: dat3.aes.sha1.mdc.s2k3.z0
 
@@ -282,6 +282,26 @@ VsxxqLSPzNLAeIspJk5G
 -- Routine text/binary mismatch.
 select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
 
+-- NUL byte in text decrypt.  Ciphertext source:
+-- printf 'a\x00\xc' | gpg --homedir /nonexistent --textmode \
+--      --personal-cipher-preferences aes --no-emit-version --batch \
+--      --symmetric --passphrase key --armor
+do $$
+begin
+  perform pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCLd9OvySmZNZg0jgBe7vGTmnje5HGXI+zsIQ99WPZu4Zs/P6pQcZ+HZ4n
+SZQHOfE8tagjB6Rqow82QpSBiOfWn4qjhQ==
+=c2cz
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+exception when others then
+  raise '%',
+    regexp_replace(sqlerrm, 'encoding "[^"]*"', 'encoding [REDACTED]');
+end
+$$;
+
 -- Decryption with a certain incorrect key yields an apparent BZip2-compressed
 -- plaintext.  Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
 -- until the random prefix gave rise to that property.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to