Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
@ 2020-10-23 15:13 Sergey Ostanevich
  2020-10-23 20:06 ` Vladislav Shpilevoy
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Sergey Ostanevich @ 2020-10-23 15:13 UTC (permalink / raw)
  To: tarantool-patches; +Cc: Vladislav Shpilevoy

Static buffer overflow in thread local pool causes random fails on OSX
platform. This was caused by an incorrect use of the allocator result:
the snprintf returns the full size of the formatted string, rather the
number of bytes written.

Fixes #5312

Branch: https://github.com/tarantool/tarantool/tree/sergos/gh-5312-crash-in-libeio
Issue: https://github.com/tarantool/tarantool/issues/5312

---
src/lib/core/sio.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
index 97a512eee..44f952c7c 100644
--- a/src/lib/core/sio.c
+++ b/src/lib/core/sio.c
@@ -53,15 +53,15 @@ sio_socketname(int fd)
      int save_errno = errno;
      int name_size = 2 * SERVICE_NAME_MAXLEN;
      char *name = static_alloc(name_size);
-       int n = snprintf(name, name_size, "fd %d", fd);
+       int n = MIN(snprintf(name, name_size, "fd %d", fd), name_size);
      if (fd >= 0) {
              struct sockaddr_storage addr;
              socklen_t addrlen = sizeof(addr);
              int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
              if (rc == 0) {
-                       n += snprintf(name + n, name_size - n, ", aka %s",
+                       n += MIN(snprintf(name + n, name_size - n, ", aka %s",
                              sio_strfaddr((struct sockaddr *)&addr,
-                                                               addrlen));
+                                                addrlen)), name_size - n);
              }
              addrlen = sizeof(addr);
              rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
-- 
2.24.3 (Apple Git-128)

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-23 15:13 [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow Sergey Ostanevich
@ 2020-10-23 20:06 ` Vladislav Shpilevoy
  2020-10-29 22:56 ` Vladislav Shpilevoy
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-10-23 20:06 UTC (permalink / raw)
  To: Sergey Ostanevich, tarantool-patches

Hi! Thanks for the patch!

It seems the patch fixes the bug. I don't have crashes anymore.

LGTM.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-23 15:13 [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow Sergey Ostanevich
  2020-10-23 20:06 ` Vladislav Shpilevoy
@ 2020-10-29 22:56 ` Vladislav Shpilevoy
  2020-10-30  7:07   ` Cyrill Gorcunov
  2020-10-31 16:33 ` Vladislav Shpilevoy
  2020-11-03 22:59 ` Vladislav Shpilevoy
  3 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-10-29 22:56 UTC (permalink / raw)
  To: Sergey Ostanevich, tarantool-patches, Kirill Yukhin

If there won't be a second review in a few days, I will
commit this. I can't properly run tests without this patch.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-29 22:56 ` Vladislav Shpilevoy
@ 2020-10-30  7:07   ` Cyrill Gorcunov
  0 siblings, 0 replies; 13+ messages in thread
From: Cyrill Gorcunov @ 2020-10-30  7:07 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tml

[-- Attachment #1: Type: text/plain, Size: 216 bytes --]

Ack

On Fri, Oct 30, 2020 at 1:56 AM Vladislav Shpilevoy <
v.shpilevoy@tarantool.org> wrote:

> If there won't be a second review in a few days, I will
> commit this. I can't properly run tests without this patch.
>

[-- Attachment #2: Type: text/html, Size: 512 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-23 15:13 [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow Sergey Ostanevich
  2020-10-23 20:06 ` Vladislav Shpilevoy
  2020-10-29 22:56 ` Vladislav Shpilevoy
@ 2020-10-31 16:33 ` Vladislav Shpilevoy
  2020-11-02 13:19   ` Sergey Ostanevich
  2020-11-03 22:59 ` Vladislav Shpilevoy
  3 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-10-31 16:33 UTC (permalink / raw)
  To: Sergey Ostanevich, tarantool-patches

Hi! Thanks for the patch!

