File 2512-Fix-ei_decode_iodata-illegal-write-and-improve-testc.patch of Package erlang
From 84ae4ec1819f254fdc4841d5d0436286104776dc Mon Sep 17 00:00:00 2001
From: Rickard Green <rickard@erlang.org>
Date: Wed, 22 Apr 2020 17:26:12 +0200
Subject: [PATCH] Fix ei_decode_iodata() illegal write and improve testcase
---
 lib/erl_interface/src/decode/decode_iodata.c       | 57 +++++++++++++------
 lib/erl_interface/test/ei_decode_SUITE.erl         |  1 +
 .../test/ei_decode_SUITE_data/ei_decode_test.c     | 64 ++++++++++++++++++++--
 3 files changed, 100 insertions(+), 22 deletions(-)
diff --git a/lib/erl_interface/src/decode/decode_iodata.c b/lib/erl_interface/src/decode/decode_iodata.c
index 2a538d2ad7..88d23d8a1e 100644
--- a/lib/erl_interface/src/decode/decode_iodata.c
+++ b/lib/erl_interface/src/decode/decode_iodata.c
@@ -19,11 +19,15 @@
  *
  */
 
+#include <string.h>
 #include "eidef.h"
 #include "eiext.h"
+#include "putget.h"
     
 static int decode_list_ext_iodata(const char *buf, int* index,
                                   int *szp, unsigned char **pp);
+static void decode_string(const char *buf, int* index,
+                          int *szp, unsigned char **pp);
 
 int ei_decode_iodata(const char *buf, int* index, int *szp, char *out_buf)
 {
@@ -50,13 +54,11 @@ int ei_decode_iodata(const char *buf, int* index, int *szp, char *out_buf)
         return 0;
     }
 
-    case ERL_STRING_EXT:
-        if (ei_decode_string(buf, index, out_buf) < 0)
-            return -1;
-        if (szp)
-            *szp += len;
+    case ERL_STRING_EXT: {
+        unsigned char *p = (unsigned char *) out_buf;
+        decode_string(buf, index, szp, p ? &p : NULL);
         return 0;
-
+    }
     case ERL_NIL_EXT:
         return ei_decode_list_header(buf, index, NULL);
 
@@ -69,7 +71,7 @@ int ei_decode_iodata(const char *buf, int* index, int *szp, char *out_buf)
     }
 
     default:
-        return -1; /* Not a list or binary... */
+        return -1; /* Not a list nor a binary... */
     }
 }
     
@@ -113,15 +115,9 @@ static int decode_list_ext_iodata(const char *buf, int* index,
             *szp += len;
             break;
         }
-        case ERL_STRING_EXT: {
-            void *p = pp ? *pp : NULL;
-            if (ei_decode_string(buf, index, p) < 0)
-                return -1;
-            if (pp)
-                *pp += len;
-            *szp += len;
+        case ERL_STRING_EXT:
+            decode_string(buf, index, szp, pp);
             break;
-        }
         case ERL_LIST_EXT:
             if (decode_list_ext_iodata(buf, index, szp, pp) < 0)
                 return -1;
@@ -131,7 +127,7 @@ static int decode_list_ext_iodata(const char *buf, int* index,
                 return -1;
             break;
         default:
-            /* Not a list, binary, nor byte sized integer... */
+            /* Not a list, a binary, nor a byte sized integer... */
             return -1;
         }
     }
@@ -139,3 +135,32 @@ static int decode_list_ext_iodata(const char *buf, int* index,
     return 0;
 }
     
