Greetings, everyone!

While working on an extension I've found an error in how length of encoded base64 string is calulated;

This error is present in 3 files across all supported versions:

/src/common/base64.c, function pg_b64_enc_len;
/src/backend/utils/adt/encode.c, function pg_base64_enc_len;
/contrib/pgcrypto/pgp-armor.c, function pg_base64_enc_len (copied from encode.c).

In all three cases the length is calculated as follows:

(srclen + 2) * 4 / 3; (plus linefeed in latter two cases)

There's also a comment /* 3 bytes will be converted to 4 */

This formula is wrong. Let's calculate encoded length for different starting lengths:

starting length 2: (2 + 2) * 4 / 3 = 5,
starting length 3: (3 + 2) * 4 / 3 = 6,
starting length 4: (4 + 2) * 4 / 3 = 8,
starting length 6: (6 + 2) * 4 / 3 = 10,
starting length 10: (10 + 2) * 4 / 3 = 16,

when it should be 4, 4, 8, 8, 16.

So the suggestion is to change the formula to a right one: (srclen + 2) / 3 * 4;

The patch is attached.

Oleg Tselebrovskiy, Postgres Pro
diff --git a/contrib/pgcrypto/pgp-armor.c b/contrib/pgcrypto/pgp-armor.c
index 679779a6ac..9128756647 100644
--- a/contrib/pgcrypto/pgp-armor.c
+++ b/contrib/pgcrypto/pgp-armor.c
@@ -165,7 +165,7 @@ pg_base64_enc_len(unsigned srclen)
 	/*
 	 * 3 bytes will be converted to 4, linefeed after 76 chars
 	 */
-	return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
+	return (srclen + 2) / 3 * 4 + srclen / (76 * 3 / 4);
 }
 
 static unsigned
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index b92191de81..e5ac3ad23d 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -385,7 +385,7 @@ static uint64
 pg_base64_enc_len(const char *src, size_t srclen)
 {
 	/* 3 bytes will be converted to 4, linefeed after 76 chars */
-	return ((uint64) srclen + 2) * 4 / 3 + (uint64) srclen / (76 * 3 / 4);
+	return ((uint64) srclen + 2) / 3 * 4 + (uint64) srclen / (76 * 3 / 4);
 }
 
 static uint64
diff --git a/src/common/base64.c b/src/common/base64.c
index 2943ac7652..ec4eb49382 100644
--- a/src/common/base64.c
+++ b/src/common/base64.c
@@ -224,7 +224,7 @@ int
 pg_b64_enc_len(int srclen)
 {
 	/* 3 bytes will be converted to 4 */
-	return (srclen + 2) * 4 / 3;
+	return (srclen + 2) / 3 * 4;
 }
 
 /*

Reply via email to