2021-08-27 16:32:31

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 0/5] Enable -Warray-bounds and -Wzero-length-bounds

v3:
- fix typo in treewide conversion (u8 should have been __u8)
- improve changelog for DECLARE_FLEX_ARRAY patch
- add acks/reviews
v2: https://lore.kernel.org/lkml/[email protected]/
v1: https://lore.kernel.org/lkml/[email protected]/

Hi,

In support of the improved buffer overflow detection for memcpy(),
this enables -Warray-bounds and -Wzero-length-bounds globally. Mostly
it involves some struct member tricks with the new DECLARE_FLEX_ARRAY()
macro. Everything else is just replacing stacked 0-element arrays
with actual unions in two related treewide patches. There is one set of
special cases that were fixed separately[1] and are needed as well.

I'm expecting to carry this series with the memcpy() series in my
"overflow" tree. Reviews appreciated! :)

Thanks!

-Kees

[1] https://lore.kernel.org/lkml/[email protected]/

Kees Cook (5):
stddef: Introduce DECLARE_FLEX_ARRAY() helper
treewide: Replace open-coded flex arrays in unions
treewide: Replace 0-element memcpy() destinations with flexible arrays
Makefile: Enable -Warray-bounds
Makefile: Enable -Wzero-length-bounds

Makefile | 2 --
drivers/crypto/chelsio/chcr_crypto.h | 14 +++++----
drivers/net/can/usb/etas_es58x/es581_4.h | 2 +-
drivers/net/can/usb/etas_es58x/es58x_fd.h | 2 +-
drivers/net/wireless/ath/ath10k/bmi.h | 10 +++----
drivers/net/wireless/ath/ath10k/htt.h | 7 +++--
.../net/wireless/intel/iwlegacy/commands.h | 6 ++--
.../net/wireless/intel/iwlwifi/dvm/commands.h | 6 ++--
.../net/wireless/intel/iwlwifi/fw/api/tx.h | 6 ++--
drivers/scsi/aic94xx/aic94xx_sds.c | 6 ++--
drivers/scsi/qla4xxx/ql4_def.h | 4 +--
drivers/staging/rtl8188eu/include/ieee80211.h | 6 ++--
drivers/staging/rtl8712/ieee80211.h | 4 +--
drivers/staging/rtl8723bs/include/ieee80211.h | 6 ++--
fs/hpfs/hpfs.h | 8 ++---
include/linux/filter.h | 6 ++--
include/linux/ieee80211.h | 30 +++++++++----------
include/linux/stddef.h | 13 ++++++++
include/scsi/sas.h | 12 +++++---
include/uapi/linux/dlm_device.h | 4 +--
include/uapi/linux/stddef.h | 16 ++++++++++
include/uapi/rdma/rdma_user_rxe.h | 4 +--
include/uapi/sound/asoc.h | 4 +--
scripts/kernel-doc | 2 ++
24 files changed, 115 insertions(+), 65 deletions(-)

--
2.30.2


2021-08-27 16:33:53

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 1/5] stddef: Introduce DECLARE_FLEX_ARRAY() helper

There are many places where kernel code wants to have several different
typed trailing flexible arrays. This would normally be done with multiple
flexible arrays in a union, but since GCC and Clang don't (on the surface)
allow this, there have been many open-coded workarounds, usually involving
neighboring 0-element arrays at the end of a structure. For example,
instead of something like this:

struct thing {
...
union {
struct type1 foo[];
struct type2 bar[];
};
};

code works around the compiler with:

struct thing {
...
struct type1 foo[0];
struct type2 bar[];
};

Another case is when a flexible array is wanted as the single member
within a struct (which itself is usually in a union). For example, this
would be worked around as:

union many {
...
struct {
struct type3 baz[0];
};
};

These kinds of work-arounds cause problems with size checks against such
zero-element arrays (for example when building with -Warray-bounds and
-Wzero-length-bounds, and with the coming FORTIFY_SOURCE improvements),
so they must all be converted to "real" flexible arrays, avoiding warnings
like this:

fs/hpfs/anode.c: In function 'hpfs_add_sector_to_btree':
fs/hpfs/anode.c:209:27: warning: array subscript 0 is outside the bounds of an interior zero-length array 'struct bplus_internal_node[0]' [-Wzero-length-bounds]
209 | anode->btree.u.internal[0].down = cpu_to_le32(a);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from fs/hpfs/hpfs_fn.h:26,
from fs/hpfs/anode.c:10:
fs/hpfs/hpfs.h:412:32: note: while referencing 'internal'
412 | struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
| ^~~~~~~~

