patch 8.2.4418: crash when using special multi-byte character

Problem:    Crash when using special multi-byte character.
Solution:   Don't use isalpha() for an arbitrary character.

unix_expandpath(
    garray_T	*gap,
    char_u	*path,
    int		wildoff,
    int		flags,		// EW_* flags
    int		didstar)	// expanded "**" once already
{
    char_u	*buf;
    char_u	*path_end;
    char_u	*p, *s, *e;
    int		start_len = gap->ga_len;
    char_u	*pat;
    regmatch_T	regmatch;
    int		starts_with_dot;
    int		matches;
    int		len;
    int		starstar = FALSE;
    static int	stardepth = 0;	    // depth for "**" expansion

    DIR		*dirp;
    struct dirent *dp;

    // Expanding "**" may take a long time, check for CTRL-C.
    if (stardepth > 0)
    {
	ui_breakcheck();
	if (got_int)
	    return 0;
    }

    // make room for file name
    buf = alloc(STRLEN(path) + BASENAMELEN + 5);
    if (buf == NULL)
	return 0;

    /*
     * Find the first part in the path name that contains a wildcard.
     * When EW_ICASE is set every letter is considered to be a wildcard.
     * Copy it into "buf", including the preceding characters.
     */
    p = buf;
    s = buf;
    e = NULL;
    path_end = path;
    while (*path_end != NUL)
    {
	// May ignore a wildcard that has a backslash before it; it will
	// be removed by rem_backslash() or file_pat_to_reg_pat() below.
	if (path_end >= path + wildoff && rem_backslash(path_end))
	    *p++ = *path_end++;
	else if (*path_end == '/')
	{
	    if (e != NULL)
		break;
	    s = p + 1;
	}
	else if (path_end >= path + wildoff
			 && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
			     || (!p_fic && (flags & EW_ICASE)
					     && isalpha(PTR2CHAR(path_end)))))
	    e = p;
	if (has_mbyte)
	{
	    len = (*mb_ptr2len)(path_end);
	    STRNCPY(p, path_end, len);
	    p += len;
	    path_end += len;
	}
	else
	    *p++ = *path_end++;
    }
    e = p;
    *e = NUL;

    // Now we have one wildcard component between "s" and "e".
    // Remove backslashes between "wildoff" and the start of the wildcard
    // component.
    for (p = buf + wildoff; p < s; ++p)
	if (rem_backslash(p))
	{
	    STRMOVE(p, p + 1);
	    --e;
	    --s;
	}

    // Check for "**" between "s" and "e".
    for (p = s; p < e; ++p)
	if (p[0] == '*' && p[1] == '*')
	    starstar = TRUE;

    // convert the file pattern to a regexp pattern
    starts_with_dot = *s == '.';
    pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
    if (pat == NULL)
    {
	vim_free(buf);
	return 0;
    }

    // compile the regexp into a program
    if (flags & EW_ICASE)
	regmatch.rm_ic = TRUE;		// 'wildignorecase' set
    else
	regmatch.rm_ic = p_fic;	// ignore case when 'fileignorecase' is set
    if (flags & (EW_NOERROR | EW_NOTWILD))
	++emsg_silent;
    regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
    if (flags & (EW_NOERROR | EW_NOTWILD))
	--emsg_silent;
    vim_free(pat);

    if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0)
    {
	vim_free(buf);
	return 0;
    }

    // If "**" is by itself, this is the first time we encounter it and more
    // is following then find matches without any directory.
    if (!didstar && stardepth < 100 && starstar && e - s == 2
							  && *path_end == '/')
    {
	STRCPY(s, path_end + 1);
	++stardepth;
	(void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
	--stardepth;
    }

    // open the directory for scanning
    *s = NUL;
    dirp = opendir(*buf == NUL ? "." : (char *)buf);

    // Find all matching entries
    if (dirp != NULL)
    {
	for (;;)
	{
	    dp = readdir(dirp);
	    if (dp == NULL)
		break;
	    if ((dp->d_name[0] != '.' || starts_with_dot
			|| ((flags & EW_DODOT)
			    && dp->d_name[1] != NUL
			    && (dp->d_name[1] != '.' || dp->d_name[2] != NUL)))
		 && ((regmatch.regprog != NULL && vim_regexec(&regmatch,
					     (char_u *)dp->d_name, (colnr_T)0))
		   || ((flags & EW_NOTWILD)
		     && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0)))
	    {
		STRCPY(s, dp->d_name);
		len = STRLEN(buf);

		if (starstar && stardepth < 100)
		{
		    // For "**" in the pattern first go deeper in the tree to
		    // find matches.
		    STRCPY(buf + len, "/**");
		    STRCPY(buf + len + 3, path_end);
		    ++stardepth;
		    (void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
		    --stardepth;
		}

		STRCPY(buf + len, path_end);
		if (mch_has_exp_wildcard(path_end)) // handle more wildcards
		{
		    // need to expand another component of the path
		    // remove backslashes for the remaining components only
		    (void)unix_expandpath(gap, buf, len + 1, flags, FALSE);
		}
		else
		{
		    stat_T  sb;

		    // no more wildcards, check if there is a match
		    // remove backslashes for the remaining components only
		    if (*path_end != NUL)
			backslash_halve(buf + len + 1);
		    // add existing file or symbolic link
		    if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
						      : mch_getperm(buf) >= 0)
		    {
#ifdef MACOS_CONVERT
			size_t precomp_len = STRLEN(buf)+1;
			char_u *precomp_buf =
			    mac_precompose_path(buf, precomp_len, &precomp_len);

			if (precomp_buf)
			{
			    mch_memmove(buf, precomp_buf, precomp_len);
			    vim_free(precomp_buf);
			}
#endif
			addfile(gap, buf, flags);
		    }
		}
	    }
	}

	closedir(dirp);
    }

    vim_free(buf);
    vim_regfree(regmatch.regprog);

    matches = gap->ga_len - start_len;
    if (matches > 0)
	qsort(((char_u **)gap->ga_data) + start_len, matches,
						   sizeof(char_u *), pstrcmp);
    return matches;
}

Use of Out-of-range Pointer Offset in GitHub repository vim/vim prior to 8.2.4418.


unix_expandpath(
    garray_T	*gap,
    char_u	*path,
    int		wildoff,
    int		flags,		// EW_* flags
    int		didstar)	// expanded "**" once already
{
    char_u	*buf;
    char_u	*path_end;
    char_u	*p, *s, *e;
    int		start_len = gap->ga_len;
    char_u	*pat;
    regmatch_T	regmatch;
    int		starts_with_dot;
    int		matches;
    int		len;
    int		starstar = FALSE;
    static int	stardepth = 0;	    // depth for "**" expansion

    DIR		*dirp;
    struct dirent *dp;

    // Expanding "**" may take a long time, check for CTRL-C.
    if (stardepth > 0)
    {
	ui_breakcheck();
	if (got_int)
	    return 0;
    }

    // make room for file name
    buf = alloc(STRLEN(path) + BASENAMELEN + 5);
    if (buf == NULL)
	return 0;

    /*
     * Find the first part in the path name that contains a wildcard.
     * When EW_ICASE is set every letter is considered to be a wildcard.
     * Copy it into "buf", including the preceding characters.
     */
    p = buf;
    s = buf;
    e = NULL;
    path_end = path;
    while (*path_end != NUL)
    {
	// May ignore a wildcard that has a backslash before it; it will
	// be removed by rem_backslash() or file_pat_to_reg_pat() below.
	if (path_end >= path + wildoff && rem_backslash(path_end))
	    *p++ = *path_end++;
	else if (*path_end == '/')
	{
	    if (e != NULL)
		break;
	    s = p + 1;
	}
	else if (path_end >= path + wildoff
			 && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL
			     || (!p_fic && (flags & EW_ICASE)
					  && vim_isalpha(PTR2CHAR(path_end)))))
	    e = p;
	if (has_mbyte)
	{
	    len = (*mb_ptr2len)(path_end);
	    STRNCPY(p, path_end, len);
	    p += len;
	    path_end += len;
	}
	else
	    *p++ = *path_end++;
    }
    e = p;
    *e = NUL;

    // Now we have one wildcard component between "s" and "e".
    // Remove backslashes between "wildoff" and the start of the wildcard
    // component.
    for (p = buf + wildoff; p < s; ++p)
	if (rem_backslash(p))
	{
	    STRMOVE(p, p + 1);
	    --e;
	    --s;
	}

    // Check for "**" between "s" and "e".
    for (p = s; p < e; ++p)
	if (p[0] == '*' && p[1] == '*')
	    starstar = TRUE;

    // convert the file pattern to a regexp pattern
    starts_with_dot = *s == '.';
    pat = file_pat_to_reg_pat(s, e, NULL, FALSE);
    if (pat == NULL)
    {
	vim_free(buf);
	return 0;
    }

    // compile the regexp into a program
    if (flags & EW_ICASE)
	regmatch.rm_ic = TRUE;		// 'wildignorecase' set
    else
	regmatch.rm_ic = p_fic;	// ignore case when 'fileignorecase' is set
    if (flags & (EW_NOERROR | EW_NOTWILD))
	++emsg_silent;
    regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
    if (flags & (EW_NOERROR | EW_NOTWILD))
	--emsg_silent;
    vim_free(pat);

    if (regmatch.regprog == NULL && (flags & EW_NOTWILD) == 0)
    {
	vim_free(buf);
	return 0;
    }

    // If "**" is by itself, this is the first time we encounter it and more
    // is following then find matches without any directory.
    if (!didstar && stardepth < 100 && starstar && e - s == 2
							  && *path_end == '/')
    {
	STRCPY(s, path_end + 1);
	++stardepth;
	(void)unix_expandpath(gap, buf, (int)(s - buf), flags, TRUE);
	--stardepth;
    }

    // open the directory for scanning
    *s = NUL;
    dirp = opendir(*buf == NUL ? "." : (char *)buf);

    // Find all matching entries
    if (dirp != NULL)
    {
	for (;;)
	{
	    dp = readdir(dirp);
	    if (dp == NULL)
		break;
	    if ((dp->d_name[0] != '.' || starts_with_dot
			|| ((flags & EW_DODOT)
			    && dp->d_name[1] != NUL
			    && (dp->d_name[1] != '.' || dp->d_name[2] != NUL)))
		 && ((regmatch.regprog != NULL && vim_regexec(&regmatch,
					     (char_u *)dp->d_name, (colnr_T)0))
		   || ((flags & EW_NOTWILD)
		     && fnamencmp(path + (s - buf), dp->d_name, e - s) == 0)))
	    {
		STRCPY(s, dp->d_name);
		len = STRLEN(buf);

		if (starstar && stardepth < 100)
		{
		    // For "**" in the pattern first go deeper in the tree to
		    // find matches.
		    STRCPY(buf + len, "/**");
		    STRCPY(buf + len + 3, path_end);
		    ++stardepth;
		    (void)unix_expandpath(gap, buf, len + 1, flags, TRUE);
		    --stardepth;
		}

		STRCPY(buf + len, path_end);
		if (mch_has_exp_wildcard(path_end)) // handle more wildcards
		{
		    // need to expand another component of the path
		    // remove backslashes for the remaining components only
		    (void)unix_expandpath(gap, buf, len + 1, flags, FALSE);
		}
		else
		{
		    stat_T  sb;

		    // no more wildcards, check if there is a match
		    // remove backslashes for the remaining components only
		    if (*path_end != NUL)
			backslash_halve(buf + len + 1);
		    // add existing file or symbolic link
		    if ((flags & EW_ALLLINKS) ? mch_lstat((char *)buf, &sb) >= 0
						      : mch_getperm(buf) >= 0)
		    {
#ifdef MACOS_CONVERT
			size_t precomp_len = STRLEN(buf)+1;
			char_u *precomp_buf =
			    mac_precompose_path(buf, precomp_len, &precomp_len);

			if (precomp_buf)
			{
			    mch_memmove(buf, precomp_buf, precomp_len);
			    vim_free(precomp_buf);
			}
#endif
			addfile(gap, buf, flags);
		    }
		}
	    }
	}

	closedir(dirp);
    }

    vim_free(buf);
    vim_regfree(regmatch.regprog);

    matches = gap->ga_len - start_len;
    if (matches > 0)
	qsort(((char_u **)gap->ga_data) + start_len, matches,
						   sizeof(char_u *), pstrcmp);
    return matches;
}


check vertexIndex which might be -1 for broken DXF

bool DL_Dxf::handleLWPolylineData(DL_CreationInterface* /*creationInterface*/) {
    // Allocate LWPolyline vertices (group code 90):
    if (groupCode==90) {
        maxVertices = toInt(groupValue);
        if (maxVertices>0) {
            if (vertices!=NULL) {
                delete[] vertices;
            }
            vertices = new double[4*maxVertices];
            for (int i=0; i<maxVertices; ++i) {
                vertices[i*4] = 0.0;
                vertices[i*4+1] = 0.0;
                vertices[i*4+2] = 0.0;
                vertices[i*4+3] = 0.0;
            }
        }
        vertexIndex=-1;
        return true;
    }

    // Process LWPolylines vertices (group codes 10/20/30/42):
    else if (groupCode==10 || groupCode==20 ||
             groupCode==30 || groupCode==42) {

        if (vertexIndex<maxVertices-1 && groupCode==10) {
            vertexIndex++;
        }

        if (groupCode<=30) {
            if (vertexIndex>=0 && vertexIndex<maxVertices) {
                vertices[4*vertexIndex + (groupCode/10-1)] = toReal(groupValue);
            }
        } else if (groupCode==42 && vertexIndex<maxVertices) {
            vertices[4*vertexIndex + 3] = toReal(groupValue);
        }
        return true;
    }
    return false;
}

