Re: [PATCH] debugfs: Fix module state check condition

2020-09-07 Thread Vladis Dronov
Hello, Greg,

- Original Message -
> From: "Greg KH" 
> Subject: Re: [PATCH] debugfs: Fix module state check condition
> 
...skip...
> > A customer which has reported this issue replied with a test result:
> > 
> > > I ran the same test.
> > > Started ib_write_bw traffic and started watch command to read RoCE
> > > stats : watch -d -n 1 "cat /sys/kernel/debug/bnxt_re/bnxt_re0/info".
> > > While the command is running, unloaded roce driver and I did not
> > > observe the call trace that was seen earlier.
> 
> Having this info, that this was affecting a user, would have been good
> in the original changelog info, otherwise this just looked like a code
> cleanup patch to me.

Thank you, Greg. Makes sense indeed, I will pay attention to this next time(s).

oh so many little but important details to know and follow...

> I'll go queue this up now, thanks.
> 
> greg k-h

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer



Re: [PATCH] debugfs: Fix module state check condition

2020-09-04 Thread Vladis Dronov
Hello, Greg, all,

- Original Message -
> From: "Greg KH" 
> Subject: Re: [PATCH] debugfs: Fix module state check condition
> 
...skip...
> 
> It's in my queue, but bugs you can only trigger while root are a bit
> lower on the priority list :)

Oh, apologies. I really thought this has been somehow lost/slipped.
Thank you for the reply and a confirmation.

/me stops bothering people.

> thanks,
> 
> greg k-h
> 
> 

Regards,
Vladis



Re: [PATCH] debugfs: Fix module state check condition

2020-09-04 Thread Vladis Dronov
Hello,

Dear maintainers, could you please look at the above patch, that
previously was sent during a merge window?

A customer which has reported this issue replied with a test result:

> I ran the same test.
> Started ib_write_bw traffic and started watch command to read RoCE
> stats : watch -d -n 1 "cat /sys/kernel/debug/bnxt_re/bnxt_re0/info".
> While the command is running, unloaded roce driver and I did not
> observe the call trace that was seen earlier.

Regards,
Vladis



[PATCH] debugfs: Fix module state check condition

2020-08-11 Thread Vladis Dronov
The '#ifdef MODULE' check in the original commit does not work as intended.
The code under the check is not built at all if CONFIG_DEBUG_FS=y. Fix this
by using a correct check.

Fixes: 275678e7a9be ("debugfs: Check module state before warning in 
{full/open}_proxy_open()")
Signed-off-by: Vladis Dronov 
---
 fs/debugfs/file.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index b167d2d02148..a768a09430c3 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -177,7 +177,7 @@ static int open_proxy_open(struct inode *inode, struct file 
*filp)
goto out;
 
if (!fops_get(real_fops)) {
-#ifdef MODULE
+#ifdef CONFIG_MODULES
if (real_fops->owner &&
real_fops->owner->state == MODULE_STATE_GOING)
goto out;
@@ -312,7 +312,7 @@ static int full_proxy_open(struct inode *inode, struct file 
*filp)
goto out;
 
if (!fops_get(real_fops)) {
-#ifdef MODULE
+#ifdef CONFIG_MODULES
if (real_fops->owner &&
real_fops->owner->state == MODULE_STATE_GOING)
goto out;
-- 
2.26.2



Re: [PATCH] powerpc: fix function annotations to avoid section mismatch warnings with gcc-10

2020-07-30 Thread Vladis Dronov
Hello, Michael,

- Original Message -
> From: "Michael Ellerman" 
> Subject: Re: [PATCH] powerpc: fix function annotations to avoid section 
> mismatch warnings with gcc-10
> 
...
> >> > So what changed?  These functions were inlined with older compilers, but
> >> > not anymore?
> >> 
> >> Yes, exactly. Gcc-10 does not inline them anymore. If this is because of
> >> my
> >> build system, this can happen to others also.
> >> 
> >> The same thing was fixed by Linus in e99332e7b4cd ("gcc-10: mark more
> >> functions
> >> __init to avoid section mismatch warnings").
> >
> > It sounds like this is part of "-finline-functions was retuned" on
> > <https://gcc.gnu.org/gcc-10/changes.html>?  So everyone should see it
> > (no matter what config or build system), and it is a good thing too :-)
> 
> I haven't seen it in my GCC 10 builds, so there must be some other
> subtlety. Probably it depends on details of the .config.
> 

I've just had this building the latest upstream for the ppc64le with a 
derivative
of the RHEL-8 config. This can probably be a compiler/linker setting, like -O2
versus -O3.

> cheers

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer



Re: [PATCH] powerpc: fix function annotations to avoid section mismatch warnings with gcc-10

2020-07-29 Thread Vladis Dronov
Hello,

- Original Message -
> From: "Segher Boessenkool" 
> To: "Vladis Dronov" 
> Cc: linuxppc-...@lists.ozlabs.org, "Aneesh Kumar K . V" 
> , linux-kernel@vger.kernel.org,
> "Paul Mackerras" 
> Sent: Wednesday, July 29, 2020 4:49:49 PM
> Subject: Re: [PATCH] powerpc: fix function annotations to avoid section 
> mismatch warnings with gcc-10
> 
> On Wed, Jul 29, 2020 at 03:37:41PM +0200, Vladis Dronov wrote:
> > Certain warnings are emitted for powerpc code when building with a gcc-10
> > toolset:
> > 
> > WARNING: modpost: vmlinux.o(.text.unlikely+0x377c): Section mismatch in
> > reference from the function remove_pmd_table() to the function
> > .meminit.text:split_kernel_mapping()
> > The function remove_pmd_table() references
> > the function __meminit split_kernel_mapping().
> > This is often because remove_pmd_table lacks a __meminit
> > annotation or the annotation of split_kernel_mapping is wrong.
> > 
> > Add the appropriate __init and __meminit annotations to make modpost not
> > complain. In all the cases there are just a single callsite from another
> > __init or __meminit function:
> > 
> > __meminit remove_pagetable() -> remove_pud_table() -> remove_pmd_table()
> > __init prom_init() -> setup_secure_guest()
> > __init xive_spapr_init() -> xive_spapr_disabled()
> 
> So what changed?  These functions were inlined with older compilers, but
> not anymore?

Yes, exactly. Gcc-10 does not inline them anymore. If this is because of my
build system, this can happen to others also.

The same thing was fixed by Linus in e99332e7b4cd ("gcc-10: mark more functions
__init to avoid section mismatch warnings").

> 
> Segher

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer



[PATCH] powerpc: fix function annotations to avoid section mismatch warnings with gcc-10

2020-07-29 Thread Vladis Dronov
Certain warnings are emitted for powerpc code when building with a gcc-10
toolset:

WARNING: modpost: vmlinux.o(.text.unlikely+0x377c): Section mismatch in
reference from the function remove_pmd_table() to the function
.meminit.text:split_kernel_mapping()
The function remove_pmd_table() references
the function __meminit split_kernel_mapping().
This is often because remove_pmd_table lacks a __meminit
annotation or the annotation of split_kernel_mapping is wrong.

Add the appropriate __init and __meminit annotations to make modpost not
complain. In all the cases there are just a single callsite from another
__init or __meminit function:

__meminit remove_pagetable() -> remove_pud_table() -> remove_pmd_table()
__init prom_init() -> setup_secure_guest()
__init xive_spapr_init() -> xive_spapr_disabled()

Signed-off-by: Vladis Dronov 
---
 arch/powerpc/kernel/prom_init.c  | 4 ++--
 arch/powerpc/mm/book3s64/radix_pgtable.c | 4 ++--
 arch/powerpc/sysdev/xive/spapr.c | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 90c604d00b7d..f6ca7f450361 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -3262,7 +3262,7 @@ static int enter_secure_mode(unsigned long kbase, 
unsigned long fdt)
 /*
  * Call the Ultravisor to transfer us to secure memory if we have an ESM blob.
  */
-static void setup_secure_guest(unsigned long kbase, unsigned long fdt)
+static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt)
 {
int ret;
 
@@ -3292,7 +3292,7 @@ static void setup_secure_guest(unsigned long kbase, 
unsigned long fdt)
}
 }
 #else
-static void setup_secure_guest(unsigned long kbase, unsigned long fdt)
+static void __init setup_secure_guest(unsigned long kbase, unsigned long fdt)
 {
 }
 #endif /* CONFIG_PPC_SVM */
diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c 
b/arch/powerpc/mm/book3s64/radix_pgtable.c
index bb00e0cba119..b868c07110e3 100644
--- a/arch/powerpc/mm/book3s64/radix_pgtable.c
+++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
@@ -800,7 +800,7 @@ static void __meminit split_kernel_mapping(unsigned long 
addr, unsigned long end
pte_clear(_mm, addr, pte);
 }
 
-static void remove_pmd_table(pmd_t *pmd_start, unsigned long addr,
+static void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr,
 unsigned long end)
 {
unsigned long next;
@@ -825,7 +825,7 @@ static void remove_pmd_table(pmd_t *pmd_start, unsigned 
long addr,
}
 }
 
-static void remove_pud_table(pud_t *pud_start, unsigned long addr,
+static void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr,
 unsigned long end)
 {
unsigned long next;
diff --git a/arch/powerpc/sysdev/xive/spapr.c b/arch/powerpc/sysdev/xive/spapr.c
index f0551a2be9df..1e3674d7ea7b 100644
--- a/arch/powerpc/sysdev/xive/spapr.c
+++ b/arch/powerpc/sysdev/xive/spapr.c
@@ -768,7 +768,7 @@ static const u8 *get_vec5_feature(unsigned int index)
return vec5 + index;
 }
 
-static bool xive_spapr_disabled(void)
+static bool __init xive_spapr_disabled(void)
 {
const u8 *vec5_xive;
 
-- 
2.26.2



Re: [PATCH v5.3-rc2] Bluetooth: hci_uart: check for missing tty operations

2019-08-01 Thread Vladis Dronov
Hello, Greg, all,

I've just double-checked your backports, indeed, they are fine.

Check for operations is not added for protocols which does not
use these operations. Thanks!

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer

- Original Message -
> From: "Greg KH" 
> To: "Vladis Dronov" 
> Cc: "Sasha Levin" , "Marcel Holtmann" 
> , torva...@linux-foundation.org,
> linux-kernel@vger.kernel.org, linux-blueto...@vger.kernel.org, 
> sta...@vger.kernel.org
> Sent: Thursday, August 1, 2019 4:06:39 PM
> Subject: Re: [PATCH v5.3-rc2] Bluetooth: hci_uart: check for missing tty 
> operations
> 
> On Thu, Aug 01, 2019 at 09:55:55AM -0400, Vladis Dronov wrote:
> > Thank you, Greg!
> > 
> > I've just noticed the patch landed in the upstream and was going to start
> > stable
> > backports, but it appeared you've already done this.
> 
> Verifying that I got the 4.4.y and 4.9.y and 4.14.y backports done
> properly would be good, as I took a guess at them :)
> 
> thanks,
> 
> greg k-h


Re: [PATCH v5.3-rc2] Bluetooth: hci_uart: check for missing tty operations

2019-08-01 Thread Vladis Dronov
Thank you, Greg!

I've just noticed the patch landed in the upstream and was going to start stable
backports, but it appeared you've already done this.

So, not only automated mailers are slow :)

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer

- Original Message -
> From: "Greg KH" 
> To: "Sasha Levin" 
> Cc: "Marcel Holtmann" , "Vladis Dronov" 
> , torva...@linux-foundation.org,
> linux-kernel@vger.kernel.org, linux-blueto...@vger.kernel.org, 
> sta...@vger.kernel.org
> Sent: Thursday, August 1, 2019 3:50:44 PM
> Subject: Re: [PATCH v5.3-rc2] Bluetooth: hci_uart: check for missing tty 
> operations
> 
> On Thu, Aug 01, 2019 at 01:31:31PM +, Sasha Levin wrote:
> > Hi,
> > 
> > [This is an automated email]
> > 
> > This commit has been processed because it contains a "Fixes:" tag,
> > fixing commit: .
> > 
> > The bot has tested the following trees: v5.2.4, v5.1.21, v4.19.62,
> > v4.14.134, v4.9.186, v4.4.186.
> > 
> > v5.2.4: Build OK!
> > v5.1.21: Build OK!
> > v4.19.62: Build OK!
> > v4.14.134: Failed to apply! Possible dependencies:
> > 25a13e382de2 ("bluetooth: hci_qca: Replace GFP_ATOMIC with GFP_KERNEL")
> > 
> > v4.9.186: Failed to apply! Possible dependencies:
> > 25a13e382de2 ("bluetooth: hci_qca: Replace GFP_ATOMIC with GFP_KERNEL")
> > 
> > v4.4.186: Failed to apply! Possible dependencies:
> > 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support")
> > 25a13e382de2 ("bluetooth: hci_qca: Replace GFP_ATOMIC with GFP_KERNEL")
> > 395174bb07c1 ("Bluetooth: hci_uart: Add Intel/AG6xx support")
> > 9e69130c4efc ("Bluetooth: hci_uart: Add Nokia Protocol identifier")
> > 
> > 
> > NOTE: The patch will not be queued to stable trees until it is upstream.
> > 
> > How should we proceed with this patch?
> 
> Already fixed up by hand and queued up, your automated email is a bit
> slow :)
> 
> greg k-h
> 


[PATCH v3] Bluetooth: hci_uart: check for missing tty operations

2019-07-29 Thread Vladis Dronov
Certain ttys operations (pty_unix98_ops) lack tiocmget() and tiocmset()
functions which are called by the certain HCI UART protocols (hci_ath,
hci_bcm, hci_intel, hci_mrvl, hci_qca) via hci_uart_set_flow_control()
or directly. This leads to an execution at NULL and can be triggered by
an unprivileged user. Fix this by adding a helper function and a check
for the missing tty operations in the protocols code.

This fixes CVE-2019-10207. The Fixes: lines list commits where calls to
tiocm[gs]et() or hci_uart_set_flow_control() were added to the HCI UART
protocols.

Link: 
https://syzkaller.appspot.com/bug?id=1b42faa2848963564a5b1b7f8c837ea7b55ffa50
Reported-by: syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com
Cc: sta...@vger.kernel.org # v2.6.36+
Fixes: b3190df62861 ("Bluetooth: Support for Atheros AR300x serial chip")
Fixes: 118612fb9165 ("Bluetooth: hci_bcm: Add suspend/resume PM functions")
Fixes: ff2895592f0f ("Bluetooth: hci_intel: Add Intel baudrate configuration 
support")
Fixes: 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support")
Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth 
chip wcn3990")
Signed-off-by: Vladis Dronov 
---

out-of-commit-message-note:

it is possible that a HCI UART protocol uses serial device's
operations and not a tty ones. i made hci_uart_has_flow_control() to
check this also, hence the name. serial device's code checks if
the needed operations are present itself.

i believe, hci_h5 does not need this check, as it uses serial device
functions only (as of now):

serdev_device_set_flow_control(h5->hu->serdev, false);
serdev_device_set_baudrate(h5->hu->serdev, 115200);

 drivers/bluetooth/hci_ath.c   | 3 +++
 drivers/bluetooth/hci_bcm.c   | 3 +++
 drivers/bluetooth/hci_intel.c | 3 +++
 drivers/bluetooth/hci_ldisc.c | 7 +++
 drivers/bluetooth/hci_mrvl.c  | 3 +++
 drivers/bluetooth/hci_qca.c   | 3 +++
 drivers/bluetooth/hci_uart.h  | 1 +
 7 files changed, 23 insertions(+)

diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index a55be205b91a..dbfe34664633 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -98,6 +98,9 @@ static int ath_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hci_uart_has_flow_control(hu))
+   return -EOPNOTSUPP;
+
ath = kzalloc(sizeof(*ath), GFP_KERNEL);
if (!ath)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 8905ad2edde7..ae2624fce913 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -406,6 +406,9 @@ static int bcm_open(struct hci_uart *hu)
 
