void CLASS xtrans_interpolate(int passes)
{
  int c, d, f, g, h, i, v, ng, row, col, top, left, mrow, mcol;

#ifdef LIBRAW_LIBRARY_BUILD
  int cstat[4]={0,0,0,0};
#endif

  int val, ndir, pass, hm[8], avg[4], color[3][8];
  static const short orth[12] = {1, 0, 0, 1, -1, 0, 0, -1, 1, 0, 0, 1},
                     patt[2][16] = {{0, 1, 0, -1, 2, 0, -1, 0, 1, 1, 1, -1, 0, 0, 0, 0},
                                    {0, 1, 0, -2, 1, 0, -2, 0, 1, 1, -2, -2, 1, -1, -1, 1}},
                     dir[4] = {1, TS, TS + 1, TS - 1};
  short allhex[3][3][2][8], *hex;
  ushort min, max, sgrow, sgcol;
  ushort(*rgb)[TS][TS][3], (*rix)[3], (*pix)[4];
  short(*lab)[TS][3], (*lix)[3];
  float(*drv)[TS][TS], diff[6], tr;
  char(*homo)[TS][TS], *buffer;

#ifdef DCRAW_VERBOSE
  if (verbose)
    fprintf(stderr, _("%d-pass X-Trans interpolation...\n"), passes);
#endif

#ifdef LIBRAW_LIBRARY_BUILD
  if(width < TS || height < TS)
	  throw LIBRAW_EXCEPTION_IO_CORRUPT; // too small image
/* Check against right pattern */
  for (row = 0; row < 6; row++)
	  for (col = 0; col < 6; col++)
		  cstat[fcol(row,col)]++;

  if(cstat[0] < 6 || cstat[0]>10 || cstat[1]< 16
    || cstat[1]>24 || cstat[2]< 6 || cstat[2]>10 || cstat[3])
	  throw LIBRAW_EXCEPTION_IO_CORRUPT;

 // Init allhex table to unreasonable values
 for(int i = 0; i < 3; i++)
  for(int j = 0; j < 3; j++)
   for(int k = 0; k < 2; k++)
    for(int l = 0; l < 8; l++)
     allhex[i][j][k][l]=32700;
#endif
  cielab(0, 0);
  ndir = 4 << (passes > 1);
  buffer = (char *)malloc(TS * TS * (ndir * 11 + 6));
  merror(buffer, "xtrans_interpolate()");
  rgb = (ushort(*)[TS][TS][3])buffer;
  lab = (short(*)[TS][3])(buffer + TS * TS * (ndir * 6));
  drv = (float(*)[TS][TS])(buffer + TS * TS * (ndir * 6 + 6));
  homo = (char(*)[TS][TS])(buffer + TS * TS * (ndir * 10 + 6));

  int minv=0,maxv=0,minh=0,maxh=0;
  /* Map a green hexagon around each non-green pixel and vice versa:	*/
  for (row = 0; row < 3; row++)
    for (col = 0; col < 3; col++)
      for (ng = d = 0; d < 10; d += 2)
      {
        g = fcol(row, col) == 1;
        if (fcol(row + orth[d], col + orth[d + 2]) == 1)
          ng = 0;
        else
          ng++;
        if (ng == 4)
        {
          sgrow = row;
          sgcol = col;
        }
        if (ng == g + 1)
          FORC(8)
          {
            v = orth[d] * patt[g][c * 2] + orth[d + 1] * patt[g][c * 2 + 1];
            h = orth[d + 2] * patt[g][c * 2] + orth[d + 3] * patt[g][c * 2 + 1];
	    minv=MIN(v,minv);
	    maxv=MAX(v,maxv);
	    minh=MIN(v,minh);
   	    maxh=MAX(v,maxh);
            allhex[row][col][0][c ^ (g * 2 & d)] = h + v * width;
            allhex[row][col][1][c ^ (g * 2 & d)] = h + v * TS;
          }
      }

#ifdef LIBRAW_LIBRARY_BUILD
   // Check allhex table initialization
  for(int i = 0; i < 3; i++)
    for(int j = 0; j < 3; j++)
      for(int k = 0; k < 2; k++)
        for(int l = 0; l < 8; l++)
	  if(allhex[i][j][k][l]>maxh+maxv*width+1 || allhex[i][j][k][l]<minh+minv*width-1)
	  throw LIBRAW_EXCEPTION_IO_CORRUPT;
  int retrycount = 0;
#endif
  /* Set green1 and green3 to the minimum and maximum allowed values:	*/
  for (row = 2; row < height - 2; row++)
    for (min = ~(max = 0), col = 2; col < width - 2; col++)
    {
      if (fcol(row, col) == 1 && (min = ~(max = 0)))
        continue;
      pix = image + row * width + col;
      hex = allhex[row % 3][col % 3][0];
      if (!max)
        FORC(6)
        {
          val = pix[hex[c]][1];
          if (min > val)
            min = val;
          if (max < val)
            max = val;
        }
      pix[0][1] = min;
      pix[0][3] = max;
      switch ((row - sgrow) % 3)
      {
      case 1:
        if (row < height - 3)
        {
          row++;
          col--;
        }
        break;
      case 2:
        if ((min = ~(max = 0)) && (col += 2) < width - 3 && row > 2)
	{
           row--;
#ifdef LIBRAW_LIBRARY_BUILD
	  if(retrycount++ > width*height)
	  	throw LIBRAW_EXCEPTION_IO_CORRUPT;
#endif
	}
      }
    }

  for (top = 3; top < height - 19; top += TS - 16)
    for (left = 3; left < width - 19; left += TS - 16)
    {
      mrow = MIN(top + TS, height - 3);
      mcol = MIN(left + TS, width - 3);
      for (row = top; row < mrow; row++)
        for (col = left; col < mcol; col++)
          memcpy(rgb[0][row - top][col - left], image[row * width + col], 6);
      FORC3 memcpy(rgb[c + 1], rgb[0], sizeof *rgb);

      /* Interpolate green horizontally, vertically, and along both diagonals: */
      for (row = top; row < mrow; row++)
        for (col = left; col < mcol; col++)
        {
          if ((f = fcol(row, col)) == 1)
            continue;
          pix = image + row * width + col;
          hex = allhex[row % 3][col % 3][0];
          color[1][0] = 174 * (pix[hex[1]][1] + pix[hex[0]][1]) - 46 * (pix[2 * hex[1]][1] + pix[2 * hex[0]][1]);
          color[1][1] = 223 * pix[hex[3]][1] + pix[hex[2]][1] * 33 + 92 * (pix[0][f] - pix[-hex[2]][f]);
          FORC(2)
          color[1][2 + c] = 164 * pix[hex[4 + c]][1] + 92 * pix[-2 * hex[4 + c]][1] +
                            33 * (2 * pix[0][f] - pix[3 * hex[4 + c]][f] - pix[-3 * hex[4 + c]][f]);
          FORC4 rgb[c ^ !((row - sgrow) % 3)][row - top][col - left][1] = LIM(color[1][c] >> 8, pix[0][1], pix[0][3]);
        }

      for (pass = 0; pass < passes; pass++)
      {
        if (pass == 1)
          memcpy(rgb += 4, buffer, 4 * sizeof *rgb);

        /* Recalculate green from interpolated values of closer pixels:	*/
        if (pass)
        {
          for (row = top + 2; row < mrow - 2; row++)
            for (col = left + 2; col < mcol - 2; col++)
            {
              if ((f = fcol(row, col)) == 1)
                continue;
              pix = image + row * width + col;
              hex = allhex[row % 3][col % 3][1];
              for (d = 3; d < 6; d++)
              {
                rix = &rgb[(d - 2) ^ !((row - sgrow) % 3)][row - top][col - left];
                val =
                    rix[-2 * hex[d]][1] + 2 * rix[hex[d]][1] - rix[-2 * hex[d]][f] - 2 * rix[hex[d]][f] + 3 * rix[0][f];
                rix[0][1] = LIM(val / 3, pix[0][1], pix[0][3]);
              }
            }
        }

        /* Interpolate red and blue values for solitary green pixels:	*/
        for (row = (top - sgrow + 4) / 3 * 3 + sgrow; row < mrow - 2; row += 3)
          for (col = (left - sgcol + 4) / 3 * 3 + sgcol; col < mcol - 2; col += 3)
          {
            rix = &rgb[0][row - top][col - left];
            h = fcol(row, col + 1);
            memset(diff, 0, sizeof diff);
            for (i = 1, d = 0; d < 6; d++, i ^= TS ^ 1, h ^= 2)
            {
              for (c = 0; c < 2; c++, h ^= 2)
              {
                g = 2 * rix[0][1] - rix[i << c][1] - rix[-i << c][1];
                color[h][d] = g + rix[i << c][h] + rix[-i << c][h];
                if (d > 1)
                  diff[d] += SQR(rix[i << c][1] - rix[-i << c][1] - rix[i << c][h] + rix[-i << c][h]) + SQR(g);
              }
              if (d > 1 && (d & 1))
                if (diff[d - 1] < diff[d])
                  FORC(2) color[c * 2][d] = color[c * 2][d - 1];
              if (d < 2 || (d & 1))
              {
                FORC(2) rix[0][c * 2] = CLIP(color[c * 2][d] / 2);
                rix += TS * TS;
              }
            }
          }

        /* Interpolate red for blue pixels and vice versa:		*/
        for (row = top + 3; row < mrow - 3; row++)
          for (col = left + 3; col < mcol - 3; col++)
          {
            if ((f = 2 - fcol(row, col)) == 1)
              continue;
            rix = &rgb[0][row - top][col - left];
            c = (row - sgrow) % 3 ? TS : 1;
            h = 3 * (c ^ TS ^ 1);
            for (d = 0; d < 4; d++, rix += TS * TS)
            {
              i = d > 1 || ((d ^ c) & 1) ||
                          ((ABS(rix[0][1] - rix[c][1]) + ABS(rix[0][1] - rix[-c][1])) <
                           2 * (ABS(rix[0][1] - rix[h][1]) + ABS(rix[0][1] - rix[-h][1])))
                      ? c
                      : h;
              rix[0][f] = CLIP((rix[i][f] + rix[-i][f] + 2 * rix[0][1] - rix[i][1] - rix[-i][1]) / 2);
            }
          }

        /* Fill in red and blue for 2x2 blocks of green:		*/
        for (row = top + 2; row < mrow - 2; row++)
          if ((row - sgrow) % 3)
            for (col = left + 2; col < mcol - 2; col++)
              if ((col - sgcol) % 3)
              {
                rix = &rgb[0][row - top][col - left];
                hex = allhex[row % 3][col % 3][1];
                for (d = 0; d < ndir; d += 2, rix += TS * TS)
                  if (hex[d] + hex[d + 1])
                  {
                    g = 3 * rix[0][1] - 2 * rix[hex[d]][1] - rix[hex[d + 1]][1];
                    for (c = 0; c < 4; c += 2)
                      rix[0][c] = CLIP((g + 2 * rix[hex[d]][c] + rix[hex[d + 1]][c]) / 3);
                  }
                  else
                  {
                    g = 2 * rix[0][1] - rix[hex[d]][1] - rix[hex[d + 1]][1];
                    for (c = 0; c < 4; c += 2)
                      rix[0][c] = CLIP((g + rix[hex[d]][c] + rix[hex[d + 1]][c]) / 2);
                  }
              }
      }
      rgb = (ushort(*)[TS][TS][3])buffer;
      mrow -= top;
      mcol -= left;

      /* Convert to CIELab and differentiate in all directions:	*/
      for (d = 0; d < ndir; d++)
      {
        for (row = 2; row < mrow - 2; row++)
          for (col = 2; col < mcol - 2; col++)
            cielab(rgb[d][row][col], lab[row][col]);
        for (f = dir[d & 3], row = 3; row < mrow - 3; row++)
          for (col = 3; col < mcol - 3; col++)
          {
            lix = &lab[row][col];
            g = 2 * lix[0][0] - lix[f][0] - lix[-f][0];
            drv[d][row][col] = SQR(g) + SQR((2 * lix[0][1] - lix[f][1] - lix[-f][1] + g * 500 / 232)) +
                               SQR((2 * lix[0][2] - lix[f][2] - lix[-f][2] - g * 500 / 580));
          }
      }

      /* Build homogeneity maps from the derivatives:			*/
      memset(homo, 0, ndir * TS * TS);
      for (row = 4; row < mrow - 4; row++)
        for (col = 4; col < mcol - 4; col++)
        {
          for (tr = FLT_MAX, d = 0; d < ndir; d++)
            if (tr > drv[d][row][col])
              tr = drv[d][row][col];
          tr *= 8;
          for (d = 0; d < ndir; d++)
            for (v = -1; v <= 1; v++)
              for (h = -1; h <= 1; h++)
                if (drv[d][row + v][col + h] <= tr)
                  homo[d][row][col]++;
        }

      /* Average the most homogenous pixels for the final result:	*/
      if (height - top < TS + 4)
        mrow = height - top + 2;
      if (width - left < TS + 4)
        mcol = width - left + 2;
      for (row = MIN(top, 8); row < mrow - 8; row++)
        for (col = MIN(left, 8); col < mcol - 8; col++)
        {
          for (d = 0; d < ndir; d++)
            for (hm[d] = 0, v = -2; v <= 2; v++)
              for (h = -2; h <= 2; h++)
                hm[d] += homo[d][row + v][col + h];
          for (d = 0; d < ndir - 4; d++)
            if (hm[d] < hm[d + 4])
              hm[d] = 0;
            else if (hm[d] > hm[d + 4])
              hm[d + 4] = 0;
          for (max = hm[0], d = 1; d < ndir; d++)
            if (max < hm[d])
              max = hm[d];
          max -= max >> 3;
          memset(avg, 0, sizeof avg);
          for (d = 0; d < ndir; d++)
            if (hm[d] >= max)
            {
              FORC3 avg[c] += rgb[d][row][col][c];
              avg[3]++;
            }
          FORC3 image[(row + top) * width + col + left][c] = avg[c] / avg[3];
        }
    }
  free(buffer);
  border_interpolate(8);
}