A code execution vulnerability exists in the DL_Dxf::handleLWPolylineData functionality of Ribbonsoft dxflib 3.17.0. A specially-crafted .dxf file can lead to a heap buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.


bool DL_Dxf::handleLWPolylineData(DL_CreationInterface* /*creationInterface*/) {
    // Allocate LWPolyline vertices (group code 90):
    if (groupCode==90) {
        maxVertices = toInt(groupValue);
        if (maxVertices>0) {
            if (vertices!=NULL) {
                delete[] vertices;
            }
            vertices = new double[4*maxVertices];
            for (int i=0; i<maxVertices; ++i) {
                vertices[i*4] = 0.0;
                vertices[i*4+1] = 0.0;
                vertices[i*4+2] = 0.0;
                vertices[i*4+3] = 0.0;
            }
        }
        vertexIndex=-1;
        return true;
    }

    // Process LWPolylines vertices (group codes 10/20/30/42):
    else if (groupCode==10 || groupCode==20 ||
             groupCode==30 || groupCode==42) {

        if (vertexIndex<maxVertices-1 && groupCode==10) {
            vertexIndex++;
        }

        if (groupCode<=30) {
            if (vertexIndex>=0 && vertexIndex<maxVertices && vertexIndex>=0) {
                vertices[4*vertexIndex + (groupCode/10-1)] = toReal(groupValue);
            }
        } else if (groupCode==42 && vertexIndex<maxVertices && vertexIndex>=0) {
            vertices[4*vertexIndex + 3] = toReal(groupValue);
        }
        return true;
    }
    return false;
}


udf: Restore i_lenAlloc when inode expansion fails

When we fail to expand inode from inline format to a normal format, we
restore inode to contain the original inline formatting but we forgot to
set i_lenAlloc back. The mismatch between i_lenAlloc and i_size was then
causing further problems such as warnings and lost data down the line.

Reported-by: butt3rflyh4ck <butterflyhuangxx@gmail.com>
CC: stable@vger.kernel.org
Fixes: 7e49b6f2480c ("udf: Convert UDF to new truncate calling sequence")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>

int udf_expand_file_adinicb(struct inode *inode)
{
	struct page *page;
	char *kaddr;
	struct udf_inode_info *iinfo = UDF_I(inode);
	int err;

	WARN_ON_ONCE(!inode_is_locked(inode));
	if (!iinfo->i_lenAlloc) {
		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
		else
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
		/* from now on we have normal address_space methods */
		inode->i_data.a_ops = &udf_aops;
		up_write(&iinfo->i_data_sem);
		mark_inode_dirty(inode);
		return 0;
	}
	/*
	 * Release i_data_sem so that we can lock a page - page lock ranks
	 * above i_data_sem. i_mutex still protects us against file changes.
	 */
	up_write(&iinfo->i_data_sem);

	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
	if (!page)
		return -ENOMEM;

	if (!PageUptodate(page)) {
		kaddr = kmap_atomic(page);
		memset(kaddr + iinfo->i_lenAlloc, 0x00,
		       PAGE_SIZE - iinfo->i_lenAlloc);
		memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr,
			iinfo->i_lenAlloc);
		flush_dcache_page(page);
		SetPageUptodate(page);
		kunmap_atomic(kaddr);
	}
	down_write(&iinfo->i_data_sem);
	memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
	       iinfo->i_lenAlloc);
	iinfo->i_lenAlloc = 0;
	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
	else
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
	/* from now on we have normal address_space methods */
	inode->i_data.a_ops = &udf_aops;
	set_page_dirty(page);
	unlock_page(page);
	up_write(&iinfo->i_data_sem);
	err = filemap_fdatawrite(inode->i_mapping);
	if (err) {
		/* Restore everything back so that we don't lose data... */
		lock_page(page);
		down_write(&iinfo->i_data_sem);
		kaddr = kmap_atomic(page);
		memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, inode->i_size);
		kunmap_atomic(kaddr);
		unlock_page(page);
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
		inode->i_data.a_ops = &udf_adinicb_aops;
		up_write(&iinfo->i_data_sem);
	}
	put_page(page);
	mark_inode_dirty(inode);

	return err;
}

A flaw null pointer dereference in the Linux kernel UDF file system functionality was found in the way user triggers udf_file_write_iter function for the malicious UDF image. A local user could use this flaw to crash the system. Actual from Linux kernel 4.2-rc1 till 5.17-rc2.


int udf_expand_file_adinicb(struct inode *inode)
{
	struct page *page;
	char *kaddr;
	struct udf_inode_info *iinfo = UDF_I(inode);
	int err;

	WARN_ON_ONCE(!inode_is_locked(inode));
	if (!iinfo->i_lenAlloc) {
		if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
		else
			iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
		/* from now on we have normal address_space methods */
		inode->i_data.a_ops = &udf_aops;
		up_write(&iinfo->i_data_sem);
		mark_inode_dirty(inode);
		return 0;
	}
	/*
	 * Release i_data_sem so that we can lock a page - page lock ranks
	 * above i_data_sem. i_mutex still protects us against file changes.
	 */
	up_write(&iinfo->i_data_sem);

	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
	if (!page)
		return -ENOMEM;

	if (!PageUptodate(page)) {
		kaddr = kmap_atomic(page);
		memset(kaddr + iinfo->i_lenAlloc, 0x00,
		       PAGE_SIZE - iinfo->i_lenAlloc);
		memcpy(kaddr, iinfo->i_data + iinfo->i_lenEAttr,
			iinfo->i_lenAlloc);
		flush_dcache_page(page);
		SetPageUptodate(page);
		kunmap_atomic(kaddr);
	}
	down_write(&iinfo->i_data_sem);
	memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00,
	       iinfo->i_lenAlloc);
	iinfo->i_lenAlloc = 0;
	if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
	else
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
	/* from now on we have normal address_space methods */
	inode->i_data.a_ops = &udf_aops;
	set_page_dirty(page);
	unlock_page(page);
	up_write(&iinfo->i_data_sem);
	err = filemap_fdatawrite(inode->i_mapping);
	if (err) {
		/* Restore everything back so that we don't lose data... */
		lock_page(page);
		down_write(&iinfo->i_data_sem);
		kaddr = kmap_atomic(page);
		memcpy(iinfo->i_data + iinfo->i_lenEAttr, kaddr, inode->i_size);
		kunmap_atomic(kaddr);
		unlock_page(page);
		iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
		inode->i_data.a_ops = &udf_adinicb_aops;
		iinfo->i_lenAlloc = inode->i_size;
		up_write(&iinfo->i_data_sem);
	}
	put_page(page);
	mark_inode_dirty(inode);

	return err;
}


static MagickBooleanType SetGrayscaleImage(Image *image,
  ExceptionInfo *exception)
{
  CacheView
    *image_view;

  MagickBooleanType
    status;

  PixelInfo
    *colormap;

  register ssize_t
    i;

  ssize_t
    *colormap_index,
    j,
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->type != GrayscaleType)
    (void) TransformImageColorspace(image,GRAYColorspace,exception);
  if (image->storage_class == PseudoClass)
    colormap_index=(ssize_t *) AcquireQuantumMemory(image->colors+1,
      sizeof(*colormap_index));
  else
    colormap_index=(ssize_t *) AcquireQuantumMemory(MaxColormapSize+1,
      sizeof(*colormap_index));
  if (colormap_index == (ssize_t *) NULL)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image->filename);
  if (image->storage_class != PseudoClass)
    {
      (void) memset(colormap_index,(-1),MaxColormapSize*
        sizeof(*colormap_index));
      if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
        {
          colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        }
      image->colors=0;
      status=MagickTrue;
      image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static) shared(status) \
        magick_number_threads(image,image,image->rows,1)
#endif
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register Quantum
          *magick_restrict q;

        register ssize_t
          x;

        if (status == MagickFalse)
          continue;
        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          {
            status=MagickFalse;
            continue;
          }
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          register size_t
            intensity;

          intensity=ScaleQuantumToMap(GetPixelRed(image,q));
          if (colormap_index[intensity] < 0)
            {
#if defined(MAGICKCORE_OPENMP_SUPPORT)
              #pragma omp critical (MagickCore_SetGrayscaleImage)
#endif
              if (colormap_index[intensity] < 0)
                {
                  colormap_index[intensity]=(ssize_t) image->colors;
                  image->colormap[image->colors].red=(double)
                    GetPixelRed(image,q);
                  image->colormap[image->colors].green=(double)
                    GetPixelGreen(image,q);
                  image->colormap[image->colors].blue=(double)
                    GetPixelBlue(image,q);
                  image->colors++;
               }
            }
          SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
          q+=GetPixelChannels(image);
        }
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
          status=MagickFalse;
      }
      image_view=DestroyCacheView(image_view);
    }
  for (i=0; i < (ssize_t) image->colors; i++)
    image->colormap[i].alpha=(double) i;
  qsort((void *) image->colormap,image->colors,sizeof(PixelInfo),
    IntensityCompare);
  colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
  if (colormap == (PixelInfo *) NULL)
    {
      colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
        image->filename);
    }
  j=0;
  colormap[j]=image->colormap[0];
  for (i=0; i < (ssize_t) image->colors; i++)
  {
    if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
      {
        j++;
        colormap[j]=image->colormap[i];
      }
    colormap_index[(ssize_t) image->colormap[i].alpha]=j;
  }
  image->colors=(size_t) (j+1);
  image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
  image->colormap=colormap;
  status=MagickTrue;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) shared(status) \
    magick_number_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
        GetPixelIndex(image,q))],q);
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
  }
  image_view=DestroyCacheView(image_view);
  colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
  image->type=GrayscaleType;
  if (SetImageMonochrome(image,exception) != MagickFalse)
    image->type=BilevelType;
  return(status);
}

In ImageMagick 7.0.8-40 Q16, there is a heap-based buffer over-read in the function WritePNMImage of coders/pnm.c, which allows an attacker to cause a denial of service or possibly information disclosure via a crafted image file. This is related to SetGrayscaleImage in MagickCore/quantize.c.


static MagickBooleanType SetGrayscaleImage(Image *image,
  ExceptionInfo *exception)
{
  CacheView
    *image_view;

  MagickBooleanType
    status;

  PixelInfo
    *colormap;

  register ssize_t
    i;

  ssize_t
    *colormap_index,
    j,
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->type != GrayscaleType)
    (void) TransformImageColorspace(image,GRAYColorspace,exception);
  if (image->storage_class == PseudoClass)
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(image->colors+1,
      MaxMap),sizeof(*colormap_index));
  else
    colormap_index=(ssize_t *) AcquireQuantumMemory(MagickMax(MaxColormapSize+1,
      MaxMap),sizeof(*colormap_index));
  if (colormap_index == (ssize_t *) NULL)
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
      image->filename);
  if (image->storage_class != PseudoClass)
    {
      (void) memset(colormap_index,(-1),MaxColormapSize*
        sizeof(*colormap_index));
      if (AcquireImageColormap(image,MaxColormapSize,exception) == MagickFalse)
        {
          colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
            image->filename);
        }
      image->colors=0;
      status=MagickTrue;
      image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static) shared(status) \
        magick_number_threads(image,image,image->rows,1)
#endif
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        register Quantum
          *magick_restrict q;

        register ssize_t
          x;

        if (status == MagickFalse)
          continue;
        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
          exception);
        if (q == (Quantum *) NULL)
          {
            status=MagickFalse;
            continue;
          }
        for (x=0; x < (ssize_t) image->columns; x++)
        {
          register size_t
            intensity;

          intensity=ScaleQuantumToMap(GetPixelRed(image,q));
          if (colormap_index[intensity] < 0)
            {
#if defined(MAGICKCORE_OPENMP_SUPPORT)
              #pragma omp critical (MagickCore_SetGrayscaleImage)
#endif
              if (colormap_index[intensity] < 0)
                {
                  colormap_index[intensity]=(ssize_t) image->colors;
                  image->colormap[image->colors].red=(double)
                    GetPixelRed(image,q);
                  image->colormap[image->colors].green=(double)
                    GetPixelGreen(image,q);
                  image->colormap[image->colors].blue=(double)
                    GetPixelBlue(image,q);
                  image->colors++;
               }
            }
          SetPixelIndex(image,(Quantum) colormap_index[intensity],q);
          q+=GetPixelChannels(image);
        }
        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
          status=MagickFalse;
      }
      image_view=DestroyCacheView(image_view);
    }
  for (i=0; i < (ssize_t) image->colors; i++)
    image->colormap[i].alpha=(double) i;
  qsort((void *) image->colormap,image->colors,sizeof(PixelInfo),
    IntensityCompare);
  colormap=(PixelInfo *) AcquireQuantumMemory(image->colors,sizeof(*colormap));
  if (colormap == (PixelInfo *) NULL)
    {
      colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
        image->filename);
    }
  j=0;
  colormap[j]=image->colormap[0];
  for (i=0; i < (ssize_t) image->colors; i++)
  {
    if (IsPixelInfoEquivalent(&colormap[j],&image->colormap[i]) == MagickFalse)
      {
        j++;
        colormap[j]=image->colormap[i];
      }
    colormap_index[(ssize_t) image->colormap[i].alpha]=j;
  }
  image->colors=(size_t) (j+1);
  image->colormap=(PixelInfo *) RelinquishMagickMemory(image->colormap);
  image->colormap=colormap;
  status=MagickTrue;
  image_view=AcquireAuthenticCacheView(image,exception);