bt_dev_dbg(hu->hdev, "hu %p", hu);
 
+   if (!hci_uart_has_flow_control(hu))
+   return -EOPNOTSUPP;
+
bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
if (!bcm)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 207bae5e0d46..31f25153087d 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -391,6 +391,9 @@ static int intel_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hci_uart_has_flow_control(hu))
+   return -EOPNOTSUPP;
+
intel = kzalloc(sizeof(*intel), GFP_KERNEL);
if (!intel)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 8950e07889fe..11049e2336f3 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -292,6 +292,13 @@ static int hci_uart_send_frame(struct hci_dev *hdev, 
struct sk_buff *skb)
return 0;
 }
 
+/* Check the underlying device or tty has flow control support */
+bool hci_uart_has_flow_control(struct hci_uart *hu)
+{
+   return hu->serdev ||
+   (hu->tty->driver->ops->tiocmget && 
hu->tty->driver->ops->tiocmset);
+}
+
 /* Flow control or un-flow control the device */
 void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
 {
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index f98e5cc343b2..fbc3f7c3a5c7 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -59,6 +59,9 @@ static int mrvl_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hci_uart_has_flow_control(hu))
+   return -EOPNOTSUPP;
+
mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
if (!mrvl)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 9a5c9c1f9484..82a0a3691a63 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -473,6 +473,9 @@ static int qca_open(struct hci_uart *hu)
 
BT_DBG("hu %p qca_open", hu);
 
+   if (!hci_uart_has_flow_control(hu))
+   re

[PATCH v2] Bluetooth: hci_ldisc: check for missing tty operations

2019-07-26 Thread Vladis Dronov
Certain ttys operations (pty_unix98_ops) lack tiocmget() and tiocmset()
functions which are called by the certain HCI UART protocols (hci_ath,
hci_bcm, hci_intel, hci_mrvl, hci_qca) via hci_uart_set_flow_control()
or directly. This leads to an execution at NULL and can be triggered by
an unprivileged user. Fix this by adding a check for the missing tty
operations the same way it is done for write().

This fixes CVE-2019-10207. The Fixes: lines list commits where calls to
tiocm[gs]et() or hci_uart_set_flow_control() were added to the HCI UART
protocols.

