On Fri, Jan 01, 2021 at 03:06:13PM -0500, Bruce Momjian wrote:
> Thanks.  I had to learn how to squash my commits into a new branch and
> then generate a format-patch on that:
> 
>       https://bugsdb.com/_en/debug/8b648ec395b86be32efa9629cb006d74
> 
> I wanted to see how the cfbot liked my original patch with the renames,
> and it didn't, so now I know I have to use this method for the
> commitfest.  Patch attached.

There are many ways to do that, indeed.  On my end, I use a local
branch. and then apply a set of git reset --soft before recreating a
single commit.

> Let me get my patch building on the cfbot and then I will address each
> of these.  I am trying to do one stage at a time since I am still
> learning the process.  Thanks.

No problem.  On my end, this stuff has been itching me for a couple of
days and I could not recall why..  Until I remembered that the design
of the hex APIs in your patch is weak with overflow handling because
we don't pass down to the function the size of the destination buffer.
We have finished with a similar set of issues on the past with SCRAM
and base64, with has led to CVE-2019-10164 and the improvements done
in cfc40d3.  So I think that we need to improve things in a safer way.
Mapping with the design for base64, I have finished with the attached
patch, and the following set:
+extern int64 pg_hex_decode(const char *src, int64 len, char *dst, int64 
dstlen);
+extern int64 pg_hex_encode(const char *src, int64 len, char *dst, int64 
dstlen);
+extern int64 pg_hex_enc_len(int64 srclen);
+extern int64 pg_hex_dec_len(int64 srclen);

This ensures that the result never overflows, which explains the
introduction of an error code for the encoding part, and does not 
use elog()/pg_log() so as external libraries can use them.  ECPG uses
long variables in a couple of places, explaining why it feels safer to
use int64.  int should give enough room to any caller of those APIs,
but there is no drawback in using a 64-bit API either, and I don't
think it is worth the risk to break ECPG either for long handling,
even if I don't believe either that folks are going to work on strings
larger than 2GB.

Things get trickier for the bytea input/output because we want more
generic error messages depending for invalid values or an incorrect
number of digits, which is why I have left the copies in encode.c.
This design could be easily extended with more types of error codes,
though I am not sure if that's worth bothering.

Even with that, this leads to much more sanity for hex buffer
manipulation in backup manifests (I don't think that using  
PG_SHAXXX_DIGEST_STRING_LENGTH is a good idea either, I'd like to get
rid of it in the long-term) and ECPG, so that's clearly a gain.

I don't have a Windows host at hand, though I think that it should
work there correctly.  What do you think about the ideas in the
attached patch?
--
Michael
From 8952fb2d5e5a7a01799fe380408a17441a185436 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Sat, 2 Jan 2021 14:00:22 +0900
Subject: [PATCH] Refactor the hex code

---
 src/include/common/hex.h                     |  23 +++
 src/include/common/hex_decode.h              |  16 --
 src/include/utils/builtins.h                 |   1 +
 src/backend/replication/backup_manifest.c    |  39 +++--
 src/backend/utils/adt/encode.c               |  60 ++++++-
 src/backend/utils/adt/varlena.c              |  12 +-
 src/common/Makefile                          |   2 +-
 src/common/hex.c                             | 166 +++++++++++++++++++
 src/common/hex_decode.c                      | 106 ------------
 src/interfaces/ecpg/ecpglib/data.c           |  92 +---------
 src/interfaces/ecpg/ecpglib/ecpglib_extern.h |   3 -
 src/interfaces/ecpg/ecpglib/execute.c        |  28 +++-
 src/interfaces/ecpg/include/ecpgerrno.h      |   1 +
 src/tools/msvc/Mkvcbuild.pm                  |   2 +-
 14 files changed, 321 insertions(+), 230 deletions(-)
 create mode 100644 src/include/common/hex.h
 create mode 100644 src/common/hex.c