#if defined(MAGICKCORE_OPENMP_SUPPORT)
  #pragma omp parallel for schedule(static) shared(status) \
    magick_number_threads(image,image,image->rows,1)
#endif
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    register Quantum
      *magick_restrict q;

    register ssize_t
      x;

    if (status == MagickFalse)
      continue;
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    if (q == (Quantum *) NULL)
      {
        status=MagickFalse;
        continue;
      }
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      SetPixelIndex(image,(Quantum) colormap_index[ScaleQuantumToMap(
        GetPixelIndex(image,q))],q);
      q+=GetPixelChannels(image);
    }
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
      status=MagickFalse;
  }
  image_view=DestroyCacheView(image_view);
  colormap_index=(ssize_t *) RelinquishMagickMemory(colormap_index);
  image->type=GrayscaleType;
  if (SetImageMonochrome(image,exception) != MagickFalse)
    image->type=BilevelType;
  return(status);
}


ALSA: control: Re-order bounds checking in get_ctl_id_hash()

These two checks are in the reverse order so it might read one element
beyond the end of the array.  First check if the "i" is within bounds
before using it.

Fixes: 6ab55ec0a938 ("ALSA: control: Fix an out-of-bounds bug in get_ctl_id_hash()")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Link: https://lore.kernel.org/r/YwjgNh/gkG1hH7po@kili
Signed-off-by: Takashi Iwai <tiwai@suse.de>

static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
{
	int i;
	unsigned long h;

	h = id->iface;
	h = MULTIPLIER * h + id->device;
	h = MULTIPLIER * h + id->subdevice;
	for (i = 0; id->name[i] && i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN; i++)
		h = MULTIPLIER * h + id->name[i];
	h = MULTIPLIER * h + id->index;
	h &= LONG_MAX;
	return h;
}

An out-of-bounds access issue was found in the Linux kernel sound subsystem. It could occur when the 'id->name' provided by the user did not end with '\0'. A privileged local user could pass a specially crafted name through ioctl() interface and crash the system or potentially escalate their privileges on the system.


static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
{
	int i;
	unsigned long h;

	h = id->iface;
	h = MULTIPLIER * h + id->device;
	h = MULTIPLIER * h + id->subdevice;
	for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
		h = MULTIPLIER * h + id->name[i];
	h = MULTIPLIER * h + id->index;
	h &= LONG_MAX;
	return h;
}


patch 9.0.0104: going beyond allocated memory when evaluating string constant

Problem:    Going beyond allocated memory when evaluating string constant.
Solution:   Properly skip over <Key> form.

eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
    char_u	*p;
    char_u	*end;
    int		extra = interpolate ? 1 : 0;
    int		off = interpolate ? 0 : 1;
    int		len;

    // Find the end of the string, skipping backslashed characters.
    for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p))
    {
	if (*p == '\\' && p[1] != NUL)
	{
	    ++p;
	    // A "\<x>" form occupies at least 4 characters, and produces up
	    // to 9 characters (6 for the char and 3 for a modifier):
	    // reserve space for 5 extra.
	    if (*p == '<')
		extra += 5;
	}
	else if (interpolate && (*p == '{' || *p == '}'))
	{
	    if (*p == '{' && p[1] != '{') // start of expression
		break;
	    ++p;
	    if (p[-1] == '}' && *p != '}') // single '}' is an error
	    {
		semsg(_(e_stray_closing_curly_str), *arg);
		return FAIL;
	    }
	    --extra;  // "{{" becomes "{", "}}" becomes "}"
	}
    }

    if (*p != '"' && !(interpolate && *p == '{'))
    {
	semsg(_(e_missing_double_quote_str), *arg);
	return FAIL;
    }

    // If only parsing, set *arg and return here
    if (!evaluate)
    {
	*arg = p + off;
	return OK;
    }

    // Copy the string into allocated memory, handling backslashed
    // characters.
    rettv->v_type = VAR_STRING;
    len = (int)(p - *arg + extra);
    rettv->vval.v_string = alloc(len);
    if (rettv->vval.v_string == NULL)
	return FAIL;
    end = rettv->vval.v_string;

    for (p = *arg + off; *p != NUL && *p != '"'; )
    {
	if (*p == '\\')
	{
	    switch (*++p)
	    {
		case 'b': *end++ = BS; ++p; break;
		case 'e': *end++ = ESC; ++p; break;
		case 'f': *end++ = FF; ++p; break;
		case 'n': *end++ = NL; ++p; break;
		case 'r': *end++ = CAR; ++p; break;
		case 't': *end++ = TAB; ++p; break;

		case 'X': // hex: "\x1", "\x12"
		case 'x':
		case 'u': // Unicode: "\u0023"
		case 'U':
			  if (vim_isxdigit(p[1]))
			  {
			      int	n, nr;
			      int	c = toupper(*p);

			      if (c == 'X')
				  n = 2;
			      else if (*p == 'u')
				  n = 4;
			      else
				  n = 8;
			      nr = 0;
			      while (--n >= 0 && vim_isxdigit(p[1]))
			      {
				  ++p;
				  nr = (nr << 4) + hex2nr(*p);
			      }
			      ++p;
			      // For "\u" store the number according to
			      // 'encoding'.
			      if (c != 'X')
				  end += (*mb_char2bytes)(nr, end);
			      else
				  *end++ = nr;
			  }
			  break;

			  // octal: "\1", "\12", "\123"
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7': *end = *p++ - '0';
			  if (*p >= '0' && *p <= '7')
			  {
			      *end = (*end << 3) + *p++ - '0';
			      if (*p >= '0' && *p <= '7')
				  *end = (*end << 3) + *p++ - '0';
			  }
			  ++end;
			  break;

			  // Special key, e.g.: "\<C-W>"
		case '<':
			  {
			      int flags = FSK_KEYCODE | FSK_IN_STRING;

			      if (p[1] != '*')
				  flags |= FSK_SIMPLIFY;
			      extra = trans_special(&p, end, flags, FALSE, NULL);
			      if (extra != 0)
			      {
				  end += extra;
				  if (end >= rettv->vval.v_string + len)
				      iemsg("eval_string() used more space than allocated");
				  break;
			      }
			  }
			  // FALLTHROUGH

		default: MB_COPY_CHAR(p, end);
			  break;
	    }
	}
	else
	{
	    if (interpolate && (*p == '{' || *p == '}'))
	    {
		if (*p == '{' && p[1] != '{') // start of expression
		    break;
		++p;  // reduce "{{" to "{" and "}}" to "}"
	    }
	    MB_COPY_CHAR(p, end);
	}
    }
    *end = NUL;
    if (*p == '"' && !interpolate)
	++p;
    *arg = p;

    return OK;
}

Heap-based Buffer Overflow in GitHub repository vim/vim prior to 9.0.0102.


eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
    char_u	*p;
    char_u	*end;
    int		extra = interpolate ? 1 : 0;
    int		off = interpolate ? 0 : 1;
    int		len;

    // Find the end of the string, skipping backslashed characters.
    for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p))
    {
	if (*p == '\\' && p[1] != NUL)
	{
	    ++p;
	    // A "\<x>" form occupies at least 4 characters, and produces up
	    // to 9 characters (6 for the char and 3 for a modifier):
	    // reserve space for 5 extra.
	    if (*p == '<')
	    {
		int		modifiers = 0;
		int		flags = FSK_KEYCODE | FSK_IN_STRING;

		extra += 5;

		// Skip to the '>' to avoid using '{' inside for string
		// interpolation.
		if (p[1] != '*')
		    flags |= FSK_SIMPLIFY;
		if (find_special_key(&p, &modifiers, flags, NULL) != 0)
		    --p;  // leave "p" on the ">"
	    }
	}
	else if (interpolate && (*p == '{' || *p == '}'))
	{
	    if (*p == '{' && p[1] != '{') // start of expression
		break;
	    ++p;
	    if (p[-1] == '}' && *p != '}') // single '}' is an error
	    {
		semsg(_(e_stray_closing_curly_str), *arg);
		return FAIL;
	    }
	    --extra;  // "{{" becomes "{", "}}" becomes "}"
	}
    }

    if (*p != '"' && !(interpolate && *p == '{'))
    {
	semsg(_(e_missing_double_quote_str), *arg);
	return FAIL;
    }

    // If only parsing, set *arg and return here
    if (!evaluate)
    {
	*arg = p + off;
	return OK;
    }

    // Copy the string into allocated memory, handling backslashed
    // characters.
    rettv->v_type = VAR_STRING;
    len = (int)(p - *arg + extra);
    rettv->vval.v_string = alloc(len);
    if (rettv->vval.v_string == NULL)
	return FAIL;
    end = rettv->vval.v_string;

    for (p = *arg + off; *p != NUL && *p != '"'; )
    {
	if (*p == '\\')
	{
	    switch (*++p)
	    {
		case 'b': *end++ = BS; ++p; break;
		case 'e': *end++ = ESC; ++p; break;
		case 'f': *end++ = FF; ++p; break;
		case 'n': *end++ = NL; ++p; break;
		case 'r': *end++ = CAR; ++p; break;
		case 't': *end++ = TAB; ++p; break;

		case 'X': // hex: "\x1", "\x12"
		case 'x':
		case 'u': // Unicode: "\u0023"
		case 'U':
			  if (vim_isxdigit(p[1]))
			  {
			      int	n, nr;
			      int	c = toupper(*p);

			      if (c == 'X')
				  n = 2;
			      else if (*p == 'u')
				  n = 4;
			      else
				  n = 8;
			      nr = 0;
			      while (--n >= 0 && vim_isxdigit(p[1]))
			      {
				  ++p;
				  nr = (nr << 4) + hex2nr(*p);
			      }
			      ++p;
			      // For "\u" store the number according to
			      // 'encoding'.
			      if (c != 'X')
				  end += (*mb_char2bytes)(nr, end);
			      else
				  *end++ = nr;
			  }
			  break;

			  // octal: "\1", "\12", "\123"
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7': *end = *p++ - '0';
			  if (*p >= '0' && *p <= '7')
			  {
			      *end = (*end << 3) + *p++ - '0';
			      if (*p >= '0' && *p <= '7')
				  *end = (*end << 3) + *p++ - '0';
			  }
			  ++end;
			  break;

			  // Special key, e.g.: "\<C-W>"
		case '<':
			  {
			      int flags = FSK_KEYCODE | FSK_IN_STRING;

			      if (p[1] != '*')
				  flags |= FSK_SIMPLIFY;
			      extra = trans_special(&p, end, flags, FALSE, NULL);
			      if (extra != 0)
			      {
				  end += extra;
				  if (end >= rettv->vval.v_string + len)
				      iemsg("eval_string() used more space than allocated");
				  break;
			      }
			  }
			  // FALLTHROUGH

		default: MB_COPY_CHAR(p, end);
			  break;
	    }
	}
	else
	{
	    if (interpolate && (*p == '{' || *p == '}'))
	    {
		if (*p == '{' && p[1] != '{') // start of expression
		    break;
		++p;  // reduce "{{" to "{" and "}}" to "}"
	    }
	    MB_COPY_CHAR(p, end);
	}
    }
    *end = NUL;
    if (*p == '"' && !interpolate)
	++p;
    *arg = p;

    return OK;
}


fix memory leaks in function cmdopts_parse