Link: 
https://syzkaller.appspot.com/bug?id=1b42faa2848963564a5b1b7f8c837ea7b55ffa50
Reported-by: syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com
Cc: sta...@vger.kernel.org # v2.6.36+
Fixes: c19483cc5e56 ("bluetooth: Fix missing NULL check")
Fixes: b3190df62861 ("Bluetooth: Support for Atheros AR300x serial chip")
Fixes: 118612fb9165 ("Bluetooth: hci_bcm: Add suspend/resume PM functions")
Fixes: ff2895592f0f ("Bluetooth: hci_intel: Add Intel baudrate configuration 
support")
Fixes: 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support")
Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth 
chip wcn3990")
Signed-off-by: Vladis Dronov 
---

out-of-commit-message-note:

I believe, this is a good location for the check. This way we protect protocols
which does not call tiocm[gs]et() or hci_uart_set_flow_control() but may
change to call them in the future.

Also we do not need hci_uart_has_tiocm_support() helper now.

 drivers/bluetooth/hci_ldisc.c | 7 ---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index c84f985f348d..4a85c51d0307 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -459,10 +459,11 @@ static int hci_uart_tty_open(struct tty_struct *tty)
 
BT_DBG("tty %p", tty);
 
-   /* Error if the tty has no write op instead of leaving an exploitable
-* hole
+   /* Error if the tty has no write or tiocm[gs]et ops instead of leaving
+* an exploitable hole
 */
-   if (tty->ops->write == NULL)
+   if (tty->ops->write == NULL || tty->ops->tiocmget == NULL ||
+   tty->ops->tiocmset == NULL)
return -EOPNOTSUPP;
 
hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
-- 
2.21.0


Re: [PATCH] Bluetooth: hci_uart: check for missing tty operations in protocol handlers

2019-07-25 Thread Vladis Dronov
Hello, Marcel,

> why is this one hidden behind CONFIG_PM? The general baud rate changes are
> independent of runtime power management support.

hci_bcm calls hci_uart_set_flow_control() only from functions hidden behind
#ifdef-CONFIG_PM (surely this can change in the future), and so without
CONFIG_PM set it cannot hit the bug (as of now). So I've hidden the check
for tiocm[gs]et() behind #ifdef-CONFIG_PM too.

If you tell me it is better to remove this #ifdef, I'll remove it.

> And I would introduce a bool hci_uart_has_tiocm_support(struct hci_uart *)
> helper.

Great, I will add it to the v2 fix. I guess a good place for it is hci_ldisc.c,
near hci_uart_set_flow_control(), isn't it?

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer

- Original Message -
> From: "Marcel Holtmann" 
> To: "Vladis Dronov" 
> Cc: "Johan Hedberg" , 
> linux-blueto...@vger.kernel.org, linux-kernel@vger.kernel.org, "Suraj
> Sumangala" , "Frederic Danis" 
> , "Loic Poulain"
> , "Balakrishna Godavarthi" , 
> syzkal...@googlegroups.com
> Sent: Thursday, July 25, 2019 2:59:42 PM
> Subject: Re: [PATCH] Bluetooth: hci_uart: check for missing tty operations in 
> protocol handlers
> 
> Hi Vladis,
> 
> > Certain ttys operations (pty_unix98_ops) lack tiocmget() and tiocmset()
> > functions which are called by the certain HCI UART protocols (hci_ath,
> > hci_bcm, hci_intel, hci_mrvl, hci_qca) via hci_uart_set_flow_control()
> > or directly. This leads to an execution at NULL and can be triggered by
> > an unprivileged user. Fix this by adding a check for the missing tty
> > operations to the protocols which use them.
> > 
> > This fixes CVE-2019-10207.
> > 
> > Link:
> > https://syzkaller.appspot.com/bug?id=1b42faa2848963564a5b1b7f8c837ea7b55ffa50
> > Reported-by: syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com
> > Cc: sta...@vger.kernel.org # v2.6.36+
> > Fixes: b3190df62861 ("Bluetooth: Support for Atheros AR300x serial chip")
> > Fixes: 118612fb9165 ("Bluetooth: hci_bcm: Add suspend/resume PM functions")
> > Fixes: ff2895592f0f ("Bluetooth: hci_intel: Add Intel baudrate
> > configuration support")
> > Fixes: 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support")
> > Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm
> > Bluetooth chip wcn3990")
> > Signed-off-by: Vladis Dronov 
> > ---
> > drivers/bluetooth/hci_ath.c   | 3 +++
> > drivers/bluetooth/hci_bcm.c   | 5 +
> > drivers/bluetooth/hci_intel.c | 3 +++
> > drivers/bluetooth/hci_mrvl.c  | 3 +++
> > drivers/bluetooth/hci_qca.c   | 4 
> > 5 files changed, 18 insertions(+)
> > 
> > diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
> > index a55be205b91a..99df8a13e47e 100644
> > --- a/drivers/bluetooth/hci_ath.c
> > +++ b/drivers/bluetooth/hci_ath.c
> > @@ -98,6 +98,9 @@ static int ath_open(struct hci_uart *hu)
> > 
> > BT_DBG("hu %p", hu);
> > 
> > +   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
> > +   return -ENOTSUPP;
> > +
> > ath = kzalloc(sizeof(*ath), GFP_KERNEL);
> > if (!ath)
> > return -ENOMEM;
> > diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
> > index 8905ad2edde7..8c3e09cc341c 100644
> > --- a/drivers/bluetooth/hci_bcm.c
> > +++ b/drivers/bluetooth/hci_bcm.c
> > @@ -406,6 +406,11 @@ static int bcm_open(struct hci_uart *hu)
> > 
> > bt_dev_dbg(hu->hdev, "hu %p", hu);
> > 
> > +#ifdef CONFIG_PM
> > +   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
> > +   return -ENOTSUPP;
> > +#endif
> > +
> 
> why is this one hidden behind CONFIG_PM? The general baud rate changes are
> independent of runtime power management support.
> 
> And I would introduce a bool hci_uart_has_tiocm_support(struct hci_uart *)
> helper.
> 
> Regards
> 
> Marcel


[PATCH] Bluetooth: hci_uart: check for missing tty operations in protocol handlers

2019-07-25 Thread Vladis Dronov
Certain ttys operations (pty_unix98_ops) lack tiocmget() and tiocmset()
functions which are called by the certain HCI UART protocols (hci_ath,
hci_bcm, hci_intel, hci_mrvl, hci_qca) via hci_uart_set_flow_control()
or directly. This leads to an execution at NULL and can be triggered by
an unprivileged user. Fix this by adding a check for the missing tty
operations to the protocols which use them.

This fixes CVE-2019-10207.

Link: 
https://syzkaller.appspot.com/bug?id=1b42faa2848963564a5b1b7f8c837ea7b55ffa50
Reported-by: syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com
Cc: sta...@vger.kernel.org # v2.6.36+
Fixes: b3190df62861 ("Bluetooth: Support for Atheros AR300x serial chip")
Fixes: 118612fb9165 ("Bluetooth: hci_bcm: Add suspend/resume PM functions")
Fixes: ff2895592f0f ("Bluetooth: hci_intel: Add Intel baudrate configuration 
support")
Fixes: 162f812f23ba ("Bluetooth: hci_uart: Add Marvell support")
Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth 
chip wcn3990")
Signed-off-by: Vladis Dronov 
---
 drivers/bluetooth/hci_ath.c   | 3 +++
 drivers/bluetooth/hci_bcm.c   | 5 +
 drivers/bluetooth/hci_intel.c | 3 +++
 drivers/bluetooth/hci_mrvl.c  | 3 +++
 drivers/bluetooth/hci_qca.c   | 4 
 5 files changed, 18 insertions(+)

diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index a55be205b91a..99df8a13e47e 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -98,6 +98,9 @@ static int ath_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
+   return -ENOTSUPP;
+
ath = kzalloc(sizeof(*ath), GFP_KERNEL);
if (!ath)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index 8905ad2edde7..8c3e09cc341c 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -406,6 +406,11 @@ static int bcm_open(struct hci_uart *hu)
 
bt_dev_dbg(hu->hdev, "hu %p", hu);
 
+#ifdef CONFIG_PM
+   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
+   return -ENOTSUPP;
+#endif
+
bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
if (!bcm)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 207bae5e0d46..a314882a1cac 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -391,6 +391,9 @@ static int intel_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
+   return -ENOTSUPP;
+
intel = kzalloc(sizeof(*intel), GFP_KERNEL);
if (!intel)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index f98e5cc343b2..4ee27c8d8679 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -59,6 +59,9 @@ static int mrvl_open(struct hci_uart *hu)
 
BT_DBG("hu %p", hu);
 
+   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
+   return -ENOTSUPP;
+
mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
if (!mrvl)
return -ENOMEM;
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 9a5c9c1f9484..a65c202e8995 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 
 #include 
@@ -473,6 +474,9 @@ static int qca_open(struct hci_uart *hu)
 
BT_DBG("hu %p qca_open", hu);
 
+   if (!hu->tty->driver->ops->tiocmget || !hu->tty->driver->ops->tiocmset)
+   return -ENOTSUPP;
+
qca = kzalloc(sizeof(struct qca_data), GFP_KERNEL);
if (!qca)
return -ENOMEM;
-- 
2.20.1



Re: [PATCH] Bluetooth: hci_ldisc: check for missing tty operations

2019-07-06 Thread Vladis Dronov
Hello, Marcel,

I totally agree, the same came to my mind some time after sending the patch.
Let me send a v2 in a while with drivers checking for needed tty operations
presence.

Best regards,
Vladis Dronov | Red Hat, Inc. | The Core Kernel | Senior Software Engineer

- Original Message -
> From: "Marcel Holtmann" 
> To: "Vladis Dronov" 
> Cc: "Johan Hedberg" , 
> linux-blueto...@vger.kernel.org, linux-kernel@vger.kernel.org,
> syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com, 
> sta...@vger.kernel.org, "Loic Poulain"
> , "Ilya Faenson" 
> Sent: Saturday, July 6, 2019 12:35:39 PM
> Subject: Re: [PATCH] Bluetooth: hci_ldisc: check for missing tty operations
> 
> Hi Vladis,
> 
> > Certain ttys lack operations (eg: pty_unix98_ops) which still can be
> > called by certain hci uart proto (eg: mrvl, ath). Currently this leads
> > to execution at address 0x0. Fix this by adding checks for missing tty
> > operations.
> 
> so I really prefer that we just fail setting the line discipline. These
> drivers need to check that the underlying transport has all the operations
> available they need. We can not just ignore them.
> 
> Regards
> 
> Marcel


[PATCH] Bluetooth: hci_ldisc: check for missing tty operations

2019-06-25 Thread Vladis Dronov
Certain ttys lack operations (eg: pty_unix98_ops) which still can be
called by certain hci uart proto (eg: mrvl, ath). Currently this leads
to execution at address 0x0. Fix this by adding checks for missing tty
operations.

Link: 
https://syzkaller.appspot.com/bug?id=1b42faa2848963564a5b1b7f8c837ea7b55ffa50
Reported-by: syzbot+79337b501d6aa974d...@syzkaller.appspotmail.com
Cc: sta...@vger.kernel.org # v2.6.39+
Signed-off-by: Vladis Dronov 
---
 drivers/bluetooth/hci_ath.c   |  7 -
 drivers/bluetooth/hci_ldisc.c | 58 ---
 2 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index a55be205b91a..92d9bded5880 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -49,7 +49,12 @@ struct ath_vendor_cmd {
 
 static int ath_wakeup_ar3k(struct tty_struct *tty)
 {
-   int status = tty->driver->ops->tiocmget(tty);
+   int status;
+
+   if (!(tty->driver->ops->tiocmget && tty->driver->ops->tiocmset))
+   return TIOCM_CTS;
+
+   status = tty->driver->ops->tiocmget(tty);
 
if (status & TIOCM_CTS)
return status;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index c84f985f348d..29b4970f9bca 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -307,32 +307,40 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool 
enable)
BT_DBG("Disabling hardware flow control: %s",
   status ? "failed" : "success");
 
-   /* Clear RTS to prevent the device from sending */
-   /* Most UARTs need OUT2 to enable interrupts */
-   status = tty->driver->ops->tiocmget(tty);
-   BT_DBG("Current tiocm 0x%x", status);
-
-   set &= ~(TIOCM_OUT2 | TIOCM_RTS);
-   clear = ~set;
-   set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
-  TIOCM_OUT2 | TIOCM_LOOP;
-   clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
-TIOCM_OUT2 | TIOCM_LOOP;
-   status = tty->driver->ops->tiocmset(tty, set, clear);
-   BT_DBG("Clearing RTS: %s", status ? "failed" : "success");
+   if (tty->driver->ops->tiocmget && tty->driver->ops->tiocmset) {
+   /* Clear RTS to prevent the device from sending */
+   /* Most UARTs need OUT2 to enable interrupts */
+   status = tty->driver->ops->tiocmget(tty);
+   BT_DBG("Current tiocm 0x%x", status);
+
+   set &= ~(TIOCM_OUT2 | TIOCM_RTS);
+   clear = ~set;
+   set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+  TIOCM_OUT2 | TIOCM_LOOP;
+   clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+TIOCM_OUT2 | TIOCM_LOOP;
+   status = tty->driver->ops->tiocmset(tty, set, clear);
+   BT_DBG("Clearing RTS: %s",
+  status ? "failed" : "success");
+   } else
+   BT_DBG("Terminal RTS control is not present");
} else {
-   /* Set RTS to allow the device to send again */
-   status = tty->driver->ops->tiocmget(tty);
-   BT_DBG("Current tiocm 0x%x", status);
-
-   set |= (TIOCM_OUT2 | TIOCM_RTS);
-   clear = ~set;
-   set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
-  TIOCM_OUT2 | TIOCM_LOOP;
-   clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
-TIOCM_OUT2 | TIOCM_LOOP;
-   status = tty->driver->ops->tiocmset(tty, set, clear);
-   BT_DBG("Setting RTS: %s", status ? "failed" : "success");
+   if (tty->driver->ops->tiocmget && tty->driver->ops->tiocmset) {
+   /* Set RTS to allow the device to send again */
+   status = tty->driver->ops->tiocmget(tty);
+   BT_DBG("Current tiocm 0x%x", status);
+
+   set |= (TIOCM_OUT2 | TIOCM_RTS);
+   clear = ~set;
+   set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+  TIOCM_OUT2 | TIOCM_LOOP;
+   clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
+TIOCM_OUT2 | TIOCM_LOOP;
+   status = tty->driver->ops->tiocmset(tty, set, clear);
+   BT_DBG("Setting RTS: %s",
+  status ? "failed" : "success");
+   } else
+   BT_DBG("Terminal RTS control is not present");
 
/* Re-enable hardware flow control */
ktermios = tty->termios;
-- 
2.20.1



Re: [PATCH v2] HID: debug: fix the ring buffer implementation

2019-01-29 Thread Vladis Dronov
> > I still think that
> >
> > __set_current_state(TASK_RUNNING);
> >
> > will look a bit better, but this is really minor.
> 
> Would you mind sending a v3 with this change? I'll apply it ASAP.

Done, please, see inbox.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security | Senior Software Engineer


[PATCH v3] HID: debug: fix the ring buffer implementation

2019-01-29 Thread Vladis Dronov
Ring buffer implementation in hid_debug_event() and hid_debug_events_read()
is strange allowing lost or corrupted data. After commit 717adfdaf147
("HID: debug: check length before copy_to_user()") it is possible to enter
an infinite loop in hid_debug_events_read() by providing 0 as count, this
locks up a system. Fix this by rewriting the ring buffer implementation
with kfifo and simplify the code.

This fixes CVE-2019-3819.

v2: fix an execution logic and add a comment
v3: use __set_current_state() instead of set_current_state()

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187
Cc: sta...@vger.kernel.org # v4.18+
Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()")
Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c   | 116 ++
 include/linux/hid-debug.h |   9 ++-
 2 files changed, 47 insertions(+), 78 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index c530476edba6..08870c909268 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -30,6 +30,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-   unsigned i;
struct hid_debug_list *list;
unsigned long flags;
 
spin_lock_irqsave(>debug_list_lock, flags);
-   list_for_each_entry(list, >debug_list, node) {
-   for (i = 0; buf[i]; i++)
-   list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
-   buf[i];
-   list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-}
+   list_for_each_entry(list, >debug_list, node)
+   kfifo_in(>hid_debug_fifo, buf, strlen(buf));
spin_unlock_irqrestore(>debug_list_lock, flags);
 
wake_up_interruptible(>debug_wait);
@@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct 
hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
 
kfree(buf);
-wake_up_interruptible(>debug_wait);
-
+   wake_up_interruptible(>debug_wait);
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
 
@@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, 
struct file *file)
goto out;
}
 
-   if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
-   err = -ENOMEM;
+   err = kfifo_alloc(>hid_debug_fifo, HID_DEBUG_FIFOSIZE, 
GFP_KERNEL);
+   if (err) {
kfree(list);
goto out;
}
@@ -1104,77 +1099,57 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
size_t count, loff_t *ppos)
 {
struct hid_debug_list *list = file->private_data;
-   int ret = 0, len;
+   int ret = 0, copied;
DECLARE_WAITQUEUE(wait, current);
 
mutex_lock(>read_mutex);
-   while (ret == 0) {
-   if (list->head == list->tail) {
-   add_wait_queue(>hdev->debug_wait, );
-   set_current_state(TASK_INTERRUPTIBLE);
-
-   while (list->head == list->tail) {
-   if (file->f_flags & O_NONBLOCK) {
-   ret = -EAGAIN;
-   break;
-   }
-   if (signal_pending(current)) {
-   ret = -ERESTARTSYS;
-   break;
-   }
-
-   if (!list->hdev || !list->hdev->debug) {
-   ret = -EIO;
-   set_current_state(TASK_RUNNING);
-   goto out;
-   }
-
-   /* allow O_NONBLOCK from other threads */
-   mutex_unlock(>read_mutex);
-   schedule();
-   mutex_lock(>read_mutex);
-   set_current_state(TASK_INTERRUPTIBLE);
-   }
-
-   set_current_state(TASK_RUNNING);
-   remove_wait_queue(>hdev->debug_wait, );
-   }
-
-   if (ret)
-   goto out;
+   if (kfifo_is_empty(>hid_debug_fifo)) {
+   add_wait_queue(>hdev->debug_wait, );
+   set_current_state(TASK_INTERRUPTIBLE);
+
+   while (kfifo_is_empty(>hid_debug_fifo)) {
+   if (file->f_flags & O_NONBLOCK) {
+

[PATCH v2] HID: debug: fix the ring buffer implementation

2019-01-26 Thread Vladis Dronov
Ring buffer implementation in hid_debug_event() and hid_debug_events_read()
is strange allowing lost or corrupted data. After commit 717adfdaf147
("HID: debug: check length before copy_to_user()") it is possible to enter
an infinite loop in hid_debug_events_read() by providing 0 as count, this
locks up a system. Fix this by rewriting the ring buffer implementation
with kfifo and simplify the code.

This fixes CVE-2019-3819.

v2: fix an execution logic and add a comment

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187
Cc: sta...@vger.kernel.org # v4.18+
Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()")
Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c   | 116 ++
 include/linux/hid-debug.h |   9 ++-
 2 files changed, 47 insertions(+), 78 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index c530476edba6..08870c909268 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -30,6 +30,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-   unsigned i;
struct hid_debug_list *list;
unsigned long flags;
 
spin_lock_irqsave(>debug_list_lock, flags);
-   list_for_each_entry(list, >debug_list, node) {
-   for (i = 0; buf[i]; i++)
-   list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
-   buf[i];
-   list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-}
+   list_for_each_entry(list, >debug_list, node)
+   kfifo_in(>hid_debug_fifo, buf, strlen(buf));
spin_unlock_irqrestore(>debug_list_lock, flags);
 
wake_up_interruptible(>debug_wait);
@@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct 
hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
 
kfree(buf);
-wake_up_interruptible(>debug_wait);
-
+   wake_up_interruptible(>debug_wait);
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
 
@@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, 
struct file *file)
goto out;
}
 
-   if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
-   err = -ENOMEM;
+   err = kfifo_alloc(>hid_debug_fifo, HID_DEBUG_FIFOSIZE, 
GFP_KERNEL);
+   if (err) {
kfree(list);
goto out;
}
@@ -1104,77 +1099,57 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
size_t count, loff_t *ppos)
 {
struct hid_debug_list *list = file->private_data;
-   int ret = 0, len;
+   int ret = 0, copied;
DECLARE_WAITQUEUE(wait, current);
 
mutex_lock(>read_mutex);
-   while (ret == 0) {
-   if (list->head == list->tail) {
-   add_wait_queue(>hdev->debug_wait, );
-   set_current_state(TASK_INTERRUPTIBLE);
-
-   while (list->head == list->tail) {
-   if (file->f_flags & O_NONBLOCK) {
-   ret = -EAGAIN;
-   break;
-   }
-   if (signal_pending(current)) {
-   ret = -ERESTARTSYS;
-   break;
-   }
-
-   if (!list->hdev || !list->hdev->debug) {
-   ret = -EIO;
-   set_current_state(TASK_RUNNING);
-   goto out;
-   }
-
-   /* allow O_NONBLOCK from other threads */
-   mutex_unlock(>read_mutex);
-   schedule();
-   mutex_lock(>read_mutex);
-   set_current_state(TASK_INTERRUPTIBLE);
-   }
-
-   set_current_state(TASK_RUNNING);
-   remove_wait_queue(>hdev->debug_wait, );
-   }
-
-   if (ret)
-   goto out;
+   if (kfifo_is_empty(>hid_debug_fifo)) {
+   add_wait_queue(>hdev->debug_wait, );
+   set_current_state(TASK_INTERRUPTIBLE);
+
+   while (kfifo_is_empty(>hid_debug_fifo)) {
+   if (file->f_flags & O_NONBLOCK) {
+   ret = -EAGAIN;
+

Re: [PATCH] HID: debug: fix the ring buffer implementation

2019-01-26 Thread Vladis Dronov
Hello, Oleg, all,

Thanks much, Oleg, these are really valuable additions:

> suspicious ;) if you add a comment the patch will be even better.

Great, will do.

> perhaps it make sense to move this check into the "if (kfifo_is_empty())"
> block.

Indeed, otherwise it kinda breaks the execution logic.

> is kfifo_is_empty() == T really possible here?

It looks like it is not. Reads are guarded by read_mutex and the only other
code which touches hid_debug_fifo is writer.

I will post v2 here soon. Lets see if it is ready for inclusion.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security | Senior Software Engineer


[PATCH] HID: debug: fix the ring buffer implementation

2019-01-25 Thread Vladis Dronov
Ring buffer implementation in hid_debug_event() and hid_debug_events_read()
is strange allowing lost or corrupted data. After commit 717adfdaf147
("HID: debug: check length before copy_to_user()") it is possible to enter
an infinite loop in hid_debug_events_read() by providing 0 as count, this
locks up a system. Fix this by rewriting the ring buffer implementation
with kfifo and simplify the code.

This fixes CVE-2019-3819.

Link: https://bugzilla.redhat.com/show_bug.cgi?id=1669187
Cc: sta...@vger.kernel.org # v4.16+
Fixes: cd667ce24796 ("HID: use debugfs for events/reports dumping")
Fixes: 717adfdaf147 ("HID: debug: check length before copy_to_user()")
Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c   | 116 ++
 include/linux/hid-debug.h |   9 ++-
 2 files changed, 47 insertions(+), 78 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index c530476edba6..08870c909268 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -30,6 +30,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -661,17 +662,12 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-   unsigned i;
struct hid_debug_list *list;
unsigned long flags;
 
spin_lock_irqsave(>debug_list_lock, flags);
-   list_for_each_entry(list, >debug_list, node) {
-   for (i = 0; buf[i]; i++)
-   list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
-   buf[i];
-   list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-}
+   list_for_each_entry(list, >debug_list, node)
+   kfifo_in(>hid_debug_fifo, buf, strlen(buf));
spin_unlock_irqrestore(>debug_list_lock, flags);
 
wake_up_interruptible(>debug_wait);
@@ -722,8 +718,7 @@ void hid_dump_input(struct hid_device *hdev, struct 
hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
 
kfree(buf);
-wake_up_interruptible(>debug_wait);
-
+   wake_up_interruptible(>debug_wait);
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
 
@@ -1083,8 +1078,8 @@ static int hid_debug_events_open(struct inode *inode, 
struct file *file)
goto out;
}
 
-   if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
-   err = -ENOMEM;
+   err = kfifo_alloc(>hid_debug_fifo, HID_DEBUG_FIFOSIZE, 
GFP_KERNEL);
+   if (err) {
kfree(list);
goto out;
}
@@ -1104,77 +1099,55 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
size_t count, loff_t *ppos)
 {
struct hid_debug_list *list = file->private_data;
-   int ret = 0, len;
+   int ret = 0, copied;
DECLARE_WAITQUEUE(wait, current);
 
mutex_lock(>read_mutex);
-   while (ret == 0) {
-   if (list->head == list->tail) {
-   add_wait_queue(>hdev->debug_wait, );
-   set_current_state(TASK_INTERRUPTIBLE);
-
-   while (list->head == list->tail) {
-   if (file->f_flags & O_NONBLOCK) {
-   ret = -EAGAIN;
-   break;
-   }
-   if (signal_pending(current)) {
-   ret = -ERESTARTSYS;
-   break;
-   }
-
-   if (!list->hdev || !list->hdev->debug) {
-   ret = -EIO;
-   set_current_state(TASK_RUNNING);
-   goto out;
-   }
-
-   /* allow O_NONBLOCK from other threads */
-   mutex_unlock(>read_mutex);
-   schedule();
-   mutex_lock(>read_mutex);
-   set_current_state(TASK_INTERRUPTIBLE);
-   }
-
-   set_current_state(TASK_RUNNING);
-   remove_wait_queue(>hdev->debug_wait, );
-   }
-
-   if (ret)
-   goto out;
+   if (kfifo_is_empty(>hid_debug_fifo)) {
+   add_wait_queue(>hdev->debug_wait, );
+   set_current_state(TASK_INTERRUPTIBLE);
+
+   while (kfifo_is_empty(>hid_debug_fifo)) {
+   if (file->f_flags & O_NONBLOCK) {
+   ret = -EAGAIN;
+   break;
+   }
+
+

Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation

2018-10-29 Thread Vladis Dronov
Hello, Jiri,

Thank you for the reply and your opinion. It appeared that my own implementation
of a ring buffer was kind of "inventing a wheel", as "kfifo" is already is the
kernel and it may work as a ring buffer quite well. I would like to rewrite my
patchset and use kfifo instead in a new one. Please, ignore this my patchset and
I'll try to submit v2 soon.

This also will answer to "how was it tested" concern, as I believe, kfifo was
quite tested.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

- Original Message -
> From: "Jiri Kosina" 
> To: "Vladis Dronov" 
> Cc: "Benjamin Tissoires" , 
> linux-in...@vger.kernel.org, linux-kernel@vger.kernel.org
> Sent: Friday, October 26, 2018 5:25:21 PM
> Subject: Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation
> 
> On Wed, 3 Oct 2018, Vladis Dronov wrote:
> 
> > This patchset is fixing some aspects of the ring buffer implementation in
> > drivers/hid/hid-debug.c. This implementation has certain problem points:
> > 
> > - it may stuck in an infinite loop
> > - it may return corrupted data
> > - a reader and a writer are not protected by spinlocks, which can lead to
> >   the corrupted data
> > 
> > The suggested patchset is a new ring buffer implementation which overwrites
> > the oldest data in case of an overflow. One can verify the suggested ring
> > buffer implementation by fuzzing it with modified kernel and fuzzer-reader
> > at: https://gist.github.com/nefigtut/33d56e3870b67493cc867344aed2a062
> 
> Vladis,
> 
> thanks for cleaning it up. I actually like your rewrite quite a lot.
> 
> Quick question -- how well was it tested in which scenarios?
> 
> --
> Jiri Kosina
> SUSE Labs


Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation

2018-10-29 Thread Vladis Dronov
Hello, Jiri,

Thank you for the reply and your opinion. It appeared that my own implementation
of a ring buffer was kind of "inventing a wheel", as "kfifo" is already is the
kernel and it may work as a ring buffer quite well. I would like to rewrite my
patchset and use kfifo instead in a new one. Please, ignore this my patchset and
I'll try to submit v2 soon.

This also will answer to "how was it tested" concern, as I believe, kfifo was
quite tested.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

- Original Message -
> From: "Jiri Kosina" 
> To: "Vladis Dronov" 
> Cc: "Benjamin Tissoires" , 
> linux-in...@vger.kernel.org, linux-kernel@vger.kernel.org
> Sent: Friday, October 26, 2018 5:25:21 PM
> Subject: Re: [PATCH 0/3] HID: debug: fix the ring buffer implementation
> 
> On Wed, 3 Oct 2018, Vladis Dronov wrote:
> 
> > This patchset is fixing some aspects of the ring buffer implementation in
> > drivers/hid/hid-debug.c. This implementation has certain problem points:
> > 
> > - it may stuck in an infinite loop
> > - it may return corrupted data
> > - a reader and a writer are not protected by spinlocks, which can lead to
> >   the corrupted data
> > 
> > The suggested patchset is a new ring buffer implementation which overwrites
> > the oldest data in case of an overflow. One can verify the suggested ring
> > buffer implementation by fuzzing it with modified kernel and fuzzer-reader
> > at: https://gist.github.com/nefigtut/33d56e3870b67493cc867344aed2a062
> 
> Vladis,
> 
> thanks for cleaning it up. I actually like your rewrite quite a lot.
> 
> Quick question -- how well was it tested in which scenarios?
> 
> --
> Jiri Kosina
> SUSE Labs


[PATCH 2/3] HID: debug: provide reader-writer locking for the ring buffer

2018-10-03 Thread Vladis Dronov
hdev->debug_list->hid_debug_buf is not protected from concurrent updates
from the writer, hid_debug_event() and reads by the reader,
hid_debug_events_read(). Fix this by adding per-list-element spinlock.
Also introduce a temporary buffer tempbuf so copy_to_user() is not called
from under a spinlock.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c   | 47 +++
 include/linux/hid-debug.h |  1 +
 2 files changed, 29 insertions(+), 19 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 20580871b0ec..e827784baf1a 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -663,15 +663,17 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
 {
unsigned i;
struct hid_debug_list *list;
-   unsigned long flags;
+   unsigned long flags, flags2;
 
spin_lock_irqsave(>debug_list_lock, flags);
list_for_each_entry(list, >debug_list, node) {
+   spin_lock_irqsave(>list_lock, flags2);
for (i = 0; buf[i]; i++)
list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
buf[i];
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-}
+   spin_unlock_irqrestore(>list_lock, flags2);
+   }
spin_unlock_irqrestore(>debug_list_lock, flags);
 
wake_up_interruptible(>debug_wait);
@@ -1095,6 +1097,7 @@ static int hid_debug_events_open(struct inode *inode, 
struct file *file)
}
list->hdev = (struct hid_device *) inode->i_private;
file->private_data = list;
+   spin_lock_init(>list_lock);
mutex_init(>read_mutex);
 
spin_lock_irqsave(>hdev->debug_list_lock, flags);
@@ -1109,6 +1112,8 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
size_t count, loff_t *ppos)
 {
struct hid_debug_list *list = file->private_data;
+   char *tmpbuf;
+   unsigned long flags;
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
 
@@ -1151,10 +1156,18 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (ret)
goto out;
 
-   /* pass the ringbuffer content to userspace */
+   tmpbuf = kmalloc(min_t(size_t, count, HID_DEBUG_BUFSIZE),
+GFP_KERNEL|__GFP_NOWARN);
+   if (!tmpbuf) {
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   /* lock and copy the ringbuffer content to tmpbuf */
+   spin_lock_irqsave(>list_lock, flags);
 copy_rest:
if (list->tail == list->head)
-   goto out;
+   goto out_unlock;
 
/* the data from the head to the tail in the buffer is linear */
if (list->tail > list->head) {
@@ -1162,11 +1174,7 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (len > count)
len = count;
 
-   if (copy_to_user(buffer + ret,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf + ret, >hid_debug_buf[list->head], len);
ret += len;
list->head += len;
} else {
@@ -1180,19 +1188,11 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (len > count) {
len = count;
 
-   if (copy_to_user(buffer,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf, >hid_debug_buf[list->head], len);
ret += len;
list->head += len;
} else {
-   if (copy_to_user(buffer,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf, >hid_debug_buf[list->head], len);
list->head = 0;
ret += len;
count -= len;
@@ -1202,6 +1202,15 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
goto copy_rest;
}
}
+
+out_unlock:
+   spin_unlock_irqrestore(>list_lock, flags);
+
+   /* copy out tmpbuf content to userspace */
+   if (ret && copy_to_user(buffer, tmpbuf, ret))
+   ret = -EFAULT;
+   kfree(tmpbuf);
+
 out:
mutex_unlock(>read_mutex);
return ret;
diff --git a/includ

[PATCH 2/3] HID: debug: provide reader-writer locking for the ring buffer

2018-10-03 Thread Vladis Dronov
hdev->debug_list->hid_debug_buf is not protected from concurrent updates
from the writer, hid_debug_event() and reads by the reader,
hid_debug_events_read(). Fix this by adding per-list-element spinlock.
Also introduce a temporary buffer tempbuf so copy_to_user() is not called
from under a spinlock.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c   | 47 +++
 include/linux/hid-debug.h |  1 +
 2 files changed, 29 insertions(+), 19 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 20580871b0ec..e827784baf1a 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -663,15 +663,17 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
 {
unsigned i;
struct hid_debug_list *list;
-   unsigned long flags;
+   unsigned long flags, flags2;
 
spin_lock_irqsave(>debug_list_lock, flags);
list_for_each_entry(list, >debug_list, node) {
+   spin_lock_irqsave(>list_lock, flags2);
for (i = 0; buf[i]; i++)
list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
buf[i];
list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
-}
+   spin_unlock_irqrestore(>list_lock, flags2);
+   }
spin_unlock_irqrestore(>debug_list_lock, flags);
 
wake_up_interruptible(>debug_wait);
@@ -1095,6 +1097,7 @@ static int hid_debug_events_open(struct inode *inode, 
struct file *file)
}
list->hdev = (struct hid_device *) inode->i_private;
file->private_data = list;
+   spin_lock_init(>list_lock);
mutex_init(>read_mutex);
 
spin_lock_irqsave(>hdev->debug_list_lock, flags);
@@ -1109,6 +1112,8 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
size_t count, loff_t *ppos)
 {
struct hid_debug_list *list = file->private_data;
+   char *tmpbuf;
+   unsigned long flags;
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
 
@@ -1151,10 +1156,18 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (ret)
goto out;
 
-   /* pass the ringbuffer content to userspace */
+   tmpbuf = kmalloc(min_t(size_t, count, HID_DEBUG_BUFSIZE),
+GFP_KERNEL|__GFP_NOWARN);
+   if (!tmpbuf) {
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   /* lock and copy the ringbuffer content to tmpbuf */
+   spin_lock_irqsave(>list_lock, flags);
 copy_rest:
if (list->tail == list->head)
-   goto out;
+   goto out_unlock;
 
/* the data from the head to the tail in the buffer is linear */
if (list->tail > list->head) {
@@ -1162,11 +1174,7 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (len > count)
len = count;
 
-   if (copy_to_user(buffer + ret,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf + ret, >hid_debug_buf[list->head], len);
ret += len;
list->head += len;
} else {
@@ -1180,19 +1188,11 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
if (len > count) {
len = count;
 
-   if (copy_to_user(buffer,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf, >hid_debug_buf[list->head], len);
ret += len;
list->head += len;
} else {
-   if (copy_to_user(buffer,
- >hid_debug_buf[list->head], len)) {
-   ret = -EFAULT;
-   goto out;
-   }
+   memcpy(tmpbuf, >hid_debug_buf[list->head], len);
list->head = 0;
ret += len;
count -= len;
@@ -1202,6 +1202,15 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
goto copy_rest;
}
}
+
+out_unlock:
+   spin_unlock_irqrestore(>list_lock, flags);
+
+   /* copy out tmpbuf content to userspace */
+   if (ret && copy_to_user(buffer, tmpbuf, ret))
+   ret = -EFAULT;
+   kfree(tmpbuf);
+
 out:
mutex_unlock(>read_mutex);
return ret;
diff --git a/includ

[PATCH 3/3] HID: debug: fix the ring buffer writer implementation

2018-10-03 Thread Vladis Dronov
ring buffer implementation hid_debug_event() is strange allowing a lost
of data. it does not move head pointer on append and uses per-byte for()
loop. fix this by introducing a new ring buffer implementation, which
overwrites the oldest data in case of the ring buffer overflow. it uses
some calculations for the buffer pointers but only 2 or less memcpy()
calls.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c | 66 -
 1 file changed, 58 insertions(+), 8 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index de640e4132c7..63247ac4a9ce 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -661,20 +661,70 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-   unsigned i;
struct hid_debug_list *list;
-   unsigned long flags, flags2;
+   unsigned long flags1, flags2;
+   unsigned int part1, part2;
+   size_t len;
 
-   spin_lock_irqsave(>debug_list_lock, flags);
+   if (!buf || !buf[0])
+   return;
+
+   len = strlen(buf);
+
+   spin_lock_irqsave(>debug_list_lock, flags1);
list_for_each_entry(list, >debug_list, node) {
spin_lock_irqsave(>list_lock, flags2);
-   for (i = 0; buf[i]; i++)
-   list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
-   buf[i];
-   list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
+
+   /* len >= BUFSIZE, copy the last HID_DEBUG_BUFSIZE-1 bytes */
+   if (len > HID_DEBUG_BUFSIZE - 1) {
+   memcpy(list->hid_debug_buf,
+   buf + len - (HID_DEBUG_BUFSIZE - 1),
+   HID_DEBUG_BUFSIZE - 1);
+
+   list->head = 0;
+   list->tail = HID_DEBUG_BUFSIZE - 1;
+   } else {
+   /* append data to the ring buffer */
+   part1 = min_t(size_t, HID_DEBUG_BUFSIZE - list->tail,
+   len);
+   part2 = len - part1;
+
+   memcpy(list->hid_debug_buf + list->tail, buf, part1);
+
+   /* check if head must be moved as the ring is full */
+   if ((list->tail < list->head) &&
+   (list->tail + part1 >= list->head)) {
+   list->head = list->tail + part1 + 1;
+
+   if (list->head >= HID_DEBUG_BUFSIZE)
+   list->head -= HID_DEBUG_BUFSIZE;
+   }
+
+   /* set tail after the end of appended data */
+   list->tail = list->tail + part1;
+   if (list->tail == HID_DEBUG_BUFSIZE) {
+   list->tail = 0;
+   if (list->head == 0)
+   list->head = 1;
+   }
+
+   /* append the 2nd part of data to the ring */
+   if (part2 > 0) {
+   memcpy(list->hid_debug_buf, buf + part1, part2);
+
+   /* check if head must be moved again as
+* the ring is full
+*/
+   if (list->tail + part2 >= list->head)
+   list->head = list->tail + part2 + 1;
+
+   list->tail = list->tail + part2;
+   }
+   }
+
spin_unlock_irqrestore(>list_lock, flags2);
}
-   spin_unlock_irqrestore(>debug_list_lock, flags);
+   spin_unlock_irqrestore(>debug_list_lock, flags1);
 
wake_up_interruptible(>debug_wait);
 }
-- 
2.19.0



[PATCH 3/3] HID: debug: fix the ring buffer writer implementation

2018-10-03 Thread Vladis Dronov
ring buffer implementation hid_debug_event() is strange allowing a lost
of data. it does not move head pointer on append and uses per-byte for()
loop. fix this by introducing a new ring buffer implementation, which
overwrites the oldest data in case of the ring buffer overflow. it uses
some calculations for the buffer pointers but only 2 or less memcpy()
calls.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c | 66 -
 1 file changed, 58 insertions(+), 8 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index de640e4132c7..63247ac4a9ce 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -661,20 +661,70 @@ EXPORT_SYMBOL_GPL(hid_dump_device);
 /* enqueue string to 'events' ring buffer */
 void hid_debug_event(struct hid_device *hdev, char *buf)
 {
-   unsigned i;
struct hid_debug_list *list;
-   unsigned long flags, flags2;
+   unsigned long flags1, flags2;
+   unsigned int part1, part2;
+   size_t len;
 
-   spin_lock_irqsave(>debug_list_lock, flags);
+   if (!buf || !buf[0])
+   return;
+
+   len = strlen(buf);
+
+   spin_lock_irqsave(>debug_list_lock, flags1);
list_for_each_entry(list, >debug_list, node) {
spin_lock_irqsave(>list_lock, flags2);
-   for (i = 0; buf[i]; i++)
-   list->hid_debug_buf[(list->tail + i) % 
HID_DEBUG_BUFSIZE] =
-   buf[i];
-   list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
+
+   /* len >= BUFSIZE, copy the last HID_DEBUG_BUFSIZE-1 bytes */
+   if (len > HID_DEBUG_BUFSIZE - 1) {
+   memcpy(list->hid_debug_buf,
+   buf + len - (HID_DEBUG_BUFSIZE - 1),
+   HID_DEBUG_BUFSIZE - 1);
+
+   list->head = 0;
+   list->tail = HID_DEBUG_BUFSIZE - 1;
+   } else {
+   /* append data to the ring buffer */
+   part1 = min_t(size_t, HID_DEBUG_BUFSIZE - list->tail,
+   len);
+   part2 = len - part1;
+
+   memcpy(list->hid_debug_buf + list->tail, buf, part1);
+
+   /* check if head must be moved as the ring is full */
+   if ((list->tail < list->head) &&
+   (list->tail + part1 >= list->head)) {
+   list->head = list->tail + part1 + 1;
+
+   if (list->head >= HID_DEBUG_BUFSIZE)
+   list->head -= HID_DEBUG_BUFSIZE;
+   }
+
+   /* set tail after the end of appended data */
+   list->tail = list->tail + part1;
+   if (list->tail == HID_DEBUG_BUFSIZE) {
+   list->tail = 0;
+   if (list->head == 0)
+   list->head = 1;
+   }
+
+   /* append the 2nd part of data to the ring */
+   if (part2 > 0) {
+   memcpy(list->hid_debug_buf, buf + part1, part2);
+
+   /* check if head must be moved again as
+* the ring is full
+*/
+   if (list->tail + part2 >= list->head)
+   list->head = list->tail + part2 + 1;
+
+   list->tail = list->tail + part2;
+   }
+   }
+
spin_unlock_irqrestore(>list_lock, flags2);
}
-   spin_unlock_irqrestore(>debug_list_lock, flags);
+   spin_unlock_irqrestore(>debug_list_lock, flags1);
 
wake_up_interruptible(>debug_wait);
 }
-- 
2.19.0



[PATCH 0/3] HID: debug: fix the ring buffer implementation

2018-10-03 Thread Vladis Dronov
This patchset is fixing some aspects of the ring buffer implementation in
drivers/hid/hid-debug.c. This implementation has certain problem points:

- it may stuck in an infinite loop
- it may return corrupted data
- a reader and a writer are not protected by spinlocks, which can lead to
  the corrupted data

The suggested patchset is a new ring buffer implementation which overwrites
the oldest data in case of an overflow. One can verify the suggested ring
buffer implementation by fuzzing it with modified kernel and fuzzer-reader
at: https://gist.github.com/nefigtut/33d56e3870b67493cc867344aed2a062

Vladis Dronov (3):
  HID: debug: avoid infinite loop and corrupting data
  HID: debug: provide reader-writer locking for the ring buffer
  HID: debug: fix ring buffer implementation

 drivers/hid/hid-debug.c   | 201 ++
 include/linux/hid-debug.h |   1 +
 2 files changed, 142 insertions(+), 60 deletions(-)

-- 
2.19.0


[PATCH 1/3] HID: debug: avoid infinite loop and corrupting data

2018-10-03 Thread Vladis Dronov
hid_debug_events_read() does not properly handle the case when tail < head
and count < HID_DEBUG_BUFSIZE - head and returns corrupted data. Also,
after commit 717adfdaf147 ("HID: debug: check length before
copy_to_user()") it can enter an infinite loop if called with count == 0.
Fix this by properly handling this case and adding a check.

Also, the function has "while (ret == 0)" loop which is not needed, remove
it.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c | 109 
 1 file changed, 65 insertions(+), 44 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index b48100236df8..20580871b0ec 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -722,7 +722,7 @@ void hid_dump_input(struct hid_device *hdev, struct 
hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
 
kfree(buf);
-wake_up_interruptible(>debug_wait);
+   wake_up_interruptible(>debug_wait);
 
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -1112,73 +1112,95 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
 
-   mutex_lock(>read_mutex);
-   while (ret == 0) {
-   if (list->head == list->tail) {
-   add_wait_queue(>hdev->debug_wait, );
-   set_current_state(TASK_INTERRUPTIBLE);
+   if (!count)
+   return 0;
 
-   while (list->head == list->tail) {
-   if (file->f_flags & O_NONBLOCK) {
-   ret = -EAGAIN;
-   break;
-   }
-   if (signal_pending(current)) {
-   ret = -ERESTARTSYS;
-   break;
-   }
+   mutex_lock(>read_mutex);
+   if (list->head == list->tail) {
+   add_wait_queue(>hdev->debug_wait, );
+   set_current_state(TASK_INTERRUPTIBLE);
+
+   while (list->head == list->tail) {
+   if (file->f_flags & O_NONBLOCK) {
+   ret = -EAGAIN;
+   break;
+   }
 
-   if (!list->hdev || !list->hdev->debug) {
-   ret = -EIO;
-   set_current_state(TASK_RUNNING);
-   goto out;
-   }
+   if (signal_pending(current)) {
+   ret = -ERESTARTSYS;
+   break;
+   }
 
-   /* allow O_NONBLOCK from other threads */
-   mutex_unlock(>read_mutex);
-   schedule();
-   mutex_lock(>read_mutex);
-   set_current_state(TASK_INTERRUPTIBLE);
+   if (!list->hdev || !list->hdev->debug) {
+   ret = -EIO;
+   set_current_state(TASK_RUNNING);
+   goto out;
}
 
-   set_current_state(TASK_RUNNING);
-   remove_wait_queue(>hdev->debug_wait, );
+   /* allow O_NONBLOCK from other threads */
+   mutex_unlock(>read_mutex);
+   schedule();
+   mutex_lock(>read_mutex);
+   set_current_state(TASK_INTERRUPTIBLE);
}
 
-   if (ret)
-   goto out;
+   set_current_state(TASK_RUNNING);
+   remove_wait_queue(>hdev->debug_wait, );
+   }
+
+   if (ret)
+   goto out;
 
-   /* pass the ringbuffer contents to userspace */
+   /* pass the ringbuffer content to userspace */
 copy_rest:
-   if (list->tail == list->head)
-   goto out;
-   if (list->tail > list->head) {
-   len = list->tail - list->head;
-   if (len > count)
-   len = count;
+   if (list->tail == list->head)
+   goto out;
+
+   /* the data from the head to the tail in the buffer is linear */
+   if (list->tail > list->head) {
+   len = list->tail - list->head;
+   if (len > count)
+   len = count;
 
-   if (copy_to_user(buffer + ret, 
>hid_debug_buf[list->head], len)) {
+   if (copy_to_user(buffe

[PATCH 0/3] HID: debug: fix the ring buffer implementation

2018-10-03 Thread Vladis Dronov
This patchset is fixing some aspects of the ring buffer implementation in
drivers/hid/hid-debug.c. This implementation has certain problem points:

- it may stuck in an infinite loop
- it may return corrupted data
- a reader and a writer are not protected by spinlocks, which can lead to
  the corrupted data

The suggested patchset is a new ring buffer implementation which overwrites
the oldest data in case of an overflow. One can verify the suggested ring
buffer implementation by fuzzing it with modified kernel and fuzzer-reader
at: https://gist.github.com/nefigtut/33d56e3870b67493cc867344aed2a062

Vladis Dronov (3):
  HID: debug: avoid infinite loop and corrupting data
  HID: debug: provide reader-writer locking for the ring buffer
  HID: debug: fix ring buffer implementation

 drivers/hid/hid-debug.c   | 201 ++
 include/linux/hid-debug.h |   1 +
 2 files changed, 142 insertions(+), 60 deletions(-)

-- 
2.19.0


[PATCH 1/3] HID: debug: avoid infinite loop and corrupting data

2018-10-03 Thread Vladis Dronov
hid_debug_events_read() does not properly handle the case when tail < head
and count < HID_DEBUG_BUFSIZE - head and returns corrupted data. Also,
after commit 717adfdaf147 ("HID: debug: check length before
copy_to_user()") it can enter an infinite loop if called with count == 0.
Fix this by properly handling this case and adding a check.

Also, the function has "while (ret == 0)" loop which is not needed, remove
it.

Signed-off-by: Vladis Dronov 
---
 drivers/hid/hid-debug.c | 109 
 1 file changed, 65 insertions(+), 44 deletions(-)

diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index b48100236df8..20580871b0ec 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -722,7 +722,7 @@ void hid_dump_input(struct hid_device *hdev, struct 
hid_usage *usage, __s32 valu
hid_debug_event(hdev, buf);
 
kfree(buf);
-wake_up_interruptible(>debug_wait);
+   wake_up_interruptible(>debug_wait);
 
 }
 EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -1112,73 +1112,95 @@ static ssize_t hid_debug_events_read(struct file *file, 
char __user *buffer,
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
 
-   mutex_lock(>read_mutex);
-   while (ret == 0) {
-   if (list->head == list->tail) {
-   add_wait_queue(>hdev->debug_wait, );
-   set_current_state(TASK_INTERRUPTIBLE);
+   if (!count)
+   return 0;
 
-   while (list->head == list->tail) {
-   if (file->f_flags & O_NONBLOCK) {
-   ret = -EAGAIN;
-   break;
-   }
-   if (signal_pending(current)) {
-   ret = -ERESTARTSYS;
-   break;
-   }
+   mutex_lock(>read_mutex);
+   if (list->head == list->tail) {
+   add_wait_queue(>hdev->debug_wait, );
+   set_current_state(TASK_INTERRUPTIBLE);
+
+   while (list->head == list->tail) {
+   if (file->f_flags & O_NONBLOCK) {
+   ret = -EAGAIN;
+   break;
+   }
 
-   if (!list->hdev || !list->hdev->debug) {
-   ret = -EIO;
-   set_current_state(TASK_RUNNING);
-   goto out;
-   }
+   if (signal_pending(current)) {
+   ret = -ERESTARTSYS;
+   break;
+   }
 
-   /* allow O_NONBLOCK from other threads */
-   mutex_unlock(>read_mutex);
-   schedule();
-   mutex_lock(>read_mutex);
-   set_current_state(TASK_INTERRUPTIBLE);
+   if (!list->hdev || !list->hdev->debug) {
+   ret = -EIO;
+   set_current_state(TASK_RUNNING);
+   goto out;
}
 
-   set_current_state(TASK_RUNNING);
-   remove_wait_queue(>hdev->debug_wait, );
+   /* allow O_NONBLOCK from other threads */
+   mutex_unlock(>read_mutex);
+   schedule();
+   mutex_lock(>read_mutex);
+   set_current_state(TASK_INTERRUPTIBLE);
}
 
-   if (ret)
-   goto out;
+   set_current_state(TASK_RUNNING);
+   remove_wait_queue(>hdev->debug_wait, );
+   }
+
+   if (ret)
+   goto out;
 
-   /* pass the ringbuffer contents to userspace */
+   /* pass the ringbuffer content to userspace */
 copy_rest:
-   if (list->tail == list->head)
-   goto out;
-   if (list->tail > list->head) {
-   len = list->tail - list->head;
-   if (len > count)
-   len = count;
+   if (list->tail == list->head)
+   goto out;
+
+   /* the data from the head to the tail in the buffer is linear */
+   if (list->tail > list->head) {
+   len = list->tail - list->head;
+   if (len > count)
+   len = count;
 
-   if (copy_to_user(buffer + ret, 
>hid_debug_buf[list->head], len)) {
+   if (copy_to_user(buffe

Re: KMSAN: uninit-value in __dev_mc_add

2018-09-27 Thread Vladis Dronov
Hello, Eric, all,

> I dunno, your patch looks quite not the right fix.

I agree, it looks more like a dirty hack. Unfortunately, I lack the deep
expertise in the network stack subsystem, so I've posted the patch to,
sort of, start a discussion and probably get some hints.
 
> If TUN is able to change dev->type,  how comes it does not set the
> appropriate dev->addr_len at the same time ?

Well,... probably, nobody cared to do so:

[drivers/net/tun.c]
case TUNSETLINK:
...
tun->dev->type = (int) arg; //<--- that's all!
tun_debug(KERN_INFO, tun, "linktype set to %d\n",
  tun->dev->type);
ret = 0;
}
break;

> Really the bug seems to be deeper, and without setting proper
> dev->addr_len, we'll need more 'fixes' like yours.

Absolutely. Unfortunately, I wasn't able to just write such deeper patch. 
Let me share what I have found and let me hope to get an advise.

- So setting just the tun->dev->type makes the dev struct inconsistent.

- There are more field to adjust, at least dev->broadcast. Also, there are
  a number of *_ops fields which are all set for the Ethernet type, most
  probably they must be adjusted also.

- There is no get_addr_len_by_link_type() or a simple way to get link layer
  properties by dev->type. Such settings are scattered in *_setup and
  *_init functions, like ipgre_tunnel_init() { ... dev->addr_len = 4; ...}

Having these, I can imagine 2 ways for a proper fix.

1) Destroy the net_device in question and recreate it when changing a link
type. This way all the dev fields are set right. Create it in a similar way
as rtnl_newlink() does. Again, we do not have get_X_by_link_type(), so it
probably will be some large switch()/case:

  $ grep '^#define ARPHRD_' include/uapi/linux/if_arp.h | wc -l
  59

2) Leave tun an Ethernet device, add some tun->pretend_to_be_this_link_type
field and change only it on TUNSETLINK. And use this field in cases for which
TUNSETLINK was invented in the first place. Unfortunately, I do not have such
a list. The initial the commit ff4cc3ac93e1 says:

  For use with
  wireless and other networking types it should be possible to set the
  ARP type via an ioctl.

Surely, there can be something else which I do not see. Could anyone suggest
an advice on this?

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


Re: KMSAN: uninit-value in __dev_mc_add

2018-09-27 Thread Vladis Dronov
Hello, Eric, all,

> I dunno, your patch looks quite not the right fix.

I agree, it looks more like a dirty hack. Unfortunately, I lack the deep
expertise in the network stack subsystem, so I've posted the patch to,
sort of, start a discussion and probably get some hints.
 
> If TUN is able to change dev->type,  how comes it does not set the
> appropriate dev->addr_len at the same time ?

Well,... probably, nobody cared to do so:

[drivers/net/tun.c]
case TUNSETLINK:
...
tun->dev->type = (int) arg; //<--- that's all!
tun_debug(KERN_INFO, tun, "linktype set to %d\n",
  tun->dev->type);
ret = 0;
}
break;

> Really the bug seems to be deeper, and without setting proper
> dev->addr_len, we'll need more 'fixes' like yours.

Absolutely. Unfortunately, I wasn't able to just write such deeper patch. 
Let me share what I have found and let me hope to get an advise.

- So setting just the tun->dev->type makes the dev struct inconsistent.

- There are more field to adjust, at least dev->broadcast. Also, there are
  a number of *_ops fields which are all set for the Ethernet type, most
  probably they must be adjusted also.

- There is no get_addr_len_by_link_type() or a simple way to get link layer
  properties by dev->type. Such settings are scattered in *_setup and
  *_init functions, like ipgre_tunnel_init() { ... dev->addr_len = 4; ...}

Having these, I can imagine 2 ways for a proper fix.

1) Destroy the net_device in question and recreate it when changing a link
type. This way all the dev fields are set right. Create it in a similar way
as rtnl_newlink() does. Again, we do not have get_X_by_link_type(), so it
probably will be some large switch()/case:

  $ grep '^#define ARPHRD_' include/uapi/linux/if_arp.h | wc -l
  59

2) Leave tun an Ethernet device, add some tun->pretend_to_be_this_link_type
field and change only it on TUNSETLINK. And use this field in cases for which
TUNSETLINK was invented in the first place. Unfortunately, I do not have such
a list. The initial the commit ff4cc3ac93e1 says:

  For use with
  wireless and other networking types it should be possible to set the
  ARP type via an ioctl.

Surely, there can be something else which I do not see. Could anyone suggest
an advice on this?

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


Re: KMSAN: uninit-value in __dev_mc_add

2018-09-27 Thread Vladis Dronov
Hello,

This report is actually for the same bug which was reported in:

https://syzkaller.appspot.com/bug?id=088efeac32fdde781038a777a63e436c0d4d7036

The note there that the bug was fixed by "Commits: net: fix uninit-value in
__hw_addr_add_ex()" is wrong. A C-reproducer from the 2nd syzkaller report
can trigger the bug from this one.

I've researched this and a result is a proposed patch, the problem is the tun
device code allowing to set an arbitrary link type.

https://lkml.org/lkml/2018/9/26/416
https://lore.kernel.org/lkml/20180926093018.6646-1-vdro...@redhat.com/T/#u
https://marc.info/?l=linux-netdev=153795423320016=2

A simplified reproducer is attached.

Best regards,
Vladis Dronov
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
  int ret, sockfd, tunfd;

  syscall(__NR_mmap, 0x2000, 0x100, 3, 0x32, -1, 0);

  // socket(AF_PACKET, SOCK_DGRAM|SOCK_NONBLOCK, 0)
  sockfd = syscall(__NR_socket, 0x11, 0x10802, 0);
  if (sockfd < 0) {
perror("socket()");
ret = 1;
goto exit_end;
  }

  memcpy((void*)0x2240, "/dev/net/tun", 13);
  tunfd = open((char *)0x2240, 0);
  if (tunfd < 0) {
perror("open()");
ret = 2;
goto exit_sock_close;
  }

  memcpy((void*)0x20c0, 
"\x69\x67\x62\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
  *(uint16_t*)0x20d0 = 0x4012;
  ret = syscall(__NR_ioctl, tunfd, 0x400454ca, 0x20c0); // TUNSETIFF 
_IOW('T', 202, int)
  if (ret < 0) {
perror("ioctl(TUNSETIFF)");
ret = 3;
goto exit_tun_close;
  }

  // TUNSETLINK _IOW('T', 205, int) / 0x30a = 778 = ARPHRD_IPGRE
  if (argc < 2)
ret = syscall(__NR_ioctl, tunfd, 0x400454cd, 0x30a);
  else
ret = syscall(__NR_ioctl, tunfd, 0x400454cd, atoi(argv[1]));
  if (ret < 0) {
perror("ioctl(TUNSETLINK)");
ret = 4;
goto exit_tun_close;
  }

  memcpy((void*)0x2040, 
"\x69\x67\x62\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
  *(uint16_t*)0x2050 = 0xa201;
  ret = syscall(__NR_ioctl, sockfd, 0x8914, 0x2040); // SIOCSIFFLAGS 0x8914
  if (ret < 0) {
perror("ioctl(SIOCSIFFLAGS)");
ret = 5;
goto exit_tun_close;
  }

  printf("done:\n");
  system("/usr/sbin/ip -details link show igb0");

exit_tun_close:
  close(tunfd);
exit_sock_close:
  close(sockfd);
exit_end:
  munmap((void *)0x2000, 0x100);
  return 0;
}

Re: KMSAN: uninit-value in __dev_mc_add

2018-09-27 Thread Vladis Dronov
Hello,

This report is actually for the same bug which was reported in:

https://syzkaller.appspot.com/bug?id=088efeac32fdde781038a777a63e436c0d4d7036

The note there that the bug was fixed by "Commits: net: fix uninit-value in
__hw_addr_add_ex()" is wrong. A C-reproducer from the 2nd syzkaller report
can trigger the bug from this one.

I've researched this and a result is a proposed patch, the problem is the tun
device code allowing to set an arbitrary link type.

https://lkml.org/lkml/2018/9/26/416
https://lore.kernel.org/lkml/20180926093018.6646-1-vdro...@redhat.com/T/#u
https://marc.info/?l=linux-netdev=153795423320016=2

A simplified reproducer is attached.

Best regards,
Vladis Dronov
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
  int ret, sockfd, tunfd;

  syscall(__NR_mmap, 0x2000, 0x100, 3, 0x32, -1, 0);

  // socket(AF_PACKET, SOCK_DGRAM|SOCK_NONBLOCK, 0)
  sockfd = syscall(__NR_socket, 0x11, 0x10802, 0);
  if (sockfd < 0) {
perror("socket()");
ret = 1;
goto exit_end;
  }

  memcpy((void*)0x2240, "/dev/net/tun", 13);
  tunfd = open((char *)0x2240, 0);
  if (tunfd < 0) {
perror("open()");
ret = 2;
goto exit_sock_close;
  }

  memcpy((void*)0x20c0, 
"\x69\x67\x62\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
  *(uint16_t*)0x20d0 = 0x4012;
  ret = syscall(__NR_ioctl, tunfd, 0x400454ca, 0x20c0); // TUNSETIFF 
_IOW('T', 202, int)
  if (ret < 0) {
perror("ioctl(TUNSETIFF)");
ret = 3;
goto exit_tun_close;
  }

  // TUNSETLINK _IOW('T', 205, int) / 0x30a = 778 = ARPHRD_IPGRE
  if (argc < 2)
ret = syscall(__NR_ioctl, tunfd, 0x400454cd, 0x30a);
  else
ret = syscall(__NR_ioctl, tunfd, 0x400454cd, atoi(argv[1]));
  if (ret < 0) {
perror("ioctl(TUNSETLINK)");
ret = 4;
goto exit_tun_close;
  }

  memcpy((void*)0x2040, 
"\x69\x67\x62\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
  *(uint16_t*)0x2050 = 0xa201;
  ret = syscall(__NR_ioctl, sockfd, 0x8914, 0x2040); // SIOCSIFFLAGS 0x8914
  if (ret < 0) {
perror("ioctl(SIOCSIFFLAGS)");
ret = 5;
goto exit_tun_close;
  }

  printf("done:\n");
  system("/usr/sbin/ip -details link show igb0");

exit_tun_close:
  close(tunfd);
exit_sock_close:
  close(sockfd);
exit_end:
  munmap((void *)0x2000, 0x100);
  return 0;
}

[PATCH] nl80211: check for the required netlink attributes presence

2017-09-12 Thread Vladis Dronov
nl80211_set_rekey_data() does not check if the required attributes
NL80211_REKEY_DATA_{REPLAY_CTR,KEK,KCK} are present when processing
NL80211_CMD_SET_REKEY_OFFLOAD request. This request can be issued by
users with CAP_NET_ADMIN privilege and may result in NULL dereference
and a system crash. Add a check for the required attributes presence.
This patch is based on the patch by bo Zhang.

This fixes CVE-2017-12153.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1491046
Fixes: e5497d766ad ("cfg80211/nl80211: support GTK rekey offload")
Cc: <sta...@vger.kernel.org> # v3.1-rc1
Reported-by: bo Zhang <zhangbo5891...@gmail.com>
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 net/wireless/nl80211.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0df8023..fbd5593 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10903,6 +10903,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, 
struct genl_info *info)
if (err)
return err;
 
+   if (!tb[NL80211_REKEY_DATA_REPLAY_CTR] || !tb[NL80211_REKEY_DATA_KEK] ||
+   !tb[NL80211_REKEY_DATA_KCK])
+   return -EINVAL;
if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != 
NL80211_REPLAY_CTR_LEN)
return -ERANGE;
if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
-- 
2.9.5


[PATCH] nl80211: check for the required netlink attributes presence

2017-09-12 Thread Vladis Dronov
nl80211_set_rekey_data() does not check if the required attributes
NL80211_REKEY_DATA_{REPLAY_CTR,KEK,KCK} are present when processing
NL80211_CMD_SET_REKEY_OFFLOAD request. This request can be issued by
users with CAP_NET_ADMIN privilege and may result in NULL dereference
and a system crash. Add a check for the required attributes presence.
This patch is based on the patch by bo Zhang.

This fixes CVE-2017-12153.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1491046
Fixes: e5497d766ad ("cfg80211/nl80211: support GTK rekey offload")
Cc:  # v3.1-rc1
Reported-by: bo Zhang 
Signed-off-by: Vladis Dronov 
---
 net/wireless/nl80211.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0df8023..fbd5593 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10903,6 +10903,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, 
struct genl_info *info)
if (err)
return err;
 
+   if (!tb[NL80211_REKEY_DATA_REPLAY_CTR] || !tb[NL80211_REKEY_DATA_KEK] ||
+   !tb[NL80211_REKEY_DATA_KCK])
+   return -EINVAL;
if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != 
NL80211_REPLAY_CTR_LEN)
return -ERANGE;
if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
-- 
2.9.5


[PATCH] video: fbdev: aty: do not leak uninitialized padding in clk to userspace

2017-08-29 Thread Vladis Dronov
'clk' is copied to a userland with padding byte(s) after 'vclk_post_div'
field unitialized, leaking data from the stack. Fix this ensuring all of
'clk' is initialized to zero.

References: https://github.com/torvalds/linux/pull/441
Reported-by: sohu0106 <sohu0...@126.com>
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 drivers/video/fbdev/aty/atyfb_base.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/aty/atyfb_base.c 
b/drivers/video/fbdev/aty/atyfb_base.c
index b55fdac..e4c91d7 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -1855,7 +1855,7 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, 
u_long arg)
 #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
case ATYIO_CLKR:
if (M64_HAS(INTEGRATED)) {
-   struct atyclk clk;
+   struct atyclk clk = { 0 };
union aty_pll *pll = >pll;
u32 dsp_config = pll->ct.dsp_config;
u32 dsp_on_off = pll->ct.dsp_on_off;
-- 
2.9.5


[PATCH] video: fbdev: aty: do not leak uninitialized padding in clk to userspace

2017-08-29 Thread Vladis Dronov
'clk' is copied to a userland with padding byte(s) after 'vclk_post_div'
field unitialized, leaking data from the stack. Fix this ensuring all of
'clk' is initialized to zero.

References: https://github.com/torvalds/linux/pull/441
Reported-by: sohu0106 
Signed-off-by: Vladis Dronov 
---
 drivers/video/fbdev/aty/atyfb_base.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/aty/atyfb_base.c 
b/drivers/video/fbdev/aty/atyfb_base.c
index b55fdac..e4c91d7 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -1855,7 +1855,7 @@ static int atyfb_ioctl(struct fb_info *info, u_int cmd, 
u_long arg)
 #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
case ATYIO_CLKR:
if (M64_HAS(INTEGRATED)) {
-   struct atyclk clk;
+   struct atyclk clk = { 0 };
union aty_pll *pll = >pll;
u32 dsp_config = pll->ct.dsp_config;
u32 dsp_on_off = pll->ct.dsp_on_off;
-- 
2.9.5


[PATCH] xfrm: policy: check policy direction value

2017-08-02 Thread Vladis Dronov
The 'dir' parameter in xfrm_migrate() is a user-controlled byte which is used
as an array index. This can lead to an out-of-bound access, kernel lockup and
DoS. Add a check for the 'dir' value.

This fixes CVE-2017-11600.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1474928
Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint 
address(es)")
Cc: <sta...@vger.kernel.org> # v2.6.21-rc1
Reported-by: "bo Zhang" <zhangbo5891...@gmail.com>
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 net/xfrm/xfrm_policy.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index ff61d85..6f5a0dad 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3308,9 +3308,15 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 
dir, u8 type,
struct xfrm_state *x_new[XFRM_MAX_DEPTH];
struct xfrm_migrate *mp;
 
+   /* Stage 0 - sanity checks */
if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
goto out;
 
+   if (dir >= XFRM_POLICY_MAX) {
+   err = -EINVAL;
+   goto out;
+   }
+
/* Stage 1 - find policy */
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
err = -ENOENT;
-- 
2.9.4



[PATCH] xfrm: policy: check policy direction value

2017-08-02 Thread Vladis Dronov
The 'dir' parameter in xfrm_migrate() is a user-controlled byte which is used
as an array index. This can lead to an out-of-bound access, kernel lockup and
DoS. Add a check for the 'dir' value.

This fixes CVE-2017-11600.

References: https://bugzilla.redhat.com/show_bug.cgi?id=1474928
Fixes: 80c9abaabf42 ("[XFRM]: Extension for dynamic update of endpoint 
address(es)")
Cc:  # v2.6.21-rc1
Reported-by: "bo Zhang" 
Signed-off-by: Vladis Dronov 
---
 net/xfrm/xfrm_policy.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index ff61d85..6f5a0dad 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3308,9 +3308,15 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 
dir, u8 type,
struct xfrm_state *x_new[XFRM_MAX_DEPTH];
struct xfrm_migrate *mp;
 
+   /* Stage 0 - sanity checks */
if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
goto out;
 
+   if (dir >= XFRM_POLICY_MAX) {
+   err = -EINVAL;
+   goto out;
+   }
+
/* Stage 1 - find policy */
if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
err = -ENOENT;
-- 
2.9.4



[PATCH v2] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-04-06 Thread Vladis Dronov
The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..e0d7ff9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1281,6 +1281,9 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, 
void *data,
if (req->multisample_count != 0)
return -EINVAL;
 
+   if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS)
+   return -EINVAL;
+
if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
-- 
2.9.3



[PATCH v2] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-04-06 Thread Vladis Dronov
The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov 
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..e0d7ff9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1281,6 +1281,9 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, 
void *data,
if (req->multisample_count != 0)
return -EINVAL;
 
+   if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS)
+   return -EINVAL;
+
if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
-- 
2.9.3



Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-04-04 Thread Vladis Dronov
Hello, Sinclair!

> Here, the check should be "> DRM_VMW_MAX_MIP_LEVELS" because req->mip_levels
> is only for one layer.

Got it, thanks!

> Also, as long as we can doing a check, I would suggest we check for 0 as
> well.

Do you mean a check for "req->mip_levels > 0" or for "req->mip_levels >= 0" ?

I glimpsed thru the code and I do not see problems with req->mip_levels being 0,
surely I may be mistaking.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


- Original Message -
From: "Sinclair Yeh" <s...@vmware.com>
To: "Vladis Dronov" <vdro...@redhat.com>
Cc: "VMware Graphics" <linux-graphics-maintai...@vmware.com>, "Thomas 
Hellstrom" <thellst...@vmware.com>, "David Airlie" <airl...@linux.ie>, 
dri-de...@lists.freedesktop.org, linux-kernel@vger.kernel.org
Sent: Friday, March 31, 2017 5:07:12 PM
Subject: Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in 
vmw_gb_surface_define_ioctl()

Hi Vladis,


On Thu, Mar 30, 2017 at 12:27:12PM +0200, Vladis Dronov wrote:
> The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
> a user-controlled 'uint32_t' value which is used as a loop count limit.
> This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.
> 
> References:
> https://urldefense.proofpoint.com/v2/url?u=https-3A__bugzilla.redhat.com_show-5Fbug.cgi-3Fid-3D1437431=DwIBAg=uilaK90D4TOVoH58JNXRgQ=HaJ2a6NYExoV0cntAYcoqA=5yR87BuuU86aoAjCveInxh6jvgOyumqIHQhTs0xLo38=tWQs7vwNLgD_b2RWMddVtusEKh9FQTIF5rKFOWudslE=
>  
> Signed-off-by: Vladis Dronov <vdro...@redhat.com>
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
> b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> index b445ce9..b30824b 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> @@ -1281,6 +1281,10 @@ int vmw_gb_surface_define_ioctl(struct drm_device 
> *dev, void *data,
>   if (req->multisample_count != 0)
>   return -EINVAL;
>  
> + if (req->mip_levels > DRM_VMW_MAX_SURFACE_FACES *
> + DRM_VMW_MAX_MIP_LEVELS)
> + return -EINVAL;
> +

Here, the check should be "> DRM_VMW_MAX_MIP_LEVELS" because req->mip_levels
is only for one layer.

Also, as long as we can doing a check, I would suggest we check for 0 as
well.

Sinclair



Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-04-04 Thread Vladis Dronov
Hello, Sinclair!

> Here, the check should be "> DRM_VMW_MAX_MIP_LEVELS" because req->mip_levels
> is only for one layer.

Got it, thanks!

> Also, as long as we can doing a check, I would suggest we check for 0 as
> well.

Do you mean a check for "req->mip_levels > 0" or for "req->mip_levels >= 0" ?

I glimpsed thru the code and I do not see problems with req->mip_levels being 0,
surely I may be mistaking.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


- Original Message -
From: "Sinclair Yeh" 
To: "Vladis Dronov" 
Cc: "VMware Graphics" , "Thomas 
Hellstrom" , "David Airlie" , 
dri-de...@lists.freedesktop.org, linux-kernel@vger.kernel.org
Sent: Friday, March 31, 2017 5:07:12 PM
Subject: Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in 
vmw_gb_surface_define_ioctl()

Hi Vladis,


On Thu, Mar 30, 2017 at 12:27:12PM +0200, Vladis Dronov wrote:
> The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
> a user-controlled 'uint32_t' value which is used as a loop count limit.
> This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.
> 
> References:
> https://urldefense.proofpoint.com/v2/url?u=https-3A__bugzilla.redhat.com_show-5Fbug.cgi-3Fid-3D1437431=DwIBAg=uilaK90D4TOVoH58JNXRgQ=HaJ2a6NYExoV0cntAYcoqA=5yR87BuuU86aoAjCveInxh6jvgOyumqIHQhTs0xLo38=tWQs7vwNLgD_b2RWMddVtusEKh9FQTIF5rKFOWudslE=
>  
> Signed-off-by: Vladis Dronov 
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
> b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> index b445ce9..b30824b 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
> @@ -1281,6 +1281,10 @@ int vmw_gb_surface_define_ioctl(struct drm_device 
> *dev, void *data,
>   if (req->multisample_count != 0)
>   return -EINVAL;
>  
> + if (req->mip_levels > DRM_VMW_MAX_SURFACE_FACES *
> + DRM_VMW_MAX_MIP_LEVELS)
> + return -EINVAL;
> +

Here, the check should be "> DRM_VMW_MAX_MIP_LEVELS" because req->mip_levels
is only for one layer.

Also, as long as we can doing a check, I would suggest we check for 0 as
well.

Sinclair



Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-03-31 Thread Vladis Dronov
This flaw was assigned an id CVE-2017-7346 by MITRE:

http://seclists.org/oss-sec/2017/q1/696

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

- Original Message -
From: "Vladis Dronov" <vdro...@redhat.com>
To: "VMware Graphics" <linux-graphics-maintai...@vmware.com>, "Sinclair Yeh" 
<s...@vmware.com>, "Thomas Hellstrom" <thellst...@vmware.com>, "David Airlie" 
<airl...@linux.ie>, dri-de...@lists.freedesktop.org, 
linux-kernel@vger.kernel.org
Cc: "Vladis Dronov" <vdro...@redhat.com>
Sent: Thursday, March 30, 2017 12:27:12 PM
Subject: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in 
vmw_gb_surface_define_ioctl()

The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov <vdro...@redhat.com>


Re: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-03-31 Thread Vladis Dronov
This flaw was assigned an id CVE-2017-7346 by MITRE:

http://seclists.org/oss-sec/2017/q1/696

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

- Original Message -
From: "Vladis Dronov" 
To: "VMware Graphics" , "Sinclair Yeh" 
, "Thomas Hellstrom" , "David Airlie" 
, dri-de...@lists.freedesktop.org, 
linux-kernel@vger.kernel.org
Cc: "Vladis Dronov" 
Sent: Thursday, March 30, 2017 12:27:12 PM
Subject: [PATCH] kernel: drm/vmwgfx: limit the number of mip levels in 
vmw_gb_surface_define_ioctl()

The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov 


[PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-03-30 Thread Vladis Dronov
The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..b30824b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1281,6 +1281,10 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, 
void *data,
if (req->multisample_count != 0)
return -EINVAL;
 
+   if (req->mip_levels > DRM_VMW_MAX_SURFACE_FACES *
+   DRM_VMW_MAX_MIP_LEVELS)
+   return -EINVAL;
+
if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
-- 
2.9.3



[PATCH] kernel: drm/vmwgfx: limit the number of mip levels in vmw_gb_surface_define_ioctl()

2017-03-30 Thread Vladis Dronov
The 'req->mip_levels' parameter in vmw_gb_surface_define_ioctl() is
a user-controlled 'uint32_t' value which is used as a loop count limit.
This can lead to a kernel lockup and DoS. Add check for 'req->mip_levels'.

References:
https://bugzilla.redhat.com/show_bug.cgi?id=1437431
Signed-off-by: Vladis Dronov 
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..b30824b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1281,6 +1281,10 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, 
void *data,
if (req->multisample_count != 0)
return -EINVAL;
 
+   if (req->mip_levels > DRM_VMW_MAX_SURFACE_FACES *
+   DRM_VMW_MAX_MIP_LEVELS)
+   return -EINVAL;
+
if (unlikely(vmw_user_surface_size == 0))
vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
-- 
2.9.3



[PATCH] drm/vmwgfx: Check check that number of mip levels is above zero in vmw_surface_define_ioctl()

2017-03-24 Thread Vladis Dronov
In vmw_surface_define_ioctl(), a num_sizes parameter is assigned a
user-controlled value which is not checked for zero. It is used in
a call to kmalloc() which returns ZERO_SIZE_PTR. Later ZERO_SIZE_PTR
is dereferenced which leads to a GPF and possibly to a kernel panic.
Add the check for zero to avoid this.

Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1435719
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..42840cc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -716,8 +716,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void 
*data,
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
num_sizes += req->mip_levels[i];
 
-   if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
-   DRM_VMW_MAX_MIP_LEVELS)
+   if (num_sizes <= 0 ||
+   num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS)
return -EINVAL;
 
size = vmw_user_surface_size + 128 +
-- 
2.9.3



[PATCH] drm/vmwgfx: Check check that number of mip levels is above zero in vmw_surface_define_ioctl()

2017-03-24 Thread Vladis Dronov
In vmw_surface_define_ioctl(), a num_sizes parameter is assigned a
user-controlled value which is not checked for zero. It is used in
a call to kmalloc() which returns ZERO_SIZE_PTR. Later ZERO_SIZE_PTR
is dereferenced which leads to a GPF and possibly to a kernel panic.
Add the check for zero to avoid this.

Reference: https://bugzilla.redhat.com/show_bug.cgi?id=1435719
Signed-off-by: Vladis Dronov 
---
 drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index b445ce9..42840cc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -716,8 +716,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void 
*data,
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
num_sizes += req->mip_levels[i];
 
-   if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
-   DRM_VMW_MAX_MIP_LEVELS)
+   if (num_sizes <= 0 ||
+   num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS)
return -EINVAL;
 
size = vmw_user_surface_size + 128 +
-- 
2.9.3



Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
From: Vladis Dronov <vdro...@redhat.com>
Subject: [PATCH] ALSA: usb-audio: Fix double-free in error paths after 
snd_usb_add_audio_stream() call

create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and
create_uaxx_quirk() functions allocate the audioformat object by themselves
and free it upon error before returning. However, once the object is linked
to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be
double-freed, eventually resulting in a memory corruption.

This patch fixes these failures in the error paths by unlinking the audioformat
object before freeing it.

Based on a patch by Takashi Iwai" <ti...@suse.de>

[Note for stable backports:
 this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor
 code cleanup in create_fixed_stream_quirk()')]

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358
Reported-by: Ralf Spenneberg <r...@spenneberg.net>
Cc: <sta...@vger.kernel.org> # see the note above
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 sound/usb/quirks.c | 4 
 sound/usb/stream.c | 6 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..6178bb5 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+   INIT_LIST_HEAD(>list);
if (fp->nr_rates > MAX_NR_RATES) {
kfree(fp);
return -EINVAL;
@@ -193,6 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
return 0;
 
  error:
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
kfree(rate_table);
return err;
@@ -469,6 +471,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+   INIT_LIST_HEAD(>list);
 
switch (fp->maxpacksize) {
case 0x120:
@@ -492,6 +495,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
return err;
}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 51258a1..6fe7f21 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -316,7 +316,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int 
channels, unsigned int bits,
 /*
  * add this endpoint to the chip instance.
  * if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
+ * if not, create a new pcm stream. note, fp is added to the substream
+ * fmt_list and will be freed on the chip instance release. do not free
+ * fp or do remove it from the substream fmt_list to avoid double-free.
  */
 int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
 int stream,
@@ -677,6 +679,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio 
*chip, int iface_no)
* (fp->maxpacksize & 0x7ff);
fp->attributes = parse_uac_endpoint_attributes(chip, alts, 
protocol, iface_no);
fp->clock = clock;
+   INIT_LIST_HEAD(>list);
 
/* some quirks for attributes here */
 
@@ -725,6 +728,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio 
*chip, int iface_no)
dev_dbg(>dev, "%u:%d: add audio endpoint %#x\n", iface_no, 
altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+   list_del(>list); /* unlink for avoiding double-free 
*/
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);
-- 
2.5.5


Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
From: Vladis Dronov 
Subject: [PATCH] ALSA: usb-audio: Fix double-free in error paths after 
snd_usb_add_audio_stream() call

create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and
create_uaxx_quirk() functions allocate the audioformat object by themselves
and free it upon error before returning. However, once the object is linked
to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be
double-freed, eventually resulting in a memory corruption.

This patch fixes these failures in the error paths by unlinking the audioformat
object before freeing it.

Based on a patch by Takashi Iwai" 

[Note for stable backports:
 this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor
 code cleanup in create_fixed_stream_quirk()')]

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358
Reported-by: Ralf Spenneberg 
Cc:  # see the note above
Signed-off-by: Vladis Dronov 
---
 sound/usb/quirks.c | 4 
 sound/usb/stream.c | 6 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..6178bb5 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+   INIT_LIST_HEAD(>list);
if (fp->nr_rates > MAX_NR_RATES) {
kfree(fp);
return -EINVAL;
@@ -193,6 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
return 0;
 
  error:
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
kfree(rate_table);
return err;
@@ -469,6 +471,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+   INIT_LIST_HEAD(>list);
 
switch (fp->maxpacksize) {
case 0x120:
@@ -492,6 +495,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
return err;
}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 51258a1..6fe7f21 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -316,7 +316,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int 
channels, unsigned int bits,
 /*
  * add this endpoint to the chip instance.
  * if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
+ * if not, create a new pcm stream. note, fp is added to the substream
+ * fmt_list and will be freed on the chip instance release. do not free
+ * fp or do remove it from the substream fmt_list to avoid double-free.
  */
 int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
 int stream,
@@ -677,6 +679,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio 
*chip, int iface_no)
* (fp->maxpacksize & 0x7ff);
fp->attributes = parse_uac_endpoint_attributes(chip, alts, 
protocol, iface_no);
fp->clock = clock;
+   INIT_LIST_HEAD(>list);
 
/* some quirks for attributes here */
 
@@ -725,6 +728,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio 
*chip, int iface_no)
dev_dbg(>dev, "%u:%d: add audio endpoint %#x\n", iface_no, 
altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
+   list_del(>list); /* unlink for avoiding double-free 
*/
kfree(fp->rate_table);
kfree(fp->chmap);
kfree(fp);
-- 
2.5.5


Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
Hello, Takashi, all,

> No, it has nothing to do with the double-free bug itself.  Such an
> optimization shouldn't be put in a fix patch

This piece of code move alone fixes the double-free bug in
create_fixed_stream_quirk(), so I believe it is related. Besides, a lot of stuff
is created and initialized in snd_usb_add_audio_stream() and while I do not see
another use-after-free bug, it could be there. By moving this code we avoid
these potential bugs we have not hit yet.

But anyway. If you still believe this code should not be moved, please, confirm,
I'll suggest the next patch version without it.

> Vladis, if you take someone's patch as the base, you have to carry the
> original authorship somewhere...

Yes, I was thinking about it, I was just not sure how should I do it. Will the
following form be fine? Or somehow else?

Based on a patch by Takashi Iwai" <ti...@suse.de>

> > + * if not, create a new pcm stream. the caller must remove fp from
> > + * the substream fmt_list in the error path to avoid double-free.
>
> This comment isn't true.  The caller may leave it as is, too, like my
> first version.  It just depends on the code.

Yes. Is the following rewrite acceptable for the next patch version?

 * if not, create a new pcm stream. Note, fp is added to the substream fmt_list
 * and will be freed on the chip instance release. Do not free fp or do remove
 * it from the substream fmt_list to avoid double-free.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
Hello, Takashi, all,

> No, it has nothing to do with the double-free bug itself.  Such an
> optimization shouldn't be put in a fix patch

This piece of code move alone fixes the double-free bug in
create_fixed_stream_quirk(), so I believe it is related. Besides, a lot of stuff
is created and initialized in snd_usb_add_audio_stream() and while I do not see
another use-after-free bug, it could be there. By moving this code we avoid
these potential bugs we have not hit yet.

But anyway. If you still believe this code should not be moved, please, confirm,
I'll suggest the next patch version without it.

> Vladis, if you take someone's patch as the base, you have to carry the
> original authorship somewhere...

Yes, I was thinking about it, I was just not sure how should I do it. Will the
following form be fine? Or somehow else?

Based on a patch by Takashi Iwai" 

> > + * if not, create a new pcm stream. the caller must remove fp from
> > + * the substream fmt_list in the error path to avoid double-free.
>
> This comment isn't true.  The caller may leave it as is, too, like my
> first version.  It just depends on the code.

Yes. Is the following rewrite acceptable for the next patch version?

 * if not, create a new pcm stream. Note, fp is added to the substream fmt_list
 * and will be freed on the chip instance release. Do not free fp or do remove
 * it from the substream fmt_list to avoid double-free.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
Hello, Takashi, all,

> > Thanks for the report.  But how about a simpler fix like below?
>
> Maybe the one below is more straightforward (and even simpler).
> Let me know if this works enough for you.

1) I would still suggest moving the code in create_fixed_stream_quirk() (marked
as (*)) after "if (altsd->bNumEndpoints < 1)" check. This way no allocations is
done in snd_usb_add_audio_stream() if we go an error path:

(*) stream = (fp->endpoint & USB_DIR_IN) 
(*) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
(*) err = snd_usb_add_audio_stream(chip, stream, fp);
(*) if (err < 0)
(*) goto error;

2) While your fix is indeed simpler, it fixes double-free bug only in
create_fixed_stream_quirk(). create_uaxx_quirk(), for example, still has this
bug:

err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {  <<< in the error path there
kfree(fp);  <<< is a double-free
return err;
}

as any other code where snd_usb_add_audio_stream() is used and *fp is freed in
case of error.

3) Not deleting fp from the fmt_list inside snd_usb_add_audio_stream() in case
of error moves this necessity to a caller, thus breaking the scope. This forces
any caller of snd_usb_add_audio_stream() to fulfill this non-obvious 
requirement.
But I agree that this is simpler and more straightforward. We need just to fix
all the places where snd_usb_add_audio_stream() is called (3 as of now), please,
have a look on the following patch.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

-- 8< --
From: Vladis Dronov <vdro...@redhat.com>
Subject: [PATCH] ALSA: usb-audio: Fix double-free in error paths after 
snd_usb_add_audio_stream() call

create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and
create_uaxx_quirk() functions allocate the audioformat object by themselves
and free it upon error before returning. However, once the object is linked
to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be
double-freed, eventually resulting in a memory corruption.

This patch fixes these failures in the error paths by unlinking the audioformat
object before freeing it. Also it moves a piece of code in
create_fixed_stream_quirk() to avoid unnecessary allocations in case of error.

[Note for stable backports:
 this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor
 code cleanup in create_fixed_stream_quirk()')]

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358
Reported-by: Ralf Spenneberg <r...@spenneberg.net>
Cc: <sta...@vger.kernel.org> # see the note above
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 sound/usb/quirks.c | 15 ++-
 sound/usb/stream.c |  5 -
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..dda5682 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+   INIT_LIST_HEAD(>list);
if (fp->nr_rates > MAX_NR_RATES) {
kfree(fp);
return -EINVAL;
@@ -164,11 +165,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
fp->rate_table = rate_table;
}

-   stream = (fp->endpoint & USB_DIR_IN)
-   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-   err = snd_usb_add_audio_stream(chip, stream, fp);
-   if (err < 0)
-   goto error;
if (fp->iface != 
get_iface_desc(>altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
err = -EINVAL;
@@ -181,6 +177,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
goto error;
}

+   stream = (fp->endpoint & USB_DIR_IN)
+   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+   err = snd_usb_add_audio_stream(chip, stream, fp);
+   if (err < 0)
+   goto error;
+
fp->protocol = altsd->bInterfaceProtocol;

if (fp->datainterval == 0)
@@ -193,6 +195,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
return 0;

  error:
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
kfree(rate_table);
return err;
@@ -469,6 +472,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+   INIT_LIST_HEAD(>lis

Re: [PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-31 Thread Vladis Dronov
Hello, Takashi, all,

> > Thanks for the report.  But how about a simpler fix like below?
>
> Maybe the one below is more straightforward (and even simpler).
> Let me know if this works enough for you.

1) I would still suggest moving the code in create_fixed_stream_quirk() (marked
as (*)) after "if (altsd->bNumEndpoints < 1)" check. This way no allocations is
done in snd_usb_add_audio_stream() if we go an error path:

(*) stream = (fp->endpoint & USB_DIR_IN) 
(*) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
(*) err = snd_usb_add_audio_stream(chip, stream, fp);
(*) if (err < 0)
(*) goto error;

2) While your fix is indeed simpler, it fixes double-free bug only in
create_fixed_stream_quirk(). create_uaxx_quirk(), for example, still has this
bug:

err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {  <<< in the error path there
kfree(fp);  <<< is a double-free
return err;
}

as any other code where snd_usb_add_audio_stream() is used and *fp is freed in
case of error.

3) Not deleting fp from the fmt_list inside snd_usb_add_audio_stream() in case
of error moves this necessity to a caller, thus breaking the scope. This forces
any caller of snd_usb_add_audio_stream() to fulfill this non-obvious 
requirement.
But I agree that this is simpler and more straightforward. We need just to fix
all the places where snd_usb_add_audio_stream() is called (3 as of now), please,
have a look on the following patch.

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer

-- 8< --
From: Vladis Dronov 
Subject: [PATCH] ALSA: usb-audio: Fix double-free in error paths after 
snd_usb_add_audio_stream() call

create_fixed_stream_quirk(), snd_usb_parse_audio_interface() and
create_uaxx_quirk() functions allocate the audioformat object by themselves
and free it upon error before returning. However, once the object is linked
to a stream, it's freed again in snd_usb_audio_pcm_free(), thus it'll be
double-freed, eventually resulting in a memory corruption.

This patch fixes these failures in the error paths by unlinking the audioformat
object before freeing it. Also it moves a piece of code in
create_fixed_stream_quirk() to avoid unnecessary allocations in case of error.

[Note for stable backports:
 this patch requires the commit 902eb7fd1e4a ('ALSA: usb-audio: Minor
 code cleanup in create_fixed_stream_quirk()')]

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1283358
Reported-by: Ralf Spenneberg 
Cc:  # see the note above
Signed-off-by: Vladis Dronov 
---
 sound/usb/quirks.c | 15 ++-
 sound/usb/stream.c |  5 -
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..dda5682 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -150,6 +150,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
usb_audio_err(chip, "cannot memdup\n");
return -ENOMEM;
}
+   INIT_LIST_HEAD(>list);
if (fp->nr_rates > MAX_NR_RATES) {
kfree(fp);
return -EINVAL;
@@ -164,11 +165,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
fp->rate_table = rate_table;
}

-   stream = (fp->endpoint & USB_DIR_IN)
-   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-   err = snd_usb_add_audio_stream(chip, stream, fp);
-   if (err < 0)
-   goto error;
if (fp->iface != 
get_iface_desc(>altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
err = -EINVAL;
@@ -181,6 +177,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
goto error;
}

+   stream = (fp->endpoint & USB_DIR_IN)
+   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+   err = snd_usb_add_audio_stream(chip, stream, fp);
+   if (err < 0)
+   goto error;
+
fp->protocol = altsd->bInterfaceProtocol;

if (fp->datainterval == 0)
@@ -193,6 +195,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
return 0;

  error:
+   list_del(>list); /* unlink for avoiding double-free */
kfree(fp);
kfree(rate_table);
return err;
@@ -469,6 +472,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
fp->datainterval = 0;
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+   INIT_LIST_HEAD(>list);

switch (fp->maxpacksize) {
case 0x120:
@@ -492,6 +496,7 @@ static int create_uaxx_quirk(struct snd_usb_audi

[PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-30 Thread Vladis Dronov
ied that all the 
callers of snd_usb_add_audio_stream() free *fp in case of error:

$ git grep snd_usb_add_audio_stream
sound/usb/quirks.c: err = snd_usb_add_audio_stream(chip, stream, fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0)
> *goto error;
> * error:
> *kfree(fp);
> *kfree(rate_table);
> *return err;

sound/usb/quirks.c: err = snd_usb_add_audio_stream(chip, stream, fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0) {
> *kfree(fp);
> *return err;
> *}

sound/usb/stream.c: err = snd_usb_add_audio_stream(chip, stream, 
fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0) {
> *kfree(fp->rate_table);
> *kfree(fp->chmap);
> *kfree(fp);
> *return err;
> *}

This means that snd_usb_add_audio_stream() should remove *fp from the 
substream's fmt_list list on the error path, if it was already added. Such 
places are:


> int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct 
> audioformat *fp)
> {
> struct snd_usb_stream *as;
> struct snd_usb_substream *subs;
> struct snd_pcm *pcm;
> int err;
> 
> list_for_each_entry(as, >pcm_list, list) {
> subs = >substream[stream];
> ...
> list_add_tail(>list, >fmt_list); <<< ADDING 
> fp HERE
> subs->num_formats++;
> return 0;  <<< NO 
> ERROR PATH HERE
> }
> }
> 
> /* look for an empty stream */
> list_for_each_entry(as, >pcm_list, list) {
> subs = >substream[stream];
> ...
> snd_usb_init_substream(as, stream, fp);  <<< ADDING fp HERE, 
> IF add_chmap() FAILS
> return add_chmap(as->pcm, stream, subs); <<< fp SHOULD BE 
> REMOVED FROM fmt_list
> }
> 
> /* create a new pcm */
> as = kzalloc(sizeof(*as), GFP_KERNEL);
> err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
> ...
> if (err < 0) {<<< fp IS NOT ADDED YET HERE, SO FINE
> kfree(as);
> return err;
> }
> ...
> snd_usb_init_substream(as, stream, fp); <<< ADDING fp 
> HERE
> ... <<< IF 
> add_chmap() FAILS fp SHOULD
> return add_chmap(pcm, stream, >substream[stream]);  <<< BE 
> REMOVED FROM fmt_list
> }

add_chmap() itself does not add anything to fmt_list list, so we indeed need to 
remove only the single list element from the list. Having all the above in 
mind, the patch follows.

4.3) How to handle possible error paths after successful call to 
snd_usb_add_audio_stream() in create_fixed_stream_quirk() is d
iscussable. Properly it should be like the below, but I believe it is 
overcomplication here would and stick to a simple error_after_add_audio_stream: 
label:

>  error2:
> snd_usb_del_audio_stream(...something...);
>  error:
> kfree(fp);
> kfree(rate_table);
> return err;

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


[PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-30 Thread Vladis Dronov
ied that all the 
callers of snd_usb_add_audio_stream() free *fp in case of error:

$ git grep snd_usb_add_audio_stream
sound/usb/quirks.c: err = snd_usb_add_audio_stream(chip, stream, fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0)
> *goto error;
> * error:
> *kfree(fp);
> *kfree(rate_table);
> *return err;

sound/usb/quirks.c: err = snd_usb_add_audio_stream(chip, stream, fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0) {
> *kfree(fp);
> *return err;
> *}

sound/usb/stream.c: err = snd_usb_add_audio_stream(chip, stream, 
fp);

> *err = snd_usb_add_audio_stream(chip, stream, fp);
> *if (err < 0) {
> *kfree(fp->rate_table);
> *kfree(fp->chmap);
> *kfree(fp);
> *return err;
> *}

This means that snd_usb_add_audio_stream() should remove *fp from the 
substream's fmt_list list on the error path, if it was already added. Such 
places are:


> int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, struct 
> audioformat *fp)
> {
> struct snd_usb_stream *as;
> struct snd_usb_substream *subs;
> struct snd_pcm *pcm;
> int err;
> 
> list_for_each_entry(as, >pcm_list, list) {
> subs = >substream[stream];
> ...
> list_add_tail(>list, >fmt_list); <<< ADDING 
> fp HERE
> subs->num_formats++;
> return 0;  <<< NO 
> ERROR PATH HERE
> }
> }
> 
> /* look for an empty stream */
> list_for_each_entry(as, >pcm_list, list) {
> subs = >substream[stream];
> ...
> snd_usb_init_substream(as, stream, fp);  <<< ADDING fp HERE, 
> IF add_chmap() FAILS
> return add_chmap(as->pcm, stream, subs); <<< fp SHOULD BE 
> REMOVED FROM fmt_list
> }
> 
> /* create a new pcm */
> as = kzalloc(sizeof(*as), GFP_KERNEL);
> err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
> ...
> if (err < 0) {<<< fp IS NOT ADDED YET HERE, SO FINE
> kfree(as);
> return err;
> }
> ...
> snd_usb_init_substream(as, stream, fp); <<< ADDING fp 
> HERE
> ... <<< IF 
> add_chmap() FAILS fp SHOULD
> return add_chmap(pcm, stream, >substream[stream]);  <<< BE 
> REMOVED FROM fmt_list
> }

add_chmap() itself does not add anything to fmt_list list, so we indeed need to 
remove only the single list element from the list. Having all the above in 
mind, the patch follows.

4.3) How to handle possible error paths after successful call to 
snd_usb_add_audio_stream() in create_fixed_stream_quirk() is d
iscussable. Properly it should be like the below, but I believe it is 
overcomplication here would and stick to a simple error_after_add_audio_stream: 
label:

>  error2:
> snd_usb_del_audio_stream(...something...);
>  error:
> kfree(fp);
> kfree(rate_table);
> return err;

Best regards,
Vladis Dronov | Red Hat, Inc. | Product Security Engineer


[PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-30 Thread Vladis Dronov
There is a double-free bug in [snd-usb-audio] module due to alloc/free logic
flaw in snd_usb_add_audio_stream() function. This leads to kernel structures
corruption and panic. Fix the code flow and alloc/free logic so there is no
double-free.

The detailed analysis: https://bugzilla.redhat.com/show_bug.cgi?id=1283358

Reported-by: Ralf Spenneberg <r...@spenneberg.net>
Signed-off-by: Vladis Dronov <vdro...@redhat.com>
---
 sound/usb/quirks.c | 17 -
 sound/usb/stream.c | 10 --
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..1d41b47 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -164,11 +164,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
fp->rate_table = rate_table;
}

-   stream = (fp->endpoint & USB_DIR_IN)
-   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-   err = snd_usb_add_audio_stream(chip, stream, fp);
-   if (err < 0)
-   goto error;
if (fp->iface != 
get_iface_desc(>altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
err = -EINVAL;
@@ -181,6 +176,17 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
goto error;
}

+   stream = (fp->endpoint & USB_DIR_IN)
+   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+   err = snd_usb_add_audio_stream(chip, stream, fp);
+   if (err < 0)
+   goto error;
+
+   /* From this point error paths should jump to
+* error_after_add_audio_stream: not to error: as fp
+* and rate_table will be freed on stream removal
+*/
+
fp->protocol = altsd->bInterfaceProtocol;

if (fp->datainterval == 0)
@@ -195,6 +201,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
  error:
kfree(fp);
kfree(rate_table);
+ error_after_add_audio_stream:
return err;
 }

diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 51258a1..f8ed8b49 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -349,7 +349,10 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if (err < 0)
return err;
snd_usb_init_substream(as, stream, fp);
-   return add_chmap(as->pcm, stream, subs);
+   err = add_chmap(as->pcm, stream, subs);
+   if (err < 0)
+   list_del_init(>list);
+   return err;
}

/* create a new pcm */
@@ -391,7 +394,10 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,

snd_usb_proc_pcm_format_add(as);

-   return add_chmap(pcm, stream, >substream[stream]);
+   err = add_chmap(pcm, stream, >substream[stream]);
+   if (err < 0)
+   list_del_init(>list);
+   return err;
 }

 static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
--
2.5.5



[PATCH] ALSA: usb-audio: Fix double-free in snd_usb_add_audio_stream()

2016-03-30 Thread Vladis Dronov
There is a double-free bug in [snd-usb-audio] module due to alloc/free logic
flaw in snd_usb_add_audio_stream() function. This leads to kernel structures
corruption and panic. Fix the code flow and alloc/free logic so there is no
double-free.

The detailed analysis: https://bugzilla.redhat.com/show_bug.cgi?id=1283358

Reported-by: Ralf Spenneberg 
Signed-off-by: Vladis Dronov 
---
 sound/usb/quirks.c | 17 -
 sound/usb/stream.c | 10 --
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index fb62bce..1d41b47 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -164,11 +164,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
fp->rate_table = rate_table;
}

-   stream = (fp->endpoint & USB_DIR_IN)
-   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-   err = snd_usb_add_audio_stream(chip, stream, fp);
-   if (err < 0)
-   goto error;
if (fp->iface != 
get_iface_desc(>altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
err = -EINVAL;
@@ -181,6 +176,17 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
goto error;
}

+   stream = (fp->endpoint & USB_DIR_IN)
+   ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+   err = snd_usb_add_audio_stream(chip, stream, fp);
+   if (err < 0)
+   goto error;
+
+   /* From this point error paths should jump to
+* error_after_add_audio_stream: not to error: as fp
+* and rate_table will be freed on stream removal
+*/
+
fp->protocol = altsd->bInterfaceProtocol;

if (fp->datainterval == 0)
@@ -195,6 +201,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio 
*chip,
  error:
kfree(fp);
kfree(rate_table);
+ error_after_add_audio_stream:
return err;
 }

diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index 51258a1..f8ed8b49 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -349,7 +349,10 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if (err < 0)
return err;
snd_usb_init_substream(as, stream, fp);
-   return add_chmap(as->pcm, stream, subs);
+   err = add_chmap(as->pcm, stream, subs);
+   if (err < 0)
+   list_del_init(>list);
+   return err;
}

/* create a new pcm */
@@ -391,7 +394,10 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,

snd_usb_proc_pcm_format_add(as);

-   return add_chmap(pcm, stream, >substream[stream]);
+   err = add_chmap(pcm, stream, >substream[stream]);
+   if (err < 0)
+   list_del_init(>list);
+   return err;
 }

 static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
--
2.5.5