On 2016-10-03 14:55:24 -0700, Andres Freund wrote:
> Hi,
>
> A colleage of me just wrote innocent looking code like
> char *shardRelationName = pnstrdup(relationName, NAMEDATALEN);
> which is at the moment wrong if relationName isn't preallocated to
> NAMEDATALEN size.
>
> /*
> * pnstrdup
> *Like pstrdup(), but append null byte to a
> *not-necessarily-null-terminated input string.
> */
> char *
> pnstrdup(const char *in, Size len)
> {
> char *out = palloc(len + 1);
>
> memcpy(out, in, len);
> out[len] = '\0';
> return out;
> }
>
> isn't that a somewhat weird behaviour / implementation? Not really like
> strndup(), which one might believe to be analoguous...
I've since hit this bug again. To fix it, you'd need strnlen. The lack
of which I'd also independently hit twice. So here's a patch adding
pg_strnlen and using that to fix pnstrdup.
Greetings,
Andres Freund
>From 89ac4ce2cdad83806f83c0bc5ddac0e9ab1e038c Mon Sep 17 00:00:00 2001
From: Andres Freund
Date: Thu, 21 Sep 2017 11:43:26 -0700
Subject: [PATCH 1/2] Add pg_strnlen() a portable implementation of strlen.
As the OS version is likely going to be more optimized, fall back to
it if available, as detected by configure.
---
configure | 2 +-
configure.in | 2 +-
src/common/string.c | 20
src/include/common/string.h | 15 +++
src/include/pg_config.h.in| 3 +++
src/include/pg_config.h.win32 | 3 +++
src/port/snprintf.c | 12 ++--
7 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/configure b/configure
index 5fa7a61025..6584e47293 100755
--- a/configure
+++ b/configure
@@ -8777,7 +8777,7 @@ fi
-for ac_func in strerror_r getpwuid_r gethostbyname_r
+for ac_func in strerror_r getpwuid_r gethostbyname_r strnlen
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index bebbd11af9..15d4c85162 100644
--- a/configure.in
+++ b/configure.in
@@ -961,7 +961,7 @@ LIBS="$LIBS $PTHREAD_LIBS"
AC_CHECK_HEADER(pthread.h, [], [AC_MSG_ERROR([
pthread.h not found; use --disable-thread-safety to disable thread safety])])
-AC_CHECK_FUNCS([strerror_r getpwuid_r gethostbyname_r])
+AC_CHECK_FUNCS([strerror_r getpwuid_r gethostbyname_r strnlen])
# Do test here with the proper thread flags
PGAC_FUNC_STRERROR_R_INT
diff --git a/src/common/string.c b/src/common/string.c
index 159d9ea7b6..901821f3d8 100644
--- a/src/common/string.c
+++ b/src/common/string.c
@@ -41,3 +41,23 @@ pg_str_endswith(const char *str, const char *end)
str += slen - elen;
return strcmp(str, end) == 0;
}
+
+
+/*
+ * Portable version of posix' strnlen.
+ *
+ * Returns the number of characters before a null-byte in the string pointed
+ * to by str, unless there's no null-byte before maxlen. In the latter case
+ * maxlen is returned.
+ */
+#ifndef HAVE_STRNLEN
+size_t
+pg_strnlen(const char *str, size_t maxlen)
+{
+ const char *p = str;
+
+ while (maxlen-- > 0 && *p)
+ p++;
+ return p - str;
+}
+#endif
diff --git a/src/include/common/string.h b/src/include/common/string.h
index 5f3ea71d61..3d46b80918 100644
--- a/src/include/common/string.h
+++ b/src/include/common/string.h
@@ -12,4 +12,19 @@
extern bool pg_str_endswith(const char *str, const char *end);
+/*
+ * Portable version of posix' strnlen.
+ *
+ * Returns the number of characters before a null-byte in the string pointed
+ * to by str, unless there's no null-byte before maxlen. In the latter case
+ * maxlen is returned.
+ *
+ * Use the system strnlen if provided, it's likely to be faster.
+ */
+#ifdef HAVE_STRNLEN
+#define pg_strnlen(str, maxlen) strnlen(str, maxlen)
+#else
+extern size_t pg_strnlen(const char *str, size_t maxlen);
+#endif
+
#endif /* COMMON_STRING_H */
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b7ae9a0702..257262908c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -496,6 +496,9 @@
/* Define to 1 if you have the `strlcpy' function. */
#undef HAVE_STRLCPY
+/* Define to 1 if you have the `strnlen' function. */
+#undef HAVE_STRNLEN
+
/* Define to use have a strong random number source */
#undef HAVE_STRONG_RANDOM
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index e6b3c5d551..08ae2f9a86 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -345,6 +345,9 @@
/* Define to 1 if you have the header file. */
#define HAVE_STRING_H 1
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN
+
/* Define to use have a strong random number source */
#define HAVE_STRONG_RANDOM 1
diff --git a/src/port/snprintf.c b/src/port/snprintf.c
index 231e5d6bdb..531d2c5ee3 100644
--- a/src/port/snprintf.c
+++ b/src/port/snprintf.c
@@ -43,6 +43,8 @@
#endif
#include
+#include "common/str