drivers/net/can/usb/etas_es58x/es58x_fd.c: In function 'es58x_fd_tx_can_msg':
drivers/net/can/usb/etas_es58x/es58x_fd.c:360:35: warning: array subscript 65535 is outside the bounds of an interior zero-length array 'u8[0]' {aka 'unsigned char[]'} [-Wzero-length-bounds]
360 | tx_can_msg = (typeof(tx_can_msg))&es58x_fd_urb_cmd->raw_msg[msg_len];
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/net/can/usb/etas_es58x/es58x_core.h:22,
from drivers/net/can/usb/etas_es58x/es58x_fd.c:17:
drivers/net/can/usb/etas_es58x/es58x_fd.h:231:6: note: while referencing 'raw_msg'
231 | u8 raw_msg[0];
| ^~~~~~~

However, it _is_ entirely possible to have one or more flexible arrays
in a struct or union: it just has to be in another struct. And since it
cannot be alone in a struct, such a struct must have at least 1 other
named member -- but that member can be zero sized. Wrap all this nonsense
into the new DECLARE_FLEX_ARRAY() in support of having flexible arrays
in unions (or alone in a struct).

As with struct_group(), since this is needed in UAPI headers as well,
implement the core there, with a non-UAPI wrapper.

Additionally update kernel-doc to understand its existence.

https://github.com/KSPP/linux/issues/137

Cc: Arnd Bergmann <[email protected]>
Cc: "Gustavo A. R. Silva" <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
---
include/linux/stddef.h | 13 +++++++++++++
include/uapi/linux/stddef.h | 16 ++++++++++++++++
scripts/kernel-doc | 2 ++
3 files changed, 31 insertions(+)