cmdopts_t *cmdopts_parse(int argc, char **argv)
{
	enum {
		CMDOPT_HELP = 0,
		CMDOPT_VERBOSE,
		CMDOPT_QUIET,
		CMDOPT_INFILE,
		CMDOPT_INFMT,
		CMDOPT_INOPT,
		CMDOPT_OUTFILE,
		CMDOPT_OUTFMT,
		CMDOPT_OUTOPT,
		CMDOPT_VERSION,
		CMDOPT_DEBUG,
		CMDOPT_CMPTNO,
		CMDOPT_SRGB,
		CMDOPT_MAXMEM,
		CMDOPT_LIST_ENABLED_CODECS,
		CMDOPT_LIST_ALL_CODECS,
		CMDOPT_ENABLE_FORMAT,
		CMDOPT_ENABLE_ALL_FORMATS,
	};

	static const jas_opt_t cmdoptions[] = {
		{CMDOPT_HELP, "help", 0},
		{CMDOPT_VERBOSE, "verbose", 0},
		{CMDOPT_QUIET, "quiet", 0},
		{CMDOPT_QUIET, "q", 0},
		{CMDOPT_INFILE, "input", JAS_OPT_HASARG},
		{CMDOPT_INFILE, "f", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "input-format", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "t", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "input-option", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "o", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "output", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "F", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "output-format", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "T", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "output-option", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "O", JAS_OPT_HASARG},
		{CMDOPT_VERSION, "version", 0},
		{CMDOPT_DEBUG, "debug-level", JAS_OPT_HASARG},
		{CMDOPT_CMPTNO, "cmptno", JAS_OPT_HASARG},
		{CMDOPT_SRGB, "force-srgb", 0},
		{CMDOPT_SRGB, "S", 0},
		{CMDOPT_MAXMEM, "memory-limit", JAS_OPT_HASARG},
		{CMDOPT_LIST_ENABLED_CODECS, "list-enabled-formats", 0},
		{CMDOPT_LIST_ALL_CODECS, "list-all-formats", 0},
		{CMDOPT_ENABLE_FORMAT, "enable-format", JAS_OPT_HASARG},
		{CMDOPT_ENABLE_ALL_FORMATS, "enable-all-formats", 0},
		{-1, 0, 0}
	};

	cmdopts_t *cmdopts;
	int c;

	if (!(cmdopts = malloc(sizeof(cmdopts_t)))) {
		fprintf(stderr, "error: insufficient memory\n");
		exit(EXIT_FAILURE);
	}

	cmdopts->infile = 0;
	cmdopts->infmt = -1;
	cmdopts->infmt_str = 0;
	cmdopts->inopts = 0;
	cmdopts->inoptsbuf[0] = '\0';
	cmdopts->outfile = 0;
	cmdopts->outfmt = -1;
	cmdopts->outfmt_str = 0;
	cmdopts->outopts = 0;
	cmdopts->outoptsbuf[0] = '\0';
	cmdopts->verbose = 0;
	cmdopts->version = 0;
	cmdopts->cmptno = -1;
	cmdopts->debug = 0;
	cmdopts->srgb = 0;
	cmdopts->list_codecs = 0;
	cmdopts->list_codecs_all = 0;
	cmdopts->help = 0;
	cmdopts->max_mem = get_default_max_mem_usage();
	cmdopts->enable_format = 0;
	cmdopts->enable_all_formats = 0;

	while ((c = jas_getopt(argc, argv, cmdoptions)) != EOF) {
		switch (c) {
		case CMDOPT_HELP:
			cmdopts->help = 1;
			break;
		case CMDOPT_VERBOSE:
			cmdopts->verbose = 1;
			break;
		case CMDOPT_QUIET:
			cmdopts->verbose = -1;
			break;
		case CMDOPT_VERSION:
			cmdopts->version = 1;
			break;
		case CMDOPT_LIST_ENABLED_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 0;
			break;
		case CMDOPT_LIST_ALL_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 1;
			break;
		case CMDOPT_DEBUG:
			cmdopts->debug = atoi(jas_optarg);
			break;
		case CMDOPT_INFILE:
			cmdopts->infile = jas_optarg;
			break;
		case CMDOPT_INFMT:
			cmdopts->infmt_str= jas_optarg;
			break;
		case CMDOPT_INOPT:
			addopt(cmdopts->inoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->inopts = cmdopts->inoptsbuf;
			break;
		case CMDOPT_OUTFILE:
			cmdopts->outfile = jas_optarg;
			break;
		case CMDOPT_OUTFMT:
			cmdopts->outfmt_str = jas_optarg;
			break;
		case CMDOPT_OUTOPT:
			addopt(cmdopts->outoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->outopts = cmdopts->outoptsbuf;
			break;
		case CMDOPT_CMPTNO:
			cmdopts->cmptno = atoi(jas_optarg);
			break;
		case CMDOPT_SRGB:
			cmdopts->srgb = 1;
			break;
		case CMDOPT_MAXMEM:
			cmdopts->max_mem = strtoull(jas_optarg, 0, 10);
			break;
		case CMDOPT_ENABLE_FORMAT:
			cmdopts->enable_format = jas_optarg;
			break;
		case CMDOPT_ENABLE_ALL_FORMATS:
			cmdopts->enable_all_formats = 1;
			break;
		default:
			badusage();
			break;
		}
	}

	while (jas_optind < argc) {
		fprintf(stderr,
		  "warning: ignoring bogus command line argument %s\n",
		  argv[jas_optind]);
		++jas_optind;
	}

	if (cmdopts->version || cmdopts->list_codecs || cmdopts->help) {
		goto done;
	}

	if (!cmdopts->outfmt_str && !cmdopts->outfile) {
		fprintf(stderr, "error: cannot determine output format\n");
		badusage();
	}

done:
	return cmdopts;
}

A vulnerability found in jasper. This security vulnerability happens because of a memory leak bug in function cmdopts_parse that can cause a crash or segmentation fault.


cmdopts_t *cmdopts_parse(int argc, char **argv)
{
	enum {
		CMDOPT_HELP = 0,
		CMDOPT_VERBOSE,
		CMDOPT_QUIET,
		CMDOPT_INFILE,
		CMDOPT_INFMT,
		CMDOPT_INOPT,
		CMDOPT_OUTFILE,
		CMDOPT_OUTFMT,
		CMDOPT_OUTOPT,
		CMDOPT_VERSION,
		CMDOPT_DEBUG,
		CMDOPT_CMPTNO,
		CMDOPT_SRGB,
		CMDOPT_MAXMEM,
		CMDOPT_LIST_ENABLED_CODECS,
		CMDOPT_LIST_ALL_CODECS,
		CMDOPT_ENABLE_FORMAT,
		CMDOPT_ENABLE_ALL_FORMATS,
	};

	static const jas_opt_t cmdoptions[] = {
		{CMDOPT_HELP, "help", 0},
		{CMDOPT_VERBOSE, "verbose", 0},
		{CMDOPT_QUIET, "quiet", 0},
		{CMDOPT_QUIET, "q", 0},
		{CMDOPT_INFILE, "input", JAS_OPT_HASARG},
		{CMDOPT_INFILE, "f", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "input-format", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "t", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "input-option", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "o", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "output", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "F", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "output-format", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "T", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "output-option", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "O", JAS_OPT_HASARG},
		{CMDOPT_VERSION, "version", 0},
		{CMDOPT_DEBUG, "debug-level", JAS_OPT_HASARG},
		{CMDOPT_CMPTNO, "cmptno", JAS_OPT_HASARG},
		{CMDOPT_SRGB, "force-srgb", 0},
		{CMDOPT_SRGB, "S", 0},
		{CMDOPT_MAXMEM, "memory-limit", JAS_OPT_HASARG},
		{CMDOPT_LIST_ENABLED_CODECS, "list-enabled-formats", 0},
		{CMDOPT_LIST_ALL_CODECS, "list-all-formats", 0},
		{CMDOPT_ENABLE_FORMAT, "enable-format", JAS_OPT_HASARG},
		{CMDOPT_ENABLE_ALL_FORMATS, "enable-all-formats", 0},
		{-1, 0, 0}
	};

	cmdopts_t *cmdopts;
	int c;

	if (!(cmdopts = malloc(sizeof(cmdopts_t)))) {
		fprintf(stderr, "error: insufficient memory\n");
		exit(EXIT_FAILURE);
	}

	cmdopts->infile = 0;
	cmdopts->infmt = -1;
	cmdopts->infmt_str = 0;
	cmdopts->inopts = 0;
	cmdopts->inoptsbuf[0] = '\0';
	cmdopts->outfile = 0;
	cmdopts->outfmt = -1;
	cmdopts->outfmt_str = 0;
	cmdopts->outopts = 0;
	cmdopts->outoptsbuf[0] = '\0';
	cmdopts->verbose = 0;
	cmdopts->version = 0;
	cmdopts->cmptno = -1;
	cmdopts->debug = 0;
	cmdopts->srgb = 0;
	cmdopts->list_codecs = 0;
	cmdopts->list_codecs_all = 0;
	cmdopts->help = 0;
	cmdopts->max_mem = get_default_max_mem_usage();
	cmdopts->enable_format = 0;
	cmdopts->enable_all_formats = 0;

	while ((c = jas_getopt(argc, argv, cmdoptions)) != EOF) {
		switch (c) {
		case CMDOPT_HELP:
			cmdopts->help = 1;
			break;
		case CMDOPT_VERBOSE:
			cmdopts->verbose = 1;
			break;
		case CMDOPT_QUIET:
			cmdopts->verbose = -1;
			break;
		case CMDOPT_VERSION:
			cmdopts->version = 1;
			break;
		case CMDOPT_LIST_ENABLED_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 0;
			break;
		case CMDOPT_LIST_ALL_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 1;
			break;
		case CMDOPT_DEBUG:
			cmdopts->debug = atoi(jas_optarg);
			break;
		case CMDOPT_INFILE:
			cmdopts->infile = jas_optarg;
			break;
		case CMDOPT_INFMT:
			cmdopts->infmt_str= jas_optarg;
			break;
		case CMDOPT_INOPT:
			addopt(cmdopts->inoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->inopts = cmdopts->inoptsbuf;
			break;
		case CMDOPT_OUTFILE:
			cmdopts->outfile = jas_optarg;
			break;
		case CMDOPT_OUTFMT:
			cmdopts->outfmt_str = jas_optarg;
			break;
		case CMDOPT_OUTOPT:
			addopt(cmdopts->outoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->outopts = cmdopts->outoptsbuf;
			break;
		case CMDOPT_CMPTNO:
			cmdopts->cmptno = atoi(jas_optarg);
			break;
		case CMDOPT_SRGB:
			cmdopts->srgb = 1;
			break;
		case CMDOPT_MAXMEM:
			cmdopts->max_mem = strtoull(jas_optarg, 0, 10);
			break;
		case CMDOPT_ENABLE_FORMAT:
			cmdopts->enable_format = jas_optarg;
			break;
		case CMDOPT_ENABLE_ALL_FORMATS:
			cmdopts->enable_all_formats = 1;
			break;
		default:
			cmdopts_destroy(cmdopts);
			badusage();
			break;
		}
	}

	while (jas_optind < argc) {
		fprintf(stderr,
		  "warning: ignoring bogus command line argument %s\n",
		  argv[jas_optind]);
		++jas_optind;
	}

	if (cmdopts->version || cmdopts->list_codecs || cmdopts->help) {
		goto done;
	}

	if (!cmdopts->outfmt_str && !cmdopts->outfile) {
		fprintf(stderr, "error: cannot determine output format\n");
		cmdopts_destroy(cmdopts);
		badusage();
	}

done:
	return cmdopts;
}


PCI: rpadlpar: Fix potential drc_name corruption in store functions

Both add_slot_store() and remove_slot_store() try to fix up the
drc_name copied from the store buffer by placing a NUL terminator at
nbyte + 1 or in place of a '\n' if present. However, the static buffer
that we copy the drc_name data into is not zeroed and can contain
anything past the n-th byte.

This is problematic if a '\n' byte appears in that buffer after nbytes
and the string copied into the store buffer was not NUL terminated to
start with as the strchr() search for a '\n' byte will mark this
incorrectly as the end of the drc_name string resulting in a drc_name
string that contains garbage data after the n-th byte.

Additionally it will cause us to overwrite that '\n' byte on the stack
with NUL, potentially corrupting data on the stack.

The following debugging shows an example of the drmgr utility writing
"PHB 4543" to the add_slot sysfs attribute, but add_slot_store()
logging a corrupted string value.

  drmgr: drmgr: -c phb -a -s PHB 4543 -d 1
  add_slot_store: drc_name = PHB 4543°|<82>!, rc = -19

Fix this by using strscpy() instead of memcpy() to ensure the string
is NUL terminated when copied into the static drc_name buffer.
Further, since the string is now NUL terminated the code only needs to
change '\n' to '\0' when present.

Cc: stable@vger.kernel.org
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
[mpe: Reformat change log and add mention of possible stack corruption]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210315214821.452959-1-tyreld@linux.ibm.com

static ssize_t remove_slot_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	int rc;
	char *end;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	rc = dlpar_remove_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}

In drivers/pci/hotplug/rpadlpar_sysfs.c in the Linux kernel through 5.11.8, the RPA PCI Hotplug driver has a user-tolerable buffer overflow when writing a new device name to the driver from userspace, allowing userspace to write data to the kernel stack frame directly. This occurs because add_slot_store and remove_slot_store mishandle drc_name '\0' termination, aka CID-cc7a0bb058b8.


static ssize_t remove_slot_store(struct kobject *kobj,
				 struct kobj_attribute *attr,
				 const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	int rc;
	char *end;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	strscpy(drc_name, buf, nbytes + 1);

	end = strchr(drc_name, '\n');
	if (end)
		*end = '\0';

	rc = dlpar_remove_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}


static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;
	int rc;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	memcpy(drc_name, buf, nbytes);

	end = strchr(drc_name, '\n');
	if (!end)
		end = &drc_name[nbytes];
	*end = '\0';

	rc = dlpar_add_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}


static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
			      const char *buf, size_t nbytes)
{
	char drc_name[MAX_DRC_NAME_LEN];
	char *end;
	int rc;

	if (nbytes >= MAX_DRC_NAME_LEN)
		return 0;

	strscpy(drc_name, buf, nbytes + 1);

	end = strchr(drc_name, '\n');
	if (end)
		*end = '\0';

	rc = dlpar_add_slot(drc_name);
	if (rc)
		return rc;

	return nbytes;
}


static SQInteger thread_call(HSQUIRRELVM v)
{
    SQObjectPtr o = stack_get(v,1);
    if(sq_type(o) == OT_THREAD) {
        SQInteger nparams = sq_gettop(v);
        _thread(o)->Push(_thread(o)->_roottable);
        for(SQInteger i = 2; i<(nparams+1); i++)
            sq_move(_thread(o),v,i);
        if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) {
            sq_move(v,_thread(o),-1);
            sq_pop(_thread(o),1);
            return 1;
        }
        v->_lasterror = _thread(o)->_lasterror;
        return SQ_ERROR;
    }
    return sq_throwerror(v,_SC("wrong parameter"));
}

