From: Julian Wiedmann <jwi@linux.ibm.com>
Subject: s390/qeth: fix length check in SNMP processing
Patch-mainline: v4.20-rc5
Git-commit: 9a764c1e59684c0358e16ccaafd870629f2cfe67
References: bsc#1117953, LTC#173657

Description:  qeth: fix length check in SNMP processing
Symptom:      Undefined behaviour.
Problem:      The response for a SNMP request can consist of multiple parts,
              which the cmd callback stages into a kernel buffer until all
              parts have been received. If the callback detects that the
              staging buffer provides insufficient space, it bails out with
              error.
              This processing is buggy for the first part of the response -
              while it initially checks for a length of 'data_len', it later
              copies an additional amount of
              'offsetof(struct qeth_snmp_cmd, data)' bytes.
Solution:     Fix the calculation of 'data_len' for the first part of the
              response.
Reproduction: -

Upstream-Description:

              s390/qeth: fix length check in SNMP processing

              The response for a SNMP request can consist of multiple parts, which
              the cmd callback stages into a kernel buffer until all parts have been
              received. If the callback detects that the staging buffer provides
              insufficient space, it bails out with error.
              This processing is buggy for the first part of the response - while it
              initially checks for a length of 'data_len', it later copies an
              additional amount of 'offsetof(struct qeth_snmp_cmd, data)' bytes.

              Fix the calculation of 'data_len' for the first part of the response.
              This also nicely cleans up the memcpy code.

              Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
              Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
              Reviewed-by: Ursula Braun <ubraun@linux.ibm.com>
              Signed-off-by: David S. Miller <davem@davemloft.net>


Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/s390/net/qeth_core_main.c |   27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -4541,8 +4541,8 @@ static int qeth_snmp_command_cb(struct q
 {
 	struct qeth_ipa_cmd *cmd;
 	struct qeth_arp_query_info *qinfo;
-	struct qeth_snmp_cmd *snmp;
 	unsigned char *data;
+	void *snmp_data;
 	__u16 data_len;
 
 	QETH_CARD_TEXT(card, 3, "snpcmdcb");
@@ -4550,7 +4550,6 @@ static int qeth_snmp_command_cb(struct q
 	cmd = (struct qeth_ipa_cmd *) sdata;
 	data = (unsigned char *)((char *)cmd - reply->offset);
 	qinfo = (struct qeth_arp_query_info *) reply->param;
-	snmp = &cmd->data.setadapterparms.data.snmp;
 
 	if (cmd->hdr.return_code) {
 		QETH_CARD_TEXT_(card, 4, "scer1%x", cmd->hdr.return_code);
@@ -4563,10 +4562,15 @@ static int qeth_snmp_command_cb(struct q
 		return 0;
 	}
 	data_len = *((__u16 *)QETH_IPA_PDU_LEN_PDU1(data));
-	if (cmd->data.setadapterparms.hdr.seq_no == 1)
-		data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
-	else
-		data_len -= (__u16)((char *)&snmp->request - (char *)cmd);
+	if (cmd->data.setadapterparms.hdr.seq_no == 1) {
+		snmp_data = &cmd->data.setadapterparms.data.snmp;
+		data_len -= offsetof(struct qeth_ipa_cmd,
+				     data.setadapterparms.data.snmp);
+	} else {
+		snmp_data = &cmd->data.setadapterparms.data.snmp.request;
+		data_len -= offsetof(struct qeth_ipa_cmd,
+				     data.setadapterparms.data.snmp.request);
+	}
 
 	/* check if there is enough room in userspace */
 	if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
@@ -4579,16 +4583,9 @@ static int qeth_snmp_command_cb(struct q
 	QETH_CARD_TEXT_(card, 4, "sseqn%i",
 		cmd->data.setadapterparms.hdr.seq_no);
 	/*copy entries to user buffer*/
-	if (cmd->data.setadapterparms.hdr.seq_no == 1) {
-		memcpy(qinfo->udata + qinfo->udata_offset,
-		       (char *)snmp,
-		       data_len + offsetof(struct qeth_snmp_cmd, data));
-		qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
-	} else {
-		memcpy(qinfo->udata + qinfo->udata_offset,
-		       (char *)&snmp->request, data_len);
-	}
+	memcpy(qinfo->udata + qinfo->udata_offset, snmp_data, data_len);
 	qinfo->udata_offset += data_len;
+
 	/* check if all replies received ... */
 		QETH_CARD_TEXT_(card, 4, "srtot%i",
 			       cmd->data.setadapterparms.hdr.used_total);