diff --git a/include/linux/stddef.h b/include/linux/stddef.h
index 8b103a53b000..ca507bd5f808 100644
--- a/include/linux/stddef.h
+++ b/include/linux/stddef.h
@@ -84,4 +84,17 @@ enum {
#define struct_group_tagged(TAG, NAME, MEMBERS...) \
__struct_group(TAG, NAME, /* no attrs */, MEMBERS)

+/**
+ * DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define DECLARE_FLEX_ARRAY(TYPE, NAME) \
+ __DECLARE_FLEX_ARRAY(TYPE, NAME)
+
#endif
diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h
index 610204f7c275..3021ea25a284 100644
--- a/include/uapi/linux/stddef.h
+++ b/include/uapi/linux/stddef.h
@@ -25,3 +25,19 @@
struct { MEMBERS } ATTRS; \
struct TAG { MEMBERS } ATTRS NAME; \
}
+
+/**
+ * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define __DECLARE_FLEX_ARRAY(TYPE, NAME) \
+ struct { \
+ struct { } __empty_ ## NAME; \
+ TYPE NAME[]; \
+ }
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index d9715efbe0b7..65088b512d14 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -1263,6 +1263,8 @@ sub dump_struct($$) {
$members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos;
# replace DECLARE_KFIFO_PTR
$members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos;
+ # replace DECLARE_FLEX_ARRAY
+ $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos;
my $declaration = $members;

# Split nested struct/union elements as newer ones
--
2.30.2

2021-08-27 16:36:37

by Kees Cook

[permalink] [raw]
Subject: [PATCH v3 5/5] Makefile: Enable -Wzero-length-bounds

With all known internal zero-length accesses fixed, it is possible to
enable -Wzero-length-bounds globally. Since this is included by default
in -Warray-bounds, we just need to stop disabling it.

Cc: "Gustavo A. R. Silva" <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Masahiro Yamada <[email protected]>
Cc: [email protected]
Signed-off-by: Kees Cook <[email protected]>
---
Makefile | 1 -
1 file changed, 1 deletion(-)

diff --git a/Makefile b/Makefile
index 8e7e73a642e2..8e732e875e78 100644
--- a/Makefile
+++ b/Makefile
@@ -994,7 +994,6 @@ KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-disable-warning, stringop-truncation)

# We'll want to enable this eventually, but it's not going away for 5.7 at least
-KBUILD_CFLAGS += $(call cc-disable-warning, zero-length-bounds)
KBUILD_CFLAGS += $(call cc-disable-warning, stringop-overflow)

# Another good warning that we'll want to enable eventually
--
2.30.2

2021-08-30 18:47:22

by Nathan Chancellor

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Enable -Warray-bounds and -Wzero-length-bounds

On Fri, Aug 27, 2021 at 09:30:10AM -0700, Kees Cook wrote:
> v3:
> - fix typo in treewide conversion (u8 should have been __u8)
> - improve changelog for DECLARE_FLEX_ARRAY patch
> - add acks/reviews
> v2: https://lore.kernel.org/lkml/[email protected]/
> v1: https://lore.kernel.org/lkml/[email protected]/
>
> Hi,
>
> In support of the improved buffer overflow detection for memcpy(),
> this enables -Warray-bounds and -Wzero-length-bounds globally. Mostly
> it involves some struct member tricks with the new DECLARE_FLEX_ARRAY()
> macro. Everything else is just replacing stacked 0-element arrays
> with actual unions in two related treewide patches. There is one set of
> special cases that were fixed separately[1] and are needed as well.
>
> I'm expecting to carry this series with the memcpy() series in my
> "overflow" tree. Reviews appreciated! :)

Hi Kees,

I ran this series through my local build tests and uncovered two
warnings in the same file that appear to be unhandled as of
next-20210830. This is from ARCH=powerpc pseries_defconfig with
clang-14, I did not try earlier versions of clang.

arch/powerpc/kernel/signal_32.c:780:2: error: array index 3 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, failed);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/powerpc/kernel/signal_32.c:85:29: note: expanded from macro 'unsafe_put_sigset_t'
#define unsafe_put_sigset_t unsafe_put_compat_sigset
^
./include/linux/compat.h:455:19: note: expanded from macro 'unsafe_put_compat_sigset'
unsafe_put_user(__s->sig[3] >> 32, &__c->sig[7], label); \
^ ~
./arch/powerpc/include/asm/uaccess.h:426:42: note: expanded from macro 'unsafe_put_user'
__put_user_size_goto((__typeof__(*(p)))(x), (p), sizeof(*(p)), e)
^
./arch/powerpc/include/asm/uaccess.h:114:30: note: expanded from macro '__put_user_size_goto'
case 1: __put_user_asm_goto(x, __pus_addr, label, "stb"); break; \
^
./arch/powerpc/include/asm/uaccess.h:89:10: note: expanded from macro '__put_user_asm_goto'
: "r" (x), "m"UPD_CONSTR (*addr) \
^
./include/linux/compiler_types.h:254:42: note: expanded from macro 'asm_volatile_goto'
#define asm_volatile_goto(x...) asm goto(x)
^
./arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
unsigned long sig[_NSIG_WORDS];
^
arch/powerpc/kernel/signal_32.c:1044:3: error: array index 2 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
unsafe_put_sigset_t(&old_ctx->uc_sigmask, &current->blocked, failed);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/powerpc/kernel/signal_32.c:85:29: note: expanded from macro 'unsafe_put_sigset_t'
#define unsafe_put_sigset_t unsafe_put_compat_sigset
^
./include/linux/compat.h:459:19: note: expanded from macro 'unsafe_put_compat_sigset'
unsafe_put_user(__s->sig[2] >> 32, &__c->sig[5], label); \
^ ~
./arch/powerpc/include/asm/uaccess.h:426:42: note: expanded from macro 'unsafe_put_user'
__put_user_size_goto((__typeof__(*(p)))(x), (p), sizeof(*(p)), e)
^
./arch/powerpc/include/asm/uaccess.h:116:30: note: expanded from macro '__put_user_size_goto'
case 4: __put_user_asm_goto(x, __pus_addr, label, "stw"); break; \
^
./arch/powerpc/include/asm/uaccess.h:89:10: note: expanded from macro '__put_user_asm_goto'
: "r" (x), "m"UPD_CONSTR (*addr) \
^
./include/linux/compiler_types.h:254:42: note: expanded from macro 'asm_volatile_goto'
#define asm_volatile_goto(x...) asm goto(x)
^
./arch/powerpc/include/uapi/asm/signal.h:18:2: note: array 'sig' declared here
unsigned long sig[_NSIG_WORDS];
^

Cheers,
Nathan

2021-08-30 20:14:48

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Enable -Warray-bounds and -Wzero-length-bounds

On Mon, Aug 30, 2021 at 11:44:54AM -0700, Nathan Chancellor wrote:
> On Fri, Aug 27, 2021 at 09:30:10AM -0700, Kees Cook wrote:
> > v3:
> > - fix typo in treewide conversion (u8 should have been __u8)
> > - improve changelog for DECLARE_FLEX_ARRAY patch
> > - add acks/reviews
> > v2: https://lore.kernel.org/lkml/[email protected]/
> > v1: https://lore.kernel.org/lkml/[email protected]/
> >
> > Hi,
> >
> > In support of the improved buffer overflow detection for memcpy(),
> > this enables -Warray-bounds and -Wzero-length-bounds globally. Mostly
> > it involves some struct member tricks with the new DECLARE_FLEX_ARRAY()
> > macro. Everything else is just replacing stacked 0-element arrays
> > with actual unions in two related treewide patches. There is one set of
> > special cases that were fixed separately[1] and are needed as well.
> >
> > I'm expecting to carry this series with the memcpy() series in my
> > "overflow" tree. Reviews appreciated! :)
>
> Hi Kees,
>
> I ran this series through my local build tests and uncovered two
> warnings in the same file that appear to be unhandled as of
> next-20210830. This is from ARCH=powerpc pseries_defconfig with
> clang-14, I did not try earlier versions of clang.

Thanks for double-checking!

>
> arch/powerpc/kernel/signal_32.c:780:2: error: array index 3 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
> unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, failed);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> [...]
> arch/powerpc/kernel/signal_32.c:1044:3: error: array index 2 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
> unsafe_put_sigset_t(&old_ctx->uc_sigmask, &current->blocked, failed);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This smells like some kind of casting issue. uc_sigmask has only a single
unsigned long element but unsafe_put_compat_sigset() seems to be doing
stuff with [3], etc. Is it expecting u8? I will keep looking...

--
Kees Cook

2021-08-30 20:17:44

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Enable -Warray-bounds and -Wzero-length-bounds

On Mon, Aug 30, 2021 at 11:44:54AM -0700, Nathan Chancellor wrote:
> arch/powerpc/kernel/signal_32.c:780:2: error: array index 3 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
> unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, failed);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Or is this a Clang DCE failure?

#define unsafe_put_compat_sigset(compat, set, label) do { \
compat_sigset_t __user *__c = compat; \
const sigset_t *__s = set; \
\
switch (_NSIG_WORDS) { \
case 4: \
unsafe_put_user(__s->sig[3] >> 32, &__c->sig[7], label); \
unsafe_put_user(__s->sig[3], &__c->sig[6], label); \
fallthrough; \
case 3: \
unsafe_put_user(__s->sig[2] >> 32, &__c->sig[5], label); \
unsafe_put_user(__s->sig[2], &__c->sig[4], label); \
fallthrough; \
case 2: \
unsafe_put_user(__s->sig[1] >> 32, &__c->sig[3], label); \
unsafe_put_user(__s->sig[1], &__c->sig[2], label); \
fallthrough; \
case 1: \
unsafe_put_user(__s->sig[0] >> 32, &__c->sig[1], label); \
unsafe_put_user(__s->sig[0], &__c->sig[0], label); \
} \
} while (0)

if "set" has only 1 element, then _NSIG_WORDS must be 1. The warnings
are coming from cases 4 and 3. (But why not 2, which would also access
beyond the end?)

--
Kees Cook

2021-08-30 22:36:00

by Nathan Chancellor

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Enable -Warray-bounds and -Wzero-length-bounds

On Mon, Aug 30, 2021 at 01:16:41PM -0700, Kees Cook wrote:
> On Mon, Aug 30, 2021 at 11:44:54AM -0700, Nathan Chancellor wrote:
> > arch/powerpc/kernel/signal_32.c:780:2: error: array index 3 is past the end of the array (which contains 1 element) [-Werror,-Warray-bounds]
> > unsafe_put_sigset_t(&frame->uc.uc_sigmask, oldset, failed);
> > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> Or is this a Clang DCE failure?
>
> #define unsafe_put_compat_sigset(compat, set, label) do { \
> compat_sigset_t __user *__c = compat; \
> const sigset_t *__s = set; \
> \
> switch (_NSIG_WORDS) { \
> case 4: \
> unsafe_put_user(__s->sig[3] >> 32, &__c->sig[7], label); \
> unsafe_put_user(__s->sig[3], &__c->sig[6], label); \
> fallthrough; \
> case 3: \
> unsafe_put_user(__s->sig[2] >> 32, &__c->sig[5], label); \
> unsafe_put_user(__s->sig[2], &__c->sig[4], label); \
> fallthrough; \
> case 2: \
> unsafe_put_user(__s->sig[1] >> 32, &__c->sig[3], label); \
> unsafe_put_user(__s->sig[1], &__c->sig[2], label); \
> fallthrough; \
> case 1: \
> unsafe_put_user(__s->sig[0] >> 32, &__c->sig[1], label); \
> unsafe_put_user(__s->sig[0], &__c->sig[0], label); \
> } \
> } while (0)
>
> if "set" has only 1 element, then _NSIG_WORDS must be 1. The warnings
> are coming from cases 4 and 3. (But why not 2, which would also access
> beyond the end?)

I trimmed the warnings down otherwise it would have been 400 lines long
:) it did warn for the 2 case.

Clang does not like the use of asm goto in unsafe_put_user on powerpc it
seems:

$ cat warray-bounds.c
#define NSIG_WORDS 1

typedef struct {
unsigned long sig[NSIG_WORDS];
} sigset_t;

int handle_rt_signal32_bad(sigset_t *);
int handle_rt_signal32_bad(sigset_t *oldset)
{
switch (NSIG_WORDS) {
case 4:
__asm__ goto("" : : "r"(oldset->sig[3] >> 32) : : failed);
__asm__ goto("" : : "r"(oldset->sig[3]) : : failed);
__attribute__((fallthrough));
case 3:
__asm__ goto("" : : "r"(oldset->sig[2] >> 32) : : failed);
__asm__ goto("" : : "r"(oldset->sig[2]) : : failed);
__attribute__((fallthrough));
case 2:
__asm__ goto("" : : "r"(oldset->sig[1] >> 32) : : failed);
__asm__ goto("" : : "r"(oldset->sig[1]) : : failed);
__attribute__((fallthrough));
case 1:
__asm__ goto("" : : "r"(oldset->sig[0] >> 32) : : failed);
__asm__ goto("" : : "r"(oldset->sig[0]) : : failed);
}

return 0;
failed:
return 1;
}

void normal_array_access(unsigned long);
int handle_rt_signal32_good(sigset_t *);
int handle_rt_signal32_good(sigset_t *oldset)
{
switch (NSIG_WORDS) {
case 4:
normal_array_access(oldset->sig[3] >> 32);
normal_array_access(oldset->sig[3]);
__attribute__((fallthrough));
case 3:
normal_array_access(oldset->sig[2] >> 32);
normal_array_access(oldset->sig[2]);
__attribute__((fallthrough));
case 2:
normal_array_access(oldset->sig[1] >> 32);
normal_array_access(oldset->sig[1]);
__attribute__((fallthrough));
case 1:
normal_array_access(oldset->sig[0] >> 32);
normal_array_access(oldset->sig[0]);
}

return 0;
}

$ clang -fsyntax-only -Weverything warray-bounds.c
warray-bounds.c:12:27: warning: array index 3 is past the end of the array (which contains 1 element) [-Warray-bounds]
__asm__ goto("" : : "r"(oldset->sig[3] >> 32) : : failed);
^ ~
warray-bounds.c:4:2: note: array 'sig' declared here
unsigned long sig[NSIG_WORDS];
^
warray-bounds.c:16:27: warning: array index 2 is past the end of the array (which contains 1 element) [-Warray-bounds]
__asm__ goto("" : : "r"(oldset->sig[2] >> 32) : : failed);
^ ~
warray-bounds.c:4:2: note: array 'sig' declared here
unsigned long sig[NSIG_WORDS];
^
warray-bounds.c:20:27: warning: array index 1 is past the end of the array (which contains 1 element) [-Warray-bounds]
__asm__ goto("" : : "r"(oldset->sig[1] >> 32) : : failed);
^ ~
warray-bounds.c:4:2: note: array 'sig' declared here
unsigned long sig[NSIG_WORDS];
^
3 warnings generated.

$ gcc -fsyntax-only -Wall -Wextra -Wpedantic warray-bounds.c

godbolt link: https://godbolt.org/z/8xYojs1WY

I've reported this on LLVM's bug tracker to see what the clang
developers can do with you on CC:

https://bugs.llvm.org/show_bug.cgi?id=51682

Cheers,
Nathan