fb6d68
From 17858779e42103ae815c8cf7c4d390207e485856 Mon Sep 17 00:00:00 2001
fb6d68
From: Jitka Plesnikova <jplesnik@redhat.com>
fb6d68
Date: Mon, 7 Jan 2019 13:38:36 +0100
fb6d68
Subject: [PATCH] Perl_my_setenv(); handle integer wrap
fb6d68
fb6d68
RT #133204
fb6d68
fb6d68
Wean this function off int/I32 and onto UV/Size_t.
fb6d68
Also, replace all malloc-ish calls with a wrapper that does
fb6d68
overflow checks,
fb6d68
fb6d68
In particular, it was doing (nlen + vlen + 2) which could wrap when
fb6d68
the combined length of the environment variable name and value
fb6d68
exceeded around 0x7fffffff.
fb6d68
fb6d68
The wrapper check function is probably overkill, but belt and braces...
fb6d68
fb6d68
NB this function has several variant parts, #ifdef'ed by platform
fb6d68
type; I have blindly changed the parts that aren't compiled under linux.
fb6d68
fb6d68
Backported David Mitchell's patch to 5.16.3
fb6d68
---
fb6d68
 util.c | 75 ++++++++++++++++++++++++++++++++++++++++------------------
fb6d68
 1 file changed, 52 insertions(+), 23 deletions(-)
fb6d68
fb6d68
diff --git a/util.c b/util.c
fb6d68
index c1dca62..1357ad5 100644
fb6d68
--- a/util.c
fb6d68
+++ b/util.c
fb6d68
@@ -2029,7 +2029,38 @@ Perl_new_warnings_bitfield(pTHX_ STRLEN *buffer, const char *const bits,
fb6d68
    *(s+(nlen+1+vlen)) = '\0'
fb6d68
 
fb6d68
 #ifdef USE_ENVIRON_ARRAY
fb6d68
-       /* VMS' my_setenv() is in vms.c */
fb6d68
+
fb6d68
+/* small wrapper for use by Perl_my_setenv that mallocs, or reallocs if
fb6d68
+ * 'current' is non-null, with up to three sizes that are added together.
fb6d68
+ * It handles integer overflow.
fb6d68
+ */
fb6d68
+static char *
fb6d68
+S_env_alloc(void *current, Size_t l1, Size_t l2, Size_t l3, Size_t size)
fb6d68
+{
fb6d68
+    void *p;
fb6d68
+    Size_t sl, l = l1 + l2;
fb6d68
+
fb6d68
+    if (l < l2)
fb6d68
+        goto panic;
fb6d68
+    l += l3;
fb6d68
+    if (l < l3)
fb6d68
+        goto panic;
fb6d68
+    sl = l * size;
fb6d68
+    if (sl < l)
fb6d68
+        goto panic;
fb6d68
+
fb6d68
+    p = current
fb6d68
+            ? safesysrealloc(current, sl)
fb6d68
+            : safesysmalloc(sl);
fb6d68
+    if (p)
fb6d68
+        return (char*)p;
fb6d68
+
fb6d68
+  panic:
fb6d68
+    Perl_croak_nocontext("%s", PL_memory_wrap);
fb6d68
+}
fb6d68
+
fb6d68
+
fb6d68
+/* VMS' my_setenv() is in vms.c */
fb6d68
 #if !defined(WIN32) && !defined(NETWARE)
fb6d68
 void
fb6d68
 Perl_my_setenv(pTHX_ const char *nam, const char *val)
fb6d68
@@ -2043,28 +2074,27 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
fb6d68
 #ifndef PERL_USE_SAFE_PUTENV
fb6d68
     if (!PL_use_safe_putenv) {
fb6d68
     /* most putenv()s leak, so we manipulate environ directly */
fb6d68
-    register I32 i;
fb6d68
-    register const I32 len = strlen(nam);
fb6d68
-    int nlen, vlen;
fb6d68
+    UV i;
fb6d68
+    Size_t vlen, nlen = strlen(nam);
fb6d68
 
fb6d68
     /* where does it go? */
fb6d68
     for (i = 0; environ[i]; i++) {
fb6d68
-        if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
fb6d68
+        if (strnEQ(environ[i], nam, nlen) && environ[i][nlen] == '=')
fb6d68
             break;
fb6d68
     }
fb6d68
 
fb6d68
     if (environ == PL_origenviron) {   /* need we copy environment? */
fb6d68
-       I32 j;
fb6d68
-       I32 max;
fb6d68
+       UV j, max;
fb6d68
        char **tmpenv;
fb6d68
 
fb6d68
        max = i;
fb6d68
        while (environ[max])
fb6d68
            max++;
fb6d68
-       tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*));
fb6d68
+       /* XXX shouldn't that be max+1 rather than max+2 ??? - DAPM */
fb6d68
+       tmpenv = (char**)S_env_alloc(NULL, max, 2, 0, sizeof(char*));
fb6d68
        for (j=0; j
fb6d68
-           const int len = strlen(environ[j]);
fb6d68
-           tmpenv[j] = (char*)safesysmalloc((len+1)*sizeof(char));
fb6d68
+           const Size_t len = strlen(environ[j]);
fb6d68
+           tmpenv[j] = S_env_alloc(NULL, len, 1, 0, 1);
fb6d68
            Copy(environ[j], tmpenv[j], len+1, char);
fb6d68
        }
fb6d68
        tmpenv[max] = NULL;
fb6d68
@@ -2079,15 +2109,15 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
fb6d68
        return;
fb6d68
     }
