CVE-2018-18311 (C, published 2018-12-07T21:29Z)
Description: value: Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-190 \u2014 Integer Overflow or Wraparound
The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
Repository: https://github.com/Perl/perl5
Files changed:
util.c
Commit message: Perl_my_setenv(); handle integer wrap

RT #133204

Wean this function off int/I32 and onto UV/Size_t.
Also, replace all malloc-ish calls with a wrapper that does
overflow checks,

In particular, it was doing (nlen + vlen + 2) which could wrap when
the combined length of the environment variable name and value
exceeded around 0x7fffffff.

The wrapper check function is probably overkill, but belt and braces...

NB this function has several variant parts, #ifdef'ed by platform
type; I have blindly changed the parts that aren't compiled under linux.


Vulnerable code:
       /* VMS' my_setenv() is in vms.c */
        I32 i;
        const I32 len = strlen(nam);
        int nlen, vlen;
            if (strnEQ(environ[i],nam,len) && environ[i][len] == '=')
            I32 j;
            I32 max;
            tmpenv = (char**)safesysmalloc((max+2) * sizeof(char*));
                const int len = strlen(environ[j]);
                tmpenv[j] = (char*)safesysmalloc((len+1)*sizeof(char));
            environ = (char**)safesysrealloc(environ, (i+2) * sizeof(char*));
        nlen = strlen(nam);
        environ[i] = (char*)safesysmalloc((nlen+vlen+2) * sizeof(char));
	    const int nlen = strlen(nam);
	    const int vlen = strlen(val);
	    char * const new_env =
                (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
	const int nlen = strlen(nam);
	int vlen;
        new_env = (char*)safesysmalloc((nlen + vlen + 2) * sizeof(char));
    const int nlen = strlen(nam);
    int vlen;
    Newx(envstr, nlen+vlen+2, char);

Fixed code:

/* small wrapper for use by Perl_my_setenv that mallocs, or reallocs if
 * 'current' is non-null, with up to three sizes that are added together.
 * It handles integer overflow.
 */
static char *
S_env_alloc(void *current, Size_t l1, Size_t l2, Size_t l3, Size_t size)
{
    void *p;
    Size_t sl, l = l1 + l2;

    if (l < l2)
        goto panic;
    l += l3;
    if (l < l3)
        goto panic;
    sl = l * size;
    if (sl < l)
        goto panic;

    p = current
            ? safesysrealloc(current, sl)
            : safesysmalloc(sl);
    if (p)
        return (char*)p;

  panic:
    croak_memory_wrap();
}


/* VMS' my_setenv() is in vms.c */

        UV i;
        Size_t vlen, nlen = strlen(nam);
            if (strnEQ(environ[i], nam, nlen) && environ[i][nlen] == '=')
            UV j, max;
            /* XXX shouldn't that be max+1 rather than max+2 ??? - DAPM */
            tmpenv = (char**)S_env_alloc(NULL, max, 2, 0, sizeof(char*));
                const Size_t len = strlen(environ[j]);
                tmpenv[j] = S_env_alloc(NULL, len, 1, 0, 1);
            environ = (char**)S_env_alloc(environ, i, 2, 0, sizeof(char*));

        environ[i] = S_env_alloc(NULL, nlen, vlen, 2, 1);
	    const Size_t nlen = strlen(nam);
	    const Size_t vlen = strlen(val);
	    char * const new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
	const Size_t nlen = strlen(nam);
	Size_t vlen;
        new_env = S_env_alloc(NULL, nlen, vlen, 2, 1);
    const Size_t nlen = strlen(nam);
    Size_t vlen;
    envstr = S_env_alloc(NULL, nlen, vlen, 2, 1);