diff --git a/src/include/common/hex.h b/src/include/common/hex.h
new file mode 100644
index 0000000000..cf36895ee4
--- /dev/null
+++ b/src/include/common/hex.h
@@ -0,0 +1,23 @@
+/*------------------------------------------------------------------------
+ *
+ *	hex.h
+ *	  Encoding and decoding routines for hex strings.
+ *
+ *	Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ *	Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		  src/include/common/hex.h
+ *
+ *------------------------------------------------------------------------
+ */
+
+#ifndef COMMON_HEX_H
+#define COMMON_HEX_H
+
+extern int64 pg_hex_decode(const char *src, int64 len, char *dst, int64 dstlen);
+extern int64 pg_hex_encode(const char *src, int64 len, char *dst, int64 dstlen);
+extern int64 pg_hex_enc_len(int64 srclen);
+extern int64 pg_hex_dec_len(int64 srclen);
+
+#endif							/* COMMON_HEX_H */
diff --git a/src/include/common/hex_decode.h b/src/include/common/hex_decode.h
index 1f99f069b2..e69de29bb2 100644
--- a/src/include/common/hex_decode.h
+++ b/src/include/common/hex_decode.h
@@ -1,16 +0,0 @@
-/*
- *	hex_decode.h
- *		hex decoding
- *
- *	Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
- *	Portions Copyright (c) 1994, Regents of the University of California
- *
- *	src/include/common/hex_decode.h
- */
-#ifndef COMMON_HEX_DECODE_H
-#define COMMON_HEX_DECODE_H
-
-extern uint64 hex_decode(const char *src, size_t len, char *dst);
-
-
-#endif							/* COMMON_HEX_DECODE_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 19271e0696..c3a0601ef5 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -32,6 +32,7 @@ extern int	errdatatype(Oid datatypeOid);
 extern int	errdomainconstraint(Oid datatypeOid, const char *conname);
 
 /* encode.c */
+extern uint64 hex_decode(const char *src, size_t len, char *dst);
 extern uint64 hex_encode(const char *src, size_t len, char *dst);
 
 /* int.c */
diff --git a/src/backend/replication/backup_manifest.c b/src/backend/replication/backup_manifest.c
index c3f339c556..6c1250ab6c 100644
--- a/src/backend/replication/backup_manifest.c
+++ b/src/backend/replication/backup_manifest.c
@@ -13,11 +13,11 @@
 #include "postgres.h"
 
 #include "access/timeline.h"
+#include "common/hex.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "mb/pg_wchar.h"
 #include "replication/backup_manifest.h"
-#include "utils/builtins.h"
 #include "utils/json.h"
 
 static void AppendStringToManifest(backup_manifest_info *manifest, char *s);
@@ -150,10 +150,17 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 	}
 	else
 	{
+		int64	dstlen = pg_hex_enc_len(pathlen);
+		int64	res;
+
 		appendStringInfoString(&buf, "{ \"Encoded-Path\": \"");
-		enlargeStringInfo(&buf, 2 * pathlen);
-		buf.len += hex_encode(pathname, pathlen,
-							  &buf.data[buf.len]);
+		enlargeStringInfo(&buf, dstlen);
+		res = pg_hex_encode(pathname, pathlen,
+							&buf.data[buf.len], dstlen);
+		if (res < 0)
+			elog(ERROR, "could not encode path \"%s\" in backup manifest",
+				 pathname);
+		buf.len += res;
 		appendStringInfoString(&buf, "\", ");
 	}
 
@@ -176,6 +183,8 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 	{
 		uint8		checksumbuf[PG_CHECKSUM_MAX_LENGTH];
 		int			checksumlen;
+		int64		dstlen;
+		int64		res;
 
 		checksumlen = pg_checksum_final(checksum_ctx, checksumbuf);
 		if (checksumlen < 0)
@@ -185,9 +194,14 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
 		appendStringInfo(&buf,
 						 ", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
 						 pg_checksum_type_name(checksum_ctx->type));
-		enlargeStringInfo(&buf, 2 * checksumlen);
-		buf.len += hex_encode((char *) checksumbuf, checksumlen,
-							  &buf.data[buf.len]);
+		dstlen = pg_hex_enc_len(checksumlen);
+		enlargeStringInfo(&buf, dstlen);
+		res = pg_hex_encode((char *) checksumbuf, checksumlen,
+							&buf.data[buf.len], dstlen);
+		if (res < 0)
+			elog(ERROR, "could not encode checksum algorithm of file \"%s\"",
+				 pathname);
+		buf.len += res;
 		appendStringInfoChar(&buf, '"');
 	}
 