Heap-based buffer overflow in sqbaselib.cpp in SQUIRREL 3.2 due to lack of a certain sq_reservestack call.


static SQInteger thread_call(HSQUIRRELVM v)
{
    SQObjectPtr o = stack_get(v,1);
    if(sq_type(o) == OT_THREAD) {
        SQInteger nparams = sq_gettop(v);
        sq_reservestack(_thread(o), nparams + 3);
        _thread(o)->Push(_thread(o)->_roottable);
        for(SQInteger i = 2; i<(nparams+1); i++)
            sq_move(_thread(o),v,i);
        if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) {
            sq_move(v,_thread(o),-1);
            sq_pop(_thread(o),1);
            return 1;
        }
        v->_lasterror = _thread(o)->_lasterror;
        return SQ_ERROR;
    }
    return sq_throwerror(v,_SC("wrong parameter"));
}


input-bmp: Increase header buffer in some cases

Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>

at_bitmap input_bmp_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data)
{
  FILE *fd;
  unsigned char buffer[64];
  int ColormapSize, rowbytes, Maps;
  gboolean Grey = FALSE;
  unsigned char ColorMap[256][3];
  at_bitmap image = at_bitmap_init(0, 0, 0, 1);
  unsigned char *image_storage;
  at_exception_type exp = at_exception_new(msg_func, msg_data);
  char magick[2];
  Bitmap_Channel masks[4];

  fd = fopen(filename, "rb");

  if (!fd) {
    LOG("Can't open \"%s\"\n", filename);
    at_exception_fatal(&exp, "bmp: cannot open input file");
    goto cleanup;
  }

  /* It is a File. Now is it a Bitmap? Read the shortest possible header. */

  if (!ReadOK(fd, magick, 2) ||
	  !(!strncmp(magick, "BA", 2) ||
		  !strncmp(magick, "BM", 2) ||
		  !strncmp(magick, "IC", 2) ||
		  !strncmp(magick, "PT", 2) ||
		  !strncmp(magick, "CI", 2) ||
		  !strncmp(magick, "CP", 2)))
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  while (!strncmp(magick, "BA", 2))
  {
	  if (!ReadOK(fd, buffer, 12))
	  {
		  LOG("%s is not a valid BMP file", filename);
		  at_exception_fatal(&exp, "bmp: invalid input file");
		  goto cleanup;
	  }

	  if (!ReadOK(fd, magick, 2))
	  {
		  LOG("%s is not a valid BMP file", filename);
		  at_exception_fatal(&exp, "bmp: invalid input file");
		  goto cleanup;
	  }
  }

  if (!ReadOK(fd, buffer, 12))////
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* bring them to the right byteorder. Not too nice, but it should work */

  Bitmap_File_Head.bfSize = ToL(&buffer[0x00]);
  Bitmap_File_Head.zzHotX = ToS(&buffer[0x04]);
  Bitmap_File_Head.zzHotY = ToS(&buffer[0x06]);
  Bitmap_File_Head.bfOffs = ToL(&buffer[0x08]);

  if (!ReadOK(fd, buffer, 4))
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  Bitmap_File_Head.biSize = ToL(&buffer[0x00]);

  /* What kind of bitmap is it? */

  if (Bitmap_File_Head.biSize == 12) {  /* OS/2 1.x ? */
    if (!ReadOK(fd, buffer, 8)) {
      LOG("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToS(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToS(&buffer[0x02]);  /* 14 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x06]);  /* 18 */
    Bitmap_Head.biCompr = 0;
    Bitmap_Head.biSizeIm = 0;
    Bitmap_Head.biXPels = Bitmap_Head.biYPels = 0;
    Bitmap_Head.biClrUsed = 0;
    Bitmap_Head.biClrImp = 0;
    Bitmap_Head.masks[0] = 0;
    Bitmap_Head.masks[1] = 0;
    Bitmap_Head.masks[2] = 0;
    Bitmap_Head.masks[3] = 0;

    memset(masks, 0, sizeof(masks));
    Maps = 3;

  } else if (Bitmap_File_Head.biSize == 40) { /* Windows 3.x */
    if (!ReadOK(fd, buffer, 36))
    {
      LOG ("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);  /* 1A */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);  /* 1C */
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);  /* 22 */
    Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */
    Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);  /* 32 */
    Bitmap_Head.masks[0] = 0;
    Bitmap_Head.masks[1] = 0;
    Bitmap_Head.masks[2] = 0;
    Bitmap_Head.masks[3] = 0;

    Maps = 4;
    memset(masks, 0, sizeof(masks));

    if (Bitmap_Head.biCompr == BI_BITFIELDS)
      {
	if (!ReadOK(fd, buffer, 3 * sizeof(unsigned long)))
	  {
	    LOG("Error reading BMP file header\n");
	    at_exception_fatal(&exp, "Error reading BMP file header");
	    goto cleanup;
	  }

	Bitmap_Head.masks[0] = ToL(&buffer[0x00]);
	Bitmap_Head.masks[1] = ToL(&buffer[0x04]);
	Bitmap_Head.masks[2] = ToL(&buffer[0x08]);

	ReadChannelMasks(&Bitmap_Head.masks[0], masks, 3);
      }
    else if (Bitmap_Head.biCompr == BI_RGB)
      {
	setMasksDefault(Bitmap_Head.biBitCnt, masks);
      }
    else if ((Bitmap_Head.biCompr != BI_RLE4) &&
	     (Bitmap_Head.biCompr != BI_RLE8))
      {
	/* BI_ALPHABITFIELDS, etc. */
	LOG("Unsupported compression in BMP file\n");
	at_exception_fatal(&exp, "Unsupported compression in BMP file");
	goto cleanup;
      }
  }
  else if (Bitmap_File_Head.biSize >= 56 &&
	   Bitmap_File_Head.biSize <= 64)
  {
    /* enhanced Windows format with bit masks */

    if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
    {

      LOG("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);  /* 1A */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);  /* 1C */
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);  /* 22 */
    Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */
    Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);  /* 32 */
    Bitmap_Head.masks[0] = ToL(&buffer[0x24]);       /* 36 */
    Bitmap_Head.masks[1] = ToL(&buffer[0x28]);       /* 3A */
    Bitmap_Head.masks[2] = ToL(&buffer[0x2C]);       /* 3E */
    Bitmap_Head.masks[3] = ToL(&buffer[0x30]);       /* 42 */

    Maps = 4;
    ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4);
  }
  else if (Bitmap_File_Head.biSize == 108 ||
           Bitmap_File_Head.biSize == 124)
  {
    /* BMP Version 4 or 5 */

    if (!ReadOK(fd, buffer, Bitmap_File_Head.biSize - 4))
    {
	    LOG("Error reading BMP file header\n");
	    at_exception_fatal(&exp, "Error reading BMP file header");
	    goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]);
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]);
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);
    Bitmap_Head.biXPels = ToL(&buffer[0x14]);
    Bitmap_Head.biYPels = ToL(&buffer[0x18]);
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]);
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);
    Bitmap_Head.masks[0] = ToL(&buffer[0x24]);
    Bitmap_Head.masks[1] = ToL(&buffer[0x28]);
    Bitmap_Head.masks[2] = ToL(&buffer[0x2C]);
    Bitmap_Head.masks[3] = ToL(&buffer[0x30]);

    Maps = 4;

    if (Bitmap_Head.biCompr == BI_BITFIELDS)
    {
	    ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4);
    }
    else if (Bitmap_Head.biCompr == BI_RGB)
    {
	    setMasksDefault(Bitmap_Head.biBitCnt, masks);
    }
  } else {
    LOG("Error reading BMP file header\n");
    at_exception_fatal(&exp, "Error reading BMP file header");
    goto cleanup;
  }

  /* Valid options 1, 4, 8, 16, 24, 32 */
  /* 16 is awful, we should probably shoot whoever invented it */

  switch (Bitmap_Head.biBitCnt)
  {
  case 1:
  case 2:
  case 4:
  case 8:
  case 16:
  case 24:
  case 32:
	  break;
  default:
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* There should be some colors used! */

  ColormapSize = (Bitmap_File_Head.bfOffs - Bitmap_File_Head.biSize - 14) / Maps;

  if ((Bitmap_Head.biClrUsed == 0) &&
      (Bitmap_Head.biBitCnt <= 8))
  {
	  ColormapSize = Bitmap_Head.biClrUsed = 1 << Bitmap_Head.biBitCnt;
  }

  if (ColormapSize > 256)
    ColormapSize = 256;

  /* Sanity checks */

  if (Bitmap_Head.biHeight == 0 ||
	  Bitmap_Head.biWidth == 0)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* biHeight may be negative, but -2147483648 is dangerous because:
	 -2147483648 == -(-2147483648) */
  if (Bitmap_Head.biWidth < 0 ||
	  Bitmap_Head.biHeight == -2147483648)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  if (Bitmap_Head.biPlanes != 1)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  if (Bitmap_Head.biClrUsed > 256 &&
	  Bitmap_Head.biBitCnt <= 8)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* protect against integer overflows caused by malicious BMPs */
  /* use divisions in comparisons to avoid type overflows */

  if (((unsigned long)Bitmap_Head.biWidth) > (unsigned int)0x7fffffff / Bitmap_Head.biBitCnt ||
	  ((unsigned long)Bitmap_Head.biWidth) > ((unsigned int)0x7fffffff /abs(Bitmap_Head.biHeight)) / 4)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* Windows and OS/2 declare filler so that rows are a multiple of
   * word length (32 bits == 4 bytes)
   */

  unsigned long overflowTest = Bitmap_Head.biWidth * Bitmap_Head.biBitCnt;
  if (overflowTest / Bitmap_Head.biWidth != Bitmap_Head.biBitCnt) {
    LOG("Error reading BMP file header. Width is too large\n");
    at_exception_fatal(&exp, "Error reading BMP file header. Width is too large");
    goto cleanup;
  }

  rowbytes = ((Bitmap_Head.biWidth * Bitmap_Head.biBitCnt - 1) / 32) * 4 + 4;

#ifdef DEBUG
  printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n", Bitmap_File_Head.bfSize, Bitmap_Head.biClrUsed, Bitmap_Head.biBitCnt, Bitmap_Head.biWidth, Bitmap_Head.biHeight, Bitmap_Head.biCompr, rowbytes);
#endif

  if (Bitmap_Head.biBitCnt <= 8)
  {
#ifdef DEBUG
    printf("Colormap read\n");
#endif
	  /* Get the Colormap */
	  if (!ReadColorMap(fd, ColorMap, ColormapSize, Maps, &Grey, &exp))
		  goto cleanup;
  }

  fseek(fd, Bitmap_File_Head.bfOffs, SEEK_SET);

  /* Get the Image and return the ID or -1 on error */
  image_storage = ReadImage(fd,
	Bitmap_Head.biWidth, Bitmap_Head.biHeight,
	ColorMap,
        Bitmap_Head.biClrUsed,
	Bitmap_Head.biBitCnt, Bitmap_Head.biCompr, rowbytes,
        Grey,
	masks,
	&exp);

  image = at_bitmap_init(image_storage, (unsigned short)Bitmap_Head.biWidth, (unsigned short)Bitmap_Head.biHeight, Grey ? 1 : 3);
cleanup:
  fclose(fd);
  return (image);
}

AutoTrace v0.40.0 was discovered to contain a heap overflow via the ReadImage function at input-bmp.c:660.


