Commit message: Fix #230: Memory corruption bug due to Integer Overflow in remap_struct()

(See problem description in issue #230.)
Check that the number of fields (long long integers, long integers or
integers) as read from a system activity binary datafile multiplied by
its alignment width doesn't overflow, i.e. the result must not be
smaller than the number of fields.

Reported-by: Ren Kimura
Signed-off-by: Sebastien GODARD <sysstat@users.noreply.github.com>
CVE: CVE-2019-16167
CWE: CWE-787
Vulnerability description: sysstat before 12.1.6 has memory corruption due to an Integer Overflow in remap_struct() in sa_common.c.

Project: sysstat
File: sa_common.c

int remap_struct(unsigned int gtypes_nr[], unsigned int ftypes_nr[],
		 void *ps, unsigned int f_size, unsigned int g_size, size_t b_size)
{
	int d;
	size_t n;

	/* Sanity check */
	if (MAP_SIZE(ftypes_nr) > f_size)
		return -1;

	/* Remap [unsigned] long fields */
	d = gtypes_nr[0] - ftypes_nr[0];
	if (d) {
		n = MINIMUM(f_size - ftypes_nr[0] * ULL_ALIGNMENT_WIDTH,
			    g_size - gtypes_nr[0] * ULL_ALIGNMENT_WIDTH);
		if ((ftypes_nr[0] * ULL_ALIGNMENT_WIDTH >= b_size) ||
		    (gtypes_nr[0] * ULL_ALIGNMENT_WIDTH + n > b_size) ||
		    (ftypes_nr[0] * ULL_ALIGNMENT_WIDTH + n > b_size))
			return -1;
		memmove(((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH,
			((char *) ps) + ftypes_nr[0] * ULL_ALIGNMENT_WIDTH, n);
		if (d > 0) {
			memset(((char *) ps) + ftypes_nr[0] * ULL_ALIGNMENT_WIDTH,
			       0, d * ULL_ALIGNMENT_WIDTH);
		}
	}
	/* Remap [unsigned] int fields */
	d = gtypes_nr[1] - ftypes_nr[1];
	if (d) {
		n = MINIMUM(f_size - ftypes_nr[0] * ULL_ALIGNMENT_WIDTH
				   - ftypes_nr[1] * UL_ALIGNMENT_WIDTH,
			    g_size - gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				   - gtypes_nr[1] * UL_ALIGNMENT_WIDTH);
		if ((gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     ftypes_nr[1] * UL_ALIGNMENT_WIDTH >= b_size) ||
		    (gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     gtypes_nr[1] * UL_ALIGNMENT_WIDTH + n > b_size) ||
		    (gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     ftypes_nr[1] * UL_ALIGNMENT_WIDTH + n > b_size))
			return -1;
		memmove(((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				      + gtypes_nr[1] * UL_ALIGNMENT_WIDTH,
			((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				      + ftypes_nr[1] * UL_ALIGNMENT_WIDTH, n);
		if (d > 0) {
			memset(((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
					     + ftypes_nr[1] * UL_ALIGNMENT_WIDTH,
			       0, d * UL_ALIGNMENT_WIDTH);
		}
	}
	/* Remap possible fields (like strings of chars) following int fields */
	d = gtypes_nr[2] - ftypes_nr[2];
	if (d) {
		n = MINIMUM(f_size - ftypes_nr[0] * ULL_ALIGNMENT_WIDTH
				   - ftypes_nr[1] * UL_ALIGNMENT_WIDTH
				   - ftypes_nr[2] * U_ALIGNMENT_WIDTH,
			    g_size - gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				   - gtypes_nr[1] * UL_ALIGNMENT_WIDTH
				   - gtypes_nr[2] * U_ALIGNMENT_WIDTH);
		if ((gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     gtypes_nr[1] * UL_ALIGNMENT_WIDTH +
		     ftypes_nr[2] * U_ALIGNMENT_WIDTH >= b_size) ||
		    (gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     gtypes_nr[1] * UL_ALIGNMENT_WIDTH +
		     gtypes_nr[2] * U_ALIGNMENT_WIDTH + n > b_size) ||
		    (gtypes_nr[0] * ULL_ALIGNMENT_WIDTH +
		     gtypes_nr[1] * UL_ALIGNMENT_WIDTH +
		     ftypes_nr[2] * U_ALIGNMENT_WIDTH + n > b_size))
			return -1;
		memmove(((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				      + gtypes_nr[1] * UL_ALIGNMENT_WIDTH
				      + gtypes_nr[2] * U_ALIGNMENT_WIDTH,
			((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
				      + gtypes_nr[1] * UL_ALIGNMENT_WIDTH
				      + ftypes_nr[2] * U_ALIGNMENT_WIDTH, n);
		if (d > 0) {
			memset(((char *) ps) + gtypes_nr[0] * ULL_ALIGNMENT_WIDTH
					     + gtypes_nr[1] * UL_ALIGNMENT_WIDTH
					     + ftypes_nr[2] * U_ALIGNMENT_WIDTH,
			       0, d * U_ALIGNMENT_WIDTH);
		}
	}
	return 0;
}