@@ -307,8 +321,9 @@ SendBackupManifest(backup_manifest_info *manifest)
 {
 	StringInfoData protobuf;
 	uint8		checksumbuf[PG_SHA256_DIGEST_LENGTH];
-	char		checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH];
+	char	   *checksumstringbuf;
 	size_t		manifest_bytes_done = 0;
+	int64		dstlen;
 
 	if (!IsManifestEnabled(manifest))
 		return;
@@ -328,8 +343,12 @@ SendBackupManifest(backup_manifest_info *manifest)
 	if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf) < 0)
 		elog(ERROR, "failed to finalize checksum of backup manifest");
 	AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
-	hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
-	checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
+	dstlen = pg_hex_enc_len(PG_SHA256_DIGEST_LENGTH);
+	checksumstringbuf = palloc0(dstlen + 1);	/* includes \0 */
+	if (pg_hex_encode((char *) checksumbuf, sizeof checksumbuf,
+					  checksumstringbuf, dstlen) < 0)
+		elog(ERROR, "could not encode SHA2 result in backup manifest");
+	checksumstringbuf[dstlen] = '\0';
 	AppendStringToManifest(manifest, checksumstringbuf);
 	AppendStringToManifest(manifest, "\"}\n");
 
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index a6c65b1657..4e8f673335 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -15,7 +15,6 @@
 
 #include <ctype.h>
 
-#include "common/hex_decode.h"
 #include "mb/pg_wchar.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
@@ -147,6 +146,17 @@ binary_decode(PG_FUNCTION_ARGS)
 
 static const char hextbl[] = "0123456789abcdef";
 
+static const int8 hexlookup[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
 uint64
 hex_encode(const char *src, size_t len, char *dst)
 {
@@ -161,6 +171,54 @@ hex_encode(const char *src, size_t len, char *dst)
 	return (uint64) len * 2;
 }
 
+static inline char
+get_hex(char c)
+{
+	int			res = -1;
+
+	if (c > 0 && c < 127)
+		res = hexlookup[(unsigned char) c];
+
+	if (res < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid hexadecimal digit: \"%c\"", c)));
+
+	return (char) res;
+}
+
+uint64
+hex_decode(const char *src, size_t len, char *dst)
+{
+	const char *s,
+			   *srcend;
+	char		v1,
+				v2,
+			   *p;
+
+	srcend = src + len;
+	s = src;
+	p = dst;
+	while (s < srcend)
+	{
+		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+		{
+			s++;
+			continue;
+		}
+		v1 = get_hex(*s++) << 4;
+		if (s >= srcend)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("invalid hexadecimal data: odd number of digits")));
+
+		v2 = get_hex(*s++);
+		*p++ = v1 | v2;
+	}
+
+	return p - dst;
+}
+
 static uint64
 hex_enc_len(const char *src, size_t srclen)
 {
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 9300d19e0c..35f0e78238 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -21,8 +21,8 @@
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "common/hashfn.h"
+#include "common/hex.h"
 #include "common/int.h"
-#include "common/hex_decode.h"
 #include "common/unicode_norm.h"
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
@@ -307,6 +307,11 @@ byteain(PG_FUNCTION_ARGS)
 
 		bc = (len - 2) / 2 + VARHDRSZ;	/* maximum possible length */
 		result = palloc(bc);
+
+		/*
+		 * Note that this does not use the hex implementation in src/common/
+		 * to get a proper error handling.
+		 */
 		bc = hex_decode(inputText + 2, len - 2, VARDATA(result));
 		SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */
 
@@ -400,6 +405,11 @@ byteaout(PG_FUNCTION_ARGS)
 		rp = result = palloc(VARSIZE_ANY_EXHDR(vlena) * 2 + 2 + 1);
 		*rp++ = '\\';
 		*rp++ = 'x';
+
+		/*
+		 * Note that this does not use the hex implementation in src/common/
+		 * to get a proper error handling.
+		 */
 		rp += hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp);
 	}
 	else if (bytea_output == BYTEA_OUTPUT_ESCAPE)