at_bitmap input_bmp_reader(gchar * filename, at_input_opts_type * opts, at_msg_func msg_func, gpointer msg_data, gpointer user_data)
{
  FILE *fd;
  unsigned char buffer[128];
  int ColormapSize, rowbytes, Maps;
  gboolean Grey = FALSE;
  unsigned char ColorMap[256][3];
  at_bitmap image = at_bitmap_init(0, 0, 0, 1);
  unsigned char *image_storage;
  at_exception_type exp = at_exception_new(msg_func, msg_data);
  char magick[2];
  Bitmap_Channel masks[4];

  fd = fopen(filename, "rb");

  if (!fd) {
    LOG("Can't open \"%s\"\n", filename);
    at_exception_fatal(&exp, "bmp: cannot open input file");
    goto cleanup;
  }

  /* It is a File. Now is it a Bitmap? Read the shortest possible header. */

  if (!ReadOK(fd, magick, 2) ||
	  !(!strncmp(magick, "BA", 2) ||
		  !strncmp(magick, "BM", 2) ||
		  !strncmp(magick, "IC", 2) ||
		  !strncmp(magick, "PT", 2) ||
		  !strncmp(magick, "CI", 2) ||
		  !strncmp(magick, "CP", 2)))
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  while (!strncmp(magick, "BA", 2))
  {
	  if (!ReadOK(fd, buffer, 12))
	  {
		  LOG("%s is not a valid BMP file", filename);
		  at_exception_fatal(&exp, "bmp: invalid input file");
		  goto cleanup;
	  }

	  if (!ReadOK(fd, magick, 2))
	  {
		  LOG("%s is not a valid BMP file", filename);
		  at_exception_fatal(&exp, "bmp: invalid input file");
		  goto cleanup;
	  }
  }

  if (!ReadOK(fd, buffer, 12))////
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* bring them to the right byteorder. Not too nice, but it should work */

  Bitmap_File_Head.bfSize = ToL(&buffer[0x00]);
  Bitmap_File_Head.zzHotX = ToS(&buffer[0x04]);
  Bitmap_File_Head.zzHotY = ToS(&buffer[0x06]);
  Bitmap_File_Head.bfOffs = ToL(&buffer[0x08]);

  if (!ReadOK(fd, buffer, 4))
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  Bitmap_File_Head.biSize = ToL(&buffer[0x00]);

  /* What kind of bitmap is it? */

  if (Bitmap_File_Head.biSize == 12) {  /* OS/2 1.x ? */
    if (!ReadOK(fd, buffer, 8)) {
      LOG("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToS(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToS(&buffer[0x02]);  /* 14 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x06]);  /* 18 */
    Bitmap_Head.biCompr = 0;
    Bitmap_Head.biSizeIm = 0;
    Bitmap_Head.biXPels = Bitmap_Head.biYPels = 0;
    Bitmap_Head.biClrUsed = 0;
    Bitmap_Head.biClrImp = 0;
    Bitmap_Head.masks[0] = 0;
    Bitmap_Head.masks[1] = 0;
    Bitmap_Head.masks[2] = 0;
    Bitmap_Head.masks[3] = 0;

    memset(masks, 0, sizeof(masks));
    Maps = 3;

  } else if (Bitmap_File_Head.biSize == 40) { /* Windows 3.x */
    if (!ReadOK(fd, buffer, 36))
    {
      LOG ("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);  /* 1A */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);  /* 1C */
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);  /* 22 */
    Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */
    Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);  /* 32 */
    Bitmap_Head.masks[0] = 0;
    Bitmap_Head.masks[1] = 0;
    Bitmap_Head.masks[2] = 0;
    Bitmap_Head.masks[3] = 0;

    Maps = 4;
    memset(masks, 0, sizeof(masks));

    if (Bitmap_Head.biCompr == BI_BITFIELDS)
      {
	if (!ReadOK(fd, buffer, 3 * sizeof(unsigned long)))
	  {
	    LOG("Error reading BMP file header\n");
	    at_exception_fatal(&exp, "Error reading BMP file header");
	    goto cleanup;
	  }

	Bitmap_Head.masks[0] = ToL(&buffer[0x00]);
	Bitmap_Head.masks[1] = ToL(&buffer[0x04]);
	Bitmap_Head.masks[2] = ToL(&buffer[0x08]);

	ReadChannelMasks(&Bitmap_Head.masks[0], masks, 3);
      }
    else if (Bitmap_Head.biCompr == BI_RGB)
      {
	setMasksDefault(Bitmap_Head.biBitCnt, masks);
      }
    else if ((Bitmap_Head.biCompr != BI_RLE4) &&
	     (Bitmap_Head.biCompr != BI_RLE8))
      {
	/* BI_ALPHABITFIELDS, etc. */
	LOG("Unsupported compression in BMP file\n");
	at_exception_fatal(&exp, "Unsupported compression in BMP file");
	goto cleanup;
      }
  }
  else if (Bitmap_File_Head.biSize >= 56 &&
	   Bitmap_File_Head.biSize <= 64)
  {
    /* enhanced Windows format with bit masks */

    if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
    {

      LOG("Error reading BMP file header\n");
      at_exception_fatal(&exp, "Error reading BMP file header");
      goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]); /* 12 */
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);  /* 16 */
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);  /* 1A */
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);  /* 1C */
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]); /* 1E */
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);  /* 22 */
    Bitmap_Head.biXPels = ToL(&buffer[0x14]); /* 26 */
    Bitmap_Head.biYPels = ToL(&buffer[0x18]); /* 2A */
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]); /* 2E */
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);  /* 32 */
    Bitmap_Head.masks[0] = ToL(&buffer[0x24]);       /* 36 */
    Bitmap_Head.masks[1] = ToL(&buffer[0x28]);       /* 3A */
    Bitmap_Head.masks[2] = ToL(&buffer[0x2C]);       /* 3E */
    Bitmap_Head.masks[3] = ToL(&buffer[0x30]);       /* 42 */

    Maps = 4;
    ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4);
  }
  else if (Bitmap_File_Head.biSize == 108 ||
           Bitmap_File_Head.biSize == 124)
  {
    /* BMP Version 4 or 5 */

    if (!ReadOK(fd, buffer, Bitmap_File_Head.biSize - 4))
    {
	    LOG("Error reading BMP file header\n");
	    at_exception_fatal(&exp, "Error reading BMP file header");
	    goto cleanup;
    }

    Bitmap_Head.biWidth = ToL(&buffer[0x00]);
    Bitmap_Head.biHeight = ToL(&buffer[0x04]);
    Bitmap_Head.biPlanes = ToS(&buffer[0x08]);
    Bitmap_Head.biBitCnt = ToS(&buffer[0x0A]);
    Bitmap_Head.biCompr = ToL(&buffer[0x0C]);
    Bitmap_Head.biSizeIm = ToL(&buffer[0x10]);
    Bitmap_Head.biXPels = ToL(&buffer[0x14]);
    Bitmap_Head.biYPels = ToL(&buffer[0x18]);
    Bitmap_Head.biClrUsed = ToL(&buffer[0x1C]);
    Bitmap_Head.biClrImp = ToL(&buffer[0x20]);
    Bitmap_Head.masks[0] = ToL(&buffer[0x24]);
    Bitmap_Head.masks[1] = ToL(&buffer[0x28]);
    Bitmap_Head.masks[2] = ToL(&buffer[0x2C]);
    Bitmap_Head.masks[3] = ToL(&buffer[0x30]);

    Maps = 4;

    if (Bitmap_Head.biCompr == BI_BITFIELDS)
    {
	    ReadChannelMasks(&Bitmap_Head.masks[0], masks, 4);
    }
    else if (Bitmap_Head.biCompr == BI_RGB)
    {
	    setMasksDefault(Bitmap_Head.biBitCnt, masks);
    }
  } else {
    LOG("Error reading BMP file header\n");
    at_exception_fatal(&exp, "Error reading BMP file header");
    goto cleanup;
  }

  /* Valid options 1, 4, 8, 16, 24, 32 */
  /* 16 is awful, we should probably shoot whoever invented it */

  switch (Bitmap_Head.biBitCnt)
  {
  case 1:
  case 2:
  case 4:
  case 8:
  case 16:
  case 24:
  case 32:
	  break;
  default:
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* There should be some colors used! */

  ColormapSize = (Bitmap_File_Head.bfOffs - Bitmap_File_Head.biSize - 14) / Maps;

  if ((Bitmap_Head.biClrUsed == 0) &&
      (Bitmap_Head.biBitCnt <= 8))
  {
	  ColormapSize = Bitmap_Head.biClrUsed = 1 << Bitmap_Head.biBitCnt;
  }

  if (ColormapSize > 256)
    ColormapSize = 256;

  /* Sanity checks */

  if (Bitmap_Head.biHeight == 0 ||
	  Bitmap_Head.biWidth == 0)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* biHeight may be negative, but -2147483648 is dangerous because:
	 -2147483648 == -(-2147483648) */
  if (Bitmap_Head.biWidth < 0 ||
	  Bitmap_Head.biHeight == -2147483648)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  if (Bitmap_Head.biPlanes != 1)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  if (Bitmap_Head.biClrUsed > 256 &&
	  Bitmap_Head.biBitCnt <= 8)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* protect against integer overflows caused by malicious BMPs */
  /* use divisions in comparisons to avoid type overflows */

  if (((unsigned long)Bitmap_Head.biWidth) > (unsigned int)0x7fffffff / Bitmap_Head.biBitCnt ||
	  ((unsigned long)Bitmap_Head.biWidth) > ((unsigned int)0x7fffffff /abs(Bitmap_Head.biHeight)) / 4)
  {
	  LOG("%s is not a valid BMP file", filename);
	  at_exception_fatal(&exp, "bmp: invalid input file");
	  goto cleanup;
  }

  /* Windows and OS/2 declare filler so that rows are a multiple of
   * word length (32 bits == 4 bytes)
   */

  unsigned long overflowTest = Bitmap_Head.biWidth * Bitmap_Head.biBitCnt;
  if (overflowTest / Bitmap_Head.biWidth != Bitmap_Head.biBitCnt) {
    LOG("Error reading BMP file header. Width is too large\n");
    at_exception_fatal(&exp, "Error reading BMP file header. Width is too large");
    goto cleanup;
  }

  rowbytes = ((Bitmap_Head.biWidth * Bitmap_Head.biBitCnt - 1) / 32) * 4 + 4;

#ifdef DEBUG
  printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n", Bitmap_File_Head.bfSize, Bitmap_Head.biClrUsed, Bitmap_Head.biBitCnt, Bitmap_Head.biWidth, Bitmap_Head.biHeight, Bitmap_Head.biCompr, rowbytes);
#endif

  if (Bitmap_Head.biBitCnt <= 8)
  {
#ifdef DEBUG
    printf("Colormap read\n");
#endif
	  /* Get the Colormap */
	  if (!ReadColorMap(fd, ColorMap, ColormapSize, Maps, &Grey, &exp))
		  goto cleanup;
  }

  fseek(fd, Bitmap_File_Head.bfOffs, SEEK_SET);

  /* Get the Image and return the ID or -1 on error */
  image_storage = ReadImage(fd,
	Bitmap_Head.biWidth, Bitmap_Head.biHeight,
	ColorMap,
        Bitmap_Head.biClrUsed,
	Bitmap_Head.biBitCnt, Bitmap_Head.biCompr, rowbytes,
        Grey,
	masks,
	&exp);

  image = at_bitmap_init(image_storage, (unsigned short)Bitmap_Head.biWidth, (unsigned short)Bitmap_Head.biHeight, Grey ? 1 : 3);
cleanup:
  fclose(fd);
  return (image);
}


