Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1232748 > unrolled thread
| Started by | Robin Murphy <robin.murphy@arm.com> |
|---|---|
| First post | 2015-09-25 14:20 +0200 |
| Last post | 2015-09-29 23:30 +0200 |
| Articles | 4 — 3 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
[PATCH 4/4] dma-debug: Allow poisoning nonzero allocations Robin Murphy <robin.murphy@arm.com> - 2015-09-25 14:20 +0200
Re: [PATCH 4/4] dma-debug: Allow poisoning nonzero allocations Russell King - ARM Linux <linux@arm.linux.org.uk> - 2015-09-25 14:50 +0200
Re: [PATCH 4/4] dma-debug: Allow poisoning nonzero allocations Robin Murphy <robin.murphy@arm.com> - 2015-09-25 19:40 +0200
Re: [PATCH 4/4] dma-debug: Allow poisoning nonzero allocations Andrew Morton <akpm@linux-foundation.org> - 2015-09-29 23:30 +0200
| From | Robin Murphy <robin.murphy@arm.com> |
|---|---|
| Date | 2015-09-25 14:20 +0200 |
| Subject | [PATCH 4/4] dma-debug: Allow poisoning nonzero allocations |
| Message-ID | <qcA8O-14l-11@gated-at.bofh.it> |
Since some dma_alloc_coherent implementations return a zeroed buffer
regardless of whether __GFP_ZERO is passed, there exist drivers which
are implicitly dependent on this and pass otherwise uninitialised
buffers to hardware. This can lead to subtle and awkward-to-debug issues
using those drivers on different platforms, where nonzero uninitialised
junk may for instance occasionally look like a valid command which
causes the hardware to start misbehaving. To help with debugging such
issues, add the option to make uninitialised buffers much more obvious.
Signed-off-by: Robin Murphy <robin.murphy@arm.com>
---
include/asm-generic/dma-mapping-common.h | 2 +-
include/linux/dma-debug.h | 6 ++++--
include/linux/poison.h | 3 +++
lib/Kconfig.debug | 10 ++++++++++
lib/dma-debug.c | 6 +++++-
5 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/include/asm-generic/dma-mapping-common.h b/include/asm-generic/dma-mapping-common.h
index b1bc954..0f3e16b 100644
--- a/include/asm-generic/dma-mapping-common.h
+++ b/include/asm-generic/dma-mapping-common.h
@@ -260,7 +260,7 @@ static inline void *dma_alloc_attrs(struct device *dev, size_t size,
return NULL;
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
- debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
+ debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr, flag);
return cpu_addr;
}
diff --git a/include/linux/dma-debug.h b/include/linux/dma-debug.h
index fe8cb61..e5f539d 100644
--- a/include/linux/dma-debug.h
+++ b/include/linux/dma-debug.h
@@ -51,7 +51,8 @@ extern void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
int nelems, int dir);
extern void debug_dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t dma_addr, void *virt);
+ dma_addr_t dma_addr, void *virt,
+ gfp_t flags);
extern void debug_dma_free_coherent(struct device *dev, size_t size,
void *virt, dma_addr_t addr);
@@ -132,7 +133,8 @@ static inline void debug_dma_unmap_sg(struct device *dev,
}
static inline void debug_dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t dma_addr, void *virt)
+ dma_addr_t dma_addr, void *virt,
+ gfp_t flags)
{
}
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 317e16d..174104e 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -73,6 +73,9 @@
#define MUTEX_DEBUG_INIT 0x11
#define MUTEX_DEBUG_FREE 0x22
+/********** lib/dma_debug.c **********/
+#define DMA_ALLOC_POISON 0xee
+
/********** lib/flex_array.c **********/
#define FLEX_ARRAY_FREE 0x6c /* for use-after-free poisoning */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ab76b99..f2da7a1 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1752,6 +1752,16 @@ config DMA_API_DEBUG
If unsure, say N.
+config DMA_API_DEBUG_POISON
+ bool "Poison coherent DMA buffers"
+ depends on DMA_API_DEBUG && EXPERT
+ help
+ Poison DMA buffers returned by dma_alloc_coherent unless __GFP_ZERO
+ is explicitly specified, to catch drivers depending on zeroed buffers
+ without passing the correct flags.
+
+ Only say Y if you're prepared for almost everything to break.
+
config TEST_LKM
tristate "Test module loading with 'hello world' module"
default n
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 908fb35..40514ed 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -30,6 +30,7 @@
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/list.h>
+#include <linux/poison.h>
#include <linux/slab.h>
#include <asm/sections.h>
@@ -1447,7 +1448,7 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
EXPORT_SYMBOL(debug_dma_unmap_sg);
void debug_dma_alloc_coherent(struct device *dev, size_t size,
- dma_addr_t dma_addr, void *virt)
+ dma_addr_t dma_addr, void *virt, gfp_t flags)
{
struct dma_debug_entry *entry;
@@ -1457,6 +1458,9 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
if (unlikely(virt == NULL))
return;
+ if (IS_ENABLED(CONFIG_DMA_API_DEBUG_POISON) && !(flags & __GFP_ZERO))
+ memset(virt, DMA_ALLOC_POISON, size);
+
entry = dma_entry_alloc();
if (!entry)
return;
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Russell King - ARM Linux <linux@arm.linux.org.uk> |
|---|---|
| Date | 2015-09-25 14:50 +0200 |
| Message-ID | <qcABQ-1BX-5@gated-at.bofh.it> |
| In reply to | #1232748 |
On Fri, Sep 25, 2015 at 01:15:46PM +0100, Robin Murphy wrote:
> Since some dma_alloc_coherent implementations return a zeroed buffer
> regardless of whether __GFP_ZERO is passed, there exist drivers which
> are implicitly dependent on this and pass otherwise uninitialised
> buffers to hardware. This can lead to subtle and awkward-to-debug issues
> using those drivers on different platforms, where nonzero uninitialised
> junk may for instance occasionally look like a valid command which
> causes the hardware to start misbehaving. To help with debugging such
> issues, add the option to make uninitialised buffers much more obvious.
The reason people started to do this is to stop a security leak in the
ALSA code: ALSA allocates the ring buffer with dma_alloc_coherent()
which used to grab pages and return them uninitialised. These pages
could contain anything - including the contents of /etc/shadow, or
your bank details.
ALSA then lets userspace mmap() that memory, which means any user process
which has access to the sound devices can read data leaked from kernel
memory.
I think I did bring it up at the time I found it, and decided that the
safest thing to do was to always return an initialised buffer - short of
constantly auditing every dma_alloc_coherent() user which also mmap()s
the buffer into userspace, I couldn't convince myself that it was safe
to avoid initialising the buffer.
I don't know whether the original problem still exists in ALSA or not,
but I do know that there are dma_alloc_coherent() implementations out
there which do not initialise prior to returning memory.
> diff --git a/lib/dma-debug.c b/lib/dma-debug.c
> index 908fb35..40514ed 100644
> --- a/lib/dma-debug.c
> +++ b/lib/dma-debug.c
> @@ -30,6 +30,7 @@
> #include <linux/sched.h>
> #include <linux/ctype.h>
> #include <linux/list.h>
> +#include <linux/poison.h>
> #include <linux/slab.h>
>
> #include <asm/sections.h>
> @@ -1447,7 +1448,7 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
> EXPORT_SYMBOL(debug_dma_unmap_sg);
>
> void debug_dma_alloc_coherent(struct device *dev, size_t size,
> - dma_addr_t dma_addr, void *virt)
> + dma_addr_t dma_addr, void *virt, gfp_t flags)
> {
> struct dma_debug_entry *entry;
>
> @@ -1457,6 +1458,9 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> if (unlikely(virt == NULL))
> return;
>
> + if (IS_ENABLED(CONFIG_DMA_API_DEBUG_POISON) && !(flags & __GFP_ZERO))
> + memset(virt, DMA_ALLOC_POISON, size);
> +
This is likely to be slow in the case of non-cached memory and large
allocations. The config option should come with a warning.
--
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Robin Murphy <robin.murphy@arm.com> |
|---|---|
| Date | 2015-09-25 19:40 +0200 |
| Message-ID | <qcF8u-8dM-13@gated-at.bofh.it> |
| In reply to | #1232766 |
Hi Russell,
On 25/09/15 13:44, Russell King - ARM Linux wrote:
> On Fri, Sep 25, 2015 at 01:15:46PM +0100, Robin Murphy wrote:
>> Since some dma_alloc_coherent implementations return a zeroed buffer
>> regardless of whether __GFP_ZERO is passed, there exist drivers which
>> are implicitly dependent on this and pass otherwise uninitialised
>> buffers to hardware. This can lead to subtle and awkward-to-debug issues
>> using those drivers on different platforms, where nonzero uninitialised
>> junk may for instance occasionally look like a valid command which
>> causes the hardware to start misbehaving. To help with debugging such
>> issues, add the option to make uninitialised buffers much more obvious.
>
> The reason people started to do this is to stop a security leak in the
> ALSA code: ALSA allocates the ring buffer with dma_alloc_coherent()
> which used to grab pages and return them uninitialised. These pages
> could contain anything - including the contents of /etc/shadow, or
> your bank details.
>
> ALSA then lets userspace mmap() that memory, which means any user process
> which has access to the sound devices can read data leaked from kernel
> memory.
>
> I think I did bring it up at the time I found it, and decided that the
> safest thing to do was to always return an initialised buffer - short of
> constantly auditing every dma_alloc_coherent() user which also mmap()s
> the buffer into userspace, I couldn't convince myself that it was safe
> to avoid initialising the buffer.
>
> I don't know whether the original problem still exists in ALSA or not,
> but I do know that there are dma_alloc_coherent() implementations out
> there which do not initialise prior to returning memory.
Indeed, I think we've discussed this before, and I don't imagine we'll
be changing the actual behaviour of the existing allocators any time soon.
[ I still don't see that as an excuse for callers not to be fixed,
though - anyone allocating something that may be exposed to userspace
has a responsibility to initialise it appropriately. After all, the DMA
API is just one source, what do we do if such a careless subsystem got
some uninitialised pages of leftover sensitive data from, say,
alloc_pages() instead? ]
That's a bit of a separate issue though. If a driver itself _needs_ a
zeroed buffer but doesn't specifically request one, or doesn't get one
even if it did, then that's just a regular bug, and it's what this patch
is intended to help weed out. We've no need for a special poison value
for data protection in the general case; zero is just fine for that.
>> diff --git a/lib/dma-debug.c b/lib/dma-debug.c
>> index 908fb35..40514ed 100644
>> --- a/lib/dma-debug.c
>> +++ b/lib/dma-debug.c
>> @@ -30,6 +30,7 @@
>> #include <linux/sched.h>
>> #include <linux/ctype.h>
>> #include <linux/list.h>
>> +#include <linux/poison.h>
>> #include <linux/slab.h>
>>
>> #include <asm/sections.h>
>> @@ -1447,7 +1448,7 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
>> EXPORT_SYMBOL(debug_dma_unmap_sg);
>>
>> void debug_dma_alloc_coherent(struct device *dev, size_t size,
>> - dma_addr_t dma_addr, void *virt)
>> + dma_addr_t dma_addr, void *virt, gfp_t flags)
>> {
>> struct dma_debug_entry *entry;
>>
>> @@ -1457,6 +1458,9 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
>> if (unlikely(virt == NULL))
>> return;
>>
>> + if (IS_ENABLED(CONFIG_DMA_API_DEBUG_POISON) && !(flags & __GFP_ZERO))
>> + memset(virt, DMA_ALLOC_POISON, size);
>> +
>
> This is likely to be slow in the case of non-cached memory and large
> allocations. The config option should come with a warning.
It depends on DMA_API_DEBUG, which already has a stern performance
warning, is additionally hidden behind EXPERT, and carries a slightly
flippant yet largely truthful warning that actually using it could break
pretty much every driver in your system; is that not enough?
If I was feeling particularly antagonistic, I'd also point out that as
discussed above you've already taken the hit of a memset(0) and cache
flush that you _didn't_ ask for, and there was no warning on that ;)
The intent is a specific troubleshooting tool - realistically it's
probably only usable at all when restricting DMA debug to a per-driver
basis. My hunch is that nobody's too fussed about the performance of a
driver that doesn't work properly, especially once they've reached the
point of dumping buffers in an attempt to figure out why, when seeing
the presence or not of uniform poison values could be helpful.
(Of course, sometimes you end up debugging the allocator itself - see
commit 7132813c3845 - which was one of the motivating factors for this
patch).
Robin.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andrew Morton <akpm@linux-foundation.org> |
|---|---|
| Date | 2015-09-29 23:30 +0200 |
| Message-ID | <qeaDg-1yp-15@gated-at.bofh.it> |
| In reply to | #1232965 |
On Fri, 25 Sep 2015 18:35:39 +0100 Robin Murphy <robin.murphy@arm.com> wrote: > Hi Russell, > > On 25/09/15 13:44, Russell King - ARM Linux wrote: > > On Fri, Sep 25, 2015 at 01:15:46PM +0100, Robin Murphy wrote: > >> Since some dma_alloc_coherent implementations return a zeroed buffer > >> regardless of whether __GFP_ZERO is passed, there exist drivers which > >> are implicitly dependent on this and pass otherwise uninitialised > >> buffers to hardware. This can lead to subtle and awkward-to-debug issues > >> using those drivers on different platforms, where nonzero uninitialised > >> junk may for instance occasionally look like a valid command which > >> causes the hardware to start misbehaving. To help with debugging such > >> issues, add the option to make uninitialised buffers much more obvious. > > > > The reason people started to do this is to stop a security leak in the > > ALSA code: ALSA allocates the ring buffer with dma_alloc_coherent() > > which used to grab pages and return them uninitialised. These pages > > could contain anything - including the contents of /etc/shadow, or > > your bank details. > > > > ALSA then lets userspace mmap() that memory, which means any user process > > which has access to the sound devices can read data leaked from kernel > > memory. > > > > I think I did bring it up at the time I found it, and decided that the > > safest thing to do was to always return an initialised buffer - short of > > constantly auditing every dma_alloc_coherent() user which also mmap()s > > the buffer into userspace, I couldn't convince myself that it was safe > > to avoid initialising the buffer. > > > > I don't know whether the original problem still exists in ALSA or not, > > but I do know that there are dma_alloc_coherent() implementations out > > there which do not initialise prior to returning memory. > > Indeed, I think we've discussed this before, and I don't imagine we'll > be changing the actual behaviour of the existing allocators any time soon. If I'm understanding things correctly, some allocators zero the memory by default and others do not. And we have an unknown number of drivers which are assuming that the memory is zeroed. Correct? If so, our options are a) audit all callers, find the ones which expect zeroed memory but aren't passing __GFP_ZERO and fix them. b) convert all allocators to zero the memory by default. Obviously, a) is better. How big a job is it? This patch will help the process, if people use it. > >> + if (IS_ENABLED(CONFIG_DMA_API_DEBUG_POISON) && !(flags & __GFP_ZERO)) > >> + memset(virt, DMA_ALLOC_POISON, size); > >> + > > > > This is likely to be slow in the case of non-cached memory and large > > allocations. The config option should come with a warning. > > It depends on DMA_API_DEBUG, which already has a stern performance > warning, is additionally hidden behind EXPERT, and carries a slightly > flippant yet largely truthful warning that actually using it could break > pretty much every driver in your system; is that not enough? It might be helpful to provide a runtime knob as well - having to rebuild&reinstall just to enable/disable this feature is a bit painful. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web