diff --git a/src/common/Makefile b/src/common/Makefile
index f624977939..93eb27a2aa 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -58,7 +58,7 @@ OBJS_COMMON = \
 	file_perm.o \
 	file_utils.o \
 	hashfn.o \
-	hex_decode.o \
+	hex.o \
 	ip.o \
 	jsonapi.o \
 	keywords.o \
diff --git a/src/common/hex.c b/src/common/hex.c
new file mode 100644
index 0000000000..1e16c3291c
--- /dev/null
+++ b/src/common/hex.c
@@ -0,0 +1,166 @@
+/*-------------------------------------------------------------------------
+ *
+ * hex.c
+ *	  Encoding and decoding routines for hex.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/common/hex.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "common/hex.h"
+#include "mb/pg_wchar.h"
+
+
+static const int8 hexlookup[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+
+static const char hextbl[] = "0123456789abcdef";
+
+static inline char
+get_hex(const char *cp)
+{
+	unsigned char c = (unsigned char) *cp;
+	int			res = -1;
+
+	if (c < 127)
+		res = hexlookup[c];
+
+	if (res < 0)
+		return -1;
+
+	return (char) res;
+}
+
+/*
+ * pg_hex_encode
+ *
+ * Encode into hex the given string.  Returns the length of the encoded
+ * string, and -1 in the event of an error with the result buffer zeroed
+ * for safety.
+ */
+int64
+pg_hex_encode(const char *src, int64 len, char *dst, int64 dstlen)
+{
+	const char *end = src + len;
+	char	   *p;
+
+	p = dst;
+
+	while (src < end)
+	{
+		/*
+		 * Leave if there is an overflow in the area allocated for the
+		 * encoded string.
+		 */
+		if ((p - dst + 2) > dstlen)
+			goto error;
+
+		*p++ = hextbl[(*src >> 4) & 0xF];
+		*p++ = hextbl[*src & 0xF];
+		src++;
+	}
+	return len * 2;
+
+error:
+	memset(dst, 0, dstlen);
+	return -1;
+}
+
+/*
+ * pg_hex_decode
+ *
+ * Decode the given hex string.  Returns the length of the decoded
+ * string on success, and -1 in the event of an error with the result
+ * buffer zeroed for safety.
+ */
+int64
+pg_hex_decode(const char *src, int64 len, char *dst, int64 dstlen)
+{
+	const char *s,
+			   *srcend;
+	char		v1,
+				v2,
+			   *p;
+
+	srcend = src + len;
+	s = src;
+	p = dst;
+	while (s < srcend)
+	{
+		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+		{
+			s++;
+			continue;
+		}
+		v1 = get_hex(s) << 4;
+		s++;
+
+		if (s >= srcend)
+			goto error;
+
+		v2 = get_hex(s);
+		s++;
+
+		/* overflow check */
+		if ((p - dst + 1) > dstlen)
+			goto error;
+
+		*p++ = v1 | v2;
+	}
+
+
+	Assert((p - dst) <= dstlen);
+	return p - dst;
+
+error:
+	memset(dst, 0, dstlen);
+	return -1;
+}
+
+/*
+ * pg_hex_enc_len
+ *
+ * Returns to caller the length of the string if it were encoded with
+ * hex based on the length provided by caller.  This is useful to* estimate
+ * how large a buffer allocation needs to be done before doing the actual
+ * encoding.
+ */
+int64
+pg_hex_enc_len(int64 srclen)
+{
+	return (srclen << 1);
+}
+
+/*
+ * pg_hex_dec_len
+ *
+ * Returns to caller the length of the string if it were to be decoded
+ * with hex, based on the length given by caller.  This is useful to
+ * estimate how large a buffer allocation needs to be done before doing
+ * the actual decoding.
+ */
+int64
+pg_hex_dec_len(int64 srclen)
+{
+	return (srclen >> 1);
+}
diff --git a/src/common/hex_decode.c b/src/common/hex_decode.c
index 3ecdc73b5c..e69de29bb2 100644
--- a/src/common/hex_decode.c
+++ b/src/common/hex_decode.c
@@ -1,106 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * hex_decode.c
- *		hex decoding
- *
- *
- * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- *	  src/common/hex_decode.c
- *
- *-------------------------------------------------------------------------
- */
-
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#ifdef FRONTEND
-#include "common/logging.h"
-#else
-#include "mb/pg_wchar.h"
-#endif
-#include "common/hex_decode.h"
-
-
-static const int8 hexlookup[128] = {
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-};
-
-static inline char
-get_hex(const char *cp)
-{
-	unsigned char c = (unsigned char) *cp;
-	int			res = -1;
-
-	if (c < 127)
-		res = hexlookup[c];
-
-	if (res < 0)
-	{
-#ifdef FRONTEND
-		pg_log_fatal("invalid hexadecimal digit");
-		exit(EXIT_FAILURE);
-#else
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("invalid hexadecimal digit: \"%.*s\"",
-						pg_mblen(cp), cp)));
-#endif
-	}
-
-	return (char) res;
-}
-
-uint64
-hex_decode(const char *src, size_t len, char *dst)
-{
-	const char *s,
-			   *srcend;
-	char		v1,
-				v2,
-			   *p;
-
-	srcend = src + len;
-	s = src;
-	p = dst;
-	while (s < srcend)
-	{
-		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
-		{
-			s++;
-			continue;
-		}
-		v1 = get_hex(s) << 4;
-		s++;
-		if (s >= srcend)
-		{
-#ifdef FRONTEND
-			pg_log_fatal("invalid hexadecimal data: odd number of digits");
-			exit(EXIT_FAILURE);
-#else
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("invalid hexadecimal data: odd number of digits")));
-#endif
-		}
-		v2 = get_hex(s);
-		s++;
-		*p++ = v1 | v2;
-	}
-
-	return p - dst;
-}
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 6bc91ef7eb..7b79a6a5de 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -5,6 +5,7 @@
 
 #include <math.h>
 