Fix oob write for dwarf with abbrev with count 0 (Fix #2083) (#2086)

static const ut8 *parse_die(const ut8 *buf, const ut8 *buf_end, RzBinDwarfDebugInfo *info, RzBinDwarfAbbrevDecl *abbrev,
	RzBinDwarfCompUnitHdr *hdr, RzBinDwarfDie *die, const ut8 *debug_str, size_t debug_str_len, bool big_endian) {
	size_t i;
	const char *comp_dir = NULL;
	ut64 line_info_offset = UT64_MAX;
	for (i = 0; i < abbrev->count - 1; i++) {
		memset(&die->attr_values[i], 0, sizeof(die->attr_values[i]));

		buf = parse_attr_value(buf, buf_end - buf, &abbrev->defs[i],
			&die->attr_values[i], hdr, debug_str, debug_str_len, big_endian);

		RzBinDwarfAttrValue *attribute = &die->attr_values[i];

		if (attribute->attr_name == DW_AT_comp_dir && (attribute->attr_form == DW_FORM_strp || attribute->attr_form == DW_FORM_string) && attribute->string.content) {
			comp_dir = attribute->string.content;
		}
		if (attribute->attr_name == DW_AT_stmt_list) {
			if (attribute->kind == DW_AT_KIND_CONSTANT) {
				line_info_offset = attribute->uconstant;
			} else if (attribute->kind == DW_AT_KIND_REFERENCE) {
				line_info_offset = attribute->reference;
			}
		}
		die->count++;
	}

	// If this is a compilation unit dir attribute, we want to cache it so the line info parsing
	// which will need this info can quickly look it up.
	if (comp_dir && line_info_offset != UT64_MAX) {
		char *name = strdup(comp_dir);
		if (name) {
			if (!ht_up_insert(info->line_info_offset_comp_dir, line_info_offset, name)) {
				free(name);
			}
		}
	}

	return buf;
}

Rizin is a UNIX-like reverse engineering framework and command-line toolset. In versions up to and including 0.3.1 there is a heap-based out of bounds write in parse_die() when reversing an AMD64 ELF binary with DWARF debug info. When a malicious AMD64 ELF binary is opened by a victim user, Rizin may crash or execute unintended actions. No workaround are known and users are advised to upgrade.


static const ut8 *parse_die(const ut8 *buf, const ut8 *buf_end, RzBinDwarfDebugInfo *info, RzBinDwarfAbbrevDecl *abbrev,
	RzBinDwarfCompUnitHdr *hdr, RzBinDwarfDie *die, const ut8 *debug_str, size_t debug_str_len, bool big_endian) {
	size_t i;
	const char *comp_dir = NULL;
	ut64 line_info_offset = UT64_MAX;
	if (abbrev->count) {
		for (i = 0; i < abbrev->count - 1; i++) {
			memset(&die->attr_values[i], 0, sizeof(die->attr_values[i]));

			buf = parse_attr_value(buf, buf_end - buf, &abbrev->defs[i],
				&die->attr_values[i], hdr, debug_str, debug_str_len, big_endian);

			RzBinDwarfAttrValue *attribute = &die->attr_values[i];

			if (attribute->attr_name == DW_AT_comp_dir && (attribute->attr_form == DW_FORM_strp || attribute->attr_form == DW_FORM_string) && attribute->string.content) {
				comp_dir = attribute->string.content;
			}
			if (attribute->attr_name == DW_AT_stmt_list) {
				if (attribute->kind == DW_AT_KIND_CONSTANT) {
					line_info_offset = attribute->uconstant;
				} else if (attribute->kind == DW_AT_KIND_REFERENCE) {
					line_info_offset = attribute->reference;
				}
			}
			die->count++;
		}
	}

	// If this is a compilation unit dir attribute, we want to cache it so the line info parsing
	// which will need this info can quickly look it up.
	if (comp_dir && line_info_offset != UT64_MAX) {
		char *name = strdup(comp_dir);
		if (name) {
			if (!ht_up_insert(info->line_info_offset_comp_dir, line_info_offset, name)) {
				free(name);
			}
		}
	}

	return buf;
}


red fixes (#468)

* red fixes

* remove magic number

TerminalUserInfo UserTerminalRouter::getInfoForId(const string &id) {
  auto it = idInfoMap.find(id);
  if (it == idInfoMap.end()) {
    STFATAL << " Tried to read from an id that no longer exists";
  }
  return it->second;
}

A race condition exists in Eternal Terminal prior to version 6.2.0 that allows an authenticated attacker to hijack other users' SSH authorization socket, enabling the attacker to login to other systems as the targeted users. The bug is in UserTerminalRouter::getInfoForId().


TerminalUserInfo UserTerminalRouter::getInfoForId(const string &id) {
  lock_guard<recursive_mutex> guard(routerMutex);
  auto it = idInfoMap.find(id);
  if (it == idInfoMap.end()) {
    STFATAL << " Tried to read from an id that no longer exists";
  }
  return it->second;
}


set<int> PipeSocketHandler::listen(const SocketEndpoint& endpoint) {
  lock_guard<std::recursive_mutex> guard(globalMutex);

  string pipePath = endpoint.name();
  if (pipeServerSockets.find(pipePath) != pipeServerSockets.end()) {
    throw runtime_error("Tried to listen twice on the same path");
  }

  sockaddr_un local;

  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
  FATAL_FAIL(fd);
  initServerSocket(fd);
  local.sun_family = AF_UNIX; /* local is declared before socket() ^ */
  strcpy(local.sun_path, pipePath.c_str());
  unlink(local.sun_path);

  FATAL_FAIL(::bind(fd, (struct sockaddr*)&local, sizeof(sockaddr_un)));
  ::listen(fd, 5);
#ifndef WIN32
  FATAL_FAIL(::chmod(local.sun_path, S_IRUSR | S_IWUSR | S_IXUSR));
#endif

  pipeServerSockets[pipePath] = set<int>({fd});
  return pipeServerSockets[pipePath];
}


set<int> PipeSocketHandler::listen(const SocketEndpoint& endpoint) {
  lock_guard<std::recursive_mutex> guard(globalMutex);

  string pipePath = endpoint.name();
  if (pipeServerSockets.find(pipePath) != pipeServerSockets.end()) {
    throw runtime_error("Tried to listen twice on the same path");
  }

  sockaddr_un local;

  int fd = socket(AF_UNIX, SOCK_STREAM, 0);
  FATAL_FAIL(fd);
  initServerSocket(fd);
  local.sun_family = AF_UNIX; /* local is declared before socket() ^ */
  strncpy(local.sun_path, pipePath.c_str(), sizeof(local.sun_path));
  unlink(local.sun_path);

  FATAL_FAIL(::bind(fd, (struct sockaddr*)&local, sizeof(sockaddr_un)));
  ::listen(fd, 5);
#ifndef WIN32
  FATAL_FAIL(::chmod(local.sun_path, S_IRUSR | S_IWUSR | S_IXUSR));
#endif

  pipeServerSockets[pipePath] = set<int>({fd});
  return pipeServerSockets[pipePath];
}


patch 9.0.0218: reading before the start of the line

Problem:    Reading before the start of the line.
Solution:   When displaying "$" check the column is not negative.

display_dollar(colnr_T col)
{
    colnr_T save_col;

    if (!redrawing())
	return;

    cursor_off();
    save_col = curwin->w_cursor.col;
    curwin->w_cursor.col = col;
    if (has_mbyte)
    {
	char_u *p;

	// If on the last byte of a multi-byte move to the first byte.
	p = ml_get_curline();
	curwin->w_cursor.col -= (*mb_head_off)(p, p + col);
    }
    curs_columns(FALSE);	    // recompute w_wrow and w_wcol
    if (curwin->w_wcol < curwin->w_width)
    {
	edit_putchar('$', FALSE);
	dollar_vcol = curwin->w_virtcol;
    }
    curwin->w_cursor.col = save_col;
}

Improper Validation of Specified Quantity in Input in GitHub repository vim/vim prior to 9.0.0218.


display_dollar(colnr_T col_arg)
{
    colnr_T col = col_arg < 0 ? 0 : col_arg;
    colnr_T save_col;

    if (!redrawing())
	return;

    cursor_off();
    save_col = curwin->w_cursor.col;
    curwin->w_cursor.col = col;
    if (has_mbyte)
    {
	char_u *p;

	// If on the last byte of a multi-byte move to the first byte.
	p = ml_get_curline();
	curwin->w_cursor.col -= (*mb_head_off)(p, p + col);
    }
    curs_columns(FALSE);	    // recompute w_wrow and w_wcol
    if (curwin->w_wcol < curwin->w_width)
    {
	edit_putchar('$', FALSE);
	dollar_vcol = curwin->w_virtcol;
    }
    curwin->w_cursor.col = save_col;
}


lib/iov_iter: initialize "flags" in new pipe_buffer

The functions copy_page_to_iter_pipe() and push_pipe() can both
allocate a new pipe_buffer, but the "flags" member initializer is
missing.

Fixes: 241699cd72a8 ("new iov_iter flavour: pipe-backed")
To: Alexander Viro <viro@zeniv.linux.org.uk>
To: linux-fsdevel@vger.kernel.org
To: linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org
Signed-off-by: Max Kellermann <max.kellermann@ionos.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t bytes,
			 struct iov_iter *i)
{
	struct pipe_inode_info *pipe = i->pipe;
	struct pipe_buffer *buf;
	unsigned int p_tail = pipe->tail;
	unsigned int p_mask = pipe->ring_size - 1;
	unsigned int i_head = i->head;
	size_t off;

	if (unlikely(bytes > i->count))
		bytes = i->count;

	if (unlikely(!bytes))
		return 0;

	if (!sanity(i))
		return 0;

	off = i->iov_offset;
	buf = &pipe->bufs[i_head & p_mask];
	if (off) {
		if (offset == off && buf->page == page) {
			/* merge with the last one */
			buf->len += bytes;
			i->iov_offset += bytes;
			goto out;
		}
		i_head++;
		buf = &pipe->bufs[i_head & p_mask];
	}
	if (pipe_full(i_head, p_tail, pipe->max_usage))
		return 0;

	buf->ops = &page_cache_pipe_buf_ops;
	get_page(page);
	buf->page = page;
	buf->offset = offset;
	buf->len = bytes;

	pipe->head = i_head + 1;
	i->iov_offset = offset + bytes;
	i->head = i_head;
out:
	i->count -= bytes;
	return bytes;
}

A flaw was found in the way the "flags" member of the new pipe buffer structure was lacking proper initialization in copy_page_to_iter_pipe and push_pipe functions in the Linux kernel and could thus contain stale values. An unprivileged local user could use this flaw to write to pages in the page cache backed by read only files and as such escalate their privileges on the system.


static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t bytes,
			 struct iov_iter *i)
{
	struct pipe_inode_info *pipe = i->pipe;
	struct pipe_buffer *buf;
	unsigned int p_tail = pipe->tail;
	unsigned int p_mask = pipe->ring_size - 1;
	unsigned int i_head = i->head;
	size_t off;

	if (unlikely(bytes > i->count))
		bytes = i->count;

	if (unlikely(!bytes))
		return 0;

	if (!sanity(i))
		return 0;

	off = i->iov_offset;
	buf = &pipe->bufs[i_head & p_mask];
	if (off) {
		if (offset == off && buf->page == page) {
			/* merge with the last one */
			buf->len += bytes;
			i->iov_offset += bytes;
			goto out;
		}
		i_head++;
		buf = &pipe->bufs[i_head & p_mask];
	}
	if (pipe_full(i_head, p_tail, pipe->max_usage))
		return 0;

	buf->ops = &page_cache_pipe_buf_ops;
	buf->flags = 0;
	get_page(page);
	buf->page = page;
	buf->offset = offset;
	buf->len = bytes;

	pipe->head = i_head + 1;
	i->iov_offset = offset + bytes;
	i->head = i_head;
out:
	i->count -= bytes;
	return bytes;
}


static size_t push_pipe(struct iov_iter *i, size_t size,
			int *iter_headp, size_t *offp)
{
	struct pipe_inode_info *pipe = i->pipe;
	unsigned int p_tail = pipe->tail;
	unsigned int p_mask = pipe->ring_size - 1;
	unsigned int iter_head;
	size_t off;
	ssize_t left;

	if (unlikely(size > i->count))
		size = i->count;
	if (unlikely(!size))
		return 0;

	left = size;
	data_start(i, &iter_head, &off);
	*iter_headp = iter_head;
	*offp = off;
	if (off) {
		left -= PAGE_SIZE - off;
		if (left <= 0) {
			pipe->bufs[iter_head & p_mask].len += size;
			return size;
		}
		pipe->bufs[iter_head & p_mask].len = PAGE_SIZE;
		iter_head++;
	}
	while (!pipe_full(iter_head, p_tail, pipe->max_usage)) {
		struct pipe_buffer *buf = &pipe->bufs[iter_head & p_mask];
		struct page *page = alloc_page(GFP_USER);
		if (!page)
			break;

		buf->ops = &default_pipe_buf_ops;
		buf->page = page;
		buf->offset = 0;
		buf->len = min_t(ssize_t, left, PAGE_SIZE);
		left -= buf->len;
		iter_head++;
		pipe->head = iter_head;

		if (left == 0)
			return size;
	}
	return size - left;
}


static size_t push_pipe(struct iov_iter *i, size_t size,
			int *iter_headp, size_t *offp)
{
	struct pipe_inode_info *pipe = i->pipe;
	unsigned int p_tail = pipe->tail;
	unsigned int p_mask = pipe->ring_size - 1;
	unsigned int iter_head;
	size_t off;
	ssize_t left;

	if (unlikely(size > i->count))
		size = i->count;
	if (unlikely(!size))
		return 0;

	left = size;
	data_start(i, &iter_head, &off);
	*iter_headp = iter_head;
	*offp = off;
	if (off) {
		left -= PAGE_SIZE - off;
		if (left <= 0) {
			pipe->bufs[iter_head & p_mask].len += size;
			return size;
		}
		pipe->bufs[iter_head & p_mask].len = PAGE_SIZE;
		iter_head++;
	}
	while (!pipe_full(iter_head, p_tail, pipe->max_usage)) {
		struct pipe_buffer *buf = &pipe->bufs[iter_head & p_mask];
		struct page *page = alloc_page(GFP_USER);
		if (!page)
			break;

		buf->ops = &default_pipe_buf_ops;
		buf->flags = 0;
		buf->page = page;
		buf->offset = 0;
		buf->len = min_t(ssize_t, left, PAGE_SIZE);
		left -= buf->len;
		iter_head++;
		pipe->head = iter_head;

		if (left == 0)
			return size;
	}
	return size - left;
}


Fix segfault in older versions of OpenSSL (before 0.9.8i)

PHP_FUNCTION(openssl_encrypt)
{
	zend_bool raw_output = 0;
	char *data, *method, *password, *iv = "";
	int data_len, method_len, password_len, iv_len = 0, max_iv_len;
	const EVP_CIPHER *cipher_type;
	EVP_CIPHER_CTX cipher_ctx;
	int i, outlen, keylen;
	unsigned char *outbuf, *key;
	zend_bool free_iv;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_output, &iv, &iv_len) == FAILURE) {
		return;
	}
	cipher_type = EVP_get_cipherbyname(method);
	if (!cipher_type) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
		RETURN_FALSE;
	}

	keylen = EVP_CIPHER_key_length(cipher_type);
	if (keylen > password_len) {
		key = emalloc(keylen);
		memset(key, 0, keylen);
		memcpy(key, password, password_len);
	} else {
		key = (unsigned char*)password;
	}

	max_iv_len = EVP_CIPHER_iv_length(cipher_type);
	if (iv_len <= 0 && max_iv_len > 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Using an empty Initialization Vector (iv) is potentially insecure and not recommended");
	}
	free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC);

	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
	outbuf = emalloc(outlen + 1);

	EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL);
	if (password_len > keylen) {
		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
	}
	EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
	EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
	outlen = i;
	if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
		outlen += i;
		if (raw_output) {
			outbuf[outlen] = '\0';
			RETVAL_STRINGL((char *)outbuf, outlen, 0);
		} else {
			int base64_str_len;
			char *base64_str;

			base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len);
			efree(outbuf);
			RETVAL_STRINGL(base64_str, base64_str_len, 0);
		}
	} else {
		efree(outbuf);
		RETVAL_FALSE;
	}
	if (key != (unsigned char*)password) {
		efree(key);
	}
	if (free_iv) {
		efree(iv);
	}
	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
}