scsi: aacraid: Don't copy uninitialized stack memory to userspace

Both aac_send_raw_srb() and aac_get_hba_info() may copy stack allocated
structs to userspace without initializing all members of these
structs. Clear out this memory to prevent information leaks.

Fixes: 423400e64d377 ("scsi: aacraid: Include HBA direct interface")
Fixes: c799d519bf088 ("scsi: aacraid: Retrieve HBA host information ioctl")
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
{
	struct fib* srbfib;
	int status;
	struct aac_srb *srbcmd = NULL;
	struct aac_hba_cmd_req *hbacmd = NULL;
	struct user_aac_srb *user_srbcmd = NULL;
	struct user_aac_srb __user *user_srb = arg;
	struct aac_srb_reply __user *user_reply;
	u32 chn;
	u32 fibsize = 0;
	u32 flags = 0;
	s32 rcode = 0;
	u32 data_dir;
	void __user *sg_user[HBA_MAX_SG_EMBEDDED];
	void *sg_list[HBA_MAX_SG_EMBEDDED];
	u32 sg_count[HBA_MAX_SG_EMBEDDED];
	u32 sg_indx = 0;
	u32 byte_count = 0;
	u32 actual_fibsize64, actual_fibsize = 0;
	int i;
	int is_native_device;
	u64 address;

	if (dev->in_reset) {
		dprintk((KERN_DEBUG"aacraid: send raw srb -EBUSY\n"));
		return -EBUSY;
	}
	if (!capable(CAP_SYS_ADMIN)){
		dprintk((KERN_DEBUG"aacraid: No permission to send raw srb\n"));
		return -EPERM;
	}
	/*
	 *	Allocate and initialize a Fib then setup a SRB command
	 */
	if (!(srbfib = aac_fib_alloc(dev))) {
		return -ENOMEM;
	}

	memset(sg_list, 0, sizeof(sg_list)); /* cleanup may take issue */
	if(copy_from_user(&fibsize, &user_srb->count,sizeof(u32))){
		dprintk((KERN_DEBUG"aacraid: Could not copy data size from user\n"));
		rcode = -EFAULT;
		goto cleanup;
	}

	if ((fibsize < (sizeof(struct user_aac_srb) - sizeof(struct user_sgentry))) ||
	    (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr)))) {
		rcode = -EINVAL;
		goto cleanup;
	}

	user_srbcmd = kmalloc(fibsize, GFP_KERNEL);
	if (!user_srbcmd) {
		dprintk((KERN_DEBUG"aacraid: Could not make a copy of the srb\n"));
		rcode = -ENOMEM;
		goto cleanup;
	}
	if(copy_from_user(user_srbcmd, user_srb,fibsize)){
		dprintk((KERN_DEBUG"aacraid: Could not copy srb from user\n"));
		rcode = -EFAULT;
		goto cleanup;
	}

	flags = user_srbcmd->flags; /* from user in cpu order */
	switch (flags & (SRB_DataIn | SRB_DataOut)) {
	case SRB_DataOut:
		data_dir = DMA_TO_DEVICE;
		break;
	case (SRB_DataIn | SRB_DataOut):
		data_dir = DMA_BIDIRECTIONAL;
		break;
	case SRB_DataIn:
		data_dir = DMA_FROM_DEVICE;
		break;
	default:
		data_dir = DMA_NONE;
	}
	if (user_srbcmd->sg.count > ARRAY_SIZE(sg_list)) {
		dprintk((KERN_DEBUG"aacraid: too many sg entries %d\n",
			user_srbcmd->sg.count));
		rcode = -EINVAL;
		goto cleanup;
	}
	if ((data_dir == DMA_NONE) && user_srbcmd->sg.count) {
		dprintk((KERN_DEBUG"aacraid:SG with no direction specified\n"));
		rcode = -EINVAL;
		goto cleanup;
	}
	actual_fibsize = sizeof(struct aac_srb) - sizeof(struct sgentry) +
		((user_srbcmd->sg.count & 0xff) * sizeof(struct sgentry));
	actual_fibsize64 = actual_fibsize + (user_srbcmd->sg.count & 0xff) *
	  (sizeof(struct sgentry64) - sizeof(struct sgentry));
	/* User made a mistake - should not continue */
	if ((actual_fibsize != fibsize) && (actual_fibsize64 != fibsize)) {
		dprintk((KERN_DEBUG"aacraid: Bad Size specified in "
		  "Raw SRB command calculated fibsize=%lu;%lu "
		  "user_srbcmd->sg.count=%d aac_srb=%lu sgentry=%lu;%lu "
		  "issued fibsize=%d\n",
		  actual_fibsize, actual_fibsize64, user_srbcmd->sg.count,
		  sizeof(struct aac_srb), sizeof(struct sgentry),
		  sizeof(struct sgentry64), fibsize));
		rcode = -EINVAL;
		goto cleanup;
	}

	chn = user_srbcmd->channel;
	if (chn < AAC_MAX_BUSES && user_srbcmd->id < AAC_MAX_TARGETS &&
		dev->hba_map[chn][user_srbcmd->id].devtype ==
		AAC_DEVTYPE_NATIVE_RAW) {
		is_native_device = 1;
		hbacmd = (struct aac_hba_cmd_req *)srbfib->hw_fib_va;
		memset(hbacmd, 0, 96);	/* sizeof(*hbacmd) is not necessary */

		/* iu_type is a parameter of aac_hba_send */
		switch (data_dir) {
		case DMA_TO_DEVICE:
			hbacmd->byte1 = 2;
			break;
		case DMA_FROM_DEVICE:
		case DMA_BIDIRECTIONAL:
			hbacmd->byte1 = 1;
			break;
		case DMA_NONE:
		default:
			break;
		}
		hbacmd->lun[1] = cpu_to_le32(user_srbcmd->lun);
		hbacmd->it_nexus = dev->hba_map[chn][user_srbcmd->id].rmw_nexus;

		/*
		 * we fill in reply_qid later in aac_src_deliver_message
		 * we fill in iu_type, request_id later in aac_hba_send
		 * we fill in emb_data_desc_count, data_length later
		 * in sg list build
		 */

		memcpy(hbacmd->cdb, user_srbcmd->cdb, sizeof(hbacmd->cdb));

		address = (u64)srbfib->hw_error_pa;
		hbacmd->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
		hbacmd->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
		hbacmd->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
		hbacmd->emb_data_desc_count =
					cpu_to_le32(user_srbcmd->sg.count);
		srbfib->hbacmd_size = 64 +
			user_srbcmd->sg.count * sizeof(struct aac_hba_sgl);

	} else {
		is_native_device = 0;
		aac_fib_init(srbfib);

		/* raw_srb FIB is not FastResponseCapable */
		srbfib->hw_fib_va->header.XferState &=
			~cpu_to_le32(FastResponseCapable);

		srbcmd = (struct aac_srb *) fib_data(srbfib);

		// Fix up srb for endian and force some values

		srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi); // Force this
		srbcmd->channel	 = cpu_to_le32(user_srbcmd->channel);
		srbcmd->id	 = cpu_to_le32(user_srbcmd->id);
		srbcmd->lun	 = cpu_to_le32(user_srbcmd->lun);
		srbcmd->timeout	 = cpu_to_le32(user_srbcmd->timeout);
		srbcmd->flags	 = cpu_to_le32(flags);
		srbcmd->retry_limit = 0; // Obsolete parameter
		srbcmd->cdb_size = cpu_to_le32(user_srbcmd->cdb_size);
		memcpy(srbcmd->cdb, user_srbcmd->cdb, sizeof(srbcmd->cdb));
	}

	byte_count = 0;
	if (is_native_device) {
		struct user_sgmap *usg32 = &user_srbcmd->sg;
		struct user_sgmap64 *usg64 =
			(struct user_sgmap64 *)&user_srbcmd->sg;

		for (i = 0; i < usg32->count; i++) {
			void *p;
			u64 addr;

			sg_count[i] = (actual_fibsize64 == fibsize) ?
				usg64->sg[i].count : usg32->sg[i].count;
			if (sg_count[i] >
				(dev->scsi_host_ptr->max_sectors << 9)) {
				pr_err("aacraid: upsg->sg[%d].count=%u>%u\n",
					i, sg_count[i],
					dev->scsi_host_ptr->max_sectors << 9);
				rcode = -EINVAL;
				goto cleanup;
			}

			p = kmalloc(sg_count[i], GFP_KERNEL);
			if (!p) {
				rcode = -ENOMEM;
				goto cleanup;
			}

			if (actual_fibsize64 == fibsize) {
				addr = (u64)usg64->sg[i].addr[0];
				addr += ((u64)usg64->sg[i].addr[1]) << 32;
			} else {
				addr = (u64)usg32->sg[i].addr;
			}

			sg_user[i] = (void __user *)(uintptr_t)addr;
			sg_list[i] = p; // save so we can clean up later
			sg_indx = i;

			if (flags & SRB_DataOut) {
				if (copy_from_user(p, sg_user[i],
					sg_count[i])) {
					rcode = -EFAULT;
					goto cleanup;
				}
			}
			addr = pci_map_single(dev->pdev, p, sg_count[i],
						data_dir);
			hbacmd->sge[i].addr_hi = cpu_to_le32((u32)(addr>>32));
			hbacmd->sge[i].addr_lo = cpu_to_le32(
						(u32)(addr & 0xffffffff));
			hbacmd->sge[i].len = cpu_to_le32(sg_count[i]);
			hbacmd->sge[i].flags = 0;
			byte_count += sg_count[i];
		}

		if (usg32->count > 0)	/* embedded sglist */
			hbacmd->sge[usg32->count-1].flags =
				cpu_to_le32(0x40000000);
		hbacmd->data_length = cpu_to_le32(byte_count);

		status = aac_hba_send(HBA_IU_TYPE_SCSI_CMD_REQ, srbfib,
					NULL, NULL);

	} else if (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64) {
		struct user_sgmap64* upsg = (struct user_sgmap64*)&user_srbcmd->sg;
		struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg;

		/*
		 * This should also catch if user used the 32 bit sgmap
		 */
		if (actual_fibsize64 == fibsize) {
			actual_fibsize = actual_fibsize64;
			for (i = 0; i < upsg->count; i++) {
				u64 addr;
				void* p;

				sg_count[i] = upsg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}

				p = kmalloc(sg_count[i], GFP_KERNEL);
				if(!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
					  sg_count[i], i, upsg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				addr = (u64)upsg->sg[i].addr[0];
				addr += ((u64)upsg->sg[i].addr[1]) << 32;
				sg_user[i] = (void __user *)(uintptr_t)addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])){
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
							sg_count[i], data_dir);

				psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
				psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		} else {
			struct user_sgmap* usg;
			usg = kmemdup(upsg,
				      actual_fibsize - sizeof(struct aac_srb)
				      + sizeof(struct sgmap), GFP_KERNEL);
			if (!usg) {
				dprintk((KERN_DEBUG"aacraid: Allocation error in Raw SRB command\n"));
				rcode = -ENOMEM;
				goto cleanup;
			}
			actual_fibsize = actual_fibsize64;

			for (i = 0; i < usg->count; i++) {
				u64 addr;
				void* p;

				sg_count[i] = usg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					kfree(usg);
					rcode = -EINVAL;
					goto cleanup;
				}

				p = kmalloc(sg_count[i], GFP_KERNEL);
				if(!p) {
					dprintk((KERN_DEBUG "aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
						sg_count[i], i, usg->count));
					kfree(usg);
					rcode = -ENOMEM;
					goto cleanup;
				}
				sg_user[i] = (void __user *)(uintptr_t)usg->sg[i].addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])) {
						kfree (usg);
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
							sg_count[i], data_dir);

				psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
				psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
			kfree (usg);
		}
		srbcmd->count = cpu_to_le32(byte_count);
		if (user_srbcmd->sg.count)
			psg->count = cpu_to_le32(sg_indx+1);
		else
			psg->count = 0;
		status = aac_fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL);
	} else {
		struct user_sgmap* upsg = &user_srbcmd->sg;
		struct sgmap* psg = &srbcmd->sg;

		if (actual_fibsize64 == fibsize) {
			struct user_sgmap64* usg = (struct user_sgmap64 *)upsg;
			for (i = 0; i < upsg->count; i++) {
				uintptr_t addr;
				void* p;

				sg_count[i] = usg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}
				p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
				if (!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
						sg_count[i], i, usg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				addr = (u64)usg->sg[i].addr[0];
				addr += ((u64)usg->sg[i].addr[1]) << 32;
				sg_user[i] = (void __user *)addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])){
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p, usg->sg[i].count, data_dir);

				psg->sg[i].addr = cpu_to_le32(addr & 0xffffffff);
				byte_count += usg->sg[i].count;
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		} else {
			for (i = 0; i < upsg->count; i++) {
				dma_addr_t addr;
				void* p;

				sg_count[i] = upsg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}
				p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
				if (!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
					  sg_count[i], i, upsg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				sg_user[i] = (void __user *)(uintptr_t)upsg->sg[i].addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])) {
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
					sg_count[i], data_dir);

				psg->sg[i].addr = cpu_to_le32(addr);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		}
		srbcmd->count = cpu_to_le32(byte_count);
		if (user_srbcmd->sg.count)
			psg->count = cpu_to_le32(sg_indx+1);
		else
			psg->count = 0;
		status = aac_fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL);
	}

	if (status == -ERESTARTSYS) {
		rcode = -ERESTARTSYS;
		goto cleanup;
	}

	if (status != 0) {
		dprintk((KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n"));
		rcode = -ENXIO;
		goto cleanup;
	}

	if (flags & SRB_DataIn) {
		for(i = 0 ; i <= sg_indx; i++){
			if (copy_to_user(sg_user[i], sg_list[i], sg_count[i])) {
				dprintk((KERN_DEBUG"aacraid: Could not copy sg data to user\n"));
				rcode = -EFAULT;
				goto cleanup;

			}
		}
	}

	user_reply = arg + fibsize;
	if (is_native_device) {
		struct aac_hba_resp *err =
			&((struct aac_native_hba *)srbfib->hw_fib_va)->resp.err;
		struct aac_srb_reply reply;

		reply.status = ST_OK;
		if (srbfib->flags & FIB_CONTEXT_FLAG_FASTRESP) {
			/* fast response */
			reply.srb_status = SRB_STATUS_SUCCESS;
			reply.scsi_status = 0;
			reply.data_xfer_length = byte_count;
			reply.sense_data_size = 0;
			memset(reply.sense_data, 0, AAC_SENSE_BUFFERSIZE);
		} else {
			reply.srb_status = err->service_response;
			reply.scsi_status = err->status;
			reply.data_xfer_length = byte_count -
				le32_to_cpu(err->residual_count);
			reply.sense_data_size = err->sense_response_data_len;
			memcpy(reply.sense_data, err->sense_response_buf,
				AAC_SENSE_BUFFERSIZE);
		}
		if (copy_to_user(user_reply, &reply,
			sizeof(struct aac_srb_reply))) {
			dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
			rcode = -EFAULT;
			goto cleanup;
		}
	} else {
		struct aac_srb_reply *reply;

		reply = (struct aac_srb_reply *) fib_data(srbfib);
		if (copy_to_user(user_reply, reply,
			sizeof(struct aac_srb_reply))) {
			dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
			rcode = -EFAULT;
			goto cleanup;
		}
	}

cleanup:
	kfree(user_srbcmd);
	if (rcode != -ERESTARTSYS) {
		for (i = 0; i <= sg_indx; i++)
			kfree(sg_list[i]);
		aac_fib_complete(srbfib);
		aac_fib_free(srbfib);
	}

	return rcode;
}

An issue was discovered in drivers/scsi/aacraid/commctrl.c in the Linux kernel before 4.13. There is potential exposure of kernel stack memory because aac_send_raw_srb does not initialize the reply structure.


static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
{
	struct fib* srbfib;
	int status;
	struct aac_srb *srbcmd = NULL;
	struct aac_hba_cmd_req *hbacmd = NULL;
	struct user_aac_srb *user_srbcmd = NULL;
	struct user_aac_srb __user *user_srb = arg;
	struct aac_srb_reply __user *user_reply;
	u32 chn;
	u32 fibsize = 0;
	u32 flags = 0;
	s32 rcode = 0;
	u32 data_dir;
	void __user *sg_user[HBA_MAX_SG_EMBEDDED];
	void *sg_list[HBA_MAX_SG_EMBEDDED];
	u32 sg_count[HBA_MAX_SG_EMBEDDED];
	u32 sg_indx = 0;
	u32 byte_count = 0;
	u32 actual_fibsize64, actual_fibsize = 0;
	int i;
	int is_native_device;
	u64 address;

	if (dev->in_reset) {
		dprintk((KERN_DEBUG"aacraid: send raw srb -EBUSY\n"));
		return -EBUSY;
	}
	if (!capable(CAP_SYS_ADMIN)){
		dprintk((KERN_DEBUG"aacraid: No permission to send raw srb\n"));
		return -EPERM;
	}
	/*
	 *	Allocate and initialize a Fib then setup a SRB command
	 */
	if (!(srbfib = aac_fib_alloc(dev))) {
		return -ENOMEM;
	}

	memset(sg_list, 0, sizeof(sg_list)); /* cleanup may take issue */
	if(copy_from_user(&fibsize, &user_srb->count,sizeof(u32))){
		dprintk((KERN_DEBUG"aacraid: Could not copy data size from user\n"));
		rcode = -EFAULT;
		goto cleanup;
	}

	if ((fibsize < (sizeof(struct user_aac_srb) - sizeof(struct user_sgentry))) ||
	    (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr)))) {
		rcode = -EINVAL;
		goto cleanup;
	}

	user_srbcmd = kmalloc(fibsize, GFP_KERNEL);
	if (!user_srbcmd) {
		dprintk((KERN_DEBUG"aacraid: Could not make a copy of the srb\n"));
		rcode = -ENOMEM;
		goto cleanup;
	}
	if(copy_from_user(user_srbcmd, user_srb,fibsize)){
		dprintk((KERN_DEBUG"aacraid: Could not copy srb from user\n"));
		rcode = -EFAULT;
		goto cleanup;
	}

	flags = user_srbcmd->flags; /* from user in cpu order */
	switch (flags & (SRB_DataIn | SRB_DataOut)) {
	case SRB_DataOut:
		data_dir = DMA_TO_DEVICE;
		break;
	case (SRB_DataIn | SRB_DataOut):
		data_dir = DMA_BIDIRECTIONAL;
		break;
	case SRB_DataIn:
		data_dir = DMA_FROM_DEVICE;
		break;
	default:
		data_dir = DMA_NONE;
	}
	if (user_srbcmd->sg.count > ARRAY_SIZE(sg_list)) {
		dprintk((KERN_DEBUG"aacraid: too many sg entries %d\n",
			user_srbcmd->sg.count));
		rcode = -EINVAL;
		goto cleanup;
	}
	if ((data_dir == DMA_NONE) && user_srbcmd->sg.count) {
		dprintk((KERN_DEBUG"aacraid:SG with no direction specified\n"));
		rcode = -EINVAL;
		goto cleanup;
	}
	actual_fibsize = sizeof(struct aac_srb) - sizeof(struct sgentry) +
		((user_srbcmd->sg.count & 0xff) * sizeof(struct sgentry));
	actual_fibsize64 = actual_fibsize + (user_srbcmd->sg.count & 0xff) *
	  (sizeof(struct sgentry64) - sizeof(struct sgentry));
	/* User made a mistake - should not continue */
	if ((actual_fibsize != fibsize) && (actual_fibsize64 != fibsize)) {
		dprintk((KERN_DEBUG"aacraid: Bad Size specified in "
		  "Raw SRB command calculated fibsize=%lu;%lu "
		  "user_srbcmd->sg.count=%d aac_srb=%lu sgentry=%lu;%lu "
		  "issued fibsize=%d\n",
		  actual_fibsize, actual_fibsize64, user_srbcmd->sg.count,
		  sizeof(struct aac_srb), sizeof(struct sgentry),
		  sizeof(struct sgentry64), fibsize));
		rcode = -EINVAL;
		goto cleanup;
	}

	chn = user_srbcmd->channel;
	if (chn < AAC_MAX_BUSES && user_srbcmd->id < AAC_MAX_TARGETS &&
		dev->hba_map[chn][user_srbcmd->id].devtype ==
		AAC_DEVTYPE_NATIVE_RAW) {
		is_native_device = 1;
		hbacmd = (struct aac_hba_cmd_req *)srbfib->hw_fib_va;
		memset(hbacmd, 0, 96);	/* sizeof(*hbacmd) is not necessary */

		/* iu_type is a parameter of aac_hba_send */
		switch (data_dir) {
		case DMA_TO_DEVICE:
			hbacmd->byte1 = 2;
			break;
		case DMA_FROM_DEVICE:
		case DMA_BIDIRECTIONAL:
			hbacmd->byte1 = 1;
			break;
		case DMA_NONE:
		default:
			break;
		}
		hbacmd->lun[1] = cpu_to_le32(user_srbcmd->lun);
		hbacmd->it_nexus = dev->hba_map[chn][user_srbcmd->id].rmw_nexus;

		/*
		 * we fill in reply_qid later in aac_src_deliver_message
		 * we fill in iu_type, request_id later in aac_hba_send
		 * we fill in emb_data_desc_count, data_length later
		 * in sg list build
		 */

		memcpy(hbacmd->cdb, user_srbcmd->cdb, sizeof(hbacmd->cdb));

		address = (u64)srbfib->hw_error_pa;
		hbacmd->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
		hbacmd->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
		hbacmd->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
		hbacmd->emb_data_desc_count =
					cpu_to_le32(user_srbcmd->sg.count);
		srbfib->hbacmd_size = 64 +
			user_srbcmd->sg.count * sizeof(struct aac_hba_sgl);

	} else {
		is_native_device = 0;
		aac_fib_init(srbfib);

		/* raw_srb FIB is not FastResponseCapable */
		srbfib->hw_fib_va->header.XferState &=
			~cpu_to_le32(FastResponseCapable);

		srbcmd = (struct aac_srb *) fib_data(srbfib);

		// Fix up srb for endian and force some values

		srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi); // Force this
		srbcmd->channel	 = cpu_to_le32(user_srbcmd->channel);
		srbcmd->id	 = cpu_to_le32(user_srbcmd->id);
		srbcmd->lun	 = cpu_to_le32(user_srbcmd->lun);
		srbcmd->timeout	 = cpu_to_le32(user_srbcmd->timeout);
		srbcmd->flags	 = cpu_to_le32(flags);
		srbcmd->retry_limit = 0; // Obsolete parameter
		srbcmd->cdb_size = cpu_to_le32(user_srbcmd->cdb_size);
		memcpy(srbcmd->cdb, user_srbcmd->cdb, sizeof(srbcmd->cdb));
	}

	byte_count = 0;
	if (is_native_device) {
		struct user_sgmap *usg32 = &user_srbcmd->sg;
		struct user_sgmap64 *usg64 =
			(struct user_sgmap64 *)&user_srbcmd->sg;

		for (i = 0; i < usg32->count; i++) {
			void *p;
			u64 addr;

			sg_count[i] = (actual_fibsize64 == fibsize) ?
				usg64->sg[i].count : usg32->sg[i].count;
			if (sg_count[i] >
				(dev->scsi_host_ptr->max_sectors << 9)) {
				pr_err("aacraid: upsg->sg[%d].count=%u>%u\n",
					i, sg_count[i],
					dev->scsi_host_ptr->max_sectors << 9);
				rcode = -EINVAL;
				goto cleanup;
			}

			p = kmalloc(sg_count[i], GFP_KERNEL);
			if (!p) {
				rcode = -ENOMEM;
				goto cleanup;
			}

			if (actual_fibsize64 == fibsize) {
				addr = (u64)usg64->sg[i].addr[0];
				addr += ((u64)usg64->sg[i].addr[1]) << 32;
			} else {
				addr = (u64)usg32->sg[i].addr;
			}

			sg_user[i] = (void __user *)(uintptr_t)addr;
			sg_list[i] = p; // save so we can clean up later
			sg_indx = i;

			if (flags & SRB_DataOut) {
				if (copy_from_user(p, sg_user[i],
					sg_count[i])) {
					rcode = -EFAULT;
					goto cleanup;
				}
			}
			addr = pci_map_single(dev->pdev, p, sg_count[i],
						data_dir);
			hbacmd->sge[i].addr_hi = cpu_to_le32((u32)(addr>>32));
			hbacmd->sge[i].addr_lo = cpu_to_le32(
						(u32)(addr & 0xffffffff));
			hbacmd->sge[i].len = cpu_to_le32(sg_count[i]);
			hbacmd->sge[i].flags = 0;
			byte_count += sg_count[i];
		}

		if (usg32->count > 0)	/* embedded sglist */
			hbacmd->sge[usg32->count-1].flags =
				cpu_to_le32(0x40000000);
		hbacmd->data_length = cpu_to_le32(byte_count);

		status = aac_hba_send(HBA_IU_TYPE_SCSI_CMD_REQ, srbfib,
					NULL, NULL);

	} else if (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64) {
		struct user_sgmap64* upsg = (struct user_sgmap64*)&user_srbcmd->sg;
		struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg;

		/*
		 * This should also catch if user used the 32 bit sgmap
		 */
		if (actual_fibsize64 == fibsize) {
			actual_fibsize = actual_fibsize64;
			for (i = 0; i < upsg->count; i++) {
				u64 addr;
				void* p;

				sg_count[i] = upsg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}

				p = kmalloc(sg_count[i], GFP_KERNEL);
				if(!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
					  sg_count[i], i, upsg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				addr = (u64)upsg->sg[i].addr[0];
				addr += ((u64)upsg->sg[i].addr[1]) << 32;
				sg_user[i] = (void __user *)(uintptr_t)addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])){
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
							sg_count[i], data_dir);

				psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
				psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		} else {
			struct user_sgmap* usg;
			usg = kmemdup(upsg,
				      actual_fibsize - sizeof(struct aac_srb)
				      + sizeof(struct sgmap), GFP_KERNEL);
			if (!usg) {
				dprintk((KERN_DEBUG"aacraid: Allocation error in Raw SRB command\n"));
				rcode = -ENOMEM;
				goto cleanup;
			}
			actual_fibsize = actual_fibsize64;

			for (i = 0; i < usg->count; i++) {
				u64 addr;
				void* p;

				sg_count[i] = usg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					kfree(usg);
					rcode = -EINVAL;
					goto cleanup;
				}

				p = kmalloc(sg_count[i], GFP_KERNEL);
				if(!p) {
					dprintk((KERN_DEBUG "aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
						sg_count[i], i, usg->count));
					kfree(usg);
					rcode = -ENOMEM;
					goto cleanup;
				}
				sg_user[i] = (void __user *)(uintptr_t)usg->sg[i].addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])) {
						kfree (usg);
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
							sg_count[i], data_dir);

				psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
				psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
			kfree (usg);
		}
		srbcmd->count = cpu_to_le32(byte_count);
		if (user_srbcmd->sg.count)
			psg->count = cpu_to_le32(sg_indx+1);
		else
			psg->count = 0;
		status = aac_fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL);
	} else {
		struct user_sgmap* upsg = &user_srbcmd->sg;
		struct sgmap* psg = &srbcmd->sg;

		if (actual_fibsize64 == fibsize) {
			struct user_sgmap64* usg = (struct user_sgmap64 *)upsg;
			for (i = 0; i < upsg->count; i++) {
				uintptr_t addr;
				void* p;

				sg_count[i] = usg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}
				p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
				if (!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
						sg_count[i], i, usg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				addr = (u64)usg->sg[i].addr[0];
				addr += ((u64)usg->sg[i].addr[1]) << 32;
				sg_user[i] = (void __user *)addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])){
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p, usg->sg[i].count, data_dir);

				psg->sg[i].addr = cpu_to_le32(addr & 0xffffffff);
				byte_count += usg->sg[i].count;
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		} else {
			for (i = 0; i < upsg->count; i++) {
				dma_addr_t addr;
				void* p;

				sg_count[i] = upsg->sg[i].count;
				if (sg_count[i] >
				    ((dev->adapter_info.options &
				     AAC_OPT_NEW_COMM) ?
				      (dev->scsi_host_ptr->max_sectors << 9) :
				      65536)) {
					rcode = -EINVAL;
					goto cleanup;
				}
				p = kmalloc(sg_count[i], GFP_KERNEL|GFP_DMA32);
				if (!p) {
					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
					  sg_count[i], i, upsg->count));
					rcode = -ENOMEM;
					goto cleanup;
				}
				sg_user[i] = (void __user *)(uintptr_t)upsg->sg[i].addr;
				sg_list[i] = p; // save so we can clean up later
				sg_indx = i;

				if (flags & SRB_DataOut) {
					if (copy_from_user(p, sg_user[i],
						sg_count[i])) {
						dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
						rcode = -EFAULT;
						goto cleanup;
					}
				}
				addr = pci_map_single(dev->pdev, p,
					sg_count[i], data_dir);

				psg->sg[i].addr = cpu_to_le32(addr);
				byte_count += sg_count[i];
				psg->sg[i].count = cpu_to_le32(sg_count[i]);
			}
		}
		srbcmd->count = cpu_to_le32(byte_count);
		if (user_srbcmd->sg.count)
			psg->count = cpu_to_le32(sg_indx+1);
		else
			psg->count = 0;
		status = aac_fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL);
	}

	if (status == -ERESTARTSYS) {
		rcode = -ERESTARTSYS;
		goto cleanup;
	}

	if (status != 0) {
		dprintk((KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n"));
		rcode = -ENXIO;
		goto cleanup;
	}

	if (flags & SRB_DataIn) {
		for(i = 0 ; i <= sg_indx; i++){
			if (copy_to_user(sg_user[i], sg_list[i], sg_count[i])) {
				dprintk((KERN_DEBUG"aacraid: Could not copy sg data to user\n"));
				rcode = -EFAULT;
				goto cleanup;

			}
		}
	}

	user_reply = arg + fibsize;
	if (is_native_device) {
		struct aac_hba_resp *err =
			&((struct aac_native_hba *)srbfib->hw_fib_va)->resp.err;
		struct aac_srb_reply reply;

		memset(&reply, 0, sizeof(reply));
		reply.status = ST_OK;
		if (srbfib->flags & FIB_CONTEXT_FLAG_FASTRESP) {
			/* fast response */
			reply.srb_status = SRB_STATUS_SUCCESS;
			reply.scsi_status = 0;
			reply.data_xfer_length = byte_count;
			reply.sense_data_size = 0;
			memset(reply.sense_data, 0, AAC_SENSE_BUFFERSIZE);
		} else {
			reply.srb_status = err->service_response;
			reply.scsi_status = err->status;
			reply.data_xfer_length = byte_count -
				le32_to_cpu(err->residual_count);
			reply.sense_data_size = err->sense_response_data_len;
			memcpy(reply.sense_data, err->sense_response_buf,
				AAC_SENSE_BUFFERSIZE);
		}
		if (copy_to_user(user_reply, &reply,
			sizeof(struct aac_srb_reply))) {
			dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
			rcode = -EFAULT;
			goto cleanup;
		}
	} else {
		struct aac_srb_reply *reply;

		reply = (struct aac_srb_reply *) fib_data(srbfib);
		if (copy_to_user(user_reply, reply,
			sizeof(struct aac_srb_reply))) {
			dprintk((KERN_DEBUG"aacraid: Copy to user failed\n"));
			rcode = -EFAULT;
			goto cleanup;
		}
	}

cleanup:
	kfree(user_srbcmd);
	if (rcode != -ERESTARTSYS) {
		for (i = 0; i <= sg_indx; i++)
			kfree(sg_list[i]);
		aac_fib_complete(srbfib);
		aac_fib_free(srbfib);
	}

	return rcode;
}


pcnet: switch to use qemu_receive_packet() for loopback

This patch switches to use qemu_receive_packet() which can detect
reentrancy and return early.

This is intended to address CVE-2021-3416.

Cc: Prasad J Pandit <ppandit@redhat.com>
Cc: qemu-stable@nongnu.org
Buglink: https://bugs.launchpad.net/qemu/+bug/1917085
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
Signed-off-by: Jason Wang <jasowang@redhat.com>

static void pcnet_transmit(PCNetState *s)
{
    hwaddr xmit_cxda = 0;
    int count = CSR_XMTRL(s)-1;
    int add_crc = 0;
    int bcnt;
    s->xmit_pos = -1;

    if (!CSR_TXON(s)) {
        s->csr[0] &= ~0x0008;
        return;
    }

    s->tx_busy = 1;

txagain:
    if (pcnet_tdte_poll(s)) {
        struct pcnet_TMD tmd;

        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));

#ifdef PCNET_DEBUG_TMD
        printf("  TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
        PRINT_TMD(&tmd);
#endif
        if (GET_FIELD(tmd.status, TMDS, STP)) {
            s->xmit_pos = 0;
            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
            if (BCR_SWSTYLE(s) != 1)
                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
        }
        if (s->lnkst == 0 &&
            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
            SET_FIELD(&tmd.status, TMDS, ERR, 1);
            SET_FIELD(&tmd.status, TMDS, OWN, 0);
            s->csr[0] |= 0xa000; /* ERR | CERR */
            s->xmit_pos = -1;
            goto txdone;
        }

        if (s->xmit_pos < 0) {
            goto txdone;
        }

        bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);

        /* if multi-tmd packet outsizes s->buffer then skip it silently.
         * Note: this is not what real hw does.
         * Last four bytes of s->buffer are used to store CRC FCS code.
         */
        if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
            s->xmit_pos = -1;
            goto txdone;
        }

        s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
                         s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
        s->xmit_pos += bcnt;

        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
            goto txdone;
        }