+#include "common/hex.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
@@ -122,86 +123,6 @@ check_special_value(char *ptr, double *retval, char **endptr)
 	return false;
 }
 
-/* imported from src/backend/utils/adt/encode.c */
-
-unsigned
-ecpg_hex_enc_len(unsigned srclen)
-{
-	return srclen << 1;
-}
-
-unsigned
-ecpg_hex_dec_len(unsigned srclen)
-{
-	return srclen >> 1;
-}
-
-static inline char
-get_hex(char c)
-{
-	static const int8 hexlookup[128] = {
-		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-		-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-	};
-	int			res = -1;
-
-	if (c > 0 && c < 127)
-		res = hexlookup[(unsigned char) c];
-
-	return (char) res;
-}
-
-static unsigned
-hex_decode(const char *src, unsigned len, char *dst)
-{
-	const char *s,
-			   *srcend;
-	char		v1,
-				v2,
-			   *p;
-
-	srcend = src + len;
-	s = src;
-	p = dst;
-	while (s < srcend)
-	{
-		if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
-		{
-			s++;
-			continue;
-		}
-		v1 = get_hex(*s++) << 4;
-		if (s >= srcend)
-			return -1;
-
-		v2 = get_hex(*s++);
-		*p++ = v1 | v2;
-	}
-
-	return p - dst;
-}
-
-unsigned
-ecpg_hex_encode(const char *src, unsigned len, char *dst)
-{
-	static const char hextbl[] = "0123456789abcdef";
-	const char *end = src + len;
-
-	while (src < end)
-	{
-		*dst++ = hextbl[(*src >> 4) & 0xF];
-		*dst++ = hextbl[*src & 0xF];
-		src++;
-	}
-	return len * 2;
-}
-
 bool
 ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 			  enum ECPGttype type, enum ECPGttype ind_type,
@@ -529,14 +450,19 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 									src_size,
 									dec_size;
 
-						dst_size = ecpg_hex_enc_len(varcharsize);
+						dst_size = pg_hex_enc_len(varcharsize);
 						src_size = size - 2;	/* exclude backslash + 'x' */
 						dec_size = src_size < dst_size ? src_size : dst_size;