fb6d68
     if (!environ[i]) {                 /* does not exist yet */
fb6d68
-       environ = (char**)safesysrealloc(environ, (i+2) * sizeof(char*));
fb6d68
+       environ = (char**)S_env_alloc(environ, i, 2, 0, sizeof(char*));
fb6d68
        environ[i+1] = NULL;    /* make sure it's null terminated */
fb6d68
     }
fb6d68
     else
fb6d68
        safesysfree(environ[i]);
fb6d68
-       nlen = strlen(nam);
fb6d68
+
fb6d68
        vlen = strlen(val);
fb6d68
 
fb6d68
-       environ[i] = (char*)safesysmalloc((nlen+vlen+2) * sizeof(char));
fb6d68
+       environ[i] = S_env_alloc(NULL, nlen, vlen, 2, 1);
fb6d68
        /* all that work just for this */
fb6d68
        my_setenv_format(environ[i], nam, nlen, val, vlen);
fb6d68
     } else {
fb6d68
@@ -2107,22 +2137,21 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
fb6d68
         if (val == NULL) {
fb6d68
             (void)unsetenv(nam);
fb6d68
         } else {
fb6d68
-	    const int nlen = strlen(nam);
fb6d68
-	    const int vlen = strlen(val);
fb6d68
-	    char * const new_env =
fb6d68
-                (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
fb6d68
+	    const Size_t nlen = strlen(nam);
fb6d68
+	    const Size_t vlen = strlen(val);
fb6d68
+	    char * const new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
fb6d68
             my_setenv_format(new_env, nam, nlen, val, vlen);
fb6d68
             (void)putenv(new_env);
fb6d68
         }
fb6d68
 #       else /* ! HAS_UNSETENV */
fb6d68
         char *new_env;
fb6d68
-	const int nlen = strlen(nam);
fb6d68
-	int vlen;
fb6d68
+	const Size_t nlen = strlen(nam);
fb6d68
+	Size_t vlen;
fb6d68
         if (!val) {
fb6d68
 	   val = "";
fb6d68
         }
fb6d68
         vlen = strlen(val);
fb6d68
-        new_env = (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
fb6d68
+        new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
fb6d68
         /* all that work just for this */
fb6d68
         my_setenv_format(new_env, nam, nlen, val, vlen);
fb6d68
         (void)putenv(new_env);
fb6d68
@@ -2141,14 +2170,14 @@ Perl_my_setenv(pTHX_ const char *nam, const char *val)
fb6d68
 {
fb6d68
     dVAR;
fb6d68
     register char *envstr;
fb6d68
-    const int nlen = strlen(nam);
fb6d68
-    int vlen;
fb6d68
+    const Size_t nlen = strlen(nam);
fb6d68
+    Size_t vlen;
fb6d68
 
fb6d68
     if (!val) {
fb6d68
        val = "";
fb6d68
     }
fb6d68
     vlen = strlen(val);
fb6d68
-    Newx(envstr, nlen+vlen+2, char);
fb6d68
+    envstr = S_env_alloc(NULL, nlen, vlen, 2, 1);
fb6d68
     my_setenv_format(envstr, nam, nlen, val, vlen);
fb6d68
     (void)PerlEnv_putenv(envstr);
fb6d68
     Safefree(envstr);
fb6d68
-- 
fb6d68
2.17.2
fb6d68