#ifdef PCNET_DEBUG
        printf("pcnet_transmit size=%d\n", s->xmit_pos);
#endif
        if (CSR_LOOP(s)) {
            if (BCR_SWSTYLE(s) == 1)
                add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
            s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
            pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
            s->looptest = 0;
        } else {
            if (s->nic) {
                qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
                                 s->xmit_pos);
            }
        }

        s->csr[0] &= ~0x0008;   /* clear TDMD */
        s->csr[4] |= 0x0004;    /* set TXSTRT */
        s->xmit_pos = -1;

txdone:
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
        if (!CSR_TOKINTD(s)
            || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) {
            s->csr[0] |= 0x0200;    /* set TINT */
        }
        if (CSR_XMTRC(s) <= 1) {
            CSR_XMTRC(s) = CSR_XMTRL(s);
        } else {
            CSR_XMTRC(s)--;
        }
        if (count--) {
            goto txagain;
        }
    } else if (s->xmit_pos >= 0) {
        struct pcnet_TMD tmd;
        TMDLOAD(&tmd, xmit_cxda);
        SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
        SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
        SET_FIELD(&tmd.status, TMDS, ERR, 1);
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, xmit_cxda);
        s->csr[0] |= 0x0200;    /* set TINT */
        if (!CSR_DXSUFLO(s)) {
            s->csr[0] &= ~0x0010;
        } else if (count--) {
            goto txagain;
        }
    }

    s->tx_busy = 0;
}

A potential stack overflow via infinite loop issue was found in various NIC emulators of QEMU in versions up to and including 5.2.0. The issue occurs in loopback mode of a NIC wherein reentrant DMA checks get bypassed. A guest user/process may use this flaw to consume CPU cycles or crash the QEMU process on the host resulting in DoS scenario.


static void pcnet_transmit(PCNetState *s)
{
    hwaddr xmit_cxda = 0;
    int count = CSR_XMTRL(s)-1;
    int add_crc = 0;
    int bcnt;
    s->xmit_pos = -1;

    if (!CSR_TXON(s)) {
        s->csr[0] &= ~0x0008;
        return;
    }

    s->tx_busy = 1;

txagain:
    if (pcnet_tdte_poll(s)) {
        struct pcnet_TMD tmd;

        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));

#ifdef PCNET_DEBUG_TMD
        printf("  TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
        PRINT_TMD(&tmd);
#endif
        if (GET_FIELD(tmd.status, TMDS, STP)) {
            s->xmit_pos = 0;
            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
            if (BCR_SWSTYLE(s) != 1)
                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
        }
        if (s->lnkst == 0 &&
            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
            SET_FIELD(&tmd.status, TMDS, ERR, 1);
            SET_FIELD(&tmd.status, TMDS, OWN, 0);
            s->csr[0] |= 0xa000; /* ERR | CERR */
            s->xmit_pos = -1;
            goto txdone;
        }

        if (s->xmit_pos < 0) {
            goto txdone;
        }

        bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);

        /* if multi-tmd packet outsizes s->buffer then skip it silently.
         * Note: this is not what real hw does.
         * Last four bytes of s->buffer are used to store CRC FCS code.
         */
        if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) {
            s->xmit_pos = -1;
            goto txdone;
        }

        s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
                         s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
        s->xmit_pos += bcnt;

        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
            goto txdone;
        }

#ifdef PCNET_DEBUG
        printf("pcnet_transmit size=%d\n", s->xmit_pos);
#endif
        if (CSR_LOOP(s)) {
            if (BCR_SWSTYLE(s) == 1)
                add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
            s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
            qemu_receive_packet(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
            s->looptest = 0;
        } else {
            if (s->nic) {
                qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
                                 s->xmit_pos);
            }
        }

        s->csr[0] &= ~0x0008;   /* clear TDMD */
        s->csr[4] |= 0x0004;    /* set TXSTRT */
        s->xmit_pos = -1;

txdone:
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
        if (!CSR_TOKINTD(s)
            || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) {
            s->csr[0] |= 0x0200;    /* set TINT */
        }
        if (CSR_XMTRC(s) <= 1) {
            CSR_XMTRC(s) = CSR_XMTRL(s);
        } else {
            CSR_XMTRC(s)--;
        }
        if (count--) {
            goto txagain;
        }
    } else if (s->xmit_pos >= 0) {
        struct pcnet_TMD tmd;
        TMDLOAD(&tmd, xmit_cxda);
        SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
        SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
        SET_FIELD(&tmd.status, TMDS, ERR, 1);
        SET_FIELD(&tmd.status, TMDS, OWN, 0);
        TMDSTORE(&tmd, xmit_cxda);
        s->csr[0] |= 0x0200;    /* set TINT */
        if (!CSR_DXSUFLO(s)) {
            s->csr[0] &= ~0x0010;
        } else if (count--) {
            goto txagain;
        }
    }

    s->tx_busy = 0;
}


Fix CVE 2013-4495. Note: this patch has been verified as fixing this security hole but has not received other regression testing.

void svr_mailowner(

  job   *pjob,       /* I */
  int   mailpoint,  /* note, single character  */
  int    force,      /* if set to MAIL_FORCE, force mail delivery */
  char *text)      /* (optional) additional message text */

  {
  char *cmdbuf;
  int    i;
  char *mailfrom;
  char  mailto[1024];
  char *bodyfmt, *subjectfmt;
  char bodyfmtbuf[1024];
  FILE *outmail;

  struct array_strings *pas;

  if ((server.sv_attr[SRV_ATR_MailDomain].at_flags & ATR_VFLAG_SET) &&
      (server.sv_attr[SRV_ATR_MailDomain].at_val.at_str != NULL) &&
      (!strcasecmp("never", server.sv_attr[SRV_ATR_MailDomain].at_val.at_str)))
    {
    /* never send user mail under any conditions */
    if (LOGLEVEL >= 3)
      {
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        "Not sending email: Mail domain set to 'never'\n");
      }

    return;
    }

  if (LOGLEVEL >= 3)
    {
    char tmpBuf[LOG_BUF_SIZE];

    snprintf(tmpBuf, LOG_BUF_SIZE, "preparing to send '%c' mail for job %s to %s (%.64s)\n",
             (char)mailpoint,
             pjob->ji_qs.ji_jobid,
             pjob->ji_wattr[JOB_ATR_job_owner].at_val.at_str,
             (text != NULL) ? text : "---");

    log_event(
      PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);
    }

  /*
   * if force is true, force the mail out regardless of mailpoint
   * unless server no_mail_force attribute is set to true
   */

  if ((force != MAIL_FORCE) ||
    (server.sv_attr[(int)SRV_ATR_NoMailForce].at_val.at_long == TRUE))
    {

    if (pjob->ji_wattr[JOB_ATR_mailpnts].at_flags & ATR_VFLAG_SET)
      {
      if (*(pjob->ji_wattr[JOB_ATR_mailpnts].at_val.at_str) ==  MAIL_NONE)
        {
        /* do not send mail. No mail requested on job */
        log_event(PBSEVENT_JOB,
                  PBS_EVENTCLASS_JOB,
                  pjob->ji_qs.ji_jobid,
                  "Not sending email: job requested no e-mail");
        return;
        }
      /* see if user specified mail of this type */
      if (strchr(
            pjob->ji_wattr[JOB_ATR_mailpnts].at_val.at_str,
            mailpoint) == NULL)
        {
        /* do not send mail */
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          "Not sending email: User does not want mail of this type.\n");

        return;
        }
      }
    else if (mailpoint != MAIL_ABORT) /* not set, default to abort */
      {
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        "Not sending email: Default mailpoint does not include this type.\n");

      return;
      }
    }

  /*
   * ok, now we will fork a process to do the mailing to not
   * hold up the server's other work.
   */

  if (fork())
    {
    return;  /* its all up to the child now */
    }

  /*
   * From here on, we are a child process of the server.
   * Fix up file descriptors and signal handlers.
   */

  rpp_terminate();

  net_close(-1);

  /* Who is mail from, if SRV_ATR_mailfrom not set use default */

  if ((mailfrom = server.sv_attr[SRV_ATR_mailfrom].at_val.at_str) == NULL)
    {
    if (LOGLEVEL >= 5)
      {
      char tmpBuf[LOG_BUF_SIZE];

      snprintf(tmpBuf,sizeof(tmpBuf),
        "Updated mailto from user list: '%s'\n",
        mailto);
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        tmpBuf);
      }
    mailfrom = PBS_DEFAULT_MAIL;
    }

  /* Who does the mail go to?  If mail-list, them; else owner */

  *mailto = '\0';

  if (pjob->ji_wattr[JOB_ATR_mailuser].at_flags & ATR_VFLAG_SET)
    {
    /* has mail user list, send to them rather than owner */

    pas = pjob->ji_wattr[JOB_ATR_mailuser].at_val.at_arst;

    if (pas != NULL)
      {
      for (i = 0;i < pas->as_usedptr;i++)
        {
        if ((strlen(mailto) + strlen(pas->as_string[i]) + 2) < sizeof(mailto))
          {
          strcat(mailto, pas->as_string[i]);
          strcat(mailto, " ");
          }
        }
      }
    }
  else
    {
    /* no mail user list, just send to owner */

    if ((server.sv_attr[SRV_ATR_MailDomain].at_flags & ATR_VFLAG_SET) &&
        (server.sv_attr[SRV_ATR_MailDomain].at_val.at_str != NULL))
      {
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_euser].at_val.at_str);
      strcat(mailto, "@");
      strcat(mailto, server.sv_attr[SRV_ATR_MailDomain].at_val.at_str);

      if (LOGLEVEL >= 5)
        {
        char tmpBuf[LOG_BUF_SIZE];

        snprintf(tmpBuf,sizeof(tmpBuf),
          "Updated mailto from job owner and mail domain: '%s'\n",
          mailto);
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          tmpBuf);
        }
      }
    else
      {
#ifdef TMAILDOMAIN
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_euser].at_val.at_str);
      strcat(mailto, "@");
      strcat(mailto, TMAILDOMAIN);
#else /* TMAILDOMAIN */
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_job_owner].at_val.at_str);
#endif /* TMAILDOMAIN */

      if (LOGLEVEL >= 5)
        {
        char tmpBuf[LOG_BUF_SIZE];

        snprintf(tmpBuf,sizeof(tmpBuf),
          "Updated mailto from job owner: '%s'\n",
          mailto);
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          tmpBuf);
        }
      }
    }

  /* mail subject line formating statement */

  if ((server.sv_attr[SRV_ATR_MailSubjectFmt].at_flags & ATR_VFLAG_SET) &&
      (server.sv_attr[SRV_ATR_MailSubjectFmt].at_val.at_str != NULL))
    {
    subjectfmt = server.sv_attr[SRV_ATR_MailSubjectFmt].at_val.at_str;
    }
  else
    {
    subjectfmt = "PBS JOB %i";
    }

  /* mail body formating statement */

  if ((server.sv_attr[SRV_ATR_MailBodyFmt].at_flags & ATR_VFLAG_SET) &&
      (server.sv_attr[SRV_ATR_MailBodyFmt].at_val.at_str != NULL))
    {
    bodyfmt = server.sv_attr[SRV_ATR_MailBodyFmt].at_val.at_str;
    }
  else
    {
    bodyfmt =  strcpy(bodyfmtbuf, "PBS Job Id: %i\n"
                                  "Job Name:   %j\n");
    if (pjob->ji_wattr[JOB_ATR_exec_host].at_flags & ATR_VFLAG_SET)
      {
      strcat(bodyfmt, "Exec host:  %h\n");
      }

    strcat(bodyfmt, "%m\n");

    if (text != NULL)
      {
      strcat(bodyfmt, "%d\n");
      }
    }
  /* setup sendmail command line with -f from_whom */

  i = strlen(SENDMAIL_CMD) + strlen(mailfrom) + strlen(mailto) + 6;

  if ((cmdbuf = malloc(i)) == NULL)
    {
    char tmpBuf[LOG_BUF_SIZE];

    snprintf(tmpBuf,sizeof(tmpBuf),
      "Unable to popen() command '%s' for writing: '%s' (error %d)\n",
      cmdbuf,
      strerror(errno),
      errno);
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);

    exit(1);
    }

  sprintf(cmdbuf, "%s -f %s %s",

          SENDMAIL_CMD,
          mailfrom,
          mailto);

  outmail = (FILE *)popen(cmdbuf, "w");

  if (outmail == NULL)
    {
    char tmpBuf[LOG_BUF_SIZE];

    snprintf(tmpBuf,sizeof(tmpBuf),
      "Unable to popen() command '%s' for writing: '%s' (error %d)\n",
      cmdbuf,
      strerror(errno),
      errno);
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);

    exit(1);
    }

  /* Pipe in mail headers: To: and Subject: */

  fprintf(outmail, "To: %s\n",
          mailto);

  fprintf(outmail, "Subject: ");
  svr_format_job(outmail, pjob, subjectfmt, mailpoint, text);
  fprintf(outmail, "\n");

  /* Set "Precedence: bulk" to avoid vacation messages, etc */

  fprintf(outmail, "Precedence: bulk\n\n");

  /* Now pipe in the email body */
  svr_format_job(outmail, pjob, bodyfmt, mailpoint, text);

  errno = 0;
  if ((i = pclose(outmail)) != 0)
    {
    char tmpBuf[LOG_BUF_SIZE];

    snprintf(tmpBuf,sizeof(tmpBuf),
      "Email '%c' to %s failed: Child process '%s' %s %d (errno %d:%s)\n",
      mailpoint,
      mailto,
      cmdbuf,
      ((WIFEXITED(i)) ? ("returned") : ((WIFSIGNALED(i)) ? ("killed by signal") : ("croaked"))),
      ((WIFEXITED(i)) ? (WEXITSTATUS(i)) : ((WIFSIGNALED(i)) ? (WTERMSIG(i)) : (i))),
      errno,
      strerror(errno));
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);
    }
  else if (LOGLEVEL >= 4)
    {
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      "Email sent successfully\n");
    }

  exit(0);

  /*NOTREACHED*/

  return;
  }  /* END svr_mailowner() */

The send_the_mail function in server/svr_mail.c in Terascale Open-Source Resource and Queue Manager (aka TORQUE Resource Manager) before 4.2.6 allows remote attackers to execute arbitrary commands via shell metacharacters in the email (-M switch) to qsub.


void svr_mailowner(

  job   *pjob,       /* I */
  int    mailpoint,  /* note, single character  */
  int    force,      /* if set to MAIL_FORCE, force mail delivery */
  char *text)      /* (optional) additional message text */

  {
  int         status = 0;
  int         numargs = 0;
  int         pipes[2];
  int         counter;
  pid_t       pid;
  char       *mailptr;
  char       *mailfrom = NULL;
  char        tmpBuf[LOG_BUF_SIZE];
  // We call sendmail with cmd_name + 2 arguments + # of mailto addresses + 1 for null
  char       *sendmail_args[100];
  char        mailto[1024];
  FILE       *stream;

  struct array_strings *pas;

  if ((server.sv_attr[SRV_ATR_MailDomain].at_flags & ATR_VFLAG_SET) &&
      (server.sv_attr[SRV_ATR_MailDomain].at_val.at_str != NULL) &&
      (!strcasecmp("never", server.sv_attr[SRV_ATR_MailDomain].at_val.at_str)))
    {
    /* never send user mail under any conditions */
    if (LOGLEVEL >= 3)
      {
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        "Not sending email: Mail domain set to 'never'\n");
      }

    return;
    }

  if (LOGLEVEL >= 3)
    {
    char tmpBuf[LOG_BUF_SIZE];

    snprintf(tmpBuf, LOG_BUF_SIZE, "preparing to send '%c' mail for job %s to %s (%.64s)\n",
             (char)mailpoint,
             pjob->ji_qs.ji_jobid,
             pjob->ji_wattr[JOB_ATR_job_owner].at_val.at_str,
             (text != NULL) ? text : "---");

    log_event(
      PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);
    }

  /*
   * if force is true, force the mail out regardless of mailpoint
   * unless server no_mail_force attribute is set to true
   */

  if ((force != MAIL_FORCE) ||
    (server.sv_attr[(int)SRV_ATR_NoMailForce].at_val.at_long == TRUE))
    {

    if (pjob->ji_wattr[JOB_ATR_mailpnts].at_flags & ATR_VFLAG_SET)
      {
      if (*(pjob->ji_wattr[JOB_ATR_mailpnts].at_val.at_str) ==  MAIL_NONE)
        {
        /* do not send mail. No mail requested on job */
        log_event(PBSEVENT_JOB,
                  PBS_EVENTCLASS_JOB,
                  pjob->ji_qs.ji_jobid,
                  "Not sending email: job requested no e-mail");
        return;
        }
      /* see if user specified mail of this type */
      if (strchr(
            pjob->ji_wattr[JOB_ATR_mailpnts].at_val.at_str,
            mailpoint) == NULL)
        {
        /* do not send mail */
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          "Not sending email: User does not want mail of this type.\n");

        return;
        }
      }
    else if (mailpoint != MAIL_ABORT) /* not set, default to abort */
      {
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        "Not sending email: Default mailpoint does not include this type.\n");

      return;
      }
    }

  /*
   * ok, now we will fork a process to do the mailing to not
   * hold up the server's other work.
   */

  if (fork())
    {
    return;  /* its all up to the child now */
    }

  /* Close the rest of the open file descriptors */
  int numfds = sysconf(_SC_OPEN_MAX);
  while (--numfds > 0)
    close(numfds);

  /* Who is mail from, if SRV_ATR_mailfrom not set use default */
  if ((mailfrom = server.sv_attr[SRV_ATR_mailfrom].at_val.at_str) == NULL)
    {
    if (LOGLEVEL >= 5)
      {
      char tmpBuf[LOG_BUF_SIZE];

      snprintf(tmpBuf,sizeof(tmpBuf),
        "Updated mailto from user list: '%s'\n",
        mailto);
      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        tmpBuf);
      }
    mailfrom = PBS_DEFAULT_MAIL;
    }

  /* Who does the mail go to?  If mail-list, them; else owner */
  *mailto = '\0';

  if (pjob->ji_wattr[JOB_ATR_mailuser].at_flags & ATR_VFLAG_SET)
    {
    /* has mail user list, send to them rather than owner */
    pas = pjob->ji_wattr[JOB_ATR_mailuser].at_val.at_arst;

    if (pas != NULL)
      {
      int i;
      for (i = 0;i < pas->as_usedptr;i++)
        {
        if ((strlen(mailto) + strlen(pas->as_string[i]) + 2) < sizeof(mailto))
          {
          strcat(mailto, pas->as_string[i]);
          strcat(mailto, " ");
          }
        }
      }
    }
  else
    {
    /* no mail user list, just send to owner */
    if ((server.sv_attr[SRV_ATR_MailDomain].at_flags & ATR_VFLAG_SET) &&
        (server.sv_attr[SRV_ATR_MailDomain].at_val.at_str != NULL))
      {
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_euser].at_val.at_str);
      strcat(mailto, "@");
      strcat(mailto, server.sv_attr[SRV_ATR_MailDomain].at_val.at_str);

      if (LOGLEVEL >= 5)
        {
        char tmpBuf[LOG_BUF_SIZE];

        snprintf(tmpBuf,sizeof(tmpBuf),
          "Updated mailto from job owner and mail domain: '%s'\n",
          mailto);
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          tmpBuf);
        }
      }
    else
      {
#ifdef TMAILDOMAIN
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_euser].at_val.at_str);
      strcat(mailto, "@");
      strcat(mailto, TMAILDOMAIN);
#else /* TMAILDOMAIN */
      strcpy(mailto, pjob->ji_wattr[JOB_ATR_job_owner].at_val.at_str);
#endif /* TMAILDOMAIN */

      if (LOGLEVEL >= 5)
        {
        char tmpBuf[LOG_BUF_SIZE];

        snprintf(tmpBuf,sizeof(tmpBuf),
          "Updated mailto from job owner: '%s'\n",
          mailto);
        log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
          PBS_EVENTCLASS_JOB,
          pjob->ji_qs.ji_jobid,
          tmpBuf);
        }
      }
    }

  sendmail_args[numargs++] = (char *)SENDMAIL_CMD;
  sendmail_args[numargs++] = (char *)"-f";
  sendmail_args[numargs++] = (char *)mailfrom;

  /* Add the e-mail addresses to the command line */
  mailptr = strdup(mailto);
  sendmail_args[numargs++] = mailptr;
  for (counter=0; counter < (int)strlen(mailptr); counter++)
    {
    if (mailptr[counter] == ',')
      {
      mailptr[counter] = '\0';
      sendmail_args[numargs++] = mailptr + counter + 1;
      if (numargs >= 99)
        break;
      }
    }

  sendmail_args[numargs] = NULL;

  /* Create a pipe to talk to the sendmail process we are about to fork */
  if (pipe(pipes) == -1)
    {
    snprintf(tmpBuf, sizeof(tmpBuf), "Unable to pipes for sending e-mail\n");
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);

    free(mailptr);
    exit(-1);
    }

  if ((pid=fork()) == -1)
    {
    snprintf(tmpBuf, sizeof(tmpBuf), "Unable to fork for sending e-mail\n");
    log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
      PBS_EVENTCLASS_JOB,
      pjob->ji_qs.ji_jobid,
      tmpBuf);

    free(mailptr);
    close(pipes[0]);
    close(pipes[1]);
    exit(-1);
    }
  else if (pid == 0)
    {
    /* CHILD */

    /* Make stdin the read end of the pipe */
    dup2(pipes[0], 0);

    /* Close the rest of the open file descriptors */
    int numfds = sysconf(_SC_OPEN_MAX);
    while (--numfds > 0)
      close(numfds);

    execv(SENDMAIL_CMD, sendmail_args);
    /* This never returns, but if the execv fails the child should exit */
    exit(1);
    }
  else
    {
    /* This is the parent */

    /* Close the read end of the pipe */
    close(pipes[0]);

    /* Write the body to the pipe */
    stream = fdopen(pipes[1], "w");
    write_email(stream, pjob, mailto, mailpoint, text);

    fflush(stream);

    /* Close and wait for the command to finish */
    if (fclose(stream) != 0)
      {
      snprintf(tmpBuf,sizeof(tmpBuf),
        "Piping mail body to sendmail closed: errno %d:%s\n",
        errno, strerror(errno));

      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        tmpBuf);
      }

    // we aren't going to block in order to find out whether or not sendmail worked
    if ((waitpid(pid, &status, WNOHANG) != 0) &&
        (status != 0))
      {
      snprintf(tmpBuf,sizeof(tmpBuf),
        "Sendmail command returned %d. Mail may not have been sent\n",
        status);

      log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
        PBS_EVENTCLASS_JOB,
        pjob->ji_qs.ji_jobid,
        tmpBuf);
      }

    // don't leave zombies
    while (waitpid(-1, &status, WNOHANG) != 0)
      {
      // zombie reaped, NO-OP
      }

    free(mailptr);
    exit(0);
    }

  /* NOT REACHED */

  exit(0);
  }  /* END svr_mailowner() */


drm/amdgpu: fix multiple memory leaks in acp_hw_init

In acp_hw_init there are some allocations that needs to be released in
case of failure:

1- adev->acp.acp_genpd should be released if any allocation attemp for
adev->acp.acp_cell, adev->acp.acp_res or i2s_pdata fails.
2- all of those allocations should be released if
mfd_add_hotplug_devices or pm_genpd_add_device fail.
3- Release is needed in case of time out values expire.

Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Navid Emamdoost <navid.emamdoost@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

static int acp_hw_init(void *handle)
{
	int r, i;
	uint64_t acp_base;
	u32 val = 0;
	u32 count = 0;
	struct device *dev;
	struct i2s_platform_data *i2s_pdata;

	struct amdgpu_device *adev = (struct amdgpu_device *)handle;

	const struct amdgpu_ip_block *ip_block =
		amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_ACP);

	if (!ip_block)
		return -EINVAL;

	r = amd_acp_hw_init(adev->acp.cgs_device,
			    ip_block->version->major, ip_block->version->minor);
	/* -ENODEV means board uses AZ rather than ACP */
	if (r == -ENODEV) {
		amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true);
		return 0;
	} else if (r) {
		return r;
	}

	if (adev->rmmio_size == 0 || adev->rmmio_size < 0x5289)
		return -EINVAL;

	acp_base = adev->rmmio_base;

	adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL);
	if (adev->acp.acp_genpd == NULL)
		return -ENOMEM;

	adev->acp.acp_genpd->gpd.name = "ACP_AUDIO";
	adev->acp.acp_genpd->gpd.power_off = acp_poweroff;
	adev->acp.acp_genpd->gpd.power_on = acp_poweron;

	adev->acp.acp_genpd->adev = adev;

	pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false);

	adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell),
							GFP_KERNEL);

	if (adev->acp.acp_cell == NULL)
		return -ENOMEM;

	adev->acp.acp_res = kcalloc(5, sizeof(struct resource), GFP_KERNEL);
	if (adev->acp.acp_res == NULL) {
		kfree(adev->acp.acp_cell);
		return -ENOMEM;
	}

	i2s_pdata = kcalloc(3, sizeof(struct i2s_platform_data), GFP_KERNEL);
	if (i2s_pdata == NULL) {
		kfree(adev->acp.acp_res);
		kfree(adev->acp.acp_cell);
		return -ENOMEM;
	}

	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
	}
	i2s_pdata[0].cap = DWC_I2S_PLAY;
	i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_PLAY_REG_OFFSET;
	i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_PLAY_REG_OFFSET;
	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_COMP_PARAM1 |
			DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_COMP_PARAM1;
	}

	i2s_pdata[1].cap = DWC_I2S_RECORD;
	i2s_pdata[1].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[1].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
	i2s_pdata[1].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;

	i2s_pdata[2].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[2].quirks |= DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		break;
	}

	i2s_pdata[2].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
	i2s_pdata[2].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[2].i2s_reg_comp1 = ACP_BT_COMP1_REG_OFFSET;
	i2s_pdata[2].i2s_reg_comp2 = ACP_BT_COMP2_REG_OFFSET;

	adev->acp.acp_res[0].name = "acp2x_dma";
	adev->acp.acp_res[0].flags = IORESOURCE_MEM;
	adev->acp.acp_res[0].start = acp_base;
	adev->acp.acp_res[0].end = acp_base + ACP_DMA_REGS_END;

	adev->acp.acp_res[1].name = "acp2x_dw_i2s_play";
	adev->acp.acp_res[1].flags = IORESOURCE_MEM;
	adev->acp.acp_res[1].start = acp_base + ACP_I2S_PLAY_REGS_START;
	adev->acp.acp_res[1].end = acp_base + ACP_I2S_PLAY_REGS_END;

	adev->acp.acp_res[2].name = "acp2x_dw_i2s_cap";
	adev->acp.acp_res[2].flags = IORESOURCE_MEM;
	adev->acp.acp_res[2].start = acp_base + ACP_I2S_CAP_REGS_START;
	adev->acp.acp_res[2].end = acp_base + ACP_I2S_CAP_REGS_END;

	adev->acp.acp_res[3].name = "acp2x_dw_bt_i2s_play_cap";
	adev->acp.acp_res[3].flags = IORESOURCE_MEM;
	adev->acp.acp_res[3].start = acp_base + ACP_BT_PLAY_REGS_START;
	adev->acp.acp_res[3].end = acp_base + ACP_BT_PLAY_REGS_END;

	adev->acp.acp_res[4].name = "acp2x_dma_irq";
	adev->acp.acp_res[4].flags = IORESOURCE_IRQ;
	adev->acp.acp_res[4].start = amdgpu_irq_create_mapping(adev, 162);
	adev->acp.acp_res[4].end = adev->acp.acp_res[4].start;

	adev->acp.acp_cell[0].name = "acp_audio_dma";
	adev->acp.acp_cell[0].num_resources = 5;
	adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
	adev->acp.acp_cell[0].platform_data = &adev->asic_type;
	adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type);

	adev->acp.acp_cell[1].name = "designware-i2s";
	adev->acp.acp_cell[1].num_resources = 1;
	adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
	adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
	adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);

	adev->acp.acp_cell[2].name = "designware-i2s";
	adev->acp.acp_cell[2].num_resources = 1;
	adev->acp.acp_cell[2].resources = &adev->acp.acp_res[2];
	adev->acp.acp_cell[2].platform_data = &i2s_pdata[1];
	adev->acp.acp_cell[2].pdata_size = sizeof(struct i2s_platform_data);

	adev->acp.acp_cell[3].name = "designware-i2s";
	adev->acp.acp_cell[3].num_resources = 1;
	adev->acp.acp_cell[3].resources = &adev->acp.acp_res[3];
	adev->acp.acp_cell[3].platform_data = &i2s_pdata[2];
	adev->acp.acp_cell[3].pdata_size = sizeof(struct i2s_platform_data);

	r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell,
								ACP_DEVS);
	if (r)
		return r;

	for (i = 0; i < ACP_DEVS ; i++) {
		dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
		r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev);
		if (r) {
			dev_err(dev, "Failed to add dev to genpd\n");
			return r;
		}
	}

	/* Assert Soft reset of ACP */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);

	val |= ACP_SOFT_RESET__SoftResetAud_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val);

	count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
	while (true) {
		val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);
		if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
		    (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
			break;
		if (--count == 0) {
			dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
			return -ETIMEDOUT;
		}
		udelay(100);
	}
	/* Enable clock to ACP and wait until the clock is enabled */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_CONTROL);
	val = val | ACP_CONTROL__ClkEn_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_CONTROL, val);

	count = ACP_CLOCK_EN_TIME_OUT_VALUE;

	while (true) {
		val = cgs_read_register(adev->acp.cgs_device, mmACP_STATUS);
		if (val & (u32) 0x1)
			break;
		if (--count == 0) {
			dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
			return -ETIMEDOUT;
		}
		udelay(100);
	}
	/* Deassert the SOFT RESET flags */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);
	val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val);
	return 0;
}

Four memory leaks in the acp_hw_init() function in drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c in the Linux kernel before 5.3.8 allow attackers to cause a denial of service (memory consumption) by triggering mfd_add_hotplug_devices() or pm_genpd_add_device() failures, aka CID-57be09c6e874. NOTE: third parties dispute the relevance of this because the attacker must already have privileges for module loading


static int acp_hw_init(void *handle)
{
	int r, i;
	uint64_t acp_base;
	u32 val = 0;
	u32 count = 0;
	struct device *dev;
	struct i2s_platform_data *i2s_pdata = NULL;

	struct amdgpu_device *adev = (struct amdgpu_device *)handle;

	const struct amdgpu_ip_block *ip_block =
		amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_ACP);

	if (!ip_block)
		return -EINVAL;

	r = amd_acp_hw_init(adev->acp.cgs_device,
			    ip_block->version->major, ip_block->version->minor);
	/* -ENODEV means board uses AZ rather than ACP */
	if (r == -ENODEV) {
		amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_ACP, true);
		return 0;
	} else if (r) {
		return r;
	}

	if (adev->rmmio_size == 0 || adev->rmmio_size < 0x5289)
		return -EINVAL;

	acp_base = adev->rmmio_base;

	adev->acp.acp_genpd = kzalloc(sizeof(struct acp_pm_domain), GFP_KERNEL);
	if (adev->acp.acp_genpd == NULL)
		return -ENOMEM;

	adev->acp.acp_genpd->gpd.name = "ACP_AUDIO";
	adev->acp.acp_genpd->gpd.power_off = acp_poweroff;
	adev->acp.acp_genpd->gpd.power_on = acp_poweron;

	adev->acp.acp_genpd->adev = adev;

	pm_genpd_init(&adev->acp.acp_genpd->gpd, NULL, false);

	adev->acp.acp_cell = kcalloc(ACP_DEVS, sizeof(struct mfd_cell),
							GFP_KERNEL);

	if (adev->acp.acp_cell == NULL) {
		r = -ENOMEM;
		goto failure;
	}

	adev->acp.acp_res = kcalloc(5, sizeof(struct resource), GFP_KERNEL);
	if (adev->acp.acp_res == NULL) {
		r = -ENOMEM;
		goto failure;
	}

	i2s_pdata = kcalloc(3, sizeof(struct i2s_platform_data), GFP_KERNEL);
	if (i2s_pdata == NULL) {
		r = -ENOMEM;
		goto failure;
	}

	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
	}
	i2s_pdata[0].cap = DWC_I2S_PLAY;
	i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_PLAY_REG_OFFSET;
	i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_PLAY_REG_OFFSET;
	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_COMP_PARAM1 |
			DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
			DW_I2S_QUIRK_COMP_PARAM1;
	}

	i2s_pdata[1].cap = DWC_I2S_RECORD;
	i2s_pdata[1].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[1].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
	i2s_pdata[1].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;

	i2s_pdata[2].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
	switch (adev->asic_type) {
	case CHIP_STONEY:
		i2s_pdata[2].quirks |= DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
		break;
	default:
		break;
	}

	i2s_pdata[2].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
	i2s_pdata[2].snd_rates = SNDRV_PCM_RATE_8000_96000;
	i2s_pdata[2].i2s_reg_comp1 = ACP_BT_COMP1_REG_OFFSET;
	i2s_pdata[2].i2s_reg_comp2 = ACP_BT_COMP2_REG_OFFSET;

	adev->acp.acp_res[0].name = "acp2x_dma";
	adev->acp.acp_res[0].flags = IORESOURCE_MEM;
	adev->acp.acp_res[0].start = acp_base;
	adev->acp.acp_res[0].end = acp_base + ACP_DMA_REGS_END;

	adev->acp.acp_res[1].name = "acp2x_dw_i2s_play";
	adev->acp.acp_res[1].flags = IORESOURCE_MEM;
	adev->acp.acp_res[1].start = acp_base + ACP_I2S_PLAY_REGS_START;
	adev->acp.acp_res[1].end = acp_base + ACP_I2S_PLAY_REGS_END;

	adev->acp.acp_res[2].name = "acp2x_dw_i2s_cap";
	adev->acp.acp_res[2].flags = IORESOURCE_MEM;
	adev->acp.acp_res[2].start = acp_base + ACP_I2S_CAP_REGS_START;
	adev->acp.acp_res[2].end = acp_base + ACP_I2S_CAP_REGS_END;

	adev->acp.acp_res[3].name = "acp2x_dw_bt_i2s_play_cap";
	adev->acp.acp_res[3].flags = IORESOURCE_MEM;
	adev->acp.acp_res[3].start = acp_base + ACP_BT_PLAY_REGS_START;
	adev->acp.acp_res[3].end = acp_base + ACP_BT_PLAY_REGS_END;

	adev->acp.acp_res[4].name = "acp2x_dma_irq";
	adev->acp.acp_res[4].flags = IORESOURCE_IRQ;
	adev->acp.acp_res[4].start = amdgpu_irq_create_mapping(adev, 162);
	adev->acp.acp_res[4].end = adev->acp.acp_res[4].start;

	adev->acp.acp_cell[0].name = "acp_audio_dma";
	adev->acp.acp_cell[0].num_resources = 5;
	adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
	adev->acp.acp_cell[0].platform_data = &adev->asic_type;
	adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type);

	adev->acp.acp_cell[1].name = "designware-i2s";
	adev->acp.acp_cell[1].num_resources = 1;
	adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
	adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
	adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);

	adev->acp.acp_cell[2].name = "designware-i2s";
	adev->acp.acp_cell[2].num_resources = 1;
	adev->acp.acp_cell[2].resources = &adev->acp.acp_res[2];
	adev->acp.acp_cell[2].platform_data = &i2s_pdata[1];
	adev->acp.acp_cell[2].pdata_size = sizeof(struct i2s_platform_data);

	adev->acp.acp_cell[3].name = "designware-i2s";
	adev->acp.acp_cell[3].num_resources = 1;
	adev->acp.acp_cell[3].resources = &adev->acp.acp_res[3];
	adev->acp.acp_cell[3].platform_data = &i2s_pdata[2];
	adev->acp.acp_cell[3].pdata_size = sizeof(struct i2s_platform_data);

	r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell,
								ACP_DEVS);
	if (r)
		goto failure;

	for (i = 0; i < ACP_DEVS ; i++) {
		dev = get_mfd_cell_dev(adev->acp.acp_cell[i].name, i);
		r = pm_genpd_add_device(&adev->acp.acp_genpd->gpd, dev);
		if (r) {
			dev_err(dev, "Failed to add dev to genpd\n");
			goto failure;
		}
	}

	/* Assert Soft reset of ACP */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);

	val |= ACP_SOFT_RESET__SoftResetAud_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val);

	count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
	while (true) {
		val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);
		if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
		    (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
			break;
		if (--count == 0) {
			dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
			r = -ETIMEDOUT;
			goto failure;
		}
		udelay(100);
	}
	/* Enable clock to ACP and wait until the clock is enabled */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_CONTROL);
	val = val | ACP_CONTROL__ClkEn_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_CONTROL, val);

	count = ACP_CLOCK_EN_TIME_OUT_VALUE;

	while (true) {
		val = cgs_read_register(adev->acp.cgs_device, mmACP_STATUS);
		if (val & (u32) 0x1)
			break;
		if (--count == 0) {
			dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
			r = -ETIMEDOUT;
			goto failure;
		}
		udelay(100);
	}
	/* Deassert the SOFT RESET flags */
	val = cgs_read_register(adev->acp.cgs_device, mmACP_SOFT_RESET);
	val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
	cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val);
	return 0;

failure:
	kfree(i2s_pdata);
	kfree(adev->acp.acp_res);
	kfree(adev->acp.acp_cell);
	kfree(adev->acp.acp_genpd);
	return r;
}


Bug 701827: fixed incorrect buffer expansion in lxm5700m_print_page().

Fixes:
    ./sanbin/gs -dBATCH -dNOPAUSE -r425 -dFitPage -sOutputFile=tmp -sDEVICE=lxm5700m ../bug-701827.eps

/* ------ Driver procedures ------ */

/* Send the page to the printer. */
static int
lxm5700m_print_page(gx_device_printer *pdev, gp_file *prn_stream)
{
    int lnum,minX, maxX, i, l, highestX, leastX, extent;
    int direction = RIGHTWARD;
    int lastY = 0;

    int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
    /* Note that in_size is a multiple of 8. */
    int in_size = line_size * (swipeHeight);
    int swipeBuf_size = in_size;
    byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lxm_print_page(buf1)");
    byte *swipeBuf =
        (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
    byte *in = buf1;

    /* Check allocations */
    if ( buf1 == 0 || swipeBuf == 0 ) {
        if ( buf1 )
quit_ignomiously: /* and a goto into an if statement is pretty ignomious! */
        gs_free(pdev->memory, (char *)buf1, in_size, 1, "lxm_print_page(buf1)");
        if ( swipeBuf )
            gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");
        return_error(gs_error_VMerror);
    }

    {	/* Initialize the printer and reset the margins. */
        static const char init_string[] = {
            init1(),
            init2(),
            init3()
        };
        gp_fwrite(init_string, 1, sizeof(init_string), prn_stream);
    }
    /* Print lines of graphics */
    for (lnum=0; lnum < pdev->height-swipeHeight ; ) { /* increment in body */
        byte *in_data;
        register byte *outp;
        int lcnt;

        {	/* test for blank scan lines.  We  maintain the */
            /* loop invariant lnum <pdev->height, but modify lnum */
            int l;

            for (l=lnum; l<pdev->height; l++) {
                /* Copy 1 scan line and test for all zero. */
                gdev_prn_get_bits(pdev, l, in, &in_data);
                if ( in_data[0] != 0 ||
                     memcmp((char *)in_data, (char *)in_data + 1, line_size - 1)
                     ) {
                    break;
                }
            }/* end for l */

            /* now l is the next non-blank scan line */
            if (l >= pdev->height) {/* if there are no more bits on this page */
                lnum = l;
                break;			/* end the loop and eject the page*/
            }

            /* leave room for following swipe to reinforce these bits */
            if (l-lnum > overLap) lnum = l - overLap;

            /* if the first non-blank near bottom of page */
            if (lnum >=pdev->height - swipeHeight) {
                /* don't move the printhead over empty air*/
                lnum = pdev->height - swipeHeight;
            }
        }

        /* Copy the the scan lines. */
        lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
        if ( lcnt < swipeHeight ) {
            /* Pad with lines of zeros. */
            memset(in + lcnt * line_size, 0,
                   in_size - lcnt * line_size);
        }

        /* compute right and left margin for this swipe */
        minX = line_size;
        maxX = 0;
        for (l=0; l<swipeHeight; l++) {/* for each line of swipe */
            for (i=0; i<minX; i++) {/* look for left-most non-zero byte*/
                if (in[l*line_size+i] !=0) {
                    minX = i;
                    break;
                }
            }
            for (i=line_size-1; i>=maxX; i--) {/* look for right-most */
                if (in[l*line_size+i] !=0) {
                    maxX = i;
                    break;
                }
            }
        }
        minX = (minX&(-2)); /* truncate to even */
        maxX = (maxX+3)&-2; /* raise to even */

        highestX = maxX*8-1;
        leastX = minX*8;
        extent = highestX -leastX +1;

        outp = swipeBuf;

        /* macro, not fcn call.  Space penalty is modest, speed helps */
#define buffer_store(x) if(outp-swipeBuf>=swipeBuf_size) {\
            gs_free(pdev->memory, (char *)swipeBuf, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
            swipeBuf_size*=2;\
            swipeBuf = (byte *)gs_malloc(pdev->memory, swipeBuf_size, 1, "lxm_print_page(swipeBuf)");\
            if (swipeBuf == 0) goto quit_ignomiously;\
            break;}\
        else *outp++ = (x)

            {/* work out the bytes to store for this swipe*/

                int sx, sxBy8, sxMask;
                int words[directorySize];
                bool f, sum;
                int retval=0;
                int j,c,y;
                int j1,c1;
                int i,b,x, directory ;

                /* want to set up pointers for (upto two) stripes covered by the output*/

                /* now for each column covered by output: */
                for (x=leastX; x<=highestX; x++) {
                    for (i=0; i<directorySize; i++) {
                        words[i] = 0;
                    }
                    directory = 0x2000;	/* empty directory != 0 */

                    /* prime loops: make comparisons here */
                    switch (direction) {
                    case(RIGHTWARD):
                        sx = (x&1)==1 ? x : x-(((lxm_device*)pdev)->headSeparation);
                        j1 = (x&1);	/* even if x even, odd if x odd */
                        break;
                    default:	/* shouldn't happen ... but compilation checks */
                    case(LEFTWARD):
                        sx = (x&1)==0 ? x : x-((lxm_device*)pdev)->headSeparation;
                        j1 = 1-(x&1);	/* odd if x even, even if x odd */
                    }
                    c1 = 0x8000 >> j1;

                    sxBy8 = sx/8;
                    sxMask = 0x80>>(sx%8);

                    /* loop through all the swipeHeight bits of this column */
                    for (i = 0, b=1, y= sxBy8+j1*line_size; i < directorySize; i++,b<<=1) {
                        sum = false;
                        for (j=j1,c=c1 /*,y=i*16*line_size+sxBy8*/; j<16; j+=2, y+=2*line_size, c>>=2) {
                            f = (in[y]&sxMask);
                            if (f) {
                                words[i] |= c;
                                sum |= f;
                            }
                        }
                        if (!sum) directory |=b;
                    }
                    retval+=2;
                    buffer_store(directory>>8); buffer_store(directory&0xff);
                    if (directory != 0x3fff) {
                        for (i=0; i<directorySize; i++) {
                            if (words[i] !=0) {
                                buffer_store(words[i]>>8) ; buffer_store(words[i]&0xff);
                                retval += 2;
                            }
                        }
                    }
                }
#undef buffer_store
            }
        {/* now write out header, then buffered bits */
            int leastY = lnum;

            /* compute size of swipe, needed for header */
            int sz = 0x1a + outp - swipeBuf;

            /* put out header*/
            int deltaY = 2*(leastY - lastY);  /* vert coordinates here are 1200 dpi */
            lastY = leastY;
            outByte(0x1b); outByte('*'); outByte(3);
            outByte(deltaY>>8); outByte(deltaY&0xff);
            outByte(0x1b); outByte('*'); outByte(4); outByte(0); outByte(0);
            outByte(sz>>8); outByte(sz&0xff); outByte(0); outByte(3);
            outByte(1); outByte(1); outByte(0x1a);
            outByte(0);
            outByte(extent>>8); outByte(extent&0xff);
            outByte(leastX>>8); outByte(leastX&0xff);
            outByte(highestX>>8); outByte(highestX&0xff);
            outByte(0); outByte(0);
            outByte(0x22); outByte(0x33); outByte(0x44);
            outByte(0x55); outByte(1);
            /* put out bytes */
            gp_fwrite(swipeBuf,1,outp-swipeBuf,prn_stream);
        }
            lnum += overLap;
            direction ^= 1;
    }/* ends the loop for swipes of the print head.*/

    /* Eject the page and reinitialize the printer */
    {
        static const char bottom[] = {
            fin() /*,  looks like I can get away with only this much ...
            init1(),
            init3(),
            fin()   ,
            top(),
            fin()  */
        };
        gp_fwrite(bottom, 1, sizeof(bottom), prn_stream);
    }
    gp_fflush(prn_stream);

A buffer overflow vulnerability in lxm5700m_print_page() in devices/gdevlxm.c of Artifex Software GhostScript v9.50 allows a remote attacker to cause a denial of service via a crafted eps file. This is fixed in v9.51.