The openssl_encrypt function in ext/openssl/openssl.c in PHP 5.3.9 through 5.3.13 does not initialize a certain variable, which allows remote attackers to obtain sensitive information from process memory by providing zero bytes of input data.


PHP_FUNCTION(openssl_decrypt)
{
	zend_bool raw_input = 0;
	char *data, *method, *password, *iv = "";
	int data_len, method_len, password_len, iv_len = 0;
	const EVP_CIPHER *cipher_type;
	EVP_CIPHER_CTX cipher_ctx;
	int i, outlen, keylen;
	unsigned char *outbuf, *key;
	int base64_str_len;
	char *base64_str = NULL;
	zend_bool free_iv;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|bs", &data, &data_len, &method, &method_len, &password, &password_len, &raw_input, &iv, &iv_len) == FAILURE) {
		return;
	}

	if (!method_len) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
		RETURN_FALSE;
	}

	cipher_type = EVP_get_cipherbyname(method);
	if (!cipher_type) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm");
		RETURN_FALSE;
	}

	if (!raw_input) {
		base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len);
		data_len = base64_str_len;
		data = base64_str;
	}

	keylen = EVP_CIPHER_key_length(cipher_type);
	if (keylen > password_len) {
		key = emalloc(keylen);
		memset(key, 0, keylen);
		memcpy(key, password, password_len);
	} else {
		key = (unsigned char*)password;
	}

	free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC);

	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
	outbuf = emalloc(outlen + 1);

	EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL);
	if (password_len > keylen) {
		EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len);
	}
	EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv);
	EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len);
	outlen = i;
	if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) {
		outlen += i;
		outbuf[outlen] = '\0';
		RETVAL_STRINGL((char *)outbuf, outlen, 0);
	} else {
		efree(outbuf);
		RETVAL_FALSE;
	}
	if (key != (unsigned char*)password) {
		efree(key);
	}
	if (free_iv) {
		efree(iv);
	}
	if (base64_str) {
		efree(base64_str);
	}
 	EVP_CIPHER_CTX_cleanup(&cipher_ctx);
}


Fix oobread in dyldcache ##crash

* Reported by @hdthky via huntr.dev
* Reproducers: poc1
* BountyID: 8ae2c61a-2220-47a5-bfe8-fe6d41ab1f82

static RList *create_cache_bins(RBinFile *bf, RDyldCache *cache) {
	RList *bins = r_list_newf ((RListFree)free_bin);
	ut16 *depArray = NULL;
	cache_imgxtr_t *extras = NULL;
	if (!bins) {
		return NULL;
	}

	char *target_libs = NULL;
	RList *target_lib_names = NULL;
	int *deps = NULL;
	target_libs = r_sys_getenv ("R_DYLDCACHE_FILTER");
	if (target_libs) {
		target_lib_names = r_str_split_list (target_libs, ":", 0);
		if (!target_lib_names) {
			r_list_free (bins);
			return NULL;
		}
		deps = R_NEWS0 (int, cache->hdr->imagesCount);
		if (!deps) {
			r_list_free (bins);
			r_list_free (target_lib_names);
			return NULL;
		}
	}

	ut32 i;
	for (i = 0; i < cache->n_hdr; i++) {
		cache_hdr_t *hdr = &cache->hdr[i];
		ut64 hdr_offset = cache->hdr_offset[i];
		ut32 maps_index = cache->maps_index[i];
		cache_img_t *img = read_cache_images (cache->buf, hdr, hdr_offset);
		if (!img) {
			goto next;
		}

		ut32 j;
		if (target_libs) {
			HtPU *path_to_idx = NULL;
			if (cache->accel) {
				depArray = R_NEWS0 (ut16, cache->accel->depListCount);
				if (!depArray) {
					goto next;
				}

				if (r_buf_fread_at (cache->buf, cache->accel->depListOffset, (ut8*) depArray, "s", cache->accel->depListCount) != cache->accel->depListCount * 2) {
					goto next;
				}

				extras = read_cache_imgextra (cache->buf, hdr, cache->accel);
				if (!extras) {
					goto next;
				}
			} else {
				path_to_idx = create_path_to_index (cache->buf, img, hdr);
			}

			for (j = 0; j < hdr->imagesCount; j++) {
				bool printing = !deps[j];
				char *lib_name = get_lib_name (cache->buf, &img[j]);
				if (!lib_name) {
					break;
				}
				if (strstr (lib_name, "libobjc.A.dylib")) {
					deps[j]++;
				}
				if (!r_list_find (target_lib_names, lib_name, string_contains)) {
					R_FREE (lib_name);
					continue;
				}
				if (printing) {
					eprintf ("FILTER: %s\n", lib_name);
				}
				R_FREE (lib_name);
				deps[j]++;

				if (extras && depArray) {
					ut32 k;
					for (k = extras[j].dependentsStartArrayIndex; depArray[k] != 0xffff; k++) {
						ut16 dep_index = depArray[k] & 0x7fff;
						deps[dep_index]++;

						char *dep_name = get_lib_name (cache->buf, &img[dep_index]);
						if (!dep_name) {
							break;
						}
						if (printing) {
							eprintf ("-> %s\n", dep_name);
						}
						free (dep_name);
					}
				} else if (path_to_idx) {
					carve_deps_at_address (cache, img, path_to_idx, img[j].address, deps, printing);
				}
			}

			ht_pu_free (path_to_idx);
			R_FREE (depArray);
			R_FREE (extras);
		}

		for (j = 0; j < hdr->imagesCount; j++) {
			if (deps && !deps[j]) {
				continue;
			}
			ut64 pa = va2pa (img[j].address, hdr->mappingCount, &cache->maps[maps_index], cache->buf, 0, NULL, NULL);
			if (pa == UT64_MAX) {
				continue;
			}
			ut8 magicbytes[4];
			r_buf_read_at (cache->buf, pa, magicbytes, 4);
			int magic = r_read_le32 (magicbytes);
			switch (magic) {
			case MH_MAGIC_64:
			{
				char file[256];
				RDyldBinImage *bin = R_NEW0 (RDyldBinImage);
				if (!bin) {
					goto next;
				}
				bin->header_at = pa;
				bin->hdr_offset = hdr_offset;
				bin->symbols_off = resolve_symbols_off (cache, pa);
				bin->va = img[j].address;
				if (r_buf_read_at (cache->buf, img[j].pathFileOffset, (ut8*) &file, sizeof (file)) == sizeof (file)) {
					file[255] = 0;
					char *last_slash = strrchr (file, '/');
					if (last_slash && *last_slash) {
						if (last_slash > file) {
							char *scan = last_slash - 1;
							while (scan > file && *scan != '/') {
								scan--;
							}
							if (*scan == '/') {
								bin->file = strdup (scan + 1);
							} else {
								bin->file = strdup (last_slash + 1);
							}
						} else {
							bin->file = strdup (last_slash + 1);
						}
					} else {
						bin->file = strdup (file);
					}
				}
				r_list_append (bins, bin);
				break;
			}
			default:
				eprintf ("Unknown sub-bin\n");
				break;
			}
		}
next:
		R_FREE (depArray);
		R_FREE (extras);
		R_FREE (img);
	}
	if (r_list_empty (bins)) {
		r_list_free (bins);
		bins = NULL;
	}
	R_FREE (deps);
	R_FREE (target_libs);
	r_list_free (target_lib_names);
	return bins;
}

heap-buffer-overflow in GitHub repository radareorg/radare2 prior to 5.6.8. This vulnerability is capable of inducing denial of service.


static RList *create_cache_bins(RBinFile *bf, RDyldCache *cache) {
	RList *bins = r_list_newf ((RListFree)free_bin);
	ut16 *depArray = NULL;
	cache_imgxtr_t *extras = NULL;
	if (!bins) {
		return NULL;
	}

	char *target_libs = NULL;
	RList *target_lib_names = NULL;
	int *deps = NULL;
	target_libs = r_sys_getenv ("R_DYLDCACHE_FILTER");
	if (target_libs) {
		target_lib_names = r_str_split_list (target_libs, ":", 0);
		if (!target_lib_names) {
			r_list_free (bins);
			return NULL;
		}
		deps = R_NEWS0 (int, cache->hdr->imagesCount);
		if (!deps) {
			r_list_free (bins);
			r_list_free (target_lib_names);
			return NULL;
		}
	}

	ut32 i;
	for (i = 0; i < cache->n_hdr; i++) {
		cache_hdr_t *hdr = &cache->hdr[i];
		ut64 hdr_offset = cache->hdr_offset[i];
		ut32 maps_index = cache->maps_index[i];
		cache_img_t *img = read_cache_images (cache->buf, hdr, hdr_offset);
		if (!img) {
			goto next;
		}

		ut32 j;
		if (target_libs) {
			HtPU *path_to_idx = NULL;
			if (cache->accel) {
				depArray = R_NEWS0 (ut16, cache->accel->depListCount);
				if (!depArray) {
					goto next;
				}

				if (r_buf_fread_at (cache->buf, cache->accel->depListOffset, (ut8*) depArray, "s", cache->accel->depListCount) != cache->accel->depListCount * 2) {
					goto next;
				}

				extras = read_cache_imgextra (cache->buf, hdr, cache->accel);
				if (!extras) {
					goto next;
				}
			} else {
				path_to_idx = create_path_to_index (cache->buf, img, hdr);
			}

			for (j = 0; j < hdr->imagesCount; j++) {
				bool printing = !deps[j];
				char *lib_name = get_lib_name (cache->buf, &img[j]);
				if (!lib_name) {
					break;
				}
				if (strstr (lib_name, "libobjc.A.dylib")) {
					deps[j]++;
				}
				if (!r_list_find (target_lib_names, lib_name, string_contains)) {
					R_FREE (lib_name);
					continue;
				}
				if (printing) {
					eprintf ("FILTER: %s\n", lib_name);
				}
				R_FREE (lib_name);
				deps[j]++;

				if (extras && depArray) {
					ut32 k;
					for (k = extras[j].dependentsStartArrayIndex; depArray[k] != 0xffff; k++) {
						ut16 dep_index = depArray[k] & 0x7fff;
						deps[dep_index]++;

						char *dep_name = get_lib_name (cache->buf, &img[dep_index]);
						if (!dep_name) {
							break;
						}
						if (printing) {
							eprintf ("-> %s\n", dep_name);
						}
						free (dep_name);
					}
				} else if (path_to_idx) {
					carve_deps_at_address (cache, img, path_to_idx, img[j].address, deps, printing);
				}
			}

			ht_pu_free (path_to_idx);
			R_FREE (depArray);
			R_FREE (extras);
		}

		for (j = 0; j < hdr->imagesCount; j++) {
			if (deps && !deps[j]) {
				continue;
			}
			// ut64 pa = va2pa (img[j].address, hdr->mappingCount, &cache->maps[maps_index], cache->buf, 0, NULL, NULL);
			ut64 pa = va2pa (img[j].address, cache->n_maps, &cache->maps[maps_index], cache->buf, 0, NULL, NULL);
			if (pa == UT64_MAX) {
				continue;
			}
			ut8 magicbytes[4];
			r_buf_read_at (cache->buf, pa, magicbytes, 4);
			int magic = r_read_le32 (magicbytes);
			switch (magic) {
			case MH_MAGIC_64:
			{
				char file[256];
				RDyldBinImage *bin = R_NEW0 (RDyldBinImage);
				if (!bin) {
					goto next;
				}
				bin->header_at = pa;
				bin->hdr_offset = hdr_offset;
				bin->symbols_off = resolve_symbols_off (cache, pa);
				bin->va = img[j].address;
				if (r_buf_read_at (cache->buf, img[j].pathFileOffset, (ut8*) &file, sizeof (file)) == sizeof (file)) {
					file[255] = 0;
					char *last_slash = strrchr (file, '/');
					if (last_slash && *last_slash) {
						if (last_slash > file) {
							char *scan = last_slash - 1;
							while (scan > file && *scan != '/') {
								scan--;
							}
							if (*scan == '/') {
								bin->file = strdup (scan + 1);
							} else {
								bin->file = strdup (last_slash + 1);
							}
						} else {
							bin->file = strdup (last_slash + 1);
						}
					} else {
						bin->file = strdup (file);
					}
				}
				r_list_append (bins, bin);
				break;
			}
			default:
				eprintf ("Unknown sub-bin\n");
				break;
			}
		}
next:
		R_FREE (depArray);
		R_FREE (extras);
		R_FREE (img);
	}
	if (r_list_empty (bins)) {
		r_list_free (bins);
		bins = NULL;
	}
	R_FREE (deps);
	R_FREE (target_libs);
	r_list_free (target_lib_names);
	return bins;
}