On 23.10.2020 17:13, Sergey Ostanevich wrote:
> Static buffer overflow in thread local pool causes random fails on OSX
> platform. This was caused by an incorrect use of the allocator result:
> the snprintf returns the full size of the formatted string, rather the
> number of bytes written.
> 
> Fixes #5312
> 
> Branch: https://github.com/tarantool/tarantool/tree/sergos/gh-5312-crash-in-libeio
> Issue: https://github.com/tarantool/tarantool/issues/5312
> 
> ---
> src/lib/core/sio.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
> index 97a512eee..44f952c7c 100644
> --- a/src/lib/core/sio.c
> +++ b/src/lib/core/sio.c
> @@ -53,15 +53,15 @@ sio_socketname(int fd)
>       int save_errno = errno;
>       int name_size = 2 * SERVICE_NAME_MAXLEN;
>       char *name = static_alloc(name_size);
> -       int n = snprintf(name, name_size, "fd %d", fd);
> +       int n = MIN(snprintf(name, name_size, "fd %d", fd), name_size);
>       if (fd >= 0) {
>               struct sockaddr_storage addr;
>               socklen_t addrlen = sizeof(addr);
>               int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
>               if (rc == 0) {
> -                       n += snprintf(name + n, name_size - n, ", aka %s",
> +                       n += MIN(snprintf(name + n, name_size - n, ", aka %s",
>                               sio_strfaddr((struct sockaddr *)&addr,
> -                                                               addrlen));
> +                                                addrlen)), name_size - n);

After thinking more I realized it is not entirely correct.
MIN(a, b) = ((a) < (b) ? (a) : (b)). It means, that either
'a' or 'b' is executed twice. If 'a' is the big snprintf above, 
it is also executed twice. So you can't really use MIN right away.

I reworked the patch to use an existing macro - SNPRINT. See the
new patch below and on the branch in a separate commit.

I added a new function, because SNPRINT returns -1 in case of
print function fail.

====================
diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
index 44f952c7c..d008526d5 100644
--- a/src/lib/core/sio.c
+++ b/src/lib/core/sio.c
@@ -46,6 +46,33 @@
 static_assert(SMALL_STATIC_SIZE > NI_MAXHOST + NI_MAXSERV,
 	      "static buffer should fit host name");
 
+/**
+ * Safely print a socket description to the given buffer, with correct overflow
+ * checks and all.
+ */
+static int
+sio_socketname_to_buffer(int fd, char *buf, int size)
+{
+	int n = 0;
+	SNPRINT(n, snprintf, buf, size, "fd %d", fd);
+	if (fd < 0)
+		return 0;
+	struct sockaddr_storage addr;
+	socklen_t addrlen = sizeof(addr);
+	int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
+	if (rc == 0) {
+		SNPRINT(n, snprintf, buf, size, ", aka %s",
+			sio_strfaddr((struct sockaddr *)&addr, addrlen));
+	}
+	addrlen = sizeof(addr);
+	rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
+	if (rc == 0) {
+		SNPRINT(n, snprintf, buf, size, ", peer of %s",
+			sio_strfaddr((struct sockaddr *)&addr, addrlen));
+	}
+	return 0;
+}
+
 const char *
 sio_socketname(int fd)
 {
@@ -53,25 +80,13 @@ sio_socketname(int fd)
 	int save_errno = errno;
 	int name_size = 2 * SERVICE_NAME_MAXLEN;
 	char *name = static_alloc(name_size);
-	int n = MIN(snprintf(name, name_size, "fd %d", fd), name_size);
-	if (fd >= 0) {
-		struct sockaddr_storage addr;
-		socklen_t addrlen = sizeof(addr);
-		int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
-		if (rc == 0) {
-			n += MIN(snprintf(name + n, name_size - n, ", aka %s",
-				sio_strfaddr((struct sockaddr *)&addr,
-                                                addrlen)), name_size - n);
-		}
-		addrlen = sizeof(addr);
-		rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
-		if (rc == 0) {
-			n += snprintf(name + n, name_size - n,
-				      ", peer of %s",
-				      sio_strfaddr((struct sockaddr *)&addr,
-								addrlen));
-		}
-	}
+	int rc = sio_socketname_to_buffer(fd, name, name_size);
+	/*
+	 * Could fail only because of a bad format in snprintf, but it is not
+	 * bad, so should not fail.
+	 */
+	assert(rc == 0);
+	(void)rc;
 	/*
 	 * Restore the original errno, it might have been reset by
 	 * snprintf() or getsockname().

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-31 16:33 ` Vladislav Shpilevoy
@ 2020-11-02 13:19   ` Sergey Ostanevich
  2020-11-02 21:09     ` Vladislav Shpilevoy
  0 siblings, 1 reply; 13+ messages in thread
From: Sergey Ostanevich @ 2020-11-02 13:19 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

Hi!

Thanks for the investigation! My bad, I used MIN as a function with
sematics of all agruments calculated before call. You're right - in case
of define it can cause a double call.

The SNPRINT although leaves some questions to me: in case 'written' is
more or equal to 'size', it forces '_buf' to be set to NULL. But in the
sio_socketname_to_buffer() there's no check for NULL between the calls.
A call to snprintf() delivers a segfault, at least for Mac.

Also, factoring out 18 loc out of a function with a total of 24 seems
redundant - IMVHO. 

I prpose to explicitly get the result from snprintf and compare it to
original limit. 

======
--- a/src/lib/core/sio.c
+++ b/src/lib/core/sio.c
@@ -53,20 +53,22 @@ sio_socketname(int fd)
        int save_errno = errno;
        int name_size = 2 * SERVICE_NAME_MAXLEN;
        char *name = static_alloc(name_size);
-       int n = snprintf(name, name_size, "fd %d", fd);
+       int to_be_printed = snprintf(name, name_size, "fd %d", fd);
+       int n = MIN(to_be_printed, name_size);
        if (fd >= 0) {
                struct sockaddr_storage addr;
                socklen_t addrlen = sizeof(addr);
                int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
                if (rc == 0) {
-                       n += snprintf(name + n, name_size - n, ", aka %s",
+                       to_be_printed = snprintf(name + n, name_size - n, ", aka %s",
                                sio_strfaddr((struct sockaddr *)&addr,
                                                                addrlen));
+                       n += MIN(to_be_printed, name_size - n);
                }
                addrlen = sizeof(addr);
                rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
                if (rc == 0) {
-                       n += snprintf(name + n, name_size - n,
+                       to_be_printed = snprintf(name + n, name_size - n,
                                      ", peer of %s",
                                      sio_strfaddr((struct sockaddr *)&addr,
                                                                addrlen));



On 31 окт 17:33, Vladislav Shpilevoy wrote:
> Hi! Thanks for the patch!
> 
> On 23.10.2020 17:13, Sergey Ostanevich wrote:
> > Static buffer overflow in thread local pool causes random fails on OSX
> > platform. This was caused by an incorrect use of the allocator result:
> > the snprintf returns the full size of the formatted string, rather the
> > number of bytes written.
> > 
> > Fixes #5312
> > 
> > Branch: https://github.com/tarantool/tarantool/tree/sergos/gh-5312-crash-in-libeio
> > Issue: https://github.com/tarantool/tarantool/issues/5312
> > 
> > ---
> > src/lib/core/sio.c | 6 +++---
> > 1 file changed, 3 insertions(+), 3 deletions(-)
> > 
> > diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
> > index 97a512eee..44f952c7c 100644
> > --- a/src/lib/core/sio.c
> > +++ b/src/lib/core/sio.c
> > @@ -53,15 +53,15 @@ sio_socketname(int fd)
> >       int save_errno = errno;
> >       int name_size = 2 * SERVICE_NAME_MAXLEN;
> >       char *name = static_alloc(name_size);
> > -       int n = snprintf(name, name_size, "fd %d", fd);
> > +       int n = MIN(snprintf(name, name_size, "fd %d", fd), name_size);
> >       if (fd >= 0) {
> >               struct sockaddr_storage addr;
> >               socklen_t addrlen = sizeof(addr);
> >               int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
> >               if (rc == 0) {
> > -                       n += snprintf(name + n, name_size - n, ", aka %s",
> > +                       n += MIN(snprintf(name + n, name_size - n, ", aka %s",
> >                               sio_strfaddr((struct sockaddr *)&addr,
> > -                                                               addrlen));
> > +                                                addrlen)), name_size - n);
> 
> After thinking more I realized it is not entirely correct.
> MIN(a, b) = ((a) < (b) ? (a) : (b)). It means, that either
> 'a' or 'b' is executed twice. If 'a' is the big snprintf above, 
> it is also executed twice. So you can't really use MIN right away.
> 
> I reworked the patch to use an existing macro - SNPRINT. See the
> new patch below and on the branch in a separate commit.
> 
> I added a new function, because SNPRINT returns -1 in case of
> print function fail.
> 
> ====================
> diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
> index 44f952c7c..d008526d5 100644
> --- a/src/lib/core/sio.c
> +++ b/src/lib/core/sio.c
> @@ -46,6 +46,33 @@
>  static_assert(SMALL_STATIC_SIZE > NI_MAXHOST + NI_MAXSERV,
>  	      "static buffer should fit host name");
>  
> +/**
> + * Safely print a socket description to the given buffer, with correct overflow
> + * checks and all.
> + */
> +static int
> +sio_socketname_to_buffer(int fd, char *buf, int size)
> +{
> +	int n = 0;
> +	SNPRINT(n, snprintf, buf, size, "fd %d", fd);
> +	if (fd < 0)
> +		return 0;
> +	struct sockaddr_storage addr;
> +	socklen_t addrlen = sizeof(addr);
> +	int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
> +	if (rc == 0) {
> +		SNPRINT(n, snprintf, buf, size, ", aka %s",
> +			sio_strfaddr((struct sockaddr *)&addr, addrlen));
> +	}
> +	addrlen = sizeof(addr);
> +	rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
> +	if (rc == 0) {
> +		SNPRINT(n, snprintf, buf, size, ", peer of %s",
> +			sio_strfaddr((struct sockaddr *)&addr, addrlen));
> +	}
> +	return 0;
> +}
> +
>  const char *
>  sio_socketname(int fd)
>  {
> @@ -53,25 +80,13 @@ sio_socketname(int fd)
>  	int save_errno = errno;
>  	int name_size = 2 * SERVICE_NAME_MAXLEN;
>  	char *name = static_alloc(name_size);
> -	int n = MIN(snprintf(name, name_size, "fd %d", fd), name_size);
> -	if (fd >= 0) {
> -		struct sockaddr_storage addr;
> -		socklen_t addrlen = sizeof(addr);
> -		int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
> -		if (rc == 0) {
> -			n += MIN(snprintf(name + n, name_size - n, ", aka %s",
> -				sio_strfaddr((struct sockaddr *)&addr,
> -                                                addrlen)), name_size - n);
> -		}
> -		addrlen = sizeof(addr);
> -		rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
> -		if (rc == 0) {
> -			n += snprintf(name + n, name_size - n,
> -				      ", peer of %s",
> -				      sio_strfaddr((struct sockaddr *)&addr,
> -								addrlen));
> -		}
> -	}
> +	int rc = sio_socketname_to_buffer(fd, name, name_size);
> +	/*
> +	 * Could fail only because of a bad format in snprintf, but it is not
> +	 * bad, so should not fail.
> +	 */
> +	assert(rc == 0);
> +	(void)rc;
>  	/*
>  	 * Restore the original errno, it might have been reset by
>  	 * snprintf() or getsockname().

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-02 13:19   ` Sergey Ostanevich
@ 2020-11-02 21:09     ` Vladislav Shpilevoy
  2020-11-02 21:18       ` Cyrill Gorcunov
  0 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-11-02 21:09 UTC (permalink / raw)
  To: Sergey Ostanevich; +Cc: tarantool-patches

> Thanks for the investigation! My bad, I used MIN as a function with
> sematics of all agruments calculated before call. You're right - in case
> of define it can cause a double call.
> 
> The SNPRINT although leaves some questions to me: in case 'written' is
> more or equal to 'size', it forces '_buf' to be set to NULL. But in the
> sio_socketname_to_buffer() there's no check for NULL between the calls.
> A call to snprintf() delivers a segfault, at least for Mac.

Woops, SNPRINT is used a lot in the code. If it is true, we need to fix SNPRINT.

> Also, factoring out 18 loc out of a function with a total of 24 seems
> redundant - IMVHO. 

With your patch it is also an issue with alignment. See below. I
tried to fix it in-place, but it was too ugly due to too big
indentation.

> --- a/src/lib/core/sio.c
> +++ b/src/lib/core/sio.c
> @@ -53,20 +53,22 @@ sio_socketname(int fd)
>         int save_errno = errno;
>         int name_size = 2 * SERVICE_NAME_MAXLEN;
>         char *name = static_alloc(name_size);
> -       int n = snprintf(name, name_size, "fd %d", fd);
> +       int to_be_printed = snprintf(name, name_size, "fd %d", fd);
> +       int n = MIN(to_be_printed, name_size);
>         if (fd >= 0) {
>                 struct sockaddr_storage addr;
>                 socklen_t addrlen = sizeof(addr);
>                 int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
>                 if (rc == 0) {
> -                       n += snprintf(name + n, name_size - n, ", aka %s",
> +                       to_be_printed = snprintf(name + n, name_size - n, ", aka %s",
>                                 sio_strfaddr((struct sockaddr *)&addr,
>                                                                 addrlen));

Here sio_strfaddr( should get +17 spaces, and that makes it hard
to read. So I extracted everything to a new function with lower
indentation.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-02 21:09     ` Vladislav Shpilevoy
@ 2020-11-02 21:18       ` Cyrill Gorcunov
  2020-11-02 21:43         ` Vladislav Shpilevoy
  0 siblings, 1 reply; 13+ messages in thread
From: Cyrill Gorcunov @ 2020-11-02 21:18 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

On Mon, Nov 02, 2020 at 10:09:29PM +0100, Vladislav Shpilevoy wrote:
> > Thanks for the investigation! My bad, I used MIN as a function with
> > sematics of all agruments calculated before call. You're right - in case
> > of define it can cause a double call.
> > 
> > The SNPRINT although leaves some questions to me: in case 'written' is
> > more or equal to 'size', it forces '_buf' to be set to NULL. But in the
> > sio_socketname_to_buffer() there's no check for NULL between the calls.
> > A call to snprintf() delivers a segfault, at least for Mac.
> 
> Woops, SNPRINT is used a lot in the code. If it is true, we need to fix SNPRINT.

Guys, I didn't follow the details of the series but thought of some
helper like below. Will it help?
---
From: Cyrill Gorcunov <gorcunov@gmail.com>
Date: Sun, 1 Nov 2020 16:10:08 +0300
Subject: [PATCH] util: introduce scnprintf helper

The misuse of snprintf is pretty common, lets
provide scnprintf helper which is just a wrapper
over vsnprintf with more sane return.

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 src/lib/core/util.c | 24 ++++++++++++++++++++++++
 src/trivia/util.h   |  3 +++
 2 files changed, 27 insertions(+)

diff --git a/src/lib/core/util.c b/src/lib/core/util.c
index dfce317f0..60e552174 100644
--- a/src/lib/core/util.c
+++ b/src/lib/core/util.c
@@ -84,6 +84,30 @@ strnindex(const char **haystack, const char *needle, uint32_t len, uint32_t hmax
 	return hmax;
 }
 
+/**
+ * scnprintf - Put formatted string into a buffer
+ *
+ * @param buf The destination buffer
+ * @param size The size of the buffer
+ * @param fmt The format string
+ * @param ... Arguments
+ *
+ * @return Number of chars written into \a buf without ending '\0'.
+ * @return 0 if \a size if 0.
+ */
+int
+scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+	va_list args;
+	int i;
+
+	va_start(args, fmt);
+	i = vsnprintf(buf, size, fmt, args);
+	va_end(args);
+
+	return i < (int)size ? i : ((size != 0) ? (int)size-1 : 0);
+}
+
 void
 close_all_xcpt(int fdc, ...)
 {
diff --git a/src/trivia/util.h b/src/trivia/util.h
index da5a3705e..cf2da15fc 100644
--- a/src/trivia/util.h
+++ b/src/trivia/util.h
@@ -98,6 +98,9 @@ strindex(const char **haystack, const char *needle, uint32_t hmax);
 uint32_t
 strnindex(const char **haystack, const char *needle, uint32_t len, uint32_t hmax);
 
+int
+scnprintf(char *buf, size_t size, const char *fmt, ...);
+
 #define nelem(x)     (sizeof((x))/sizeof((x)[0]))
 #define field_sizeof(compound_type, field) sizeof(((compound_type *)NULL)->field)
 #ifndef lengthof
-- 
2.26.2

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-02 21:18       ` Cyrill Gorcunov
@ 2020-11-02 21:43         ` Vladislav Shpilevoy
  2020-11-02 21:47           ` Cyrill Gorcunov
  0 siblings, 1 reply; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-11-02 21:43 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches

On 02.11.2020 22:18, Cyrill Gorcunov wrote:
> On Mon, Nov 02, 2020 at 10:09:29PM +0100, Vladislav Shpilevoy wrote:
>>> Thanks for the investigation! My bad, I used MIN as a function with
>>> sematics of all agruments calculated before call. You're right - in case
>>> of define it can cause a double call.
>>>
>>> The SNPRINT although leaves some questions to me: in case 'written' is
>>> more or equal to 'size', it forces '_buf' to be set to NULL. But in the
>>> sio_socketname_to_buffer() there's no check for NULL between the calls.
>>> A call to snprintf() delivers a segfault, at least for Mac.
>>
>> Woops, SNPRINT is used a lot in the code. If it is true, we need to fix SNPRINT.
> 
> Guys, I didn't follow the details of the series but thought of some
> helper like below. Will it help?

In some places yes. But SNPRINT is used not only with snprintf.

It also is used with vsnprintf, mp_snprint, vy_run_snprint_filename,
tuple_snprint, say_format_plain_tail, json_escape, strftime, and probably
more. So it would be better to fix SNPRINT. To cover all its usage
cases.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-02 21:43         ` Vladislav Shpilevoy
@ 2020-11-02 21:47           ` Cyrill Gorcunov
  2020-11-03 13:59             ` Sergey Ostanevich
  0 siblings, 1 reply; 13+ messages in thread
From: Cyrill Gorcunov @ 2020-11-02 21:47 UTC (permalink / raw)
  To: Vladislav Shpilevoy; +Cc: tarantool-patches

On Mon, Nov 02, 2020 at 10:43:02PM +0100, Vladislav Shpilevoy wrote:
> > 
> > Guys, I didn't follow the details of the series but thought of some
> > helper like below. Will it help?
> 
> In some places yes. But SNPRINT is used not only with snprintf.
> 
> It also is used with vsnprintf, mp_snprint, vy_run_snprint_filename,
> tuple_snprint, say_format_plain_tail, json_escape, strftime, and probably
> more. So it would be better to fix SNPRINT. To cover all its usage
> cases.

OK, sounds reasonable.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-02 21:47           ` Cyrill Gorcunov
@ 2020-11-03 13:59             ` Sergey Ostanevich
  2020-11-03 14:08               ` Cyrill Gorcunov
  0 siblings, 1 reply; 13+ messages in thread
From: Sergey Ostanevich @ 2020-11-03 13:59 UTC (permalink / raw)
  To: Cyrill Gorcunov; +Cc: tarantool-patches, Vladislav Shpilevoy

Hi!

I double checked the snprintf fails only if buf is set to NULL _and_ the
len is set to a non-zero value. 

So I would follow Vlad's proposal on factoring out the code to make it
clearer.

Sergos.

====

commit 6f6eb1b8f12679b69eba40ea960a9ebb439c798e
Author: Sergey Ostanevich <sergos@tarantool.org>
Date:   Fri Oct 23 16:09:31 2020 +0300

    core: fix static_alloc buffer overflow
    
    Static buffer overflow in thread local pool causes random fails on OSX
    platform. This was caused by an incorrect use of the allocator result.
    
    Fixes #5312
    
    Co-authored-by: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>

diff --git a/src/lib/core/sio.c b/src/lib/core/sio.c
index 97a512eee..d008526d5 100644
--- a/src/lib/core/sio.c
+++ b/src/lib/core/sio.c
@@ -46,6 +46,33 @@
 static_assert(SMALL_STATIC_SIZE > NI_MAXHOST + NI_MAXSERV,
 	      "static buffer should fit host name");
 
+/**
+ * Safely print a socket description to the given buffer, with correct overflow
+ * checks and all.
+ */
+static int
+sio_socketname_to_buffer(int fd, char *buf, int size)
+{
+	int n = 0;
+	SNPRINT(n, snprintf, buf, size, "fd %d", fd);
+	if (fd < 0)
+		return 0;
+	struct sockaddr_storage addr;
+	socklen_t addrlen = sizeof(addr);
+	int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
+	if (rc == 0) {
+		SNPRINT(n, snprintf, buf, size, ", aka %s",
+			sio_strfaddr((struct sockaddr *)&addr, addrlen));
+	}
+	addrlen = sizeof(addr);
+	rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
+	if (rc == 0) {
+		SNPRINT(n, snprintf, buf, size, ", peer of %s",
+			sio_strfaddr((struct sockaddr *)&addr, addrlen));
+	}
+	return 0;
+}
+
 const char *
 sio_socketname(int fd)
 {
@@ -53,25 +80,13 @@ sio_socketname(int fd)
 	int save_errno = errno;
 	int name_size = 2 * SERVICE_NAME_MAXLEN;
 	char *name = static_alloc(name_size);
-	int n = snprintf(name, name_size, "fd %d", fd);
-	if (fd >= 0) {
-		struct sockaddr_storage addr;
-		socklen_t addrlen = sizeof(addr);
-		int rc = getsockname(fd, (struct sockaddr *) &addr, &addrlen);
-		if (rc == 0) {
-			n += snprintf(name + n, name_size - n, ", aka %s",
-				sio_strfaddr((struct sockaddr *)&addr,
-								addrlen));
-		}
-		addrlen = sizeof(addr);
-		rc = getpeername(fd, (struct sockaddr *) &addr, &addrlen);
-		if (rc == 0) {
-			n += snprintf(name + n, name_size - n,
-				      ", peer of %s",
-				      sio_strfaddr((struct sockaddr *)&addr,
-								addrlen));
-		}
-	}
+	int rc = sio_socketname_to_buffer(fd, name, name_size);
+	/*
+	 * Could fail only because of a bad format in snprintf, but it is not
+	 * bad, so should not fail.
+	 */
+	assert(rc == 0);
+	(void)rc;
 	/*
 	 * Restore the original errno, it might have been reset by
 	 * snprintf() or getsockname().

On 03 ноя 00:47, Cyrill Gorcunov wrote:
> On Mon, Nov 02, 2020 at 10:43:02PM +0100, Vladislav Shpilevoy wrote:
> > > 
> > > Guys, I didn't follow the details of the series but thought of some
> > > helper like below. Will it help?
> > 
> > In some places yes. But SNPRINT is used not only with snprintf.
> > 
> > It also is used with vsnprintf, mp_snprint, vy_run_snprint_filename,
> > tuple_snprint, say_format_plain_tail, json_escape, strftime, and probably
> > more. So it would be better to fix SNPRINT. To cover all its usage
> > cases.
> 
> OK, sounds reasonable.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-11-03 13:59             ` Sergey Ostanevich
@ 2020-11-03 14:08               ` Cyrill Gorcunov
  0 siblings, 0 replies; 13+ messages in thread
From: Cyrill Gorcunov @ 2020-11-03 14:08 UTC (permalink / raw)
  To: Sergey Ostanevich; +Cc: tarantool-patches, Vladislav Shpilevoy

On Tue, Nov 03, 2020 at 04:59:42PM +0300, Sergey Ostanevich wrote:
> Hi!
> 
> I double checked the snprintf fails only if buf is set to NULL _and_ the
> len is set to a non-zero value. 
> 
> So I would follow Vlad's proposal on factoring out the code to make it
> clearer.
> 
> Sergos.

Looks ok to me.

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow
  2020-10-23 15:13 [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow Sergey Ostanevich
                   ` (2 preceding siblings ...)
  2020-10-31 16:33 ` Vladislav Shpilevoy
@ 2020-11-03 22:59 ` Vladislav Shpilevoy
  3 siblings, 0 replies; 13+ messages in thread
From: Vladislav Shpilevoy @ 2020-11-03 22:59 UTC (permalink / raw)
  To: Sergey Ostanevich, tarantool-patches

Pushed to 1.10, 2.5, 2.6, and master.

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2020-11-03 22:59 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-23 15:13 [Tarantool-patches] [PATCH] core: fix static_alloc buffer overflow Sergey Ostanevich
2020-10-23 20:06 ` Vladislav Shpilevoy
2020-10-29 22:56 ` Vladislav Shpilevoy
2020-10-30  7:07   ` Cyrill Gorcunov
2020-10-31 16:33 ` Vladislav Shpilevoy
2020-11-02 13:19   ` Sergey Ostanevich
2020-11-02 21:09     ` Vladislav Shpilevoy
2020-11-02 21:18       ` Cyrill Gorcunov
2020-11-02 21:43         ` Vladislav Shpilevoy
2020-11-02 21:47           ` Cyrill Gorcunov
2020-11-03 13:59             ` Sergey Ostanevich
2020-11-03 14:08               ` Cyrill Gorcunov
2020-11-03 22:59 ` Vladislav Shpilevoy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox