CVE-2014-9765 (C, published 2016-04-19T21:59Z)
Description: value: Buffer overflow in the main_get_appheader function in xdelta3-main.h in xdelta3 before 3.0.9 allows remote attackers to execute arbitrary code via a crafted input file.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Repository: https://github.com/jmacd/xdelta-devel
Files changed:
xdelta3/xdelta3-main.h
xdelta3/xdelta3-test.h
Commit message: Add appheader tests; fix buffer overflow in main_get_appheader

Vulnerable code:
      char *parsed[4];
      while ((slash = strchr (start, '/')) != NULL)
 * 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012.  
	(1812433253UL * (mt->mt_buffer_[i-1] ^ 
      y = (mt->mt_buffer_[kk] & UPPER_MASK) | 
      mt->mt_buffer_[kk] = mt->mt_buffer_[kk + MT_IA] ^ 
      y = (mt->mt_buffer_[kk] & UPPER_MASK) | 
      mt->mt_buffer_[kk] = mt->mt_buffer_[kk + (MT_IA - MT_LEN)] ^ 
    y = (mt->mt_buffer_[MT_LEN - 1] & UPPER_MASK) | 
    mt->mt_buffer_[MT_LEN - 1] = mt->mt_buffer_[MT_IA - 1] ^ 
      return XD3_INTERNAL;
  usize_t ts = (mt_random (&static_mtrand) % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2;
  usize_t ss = (mt_random (&static_mtrand) % TEST_FILE_MEAN) + TEST_FILE_MEAN / 2;
	      XPR(NT "byte %u (read %u @ %"Q"u) %d != %d\n", 
    if (diffs != 0) 
test_save_copy (const char *origname)
  snprintf_func (buf, TESTBUFSIZE, "cp -f %s %s", origname, TEST_COPY_FILE);
  if ((ret = xd3_read_uint32_t (stream, & inp, max, & rval)) != 
  for (i = 0; i < (2 << 20); i += 256) 
      for (j = 0; j < 256; j++) 
  if ((ret = test_streaming (stream, 
			     buf, 
			     buf + (1 << 20), 
			     buf + (2 << 20), 
			     1 << 12))) 
  snprintf_func (ecmd, TESTBUFSIZE, "%s %s -f %s %s %s %s %s %s %s", 
	    !change_apphead ? "" : 
	  snprintf_func (rptr, rbuf+TESTBUFSIZE-rptr, "%d/%d", 

Fixed code:
      const int kMaxArgs = 4;
      char *parsed[kMaxArgs];
      while ((slash = strchr (start, '/')) != NULL && place < (kMaxArgs-1))
 * 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012.
	(1812433253UL * (mt->mt_buffer_[i-1] ^
      y = (mt->mt_buffer_[kk] & UPPER_MASK) |
      mt->mt_buffer_[kk] = mt->mt_buffer_[kk + MT_IA] ^
      y = (mt->mt_buffer_[kk] & UPPER_MASK) |
      mt->mt_buffer_[kk] = mt->mt_buffer_[kk + (MT_IA - MT_LEN)] ^
    y = (mt->mt_buffer_[MT_LEN - 1] & UPPER_MASK) |
    mt->mt_buffer_[MT_LEN - 1] = mt->mt_buffer_[MT_IA - 1] ^
      return ret;
  usize_t ts = (mt_random (&static_mtrand) % TEST_FILE_MEAN) +
    TEST_FILE_MEAN / 2;
  usize_t ss = (mt_random (&static_mtrand) % TEST_FILE_MEAN) +
    TEST_FILE_MEAN / 2;
	      XPR(NT "byte %u (read %u @ %"Q"u) %d != %d\n",
    if (diffs != 0)
test_copy_to (const char *from, const char *to)
  snprintf_func (buf, TESTBUFSIZE, "cp -f %s %s", from, to);
static int
test_save_copy (const char *origname)
{
  return test_copy_to(origname, TEST_COPY_FILE);
}

  if ((ret = xd3_read_uint32_t (stream, & inp, max, & rval)) !=
  for (i = 0; i < (2 << 20); i += 256)
      for (j = 0; j < 256; j++)
  if ((ret = test_streaming (stream,
			     buf,
			     buf + (1 << 20),
			     buf + (2 << 20),
			     1 << 12)))
  snprintf_func (ecmd, TESTBUFSIZE, "%s %s -f %s %s %s %s %s %s %s",
	    !change_apphead ? "" :
/* This tests that the default appheader works */
static int
test_appheader (xd3_stream *stream, int ignore)
{
  int i;
  int ret;
  char buf[TESTBUFSIZE];
  char bogus[TESTBUFSIZE];
  xoff_t ssize, tsize;
  test_setup ();

  if ((ret = test_make_inputs (stream, &ssize, &tsize))) { return ret; }

  snprintf_func (buf, TESTBUFSIZE, "%s -q -f -e -s %s %s %s", program_name,
		 TEST_SOURCE_FILE, TEST_TARGET_FILE, TEST_DELTA_FILE);
  if ((ret = do_cmd (stream, buf))) { return ret; }

  if ((ret = test_copy_to (program_name, TEST_RECON2_FILE))) { return ret; }

  snprintf_func (buf, TESTBUFSIZE, "chmod 0700 %s", TEST_RECON2_FILE);
  if ((ret = do_cmd (stream, buf))) { return ret; }

  if ((ret = test_save_copy (TEST_TARGET_FILE))) { return ret; }
  if ((ret = test_copy_to (TEST_SOURCE_FILE, TEST_TARGET_FILE))) { return ret; }

  if ((ret = test_compare_files (TEST_TARGET_FILE, TEST_COPY_FILE)) == 0)
    {
      return XD3_INVALID;  // I.e., files are different!
    }

  // Test that the target file is restored.
  snprintf_func (buf, TESTBUFSIZE, "(cd /tmp && %s -q -f -d %s)",
		 TEST_RECON2_FILE,
		 TEST_DELTA_FILE);
  if ((ret = do_cmd (stream, buf))) { return ret; }

  if ((ret = test_compare_files (TEST_TARGET_FILE, TEST_COPY_FILE)) != 0)
    {
      return ret;
    }

  // Test a malicious string w/ entries > 4 in the appheader by having
  // the encoder write it:
  for (i = 0; i < TESTBUFSIZE / 4; ++i)
    {
      bogus[2*i] = 'G';
      bogus[2*i+1] = '/';
    }
  bogus[TESTBUFSIZE/2-1] = 0;

  snprintf_func (buf, TESTBUFSIZE, 
		 "%s -q -f -A=%s -e -s %s %s %s", program_name, bogus,
		 TEST_SOURCE_FILE, TEST_TARGET_FILE, TEST_DELTA_FILE);
  if ((ret = do_cmd (stream, buf))) { return ret; }
  // Then read it:
  snprintf_func (buf, TESTBUFSIZE, "(cd /tmp && %s -q -f -d %s)",
		 TEST_RECON2_FILE,
		 TEST_DELTA_FILE);
  if ((ret = do_cmd (stream, buf)) == 0) 
    { 
      return XD3_INVALID;  // Impossible
    }
  if (!WIFEXITED(ret))
    {
      return XD3_INVALID;  // Must have crashed!
    }

  return 0;
}

	  snprintf_func (rptr, rbuf+TESTBUFSIZE-rptr, "%d/%d",
  DO_TEST (appheader, 0, 0);