-						variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+						variable->len = pg_hex_decode(pval + 2, dec_size, variable->arr, dst_size);
+
+						if (variable->len < 0)
+							ecpg_raise(lineno, ECPG_OVERFLOW,
+									   ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
+									   NULL);
 
 						if (dst_size < src_size)
 						{
-							long		rcv_size = ecpg_hex_dec_len(size - 2);
+							long		rcv_size = pg_hex_dec_len(size - 2);
 
 							/* truncation */
 							switch (ind_type)
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
index 1a98dea1b5..cdde4903ec 100644
--- a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -215,9 +215,6 @@ struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_M
 void		ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
 struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
 void		ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
-unsigned	ecpg_hex_dec_len(unsigned srclen);
-unsigned	ecpg_hex_enc_len(unsigned srclen);
-unsigned	ecpg_hex_encode(const char *src, unsigned len, char *dst);
 
 #ifdef ENABLE_NLS
 extern char *ecpg_gettext(const char *msgid) pg_attribute_format_arg(1);
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 930b6adbe4..5b16f5ed2c 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -19,6 +19,7 @@
 #include <math.h>
 
 #include "catalog/pg_type_d.h"
+#include "common/hex.h"
 #include "ecpgerrno.h"
 #include "ecpglib.h"
 #include "ecpglib_extern.h"
@@ -491,16 +492,18 @@ static char *
 convert_bytea_to_string(char *from_data, int from_len, int lineno)
 {
 	char	   *to_data;
-	int			to_len = ecpg_hex_enc_len(from_len) + 4 + 1;	/* backslash + 'x' +
-																 * quote + quote */
+	int			dstlen = pg_hex_enc_len(from_len);
+	int			to_len = dstlen + 4 + 1;	/* backslash + 'x' +
+											 * quote + quote */
 
 	to_data = ecpg_alloc(to_len, lineno);
 	if (!to_data)
 		return NULL;
 
 	strcpy(to_data, "'\\x");
-	ecpg_hex_encode(from_data, from_len, to_data + 3);
-	strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'");
+	if (pg_hex_encode(from_data, from_len, to_data + 3, dstlen) < 0)
+		return NULL;
+	strcpy(to_data + 3 + pg_hex_enc_len(from_len), "\'");
 
 	return to_data;
 }
@@ -1087,12 +1090,21 @@ print_param_value(char *value, int len, int is_binary, int lineno, int nth)
 		value_s = value;
 	else
 	{
-		value_s = ecpg_alloc(ecpg_hex_enc_len(len) + 1, lineno);
+		int		dstlen = pg_hex_enc_len(len);
+
+		value_s = ecpg_alloc(dstlen + 1, lineno);
 		if (value_s != NULL)
 		{
-			ecpg_hex_encode(value, len, value_s);
-			value_s[ecpg_hex_enc_len(len)] = '\0';
-			malloced = true;
+			if (pg_hex_encode(value, len, value_s, dstlen) >= 0)
+			{
+				value_s[dstlen] = '\0';
+				malloced = true;
+			}
+			else
+			{
+				ecpg_free(value_s);
+				value_s = "error when encoding parameter";
+			}
 		}
 		else
 			value_s = "no memory for logging of parameter";
diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h
index c4bc526463..ee88740035 100644
--- a/src/interfaces/ecpg/include/ecpgerrno.h
+++ b/src/interfaces/ecpg/include/ecpgerrno.h
@@ -32,6 +32,7 @@
 #define ECPG_NO_ARRAY			-214
 #define ECPG_DATA_NOT_ARRAY		-215
 #define ECPG_ARRAY_INSERT		-216
+#define ECPG_OVERFLOW		-217
 
 #define ECPG_NO_CONN			-220
 #define ECPG_NOT_CONN			-221
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 7f014a12c9..60b216cce0 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -121,7 +121,7 @@ sub mkvcbuild
 	our @pgcommonallfiles = qw(
 	  archive.c base64.c checksum_helper.c
 	  config_info.c controldata_utils.c d2s.c encnames.c exec.c
-	  f2s.c file_perm.c file_utils.c hashfn.c hex_decode.c ip.c jsonapi.c
+	  f2s.c file_perm.c file_utils.c hashfn.c hex.c ip.c jsonapi.c
 	  keywords.c kwlookup.c link-canary.c md5_common.c
 	  pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
 	  saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
-- 
2.30.0

Attachment: signature.asc
Description: PGP signature

Reply via email to