Bug 196135 - Double fetch problem in Linux-4.10.1/sound/oss/msnd_pinnacle.c
Summary: Double fetch problem in Linux-4.10.1/sound/oss/msnd_pinnacle.c
Status: NEW
Alias: None
Product: Drivers
Classification: Unclassified
Component: Sound(OSS) (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: drivers_sound
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-06-20 13:16 UTC by Pengfei Wang
Modified: 2017-08-05 01:46 UTC (History)
2 users (show)

See Also:
Kernel Version: 4.10.1
Subsystem:
Regression: No
Bisected commit-id:


Attachments
source file (47.49 KB, text/x-csrc)
2017-06-20 13:16 UTC, Pengfei Wang
Details

Description Pengfei Wang 2017-06-20 13:16:09 UTC
Created attachment 257089 [details]
source file

Hi,

I found this double fetch problem in Linux-4.10.1/sound/oss/msnd_pinnacle.c
 when I was examining the source code.

In this file, function intr() (line 1100) uses readw() to fetch peripheral device data from I/O memory to the kernel (driver). However, a double fetch problem exists in this procedure. It first uses line 1105 to fetch the header pointer (dev.DSPQ + JQS_wHead) of a message queue and check it with the tail pointer to proceed the loop. Then, at line 1109, it fetches the header pointer again to use it to get the message value from the queue and evaluates it in function eval_dsp_msg(). This double-fetch situation of the header pointer is problematic because the value of the pointer is likely to be changed (e.g. by unexpected data flip or intentionally compromised hardware) between the check and the use of the pointer. Once the pointer value is changed, a result like an array (queue) over-access could be caused.

The pointer is fetched from the I/O memory multiple times in this function (line 1105, 1109, 1111). A secure and effective way to handle this pointer is to read it to a local variable the first time and use the variable instead until it is written back at line 1114.

Iā€™m looking forward to a reply to confirm this, and if I am right, please fix it. Thanks.

Kind regards
Pengfei Wang
Comment 1 Takashi Iwai 2017-06-29 10:06:26 UTC
Basically same as bug 196131.

IMO, it's an optimization issue rather than the security problem.
Comment 2 Pengfei Wang 2017-08-05 01:40:18 UTC
We proposed a patch for this issue, hoping it can be helpful, thanks!
=====================================================================
--- oss-msnd_pinnacle.c	2017-06-20 21:08:30.000000000 +0800
+++ oss-msnd_pinnacle-patched.c	2017-06-27 09:42:03.000000000 +0800
@@ -1103,15 +1103,21 @@ 			clear_bit(F_READING, &dev.flags);
 	msnd_inb(dev.io + HP_RXL);
 
 	/* Evaluate queued DSP messages */
-	while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
+	register WORD head = readw(dev.DSPQ + JQS_wHead);
+	register WORD tail = readw(dev.DSPQ + JQS_wTail);
+	
+	while (tail != head) {
 		register WORD wTmp;
 
-		eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead)));
+		eval_dsp_msg(readw(dev.pwDSPQData + 2*head));
 
-		if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
+		if ((wTmp = head + 1) > readw(dev.DSPQ + JQS_wSize))
 			writew(0, dev.DSPQ + JQS_wHead);
 		else
 			writew(wTmp, dev.DSPQ + JQS_wHead);
+
+		head = readw(dev.DSPQ + JQS_wHead);
+		tail = readw(dev.DSPQ + JQS_wTail);
 	}
 	return IRQ_HANDLED;
 }
======================================================================
Comment 3 Pengfei Wang 2017-08-05 01:46:15 UTC
(In reply to Takashi Iwai from comment #1)
> Basically same as bug 196131.
> 
> IMO, it's an optimization issue rather than the security problem.

We proposed a patch for this issue, hoping it can be fixed, thanks!
=====================================================================
--- oss-msnd_pinnacle.c	2017-06-20 21:08:30.000000000 +0800
+++ oss-msnd_pinnacle-patched.c	2017-06-27 09:42:03.000000000 +0800
@@ -1103,15 +1103,21 @@ 			clear_bit(F_READING, &dev.flags);
 	msnd_inb(dev.io + HP_RXL);
 
 	/* Evaluate queued DSP messages */
-	while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
+	register WORD head = readw(dev.DSPQ + JQS_wHead);
+	register WORD tail = readw(dev.DSPQ + JQS_wTail);
+	
+	while (tail != head) {
 		register WORD wTmp;
 
-		eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead)));
+		eval_dsp_msg(readw(dev.pwDSPQData + 2*head));
 
-		if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
+		if ((wTmp = head + 1) > readw(dev.DSPQ + JQS_wSize))
 			writew(0, dev.DSPQ + JQS_wHead);
 		else
 			writew(wTmp, dev.DSPQ + JQS_wHead);
+
+		head = readw(dev.DSPQ + JQS_wHead);
+		tail = readw(dev.DSPQ + JQS_wTail);
 	}
 	return IRQ_HANDLED;
 }
======================================================================

Note You need to log in before you can comment on or make changes to this bug.