+static void
+decode_string(const char *buf, int* index, int *szp, unsigned char **pp)
+{
+    /*
+     * ei_decode_string() null-terminates the string
+     * which we do not want, so we decode it ourselves
+     * here instead...
+     */
+    int len;
+    char *s = (char *) buf + *index;
+    char *s0 = s;
+
+    /* ASSERT(*s == ERL_STRING_EXT); */
+    s++;
+
+    len = get16be(s);
+
+    if (pp) {
+        memcpy(*pp, s, len); 
+        *pp += len;
+    }
+
+    if (szp)
+        *szp += len;
+
+    s += len;
+    *index += s-s0; 
+
+}
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index 37b51329ea..cde6a687eb 100644
--- a/lib/erl_interface/test/ei_decode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_SUITE.erl
@@ -228,6 +228,7 @@ test_ei_decode_iodata(Config) when is_list(Config) ->
     check_decode_iodata(P, [$a|$a], false),
     check_decode_iodata(P, [[$a|$a],$a], false),
     check_decode_iodata(P, "hej", true),
+    check_decode_iodata(P, ["hej", " ", "hopp"], true),
     check_decode_iodata(P, <<"hopp san sa">>, true),
     check_decode_iodata(P, [$a | <<"a">>], true),
     check_decode_iodata(P, [[[["hej"]]], [$ , <<"hopp">>, $ , "san" | <<" sa">>]], true),
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index 961137f36c..bf51e3db87 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -756,12 +756,16 @@ TESTCASE(test_ei_decode_utf8_atom)
 
 TESTCASE(test_ei_decode_iodata)
 {
+    char *buf = NULL, *data = NULL;
     ei_init();
 
     while (1) {
-        char *buf, *data;
+        int unexpected_write = 0;
+        int i;
         int len, index, saved_index, err;
 
+        if (buf)
+            free_packet(buf);
         buf = read_packet(&len);
 
         if (len == 4
@@ -774,8 +778,10 @@ TESTCASE(test_ei_decode_iodata)
         
         index = 0;
         err = ei_decode_version(buf, &index, NULL);
-        if (err != 0)
+        if (err != 0) {
+            free_packet(buf);
             fail1("ei_decode_version returned %d", err);
+        }
         saved_index = index;
         err = ei_decode_iodata(buf, &index, &len, NULL);
         if (err != 0) {
@@ -786,7 +792,22 @@ TESTCASE(test_ei_decode_iodata)
             ei_x_free(&x);
             continue;
         }
-        data = malloc(len);
+        if (data) {
+            data -= 100;
+            free(data);
+        }
+        data = malloc(len + 200);
+        if (!data) {
+            ei_x_buff x;
+            ei_x_new_with_version(&x);
+            ei_x_encode_atom(&x, "malloc_failed");
+            send_bin_term(&x);
+            ei_x_free(&x);
+            continue;
+        }
+        for (i = 0; i < len + 200; i++)
+            data[i] = 'Y';
+        data += 100;
         err = ei_decode_iodata(buf, &saved_index, NULL, (unsigned char *) data);
         if (err != 0) {
             ei_x_buff x;
@@ -794,14 +815,45 @@ TESTCASE(test_ei_decode_iodata)
             ei_x_encode_atom(&x, "decode_data_failed");
             send_bin_term(&x);
             ei_x_free(&x);
-            free(data);
             continue;
         }
 
-        send_buffer(data, len);
-        free(data);
+        for (i = -100; i < 0; i++) {
+            if (data[i] != 'Y') {
+                ei_x_buff x;
+                ei_x_new_with_version(&x);
+                ei_x_encode_atom(&x, "unexpected_write_before_data");
+                send_bin_term(&x);
+                ei_x_free(&x);
+                unexpected_write = !0;
+                break;
+            }
+        }
+
+        if (!unexpected_write) {
+            for (i = len; i < len + 100; i++) {
+                if (data[i] != 'Y') {
+                    ei_x_buff x;
+                    ei_x_new_with_version(&x);
+                    ei_x_encode_atom(&x, "unexpected_write_after_data");
+                    send_bin_term(&x);
+                    ei_x_free(&x);
+                    unexpected_write = !0;
+                    break;
+                }
+            }
+        }
+
+        if (!unexpected_write)
+            send_buffer(data, len);
     }
 
+    if (buf)
+        free_packet(buf);
+    if (data) {
+        data -= 100;
+        free(data);
+    }
     report(1);
 }
 
-- 
2.16.4