Skip to contentSkip to conversation

kernel.h: Skip single-eval logic on literals in min()/max()

PATCH In review Kees Cook 11 participants 51 messages Mar 9, 2018 – Mar 14, 2018

Subsystems (4)
lore
Share & tools
lore

Kees Cook

author
#0 lore raw

PATCH v3 1/1 kernel.h: Skip single-eval logic on literals in min()/max()

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]

Based on an earlier patch from Josh Poimboeuf.

---
v3:
- drop __builtin_types_compatible_p() (Rasmus, Linus) v2:
- fix copy/paste-o max1_/max2_ (ijc)
- clarify "compile-time" constant in comment (Rasmus)
- clean up formatting on min_t()/max_t()

---
 include/linux/kernel.h | 48 ++++++++++++++++++++++++++++++------------------
 1 file changed, 30 insertions(+), 18 deletions(-)
include/linux/kernel.h +30 -18
index 3fd291503576..a0fca4deb3ab 100644
@@ -787,37 +787,55 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
  * strict type-checking.. See the
  * "unnecessary" pointer comparison.
  */
-#define __min(t1, t2, min1, min2, x, y) ({		\
+#define __single_eval_min(t1, t2, min1, min2, x, y) ({	\
 	t1 min1 = (x);					\
 	t2 min2 = (y);					\
 	(void) (&min1 == &min2);			\
 	min1 < min2 ? min1 : min2; })
 
+/*
+ * In the case of compile-time constant values, there is no need to do
+ * the double-evaluation protection, so the raw comparison can be made.
+ * This allows min()/max() to be used in stack array allocations and
+ * avoid the compiler thinking it is a dynamic value leading to an
+ * accidental VLA.
+ */
+#define __min(t1, t2, x, y)						\
+	__builtin_choose_expr(__builtin_constant_p(x) &&		\
+			      __builtin_constant_p(y),			\
+			      (t1)(x) < (t2)(y) ? (t1)(x) : (t2)(y),	\
+			      __single_eval_min(t1, t2,			\
+						__UNIQUE_ID(min1_),	\
+						__UNIQUE_ID(min2_),	\
+						x, y))
+
 /**
  * min - return minimum of two values of the same or compatible types
  * @x: first value
  * @y: second value
  */
-#define min(x, y)					\
-	__min(typeof(x), typeof(y),			\
-	      __UNIQUE_ID(min1_), __UNIQUE_ID(min2_),	\
-	      x, y)
+#define min(x, y)	__min(typeof(x), typeof(y), x, y)
 
-#define __max(t1, t2, max1, max2, x, y) ({		\
+#define __single_eval_max(t1, t2, max1, max2, x, y) ({	\
 	t1 max1 = (x);					\
 	t2 max2 = (y);					\
 	(void) (&max1 == &max2);			\
 	max1 > max2 ? max1 : max2; })
 
+#define __max(t1, t2, x, y)						\
+	__builtin_choose_expr(__builtin_constant_p(x) &&		\
+			      __builtin_constant_p(y),			\
+			      (t1)(x) > (t2)(y) ? (t1)(x) : (t2)(y),	\
+			      __single_eval_max(t1, t2,			\
+						__UNIQUE_ID(max1_),	\
+						__UNIQUE_ID(max2_),	\
+						x, y))
 /**
  * max - return maximum of two values of the same or compatible types
  * @x: first value
  * @y: second value
  */
-#define max(x, y)					\
-	__max(typeof(x), typeof(y),			\
-	      __UNIQUE_ID(max1_), __UNIQUE_ID(max2_),	\
-	      x, y)
+#define max(x, y)	__max(typeof(x), typeof(y), x, y)
 
 /**
  * min3 - return minimum of three values
@@ -869,10 +887,7 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
  * @x: first value
  * @y: second value
  */
-#define min_t(type, x, y)				\
-	__min(type, type,				\
-	      __UNIQUE_ID(min1_), __UNIQUE_ID(min2_),	\
-	      x, y)
+#define min_t(type, x, y)	 __min(type, type, x, y)
 
 /**
  * max_t - return maximum of two values, using the specified type
@@ -880,10 +895,7 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
  * @x: first value
  * @y: second value
  */
-#define max_t(type, x, y)				\
-	__max(type, type,				\
-	      __UNIQUE_ID(min1_), __UNIQUE_ID(min2_),	\
-	      x, y)
+#define max_t(type, x, y)	__max(type, type, x, y)
 
 /**
  * clamp_t - return a value clamped to a given range using a given type
Signature · 6 lines
2.7.4


-- 
Kees Cook
Pixel Security
#1 lore raw
Quoted 5 lines · Kees Cook

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

Ok, looks good.

I just have a couple of questions about applying it.

In particular, if this will help people working on getting rid of VLA's in the short term, I can apply it directly. But if people who are looking at it (anybody else than Kees?) don't much care, then this might be a 4.17 thing or at least "random -mm queue"?

The other unrelated reaction I had to this was that "we're passing those types down very deep, even though nobody _cares_ about them all that much at that deep level".

Honestly, the only case that really cares is the very top level, and the rest could just take the properly cast versions.

For example, "max_t/min_t" really don't care at all, since they - by definition - just take the single specified type.

So I'm wondering if we should just drop the types from __max/__min (and everything they call) entirely, and instead do

    #define __check_type(x,y) ((void)((typeof(x)*)1==(typeof(y)*)1))
    #define min(x,y)   (__check_type(x,y),__min(x,y))
    #define max(x,y)   (__check_type(x,y),__max(x,y))

    #define min_t(t,x,y) __min((t)(x),(t)(y))
    #define max_t(t,x,y) __max((t)(x),(t)(y))

and then __min/__max and friends are much simpler (and can just assume that the type is already fine, and the casting has been done).

This is technically entirely independent of this VLA cleanup thing, but the "passing the types around unnecessarily" just becomes more obvious when there's now another level of macros, _and_ it's a more complex expression too.

Yes, yes, the __single_eval_xyz() functions still end up wanting the types for the declaration of the new single-evaluation variables, but the 'typeof' pattern is the standard pattern, so

#define __single_eval_max(max1, max2, x, y) ({  \
        typeof (x) max1 = (x);                  \
        typeof (y) max2 = (y);                  \
        max1 > max2 ? max1 : max2; })

actually looks more natural to me than passing the two types in as arguments to the macro.

(That form also is amenable to things like "__auto_type" etc simplifications).

Side note: do we *really* need the unique variable names? That's what makes those things _really_ illegible. I thgink it's done just for a sparse warning that we should probably ignore. It's off by default anyway exactly because it doesn't work that well due to nested macro expansions like this.

There is very real value to keeping our odd macros legible, I feel. Even when they are complicated by issues like this, it would be good to keep them as simple as possible.

Comments?

Linus

author
#2 lore raw
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Kees Cook

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

Quoted 6 lines

Ok, looks good.

I just have a couple of questions about applying it.

In particular, if this will help people working on getting rid of VLA's in the short term, I can apply it directly.

AFAIK, all the VLAs effected by max() are fixed with this patch. i.e. no other VLA work I'm aware of depends on this (famous last words).

Quoted 1 line

But if people who are looking at it (anybody else than Kees?)

FWIW, I've seen at least 6 other people helping out now with VLA clean up patches. Whee. :)

Quoted 2 lines

don't much care, then this
might be a 4.17 thing or at least "random -mm queue"?

Andrew has this (v2) queued in -mm for 4.17. I didn't view VLA clean up (or this macro change) as urgent by any means.

Quoted 7 lines
#define __single_eval_max(max1, max2, x, y) ({  \
        typeof (x) max1 = (x);                  \
        typeof (y) max2 = (y);                  \
        max1 > max2 ? max1 : max2; })

actually looks more natural to me than passing the two types in as arguments to the macro.

I (obviously) didn't design this macro originally, but my take on this was that it's safer to keep the type check together with the comparison so that it is never possible for someone to run off and use __single_eval_max() directly and accidentally bypass the type check. While it does look silly from the max_t()/min_t() perspective, it just seems more defensive.

Quoted 1 line

(That form also is amenable to things like "__auto_type" etc simplifications).

Agreed, I think that's the biggest reason to lift the types. However, since we're still not actually doing anything with the types (i.e. this change doesn't weaken the type checking), I would think it would be orthogonal. While it would be nice to resolve differing types sanely, there doesn't appear to be a driving reason to make this change. The example case of max(5, sizeof(thing)) doesn't currently exist in the code and I don't see how making min()/max() _less_ strict would be generically useful (in fact, it has proven useful to have strict type checking).

Quoted 5 lines

Side note: do we *really* need the unique variable names? That's what makes those things _really_ illegible. I thgink it's done just for a sparse warning that we should probably ignore. It's off by default anyway exactly because it doesn't work that well due to nested macro expansions like this.

That I'm not sure about. I'd be fine to remove them; I left them in place because it seemed quite intentional. :)

Quoted 5 lines

There is very real value to keeping our odd macros legible, I feel. Even when they are complicated by issues like this, it would be good to keep them as simple as possible.

Comments?

I'm open to whatever! I'm just glad this gets to kill a handful of "accidental" stack VLAs. :)

-Kees

Signature · 90 lines
Kees Cook
Pixel Security<div class="gmail_extra"><br><div class="gmail_quote">On
Fri, Mar 9, 2018 at 1:10 PM, Linus Torvalds <span dir="ltr">&lt;<a
href="mailto:[email protected]"
target="_blank">[email protected]</a>&gt;</span>
wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On
Fri, Mar 9, 2018 at 12:05 PM, Kees Cook &lt;<a
href="mailto:[email protected]">[email protected]</a>&gt;
wrote:<br>
&gt; When max() is used in stack array size calculations from literal values<br>
&gt; (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler<br>
&gt; thinks this is a dynamic calculation due to the single-eval
logic, which<br>
&gt; is not needed in the literal case. This change removes several
accidental<br>
&gt; stack VLAs from an x86 allmodconfig build:<br>
<br>
</span>Ok, looks good.<br>
<br>
I just have a couple of questions about applying it.<br>
<br>
In particular, if this will help people working on getting rid of<br>
VLA's in the short term, I can apply it directly. But if people who<br>
are looking at it (anybody else than Kees?) don't much care, then this<br>
might be a 4.17 thing or at least "random -mm queue"?<br>
<br>
The other unrelated reaction I had to this was that "we're passing<br>
those types down very deep, even though nobody _cares_ about them all<br>
that much at that deep level".<br>
<br>
Honestly, the only case that really cares is the very top level, and<br>
the rest could just take the properly cast versions.<br>
<br>
For example, "max_t/min_t" really don't care at all, since they - by<br>
definition - just take the single specified type.<br>
<br>
So I'm wondering if we should just drop the types from __max/__min<br>
(and everything they call) entirely, and instead do<br>
<br>
&nbsp; &nbsp; #define __check_type(x,y)
((void)((typeof(x)*)1==(<wbr>typeof(y)*)1))<br>
&nbsp; &nbsp; #define min(x,y)&nbsp; &nbsp;(__check_type(x,y),__min(x,y))<br>
&nbsp; &nbsp; #define max(x,y)&nbsp; &nbsp;(__check_type(x,y),__max(x,y))<br>
<br>
&nbsp; &nbsp; #define min_t(t,x,y) __min((t)(x),(t)(y))<br>
&nbsp; &nbsp; #define max_t(t,x,y) __max((t)(x),(t)(y))<br>
<br>
and then __min/__max and friends are much simpler (and can just assume<br>
that the type is already fine, and the casting has been done).<br>
<br>
This is technically entirely independent of this VLA cleanup thing,<br>
but the "passing the types around unnecessarily" just becomes more<br>
obvious when there's now another level of macros, _and_ it's a more<br>
complex expression too.<br>
<br>
Yes, yes, the __single_eval_xyz() functions still end up wanting the<br>
types for the declaration of the new single-evaluation variables, but<br>
the 'typeof' pattern is the standard pattern, so<br>
<br>
#define __single_eval_max(max1, max2, x, y) ({&nbsp; \<br>
&nbsp; &nbsp; &nbsp; &nbsp; typeof (x) max1 = (x);&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \<br>
&nbsp; &nbsp; &nbsp; &nbsp; typeof (y) max2 = (y);&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \<br>
<span class="">&nbsp; &nbsp; &nbsp; &nbsp; max1 &gt; max2 ? max1 : max2; })<br>
<br>
</span>actually looks more natural to me than passing the two types in as<br>
arguments to the macro.<br>
<br>
(That form also is amenable to things like "__auto_type" etc
simplifications).<br>
<br>
Side note: do we *really* need the unique variable names? That's what<br>
makes those things _really_ illegible. I thgink it's done just for a<br>
sparse warning that we should probably ignore. It's off by default<br>
anyway exactly because it doesn't work that well due to nested macro<br>
expansions like this.<br>
<br>
There is very real value to keeping our odd macros legible, I feel.<br>
Even when they are complicated by issues like this, it would be good<br>
to keep them as simple as possible.<br>
<br>
Comments?<br>
<span class="HOEnZb"><font color="#888888"><br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Linus<br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>--
<br><div class="gmail_signature" data-smartmail="gmail_signature">Kees
Cook<br>Pixel Security</div>
</div>
#3 lore raw
In reply to Linus Torvalds
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Kees Cook

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

Quoted 8 lines

Ok, looks good.

I just have a couple of questions about applying it.

In particular, if this will help people working on getting rid of VLA's in the short term, I can apply it directly. But if people who are looking at it (anybody else than Kees?) don't much care, then this might be a 4.17 thing or at least "random -mm queue"?

It's easy enough to work on the other VLA removals without basing on this, no rush.

thanks,
Tobin.

#4 lore raw
In reply to Linus Torvalds

The amount of replicated defined could also be reduced by passing > or < to a min_max() macro.
So you start off with something like:
#define min(x, y) __min_max(x, <, y)
#define max(x, y) __min_max(x, >, y)
then have:
#define __min_max(x, cond, y) ((x) cond (y) ? (x) : (y))
in all its associated flavours.

David

#5 lore raw
In reply to Kees Cook
Quoted 14 lines · On Fri, 9 Mar 2018 12:05:36 -0800 Kees Cook <[email protected]> wrote:

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
(quoted diff) +0 -6
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]
Quoted 1 line

Based on an earlier patch from Josh Poimboeuf.

v1, v2 and v3 of this patch all fail with gcc-4.4.4:

./include/linux/jiffies.h: In function 'jiffies_delta_to_clock_t':
./include/linux/jiffies.h:444: error: first argument to '__builtin_choose_expr' not a constant

That's with

#define __max(t1, t2, x, y)						\
	__builtin_choose_expr(__builtin_constant_p(x) &&		\
			      __builtin_constant_p(y) &&		\
			      __builtin_types_compatible_p(t1, t2),	\
			      (t1)(x) > (t2)(y) ? (t1)(x) : (t2)(y),	\
			      __single_eval_max(t1, t2,			\
						__UNIQUE_ID(max1_),	\
						__UNIQUE_ID(max2_),	\
						x, y))

/**
* max - return maximum of two values of the same or compatible types
* @x: first value
* @y: second value
*/
#define max(x, y) __max(typeof(x), typeof(y), x, y)

A brief poke failed to reveal a workaround - gcc-4.4.4 doesn't appear to know that __builtin_constant_p(x) is a constant. Or something.

Sigh. Wasn't there some talk about modernizing our toolchain requirements?

#6 lore raw
Quoted 3 lines · Andrew Morton

A brief poke failed to reveal a workaround - gcc-4.4.4 doesn't appear to know that __builtin_constant_p(x) is a constant. Or something.

LOL.

I suspect it might be that it wants to evaluate __builtin_choose_expr() at an earlier stage than it evaluates __builtin_constant_p(), so it's not that it doesn't know that __builtin_constant_p() is a constant, it just might not know it *yet*.

Maybe.

Side note, if it's not that, but just the "complex" expression that has the logical 'and' etc, maybe the code could just use

__builtin_constant_p((x)+(y))

or something.

But yeah:

Quoted 2 lines

Sigh. Wasn't there some talk about modernizing our toolchain requirements?

Maybe it's just time to give up on 4.4. We wanted 4.5 for "asm goto", and once we upgrade to 4.5 I think Arnd said that no distro actually ships it, so we might as well go to 4.6.

So maybe this is just the excuse to finally make that official, if there is no clever workaround any more.

Linus

#7 lore raw
Quoted 12 lines · On Fri, 9 Mar 2018 16:28:51 -0800 Linus Torvalds…
Quoted 3 lines · Andrew Morton

A brief poke failed to reveal a workaround - gcc-4.4.4 doesn't appear to know that __builtin_constant_p(x) is a constant. Or something.

LOL.

I suspect it might be that it wants to evaluate __builtin_choose_expr() at an earlier stage than it evaluates __builtin_constant_p(), so it's not that it doesn't know that __builtin_constant_p() is a constant, it just might not know it *yet*.

Quoted 8 lines

Maybe.

Side note, if it's not that, but just the "complex" expression that has the logical 'and' etc, maybe the code could just use

__builtin_constant_p((x)+(y))

or something.

I'll do a bit more poking at it.

Quoted 11 lines

But yeah:

Quoted 2 lines

Sigh. Wasn't there some talk about modernizing our toolchain requirements?

Maybe it's just time to give up on 4.4. We wanted 4.5 for "asm goto", and once we upgrade to 4.5 I think Arnd said that no distro actually ships it, so we might as well go to 4.6.

So maybe this is just the excuse to finally make that official, if there is no clever workaround any more.

I wonder which gcc versions actually accept Kees's addition.

#8 lore raw
Quoted 2 lines · Andrew Morton

I wonder which gcc versions actually accept Kees's addition.

Note that we already do have this pattern, as seen by:

git grep -2 __builtin_choose_expr | grep -2 __builtin_constant_p

which show drivers/pinctrl/intel/pinctrl-intel.h, so the pattern already exists current kernels and _works_ - it apparently just doesn't work in slightly more complicated cases.

It's one reason why I wondered if simplifying the expression to have just that single __builtin_constant_p() might not end up working..

Linus

author
#9 lore raw
Quoted 3 lines · Linus Torvalds
Quoted 2 lines · Andrew Morton

I wonder which gcc versions actually accept Kees's addition.

Ah, my old nemesis, gcc 4.4.4. *sob*

Quoted 7 lines

Note that we already do have this pattern, as seen by:

git grep -2 __builtin_choose_expr | grep -2 __builtin_constant_p

which show drivers/pinctrl/intel/pinctrl-intel.h, so the pattern already exists current kernels and _works_ - it apparently just doesn't work in slightly more complicated cases.

Fun. Yeah, so all the PIN_GROUP() callers have either a literal or an array name for that argument, so the argument to __builtin_constant_p() isn't complex.

git grep '\bPIN_GROUP\b' | egrep -v '(1|2|3|true|false)\)'

Quoted 2 lines

It's one reason why I wondered if simplifying the expression to have just that single __builtin_constant_p() might not end up working..

Yeah, it seems like it doesn't bail out as "false" for complex expressions given to __builtin_constant_p().

If no magic solution, then which of these?

- go back to my original "multi-eval max only for constants" macro (meh)
- add gcc version checks around this and similarly for -Wvla in the future (eww)
- raise gcc version (yikes)

-Kees

Signature · 30 lines
Kees Cook
Pixel Security<div class="gmail_extra"><br><div class="gmail_quote">On
Fri, Mar 9, 2018 at 4:38 PM, Linus Torvalds <span dir="ltr">&lt;<a
href="mailto:[email protected]"
target="_blank">[email protected]</a>&gt;</span>
wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On
Fri, Mar 9, 2018 at 4:32 PM, Andrew Morton &lt;<a
href="mailto:[email protected]">[email protected]</a>&gt;
wrote:<br>
&gt;<br>
&gt; I wonder which gcc versions actually accept Kees's addition.<br>
<br>
</span>Note that we already do have this pattern, as seen by:<br>
<br>
&nbsp; git grep -2&nbsp; __builtin_choose_expr | grep -2
__builtin_constant_p<br>
<br>
which show drivers/pinctrl/intel/pinctrl-<wbr>intel.h, so the pattern<br>
already exists current kernels and _works_ - it apparently just<br>
doesn't work in slightly more complicated cases.<br>
<br>
It's one reason why I wondered if simplifying the expression to have<br>
just that single __builtin_constant_p() might not end up working..<br>
<span class="HOEnZb"><font color="#888888"><br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Linus<br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>--
<br><div class="gmail_signature" data-smartmail="gmail_signature">Kees
Cook<br>Pixel Security</div>
</div>
author
#10 lore raw
Quoted 4 lines · Kees Cook

--
Kees Cook
Pixel Security<div class="gmail_extra"><br><div class="gmail_quote">On [...]

WTF, gmail just blasted HTML into my explicitly plain-text email?! Apologies...

Signature · 98 lines
Kees Cook
Pixel Security<div class="gmail_extra"><br><div class="gmail_quote">On
Fri, Mar 9, 2018 at 5:30 PM, Kees Cook <span dir="ltr">&lt;<a
href="mailto:[email protected]"
target="_blank">[email protected]</a>&gt;</span>
wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0
.8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On
Fri, Mar 9, 2018 at 4:38 PM, Linus Torvalds<br>
&lt;<a href="mailto:[email protected]">[email protected]</a><wbr>&gt;
wrote:<br>
&gt; On Fri, Mar 9, 2018 at 4:32 PM, Andrew Morton &lt;<a
href="mailto:[email protected]">[email protected]</a>&gt;
wrote:<br>
&gt;&gt;<br>
&gt;&gt; I wonder which gcc versions actually accept Kees's addition.<br>
<br>
</span>Ah, my old nemesis, gcc 4.4.4. *sob*<br>
<span class=""><br>
&gt; Note that we already do have this pattern, as seen by:<br>
&gt;<br>
&gt;&nbsp; &nbsp;git grep -2&nbsp; __builtin_choose_expr | grep -2
__builtin_constant_p<br>
&gt;<br>
&gt; which show drivers/pinctrl/intel/pinctrl-<wbr>intel.h, so the pattern<br>
&gt; already exists current kernels and _works_ - it apparently just<br>
&gt; doesn't work in slightly more complicated cases.<br>
<br>
</span>Fun. Yeah, so all the PIN_GROUP() callers have either a literal or an<br>
array name for that argument, so the argument to<br>
__builtin_constant_p() isn't complex.<br>
<br>
git grep '\bPIN_GROUP\b' | egrep -v '(1|2|3|true|false)\)'<br>
<span class=""><br>
&gt; It's one reason why I wondered if simplifying the expression to have<br>
&gt; just that single __builtin_constant_p() might not end up working..<br>
<br>
</span>Yeah, it seems like it doesn't bail out as "false" for complex<br>
expressions given to __builtin_constant_p().<br>
<br>
If no magic solution, then which of these?<br>
<br>
- go back to my original "multi-eval max only for constants" macro (meh)<br>
- add gcc version checks around this and similarly for -Wvla in the
future (eww)<br>
- raise gcc version (yikes)<br>
<br>
-Kees<br>
<br>
--<br>
Kees Cook<br>
Pixel Security&lt;div class="gmail_extra"&gt;&lt;br&gt;&lt;div
class="gmail_quote"&gt;On<br>
Fri, Mar 9, 2018 at 4:38 PM, Linus Torvalds &lt;span
dir="ltr"&gt;&amp;lt;&lt;a<br>
<span class="">href="mailto:<a
href="mailto:[email protected]">torvalds@linux-<wbr>foundation.org</a>"<br>
target="_blank"&gt;<a
href="mailto:[email protected]">torvalds@<wbr>linux-foundation.org</a>&lt;/a&gt;&amp;gt;&lt;/<wbr>span&gt;<br>
wrote:&lt;br&gt;&lt;blockquote class="gmail_quote" style="margin:0 0 0<br>
</span>.8ex;border-left:1px #ccc solid;padding-left:1ex"&gt;&lt;span
class=""&gt;On<br>
Fri, Mar 9, 2018 at 4:32 PM, Andrew Morton &amp;lt;&lt;a<br>
href="mailto:<a
href="mailto:[email protected]">akpm@linux-<wbr>foundation.org</a>"&gt;<a
href="mailto:[email protected]">akpm@linux-<wbr>foundation.org</a>&lt;/a&gt;&amp;gt;<br>
wrote:&lt;br&gt;<br>
&amp;gt;&lt;br&gt;<br>
&amp;gt; I wonder which gcc versions actually accept Kees's
addition.&lt;br&gt;<br>
&lt;br&gt;<br>
&lt;/span&gt;Note that we already do have this pattern, as seen
by:&lt;br&gt;<br>
&lt;br&gt;<br>
&amp;nbsp; git grep -2&amp;nbsp; __builtin_choose_expr | grep -2<br>
__builtin_constant_p&lt;br&gt;<br>
&lt;br&gt;<br>
which show drivers/pinctrl/intel/pinctrl-<wbr>&lt;wbr&gt;intel.h, so
the pattern&lt;br&gt;<br>
already exists current kernels and _works_ - it apparently just&lt;br&gt;<br>
doesn't work in slightly more complicated cases.&lt;br&gt;<br>
&lt;br&gt;<br>
It's one reason why I wondered if simplifying the expression to
have&lt;br&gt;<br>
just that single __builtin_constant_p() might not end up working..&lt;br&gt;<br>
<div class="HOEnZb"><div class="h5">&lt;span
class="HOEnZb"&gt;&lt;font color="#888888"&gt;&lt;br&gt;<br>
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;
&amp;nbsp; Linus&lt;br&gt;<br>
&lt;/font&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;/<wbr>div&gt;&lt;br&gt;&lt;br
clear="all"&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;--<br>
&lt;br&gt;&lt;div class="gmail_signature"
data-smartmail="gmail_<wbr>signature"&gt;Kees<br>
Cook&lt;br&gt;Pixel Security&lt;/div&gt;<br>
&lt;/div&gt;<br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>--
<br><div class="gmail_signature" data-smartmail="gmail_signature">Kees
Cook<br>Pixel Security</div>
</div>
#11 lore raw
Quoted 2 lines · Kees Cook

WTF, gmail just blasted HTML into my explicitly plain-text email?! Apologies...

There's more now in your email, I think maybe it's triggered by your signature file and some gmail web interface bug. Or it just tries to force quote using html.

There's some html email disease inside google, where some parts of the company seems to think that html email is _good_.

The official gmail Android app is a big pain, int hat it doesn't *have* a plain-text mode, even though it knows how to format the plain-text part of the email.

You might want to be on the lookout for some bad drugs at the office. Because the world would thank you if you took them away from the gmail app people.

Linus

#12 lore raw
In reply to Kees Cook
Quoted 11 lines · On Fri, 9 Mar 2018 17:30:15 -0800 Kees Cook <[email protected]> wrote:
Quoted 2 lines

It's one reason why I wondered if simplifying the expression to have just that single __builtin_constant_p() might not end up working..

Yeah, it seems like it doesn't bail out as "false" for complex expressions given to __builtin_constant_p().

If no magic solution, then which of these?

- go back to my original "multi-eval max only for constants" macro (meh)
- add gcc version checks around this and similarly for -Wvla in the future (eww)
- raise gcc version (yikes)

Replacing the __builtin_choose_expr() with ?: works of course. What will be the runtime effects?

I tried replacing

	__builtin_choose_expr(__builtin_constant_p(x) &&
                              __builtin_constant_p(y),

with

	__builtin_choose_expr(__builtin_constant_p(x + y),

but no success.

I'm not sure what else to try to trick gcc into working.

include/linux/kernel.h renamed from include/linux/kernel.h~kernelh-skip-single-eval-logic-on-literals-in-min-max-v3-fix +9 -14
@@ -804,13 +804,10 @@ static inline void ftrace_dump(enum ftra
  * accidental VLA.
  */
 #define __min(t1, t2, x, y)						\
-	__builtin_choose_expr(__builtin_constant_p(x) &&		\
-			      __builtin_constant_p(y),			\
-			      (t1)(x) < (t2)(y) ? (t1)(x) : (t2)(y),	\
-			      __single_eval_min(t1, t2,			\
-						__UNIQUE_ID(min1_),	\
-						__UNIQUE_ID(min2_),	\
-						x, y))
+	((__builtin_constant_p(x) && __builtin_constant_p(y)) ?		\
+		((t1)(x) < (t2)(y) ? (t1)(x) : (t2)(y)) :		\
+		(__single_eval_min(t1, t2, __UNIQUE_ID(min1_),		\
+				  __UNIQUE_ID(min2_), x, y)))
 
 /**
  * min - return minimum of two values of the same or compatible types
@@ -826,13 +823,11 @@ static inline void ftrace_dump(enum ftra
 	max1 > max2 ? max1 : max2; })
 
 #define __max(t1, t2, x, y)						\
-	__builtin_choose_expr(__builtin_constant_p(x) &&		\
-			      __builtin_constant_p(y),			\
-			      (t1)(x) > (t2)(y) ? (t1)(x) : (t2)(y),	\
-			      __single_eval_max(t1, t2,			\
-						__UNIQUE_ID(max1_),	\
-						__UNIQUE_ID(max2_),	\
-						x, y))
+	((__builtin_constant_p(x) && __builtin_constant_p(y)) ?		\
+		((t1)(x) > (t2)(y) ? (t1)(x) : (t2)(y)) :		\
+		(__single_eval_max(t1, t2, __UNIQUE_ID(max1_),		\
+				  __UNIQUE_ID(max2_), x, y)))
+
 /**
  * max - return maximum of two values of the same or compatible types
  * @x: first value

_

#13 lore raw
Quoted 2 lines · Andrew Morton

Replacing the __builtin_choose_expr() with ?: works of course.

Hmm. That sounds like the right thing to do. We were so myopically staring at the __builtin_choose_expr() problem that we overlooked the obvious solution.

Using __builtin_constant_p() together with a ?: is in fact our common pattern, so that should be fine. The only real reason to use __builtin_choose_expr() is if you want to get the *type* to vary depending on which side you choose, but that's not an issue for min/max.

Quoted 1 line

What will be the runtime effects?

There should be none. Gcc will turn the conditional for the ?: into a constant, and DTRT.

Linus

author
#14 lore raw
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Andrew Morton

Replacing the __builtin_choose_expr() with ?: works of course.

Hmm. That sounds like the right thing to do. We were so myopically staring at the __builtin_choose_expr() problem that we overlooked the obvious solution.

Quoted 5 lines

Using __builtin_constant_p() together with a ?: is in fact our common pattern, so that should be fine. The only real reason to use __builtin_choose_expr() is if you want to get the *type* to vary depending on which side you choose, but that's not an issue for min/max.

This doesn't solve it for -Wvla, unfortunately. That was the point of Josh's original suggestion of __builtin_choose_expr().

Try building with KCFLAGS=-Wval and checking net/ipv6/proc.c:

net/ipv6/proc.c: In function ‘snmp6_seq_show_item’:
net/ipv6/proc.c:198:2: warning: ISO C90 forbids array ‘buff’ whose size can’t be evaluated [-Wvla]
unsigned long buff[SNMP_MIB_MAX];
^~~~~~~~

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#15 lore raw
Quoted 20 lines · On Mon, 12 Mar 2018 21:28:57 -0700 Kees Cook <[email protected]> wrote:
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Andrew Morton

Replacing the __builtin_choose_expr() with ?: works of course.

Hmm. That sounds like the right thing to do. We were so myopically staring at the __builtin_choose_expr() problem that we overlooked the obvious solution.

Quoted 5 lines

Using __builtin_constant_p() together with a ?: is in fact our common pattern, so that should be fine. The only real reason to use __builtin_choose_expr() is if you want to get the *type* to vary depending on which side you choose, but that's not an issue for min/max.

This doesn't solve it for -Wvla, unfortunately. That was the point of Josh's original suggestion of __builtin_choose_expr().

Quoted 7 lines

Try building with KCFLAGS=-Wval and checking net/ipv6/proc.c:

net/ipv6/proc.c: In function ‘snmp6_seq_show_item’:
net/ipv6/proc.c:198:2: warning: ISO C90 forbids array ‘buff’ whose size can’t be evaluated [-Wvla]
unsigned long buff[SNMP_MIB_MAX];
^~~~~~~~

PITA. Didn't we once have a different way of detecting VLAs? Some post-compilation asm parser, iirc.

I suppose the world wouldn't end if we had a gcc version ifdef in kernel.h. We'll get to remove it in, oh, ten years.

author
#16 lore raw
Quoted 30 lines · Andrew Morton
Quoted 20 lines · On Mon, 12 Mar 2018 21:28:57 -0700 Kees Cook <[email protected]> wrote:
Quoted 9 lines · Linus Torvalds
Quoted 2 lines · Andrew Morton

Replacing the __builtin_choose_expr() with ?: works of course.

Hmm. That sounds like the right thing to do. We were so myopically staring at the __builtin_choose_expr() problem that we overlooked the obvious solution.

Quoted 5 lines

Using __builtin_constant_p() together with a ?: is in fact our common pattern, so that should be fine. The only real reason to use __builtin_choose_expr() is if you want to get the *type* to vary depending on which side you choose, but that's not an issue for min/max.

This doesn't solve it for -Wvla, unfortunately. That was the point of Josh's original suggestion of __builtin_choose_expr().

Quoted 7 lines

Try building with KCFLAGS=-Wval and checking net/ipv6/proc.c:

net/ipv6/proc.c: In function ‘snmp6_seq_show_item’:
net/ipv6/proc.c:198:2: warning: ISO C90 forbids array ‘buff’ whose size can’t be evaluated [-Wvla]
unsigned long buff[SNMP_MIB_MAX];
^~~~~~~~

Quoted 5 lines

PITA. Didn't we once have a different way of detecting VLAs? Some post-compilation asm parser, iirc.

I suppose the world wouldn't end if we had a gcc version ifdef in kernel.h. We'll get to remove it in, oh, ten years.

For fixing only 6 VLAs, we don't need all this effort. When it looked like we could get away with just a "better" max(), sure. ;)

I'll send a "const_max()" which will refuse to work on non-constant-values (so it doesn't get accidentally used on variables that could be exposed to double-evaluation), and will work for stack array declarations (to avoid the overly-sensitive -Wvla checks).

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#17 lore raw

From: Kees Cook

Quoted 1 line

Sent: 13 March 2018 22:15

...

Quoted 4 lines

I'll send a "const_max()" which will refuse to work on non-constant-values (so it doesn't get accidentally used on variables that could be exposed to double-evaluation), and will work for stack array declarations (to avoid the overly-sensitive -Wvla checks).

ISTR the definitions were of the form:

	char foo[max(sizeof (struct bah), sizeof (struct baz))];

This doesn't generate a 'foo' with the required alignment. It would be much better to use a union.

David

#18 lore raw
In reply to Andrew Morton
Quoted 16 lines · Andrew Morton
Quoted 14 lines · On Fri, 9 Mar 2018 12:05:36 -0800 Kees Cook <[email protected]> wrote:

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
(quoted diff) +0 -6
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]
Quoted 6 lines
Quoted 1 line

Based on an earlier patch from Josh Poimboeuf.

v1, v2 and v3 of this patch all fail with gcc-4.4.4:

./include/linux/jiffies.h: In function 'jiffies_delta_to_clock_t':
./include/linux/jiffies.h:444: error: first argument to '__builtin_choose_expr' not a constant

I'm seeing that problem with

Quoted 1 line

gcc --version

gcc (SUSE Linux) 4.8.5

in mmotm.

Quoted 24 lines

That's with

#define __max(t1, t2, x, y)						\
	__builtin_choose_expr(__builtin_constant_p(x) &&		\
			      __builtin_constant_p(y) &&		\
			      __builtin_types_compatible_p(t1, t2),	\
			      (t1)(x) > (t2)(y) ? (t1)(x) : (t2)(y),	\
			      __single_eval_max(t1, t2,			\
						__UNIQUE_ID(max1_),	\
						__UNIQUE_ID(max2_),	\
						x, y))

/**
* max - return maximum of two values of the same or compatible types
* @x: first value
* @y: second value
*/
#define max(x, y) __max(typeof(x), typeof(y), x, y)

A brief poke failed to reveal a workaround - gcc-4.4.4 doesn't appear to know that __builtin_constant_p(x) is a constant. Or something.

Sigh. Wasn't there some talk about modernizing our toolchain requirements?

Signature · 1 line
~Randy
#19 lore raw
Quoted 21 lines · Randy Dunlap
Quoted 20 lines · Andrew Morton
Quoted 14 lines · On Fri, 9 Mar 2018 12:05:36 -0800 Kees Cook <[email protected]> wrote:

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
(quoted diff) +0 -6
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]
Quoted 1 line

Based on an earlier patch from Josh Poimboeuf.

v1, v2 and v3 of this patch all fail with gcc-4.4.4:

Quoted 7 lines
Quoted 2 lines

./include/linux/jiffies.h: In function 'jiffies_delta_to_clock_t':
./include/linux/jiffies.h:444: error: first argument to '__builtin_choose_expr' not a constant

I'm seeing that problem with

Quoted 1 line

gcc --version

gcc (SUSE Linux) 4.8.5

Same here, 4.8.5 fails. gcc 5.4.1 seems to work. I compiled a minimal 5.1.0 and it seems to work as well.

Cheers,
Miguel

#20 lore raw
Quoted 26 lines · Miguel Ojeda
Quoted 25 lines · Randy Dunlap
Quoted 22 lines · Andrew Morton
Quoted 14 lines · On Fri, 9 Mar 2018 12:05:36 -0800 Kees Cook <[email protected]> wrote:

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
(quoted diff) +0 -6
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]
Quoted 1 line

Based on an earlier patch from Josh Poimboeuf.

v1, v2 and v3 of this patch all fail with gcc-4.4.4:

./include/linux/jiffies.h: In function 'jiffies_delta_to_clock_t':
./include/linux/jiffies.h:444: error: first argument to '__builtin_choose_expr' not a constant

Quoted 7 lines
Quoted 3 lines

I'm seeing that problem with

Quoted 1 line

gcc --version

gcc (SUSE Linux) 4.8.5

Same here, 4.8.5 fails. gcc 5.4.1 seems to work. I compiled a minimal 5.1.0 and it seems to work as well.

Just compiled 4.9.0 and it seems to work -- so that would be the minimum required.

Sigh...

Some enterprise distros are either already shipping gcc >= 5 or will probably be shipping it soon (e.g. RHEL 8), so how much does it hurt to ask for a newer gcc? Are there many users/companies out there using enterprise distributions' gcc to compile and run the very latest kernels?

Miguel

#21 lore raw
Quoted 11 lines · Miguel Ojeda

Just compiled 4.9.0 and it seems to work -- so that would be the minimum required.

Sigh...

Some enterprise distros are either already shipping gcc >= 5 or will probably be shipping it soon (e.g. RHEL 8), so how much does it hurt to ask for a newer gcc? Are there many users/companies out there using enterprise distributions' gcc to compile and run the very latest kernels?

I wouldn't mind upping the compiler requirements, and we have other reasons to go to 4.6.

But _this_ particular issue doesn't seem worth it to then go even further. Annoying.

Linus

author
#22 lore raw
In reply to Miguel Ojeda
Quoted 26 lines · Miguel Ojeda
Quoted 25 lines · Randy Dunlap
Quoted 22 lines · Andrew Morton
Quoted 14 lines · On Fri, 9 Mar 2018 12:05:36 -0800 Kees Cook <[email protected]> wrote:

When max() is used in stack array size calculations from literal values (e.g. "char foo[max(sizeof(struct1), sizeof(struct2))]", the compiler thinks this is a dynamic calculation due to the single-eval logic, which is not needed in the literal case. This change removes several accidental stack VLAs from an x86 allmodconfig build:

$ diff -u before.txt after.txt | grep ^-
(quoted diff) +0 -6
-drivers/input/touchscreen/cyttsp4_core.c:871:2: warning: ISO C90 forbids variable length array ‘ids’ [-Wvla]
-fs/btrfs/tree-checker.c:344:4: warning: ISO C90 forbids variable length array ‘namebuf’ [-Wvla]
-lib/vsprintf.c:747:2: warning: ISO C90 forbids variable length array ‘sym’ [-Wvla]
-net/ipv4/proc.c:403:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:198:2: warning: ISO C90 forbids variable length array ‘buff’ [-Wvla]
-net/ipv6/proc.c:218:2: warning: ISO C90 forbids variable length array ‘buff64’ [-Wvla]
Quoted 1 line

Based on an earlier patch from Josh Poimboeuf.

v1, v2 and v3 of this patch all fail with gcc-4.4.4:

./include/linux/jiffies.h: In function 'jiffies_delta_to_clock_t':
./include/linux/jiffies.h:444: error: first argument to '__builtin_choose_expr' not a constant

Quoted 6 lines
Quoted 3 lines

I'm seeing that problem with

Quoted 1 line

gcc --version

gcc (SUSE Linux) 4.8.5

Same here, 4.8.5 fails. gcc 5.4.1 seems to work. I compiled a minimal 5.1.0 and it seems to work as well.

And sparse freaks out too:

drivers/net/ethernet/via/via-velocity.c:97:26: sparse: incorrect
type in initializer (different address spaces) @@ expected void
*addr @@ got struct mac_regs [noderef] <avoid *addr @@
drivers/net/ethernet/via/via-velocity.c:97:26: expected void *addr
drivers/net/ethernet/via/via-velocity.c:97:26: got struct
mac_regs [noderef] <asn:2>*mac_regs
drivers/net/ethernet/via/via-velocity.c:100:49: sparse: incorrect
type in argument 2 (different base types) @@ expected restricted
pci_power_t [usertype] state @@ got _t [usertype] state @@

Alright, I'm giving up on fixing max(). I'll go back to STACK_MAX() or some other name for the simple macro. Bleh.

-Kees

Signature · 2 lines
Kees Cook
Pixel Security
#23 lore raw
Quoted 6 lines · Kees Cook

And sparse freaks out too:

drivers/net/ethernet/via/via-velocity.c:97:26: sparse: incorrect
type in initializer (different address spaces) @@ expected void
*addr @@ got struct mac_regs [noderef] <avoid *addr @@

Actually, this seems a valid warning - it's assigning a __iomem pointer to a regular void, and dropping the iomem in the process.

Sparse does *not* like VLA's, so what may be going on is that fixing something VLA-related in your tree just makes sparse (correctly) check something that it had given up on before.

So don't worry about the sparse ones, if they are new. They seem correct.

Linus

#24 lore raw
In reply to Kees Cook
Quoted 3 lines · Kees Cook

Alright, I'm giving up on fixing max(). I'll go back to STACK_MAX() or some other name for the simple macro. Bleh.

Oh, and I'm starting to see the real problem.

It's not that our current "min/max()" are broiken. It's that "-Wvla" is garbage.

Lookie here:

        int array[(1,2)];

results in gcc saying

     warning: ISO C90 forbids variable length array ‘array’ [-Wvla]
       int array[(1,2)];
       ^~~

and that error message - and the name of the flag - is obviously pure garbage.

What is *actually* going on is that ISO C90 requires an array size to be not a constant value, but a constant *expression*. Those are two different things.

A constant expression has little to do with "compile-time constant". It's a more restricted form of it, and has actual syntax requirements. A comma expression is not a constant expression, for example, which was why I tested this.

So "-Wvla" is garbage, with a misleading name, and a misleading warning string. It has nothing to do with "variable length" and whether the compiler can figure it out at build time, and everything to do with a _syntax_ rule.

Linus

#25 lore raw
Quoted 30 lines · Linus Torvalds
Quoted 3 lines · Kees Cook

Alright, I'm giving up on fixing max(). I'll go back to STACK_MAX() or some other name for the simple macro. Bleh.

Oh, and I'm starting to see the real problem.

It's not that our current "min/max()" are broiken. It's that "-Wvla" is garbage.

Lookie here:

        int array[(1,2)];

results in gcc saying

     warning: ISO C90 forbids variable length array ‘array’ [-Wvla]
       int array[(1,2)];
       ^~~

and that error message - and the name of the flag - is obviously pure garbage.

What is *actually* going on is that ISO C90 requires an array size to be not a constant value, but a constant *expression*. Those are two different things.

A constant expression has little to do with "compile-time constant". It's a more restricted form of it, and has actual syntax requirements. A comma expression is not a constant expression, for example, which was why I tested this.

Quoted 4 lines

So "-Wvla" is garbage, with a misleading name, and a misleading warning string. It has nothing to do with "variable length" and whether the compiler can figure it out at build time, and everything to do with a _syntax_ rule.

The warning string is basically the same to the one used for C++, i.e.:

    int size2 = 2;
    constexpr int size3 = 2;

    int array1[(2,2)];
    int array2[(size2, size2)];
    int array3[(size3, size3)];

only warns for array2 with:

warning: ISO C++ forbids variable length array 'array2' [-Wvla]
int array2[(size2, size2)];

So the warning is probably implemented to just trigger whenever VLAs are used but the given standard does not allow them, for all languages. The problem is why the ISO C90 frontend is not giving an error for using invalid syntax for array sizes to begin with?

Miguel

#26 lore raw
Quoted 5 lines · Miguel Ojeda

So the warning is probably implemented to just trigger whenever VLAs are used but the given standard does not allow them, for all languages. The problem is why the ISO C90 frontend is not giving an error for using invalid syntax for array sizes to begin with?

So in *historical* context - when a compiler didn't do variable length arrays at all - the original semantics of C "constant expressions" actually make a ton of sense.

You can basically think of a constant expression as something that can be (historically) handled by the front-end without any real complexity, and no optimization phases - just evaluating a simple parse tree with purely compile-time constants.

So there's a good and perfectly valid reason for why C limits certain expressions to just a very particular subset. It's not just array sizes, it's case statements etc too. And those are very much part of the C standard.

So an error message like

warning: ISO C90 requires array sizes to be constant-expressions

would be technically correct and useful from a portability angle. It tells you when you're doing something non-portable, and should be automatically enabled with "-ansi -pedantic", for example.

So what's misleading is actually the name of the warning and the message, not that it happens. The warning isn't about "variable length", it's literally about the rules for what a "constant-expression" is.

And in C, the expression (2,3) has a constant _value_ (namely 3), but it isn't a constant-expression as specified by the language.

Now, the thing is that once you actually do variable length arrays, those old front-end rules make no sense any more (outside of the "give portability warnings" thing).

Because once you do variable length arrays, you obviously _parse_ everything just fine, and you're doing to evaluate much more complex expressions than some limited constant-expression rule.

And at that point it would make a whole lot more sense to add a *new* warning that basically says "I have to generate code for a variable-sized array", if you actually talk about VLA's.

But that's clearly not what gcc actually did.

So the problem really is that -Wvla doesn't actually warn about VLA's, but about something technically completely different.

And that's why those stupid syntactic issues with min/max matter. It's not whether the end result is a compile-time constant or not, it's about completely different issues, like whether there is a comma-expression in it.

Linus

#27 lore raw
Quoted 39 lines · Linus Torvalds

So in *historical* context - when a compiler didn't do variable length arrays at all - the original semantics of C "constant expressions" actually make a ton of sense.

You can basically think of a constant expression as something that can be (historically) handled by the front-end without any real complexity, and no optimization phases - just evaluating a simple parse tree with purely compile-time constants.

So there's a good and perfectly valid reason for why C limits certain expressions to just a very particular subset. It's not just array sizes, it's case statements etc too. And those are very much part of the C standard.

So an error message like

warning: ISO C90 requires array sizes to be constant-expressions

would be technically correct and useful from a portability angle. It tells you when you're doing something non-portable, and should be automatically enabled with "-ansi -pedantic", for example.

So what's misleading is actually the name of the warning and the message, not that it happens. The warning isn't about "variable length", it's literally about the rules for what a "constant-expression" is.

And in C, the expression (2,3) has a constant _value_ (namely 3), but it isn't a constant-expression as specified by the language.

Now, the thing is that once you actually do variable length arrays, those old front-end rules make no sense any more (outside of the "give portability warnings" thing).

Because once you do variable length arrays, you obviously _parse_ everything just fine, and you're doing to evaluate much more complex expressions than some limited constant-expression rule.

Quoted 9 lines

And at that point it would make a whole lot more sense to add a *new* warning that basically says "I have to generate code for a variable-sized array", if you actually talk about VLA's.

But that's clearly not what gcc actually did.

So the problem really is that -Wvla doesn't actually warn about VLA's, but about something technically completely different.

I *think* I followed your reasoning. For gcc, -Wvla is the "I have to generate code for a variable-sized array" one; but in this case, the array size is the actual issue that you would have liked to be warned about; since people writing:

    int a[(2,3)];

did not really mean to declare a VLA. Therefore, you say warning them about the "warning: ISO C90 requires array sizes to be constant-expressions" (let's call it -Wpedantic-array-sizes) would be more helpful here instead of saying stuff about VLAs.

In my case, I was just expecting gcc to give us both warnings and that's it, instead of trying to be smart and give only the -Wpedantic-array-sizes one (which is the one I was wondering in my previous email about why it was missing). I think it would be clear enough if both warnings are shown are the same time. And it makes sense, since if you write that line in ISO C90 it means there really are 2 things going wrong in the end (fishy syntax while in ISO C90 mode and, due to that, VLA code generated as well), no?

Thanks for taking the time to write about the historical context, by the way!

Miguel

Quoted 6 lines

And that's why those stupid syntactic issues with min/max matter. It's not whether the end result is a compile-time constant or not, it's about completely different issues, like whether there is a comma-expression in it.

Linus

#28 lore raw
In reply to Linus Torvalds
Quoted 16 lines · Linus Torvalds

So an error message like

warning: ISO C90 requires array sizes to be constant-expressions

would be technically correct and useful from a portability angle. It tells you when you're doing something non-portable, and should be automatically enabled with "-ansi -pedantic", for example.

So what's misleading is actually the name of the warning and the message, not that it happens. The warning isn't about "variable length", it's literally about the rules for what a "constant-expression" is.

And in C, the expression (2,3) has a constant _value_ (namely 3), but it isn't a constant-expression as specified by the language.

Quoted 7 lines

Now, the thing is that once you actually do variable length arrays, those old front-end rules make no sense any more (outside of the "give portability warnings" thing).

Because once you do variable length arrays, you obviously _parse_ everything just fine, and you're doing to evaluate much more complex expressions than some limited constant-expression rule.

BTW., while I fully agree with everything you said, it's not entirely correct to claim that if a C compiler can generate VLA code it is necessarily able to parse and evaluate constant array sizes "just fine".

Constant expressions are typically parsed very early on, at the preprocessing stage. They can be used with some preprocessor directives as well, such as '#if' (with some further limitations on their syntax).

If VLA support is implemented in a later stage, and results in heavy-handed code generation that will technically work for constant value expressions as well but results in suboptimal code, then a warning should probably be emitted - and it wouldn't be pedantic.

The existing warning is still very misleading:

warning: ISO C90 forbids variable length array ‘array’ [-Wvla]

... and if my above theory is correct then I think a better warning would be something like:

warning: Array declaration is not a C90 constant expression, resulting in VLA code generation

... and note that in this specific case it's not misleading to talk about VLAs in the warning text, because the array size, even if it's constant value, results in VLA code generation.

I don't know whether GCC has such a limitation, but a quick experiment with GCC 7.2 suggests that a (2,3) array size expression results in a lot more code being generated than with a constant expression.

Thanks,

Ingo

#29 lore raw
Quoted 8 lines · Ingo Molnar

BTW., while I fully agree with everything you said, it's not entirely correct to claim that if a C compiler can generate VLA code it is necessarily able to parse and evaluate constant array sizes "just fine".

Constant expressions are typically parsed very early on, at the preprocessing stage. They can be used with some preprocessor directives as well, such as '#if' (with some further limitations on their syntax).

Yes. But constant simplification and CSE etc is just a very fundamental part of a compiler, and anybody who actually implements VLA's would have to do it anyway.

So no, a message like

warning: Array declaration is not a C90 constant expression,
resulting in VLA code generation

would be moronic. Only some completely mindless broken shit would do "oh, it's not a parse-time constant, so it will be variable". The two just do not follow AT ALL.

So the message might be about _possibly_ resulting in VLA code generation, but honestly, at that point you should just add the warning when you actually generate the code to do the stack allocation.

Because at that point, you know whether it's variable or not.

And trust me, it won't be variable for things like (2,3), or even for our "max()" thing with other odd builtins. Not unless the compiler doesn't really support VLA at all (maybe some bolted-on crazy thing that just turns a VLA at the front-end time into just an alloca), or the user has explicitly asked to disable some fundamental optimization phase.

Linus

maintainer
#30 lore raw

cover tracing: Rewrite the function filter code

Al Viro reviewed the ftrace event filter logic and was horrified. He wrote Tom and I a lengthy private email with a formal proof on how to do it simpler as well as more efficient.

Currently the filter logic creates a binary search tree (with some optimizations), and walks it to get a result.

Say we had the following filter: a && !(!b || c) || d && !e Where each letter is a predicate. A tree would be made to look something like:

             ||
            /  \
          &&    &&
         /  |   | \
        a  !||  d  !e
           /  \
          !b   c

If a == 1, b == 1, c == 1, d == 1, e == 0, then all nodes would be walked.

 || -> && -> a -> && -> !|| -> !b -> !|| -> c -> !|| -> && -> || -> && ->
 d -> && -> !e -> && -> || -> return TRUE!

That's 16 steps across vertices.

Al's method would create a program using an array that denotes the predicate function, when to branch, and a target to branch to if the value is the same as when to branch. Storing the array as:

prog[0] = { a, 0, 2}; // predicate, when_to_branch, target prog[1] = { b, 0, 2};
prog[2] = { c, 0, 4};
prog[3] = { d, 0, 5};
prog[4] = { e, 1, 5};
prog[5] = { NULL, 0, 1}; // TRUE
prog[6] = { NULL, 0, 0}; // FALSE

Where the code to execute the above looks like:

	for (i = 0; prog[i].pred; i++) {
		struct filter_pred *pred = prog[i].pred;
		int match = pred->fn(pred, rec);
		if (match == prog[i].when_to_branch)
			i = prog[i].target;
	}
	return prog[i].target;

Which translates the above program array into:

n0: if (!a) goto n3;
n1: if (!b) goto n3;
n2: if (!c) goto T;
n3: if (!d) goto F;
n4: if (e) goto F;
T: return TRUE;
F: return FALSE;

And the above example only takes 6 steps to return TRUE.

Special thanks goes out to Al for his patience and his time that he spent in educating us in a proper logical parser.

Two patches were added to do some initial clean up. The last patch implements Al's suggestions. I wrote up a very lengthy comment describing what Al taught us in my own words (hopefully I got it right), and the implementation is similar to what Al showed with a few changes (again, hopefully I got that right too). I tested this with lots of different filters and it looks to be holding up.

Jiri,

This affects perf as well. I ran some tests and I believe I got it woking as it does today.

Steven Rostedt (VMware) (3):
tracing: Combine enum and arrays into single macro in filter code
tracing: Clean up and document pred_funcs_##type creation and use
tracing: Rewrite filter logic to be simpler and faster

----

 kernel/trace/trace.h               |   15 +-
 kernel/trace/trace_events_filter.c | 2372 ++++++++++++++++--------------------
 2 files changed, 1083 insertions(+), 1304 deletions(-)
maintainer
#31 lore raw

PATCH 1/3 tracing: Combine enum and arrays into single macro in filter code

From: "Steven Rostedt (VMware)" <[email protected]>

Instead of having a separate enum that is the index into another array, like a string array, make a single macro that combines them into a single list, and then the two can not get out of sync. This makes it easier to add and remove items.

The macro trick is:

#define DOGS \

  C( JACK,     "Jack Russell")		\
  C( ITALIAN,  "Italian Greyhound")	\
  C( GERMAN,   "German Shepherd")

#undef C
#define C(a, b) a

enum { DOGS };

#undef C
#define C(a, b) b

static char dogs[] = { DOGS };

---
 kernel/trace/trace_events_filter.c | 112 ++++++++++++++++---------------------
 1 file changed, 48 insertions(+), 64 deletions(-)
kernel/trace/trace_events_filter.c +48 -64
index c3c6eee1e4df..a2ef393b3bb2 100644
@@ -33,22 +33,26 @@
 	"# Only events with the given fields will be affected.\n"	\
 	"# If no events are modified, an error message will be displayed here"
 
-enum filter_op_ids
-{
-	OP_OR,
-	OP_AND,
-	OP_GLOB,
-	OP_NE,
-	OP_EQ,
-	OP_LT,
-	OP_LE,
-	OP_GT,
-	OP_GE,
-	OP_BAND,
-	OP_NOT,
-	OP_NONE,
-	OP_OPEN_PAREN,
-};
+#define OPS					\
+	C( OP_OR,	"||",		1 ),	\
+	C( OP_AND,	"&&",		2 ),	\
+	C( OP_GLOB,	"~",		4 ),	\
+	C( OP_NE,	"!=",		4 ),	\
+	C( OP_EQ,	"==",		4 ),	\
+	C( OP_LT,	"<",		5 ),	\
+	C( OP_LE,	"<=",		5 ),	\
+	C( OP_GT,	">",		5 ),	\
+	C( OP_GE,	">=",		5 ),	\
+	C( OP_BAND,	"&",		6 ),	\
+	C( OP_NOT,	"!",		6 ),	\
+	C( OP_NONE,	"OP_NONE",	0 ),	\
+	C( OP_OPEN_PAREN, "(",		0 ),	\
+	C( OP_MAX,	NULL,		0 )
+
+#undef C
+#define C(a, b, c)	a
+
+enum filter_op_ids { OPS };
 
 struct filter_op {
 	int id;
@@ -56,56 +60,36 @@ struct filter_op {
 	int precedence;
 };
 
-/* Order must be the same as enum filter_op_ids above */
-static struct filter_op filter_ops[] = {
-	{ OP_OR,	"||",		1 },
-	{ OP_AND,	"&&",		2 },
-	{ OP_GLOB,	"~",		4 },
-	{ OP_NE,	"!=",		4 },
-	{ OP_EQ,	"==",		4 },
-	{ OP_LT,	"<",		5 },
-	{ OP_LE,	"<=",		5 },
-	{ OP_GT,	">",		5 },
-	{ OP_GE,	">=",		5 },
-	{ OP_BAND,	"&",		6 },
-	{ OP_NOT,	"!",		6 },
-	{ OP_NONE,	"OP_NONE",	0 },
-	{ OP_OPEN_PAREN, "(",		0 },
-};
+#undef C
+#define C(a, b, c)	{ a, b, c }
 
-enum {
-	FILT_ERR_NONE,
-	FILT_ERR_INVALID_OP,
-	FILT_ERR_UNBALANCED_PAREN,
-	FILT_ERR_TOO_MANY_OPERANDS,
-	FILT_ERR_OPERAND_TOO_LONG,
-	FILT_ERR_FIELD_NOT_FOUND,
-	FILT_ERR_ILLEGAL_FIELD_OP,
-	FILT_ERR_ILLEGAL_INTVAL,
-	FILT_ERR_BAD_SUBSYS_FILTER,
-	FILT_ERR_TOO_MANY_PREDS,
-	FILT_ERR_MISSING_FIELD,
-	FILT_ERR_INVALID_FILTER,
-	FILT_ERR_IP_FIELD_ONLY,
-	FILT_ERR_ILLEGAL_NOT_OP,
-};
+static struct filter_op filter_ops[] = { OPS };
 
-static char *err_text[] = {
-	"No error",
-	"Invalid operator",
-	"Unbalanced parens",
-	"Too many operands",
-	"Operand too long",
-	"Field not found",
-	"Illegal operation for field type",
-	"Illegal integer value",
-	"Couldn't find or set field in one of a subsystem's events",
-	"Too many terms in predicate expression",
-	"Missing field name and/or value",
-	"Meaningless filter expression",
-	"Only 'ip' field is supported for function trace",
-	"Illegal use of '!'",
-};
+#define ERRORS								\
+	C( NONE,	 	"No error"),				\
+	C( INVALID_OP,		"Invalid operator"),			\
+	C( UNBALANCED_PAREN,	"Unbalanced parens"),			\
+	C( TOO_MANY_OPERANDS,	"Too many operands"),			\
+	C( OPERAND_TOO_LONG,	"Operand too long"),			\
+	C( FIELD_NOT_FOUND,	"Field not found"),			\
+	C( ILLEGAL_FIELD_OP,	"Illegal operation for field type"),	\
+	C( ILLEGAL_INTVAL,	"Illegal integer value"),		\
+	C( BAD_SUBSYS_FILTER,	"Couldn't find or set field in one of a subsystem's events"), \
+	C( TOO_MANY_PREDS,	"Too many terms in predicate expression"), \
+	C( MISSING_FIELD,	"Missing field name and/or value"),	\
+	C( INVALID_FILTER,	"Meaningless filter expression"),	\
+	C( IP_FIELD_ONLY,	"Only 'ip' field is supported for function trace"), \
+	C( ILLEGAL_NOT_OP,	"Illegal use of '!'"),
+
+#undef C
+#define C(a, b)		FILT_ERR_##a
+
+enum { ERRORS };
+
+#undef C
+#define C(a, b)		b
+
+static char *err_text[] = { ERRORS };
 
 struct opstack_op {
 	enum filter_op_ids op;
Signature · 1 line
2.15.1
maintainer Reviewed-by on v1 1/3
#32 lore raw

Re: [PATCH 1/3] tracing: Combine enum and arrays into single macro in filter code

Quoted 17 lines · On Fri, 09 Mar 2018 21:34:43 -0500 Steven Rostedt <[email protected]> wrote:

From: "Steven Rostedt (VMware)" <[email protected]>

Instead of having a separate enum that is the index into another array, like a string array, make a single macro that combines them into a single list, and then the two can not get out of sync. This makes it easier to add and remove items.

The macro trick is:

#define DOGS \

  C( JACK,     "Jack Russell")		\
  C( ITALIAN,  "Italian Greyhound")	\
  C( GERMAN,   "German Shepherd")

#undef C
#define C(a, b) a

Quoted 6 lines

enum { DOGS };

#undef C
#define C(a, b) b

static char dogs[] = { DOGS };

Looks good to me, and nice idea :)

Thanks,

Quoted 142 lines
---
 kernel/trace/trace_events_filter.c | 112 ++++++++++++++++---------------------
 1 file changed, 48 insertions(+), 64 deletions(-)
kernel/trace/trace_events_filter.c +48 -64
index c3c6eee1e4df..a2ef393b3bb2 100644
@@ -33,22 +33,26 @@
 	"# Only events with the given fields will be affected.\n"	\
 	"# If no events are modified, an error message will be displayed here"
 
-enum filter_op_ids
-{
-	OP_OR,
-	OP_AND,
-	OP_GLOB,
-	OP_NE,
-	OP_EQ,
-	OP_LT,
-	OP_LE,
-	OP_GT,
-	OP_GE,
-	OP_BAND,
-	OP_NOT,
-	OP_NONE,
-	OP_OPEN_PAREN,
-};
+#define OPS					\
+	C( OP_OR,	"||",		1 ),	\
+	C( OP_AND,	"&&",		2 ),	\
+	C( OP_GLOB,	"~",		4 ),	\
+	C( OP_NE,	"!=",		4 ),	\
+	C( OP_EQ,	"==",		4 ),	\
+	C( OP_LT,	"<",		5 ),	\
+	C( OP_LE,	"<=",		5 ),	\
+	C( OP_GT,	">",		5 ),	\
+	C( OP_GE,	">=",		5 ),	\
+	C( OP_BAND,	"&",		6 ),	\
+	C( OP_NOT,	"!",		6 ),	\
+	C( OP_NONE,	"OP_NONE",	0 ),	\
+	C( OP_OPEN_PAREN, "(",		0 ),	\
+	C( OP_MAX,	NULL,		0 )
+
+#undef C
+#define C(a, b, c)	a
+
+enum filter_op_ids { OPS };
 
 struct filter_op {
 	int id;
@@ -56,56 +60,36 @@ struct filter_op {
 	int precedence;
 };
 
-/* Order must be the same as enum filter_op_ids above */
-static struct filter_op filter_ops[] = {
-	{ OP_OR,	"||",		1 },
-	{ OP_AND,	"&&",		2 },
-	{ OP_GLOB,	"~",		4 },
-	{ OP_NE,	"!=",		4 },
-	{ OP_EQ,	"==",		4 },
-	{ OP_LT,	"<",		5 },
-	{ OP_LE,	"<=",		5 },
-	{ OP_GT,	">",		5 },
-	{ OP_GE,	">=",		5 },
-	{ OP_BAND,	"&",		6 },
-	{ OP_NOT,	"!",		6 },
-	{ OP_NONE,	"OP_NONE",	0 },
-	{ OP_OPEN_PAREN, "(",		0 },
-};
+#undef C
+#define C(a, b, c)	{ a, b, c }
 
-enum {
-	FILT_ERR_NONE,
-	FILT_ERR_INVALID_OP,
-	FILT_ERR_UNBALANCED_PAREN,
-	FILT_ERR_TOO_MANY_OPERANDS,
-	FILT_ERR_OPERAND_TOO_LONG,
-	FILT_ERR_FIELD_NOT_FOUND,
-	FILT_ERR_ILLEGAL_FIELD_OP,
-	FILT_ERR_ILLEGAL_INTVAL,
-	FILT_ERR_BAD_SUBSYS_FILTER,
-	FILT_ERR_TOO_MANY_PREDS,
-	FILT_ERR_MISSING_FIELD,
-	FILT_ERR_INVALID_FILTER,
-	FILT_ERR_IP_FIELD_ONLY,
-	FILT_ERR_ILLEGAL_NOT_OP,
-};
+static struct filter_op filter_ops[] = { OPS };
 
-static char *err_text[] = {
-	"No error",
-	"Invalid operator",
-	"Unbalanced parens",
-	"Too many operands",
-	"Operand too long",
-	"Field not found",
-	"Illegal operation for field type",
-	"Illegal integer value",
-	"Couldn't find or set field in one of a subsystem's events",
-	"Too many terms in predicate expression",
-	"Missing field name and/or value",
-	"Meaningless filter expression",
-	"Only 'ip' field is supported for function trace",
-	"Illegal use of '!'",
-};
+#define ERRORS								\
+	C( NONE,	 	"No error"),				\
+	C( INVALID_OP,		"Invalid operator"),			\
+	C( UNBALANCED_PAREN,	"Unbalanced parens"),			\
+	C( TOO_MANY_OPERANDS,	"Too many operands"),			\
+	C( OPERAND_TOO_LONG,	"Operand too long"),			\
+	C( FIELD_NOT_FOUND,	"Field not found"),			\
+	C( ILLEGAL_FIELD_OP,	"Illegal operation for field type"),	\
+	C( ILLEGAL_INTVAL,	"Illegal integer value"),		\
+	C( BAD_SUBSYS_FILTER,	"Couldn't find or set field in one of a subsystem's events"), \
+	C( TOO_MANY_PREDS,	"Too many terms in predicate expression"), \
+	C( MISSING_FIELD,	"Missing field name and/or value"),	\
+	C( INVALID_FILTER,	"Meaningless filter expression"),	\
+	C( IP_FIELD_ONLY,	"Only 'ip' field is supported for function trace"), \
+	C( ILLEGAL_NOT_OP,	"Illegal use of '!'"),
+
+#undef C
+#define C(a, b)		FILT_ERR_##a
+
+enum { ERRORS };
+
+#undef C
+#define C(a, b)		b
+
+static char *err_text[] = { ERRORS };
 
 struct opstack_op {
 	enum filter_op_ids op;
Signature · 1 line
2.15.1
Signature · 1 line
Masami Hiramatsu <[email protected]>
maintainer
#33 lore raw

PATCH 2/3 tracing: Clean up and document pred_funcs_##type creation and use

In reply to Steven Rostedt

From: "Steven Rostedt (VMware)" <[email protected]>

The pred_funcs_##type arrays consist of five functions that are assigned based on the ops. The array must be in the same order of the ops each function represents. The PRED_FUNC_START macro denotes the op enum that starts the op that maps to the pred_funcs_##type arrays. This is all very subtle and prone to bugs if the code is changed.

Add comments describing how PRED_FUNC_START and pred_funcs_##type array is used, and also a PRED_FUNC_MAX that is the maximum number of functions in the arrays.

Clean up select_comparison_fn() that assigns the predicates to the pred_funcs_##type array function as well as add protection in case an op is passed in that does not map correctly to the array.

---
 kernel/trace/trace_events_filter.c | 46 ++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 14 deletions(-)
kernel/trace/trace_events_filter.c +32 -14
index a2ef393b3bb2..9d383f4383dc 100644
@@ -65,6 +65,13 @@ struct filter_op {
 
 static struct filter_op filter_ops[] = { OPS };
 
+/*
+ * pred functions are OP_LT, OP_LE, OP_GT, OP_GE, and OP_BAND
+ * pred_funcs_##type below must match the order of them above.
+ */
+#define PRED_FUNC_START			OP_LT
+#define PRED_FUNC_MAX			(OP_BAND - PRED_FUNC_START)
+
 #define ERRORS								\
 	C( NONE,	 	"No error"),				\
 	C( INVALID_OP,		"Invalid operator"),			\
@@ -172,8 +179,6 @@ static const filter_pred_fn_t pred_funcs_##type[] = {			\
 	filter_pred_BAND_##type,					\
 };
 
-#define PRED_FUNC_START			OP_LT
-
 #define DEFINE_EQUALITY_PRED(size)					\
 static int filter_pred_##size(struct filter_pred *pred, void *event)	\
 {									\
@@ -946,39 +951,52 @@ static filter_pred_fn_t select_comparison_fn(enum filter_op_ids op,
 					    int field_size, int field_is_signed)
 {
 	filter_pred_fn_t fn = NULL;
+	int pred_func_index = -1;
+
+	switch (op) {
+	case OP_EQ:
+	case OP_NE:
+		break;
+	default:
+		if (WARN_ON_ONCE(op < PRED_FUNC_START))
+			return NULL;
+		pred_func_index = op - PRED_FUNC_START;
+		if (WARN_ON_ONCE(pred_func_index > PRED_FUNC_MAX))
+			return NULL;
+	}
 
 	switch (field_size) {
 	case 8:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_64;
 		else if (field_is_signed)
-			fn = pred_funcs_s64[op - PRED_FUNC_START];
+			fn = pred_funcs_s64[pred_func_index];
 		else
-			fn = pred_funcs_u64[op - PRED_FUNC_START];
+			fn = pred_funcs_u64[pred_func_index];
 		break;
 	case 4:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_32;
 		else if (field_is_signed)
-			fn = pred_funcs_s32[op - PRED_FUNC_START];
+			fn = pred_funcs_s32[pred_func_index];
 		else
-			fn = pred_funcs_u32[op - PRED_FUNC_START];
+			fn = pred_funcs_u32[pred_func_index];
 		break;
 	case 2:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_16;
 		else if (field_is_signed)
-			fn = pred_funcs_s16[op - PRED_FUNC_START];
+			fn = pred_funcs_s16[pred_func_index];
 		else
-			fn = pred_funcs_u16[op - PRED_FUNC_START];
+			fn = pred_funcs_u16[pred_func_index];
 		break;
 	case 1:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_8;
 		else if (field_is_signed)
-			fn = pred_funcs_s8[op - PRED_FUNC_START];
+			fn = pred_funcs_s8[pred_func_index];
 		else
-			fn = pred_funcs_u8[op - PRED_FUNC_START];
+			fn = pred_funcs_u8[pred_func_index];
 		break;
 	}
 
Signature · 1 line
2.15.1
maintainer Reviewed-by on v1 2/3
#34 lore raw

Re: [PATCH 2/3] tracing: Clean up and document pred_funcs_##type creation and use

Quoted 8 lines · On Fri, 09 Mar 2018 21:34:44 -0500 Steven Rostedt <[email protected]> wrote:

From: "Steven Rostedt (VMware)" <[email protected]>

The pred_funcs_##type arrays consist of five functions that are assigned based on the ops. The array must be in the same order of the ops each function represents. The PRED_FUNC_START macro denotes the op enum that starts the op that maps to the pred_funcs_##type arrays. This is all very subtle and prone to bugs if the code is changed.

Quoted 8 lines

Add comments describing how PRED_FUNC_START and pred_funcs_##type array is used, and also a PRED_FUNC_MAX that is the maximum number of functions in the arrays.

Clean up select_comparison_fn() that assigns the predicates to the pred_funcs_##type array function as well as add protection in case an op is passed in that does not map correctly to the array.

This looks good to me.

Thanks,

Quoted 101 lines
---
 kernel/trace/trace_events_filter.c | 46 ++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 14 deletions(-)
kernel/trace/trace_events_filter.c +32 -14
index a2ef393b3bb2..9d383f4383dc 100644
@@ -65,6 +65,13 @@ struct filter_op {
 
 static struct filter_op filter_ops[] = { OPS };
 
+/*
+ * pred functions are OP_LT, OP_LE, OP_GT, OP_GE, and OP_BAND
+ * pred_funcs_##type below must match the order of them above.
+ */
+#define PRED_FUNC_START			OP_LT
+#define PRED_FUNC_MAX			(OP_BAND - PRED_FUNC_START)
+
 #define ERRORS								\
 	C( NONE,	 	"No error"),				\
 	C( INVALID_OP,		"Invalid operator"),			\
@@ -172,8 +179,6 @@ static const filter_pred_fn_t pred_funcs_##type[] = {			\
 	filter_pred_BAND_##type,					\
 };
 
-#define PRED_FUNC_START			OP_LT
-
 #define DEFINE_EQUALITY_PRED(size)					\
 static int filter_pred_##size(struct filter_pred *pred, void *event)	\
 {									\
@@ -946,39 +951,52 @@ static filter_pred_fn_t select_comparison_fn(enum filter_op_ids op,
 					    int field_size, int field_is_signed)
 {
 	filter_pred_fn_t fn = NULL;
+	int pred_func_index = -1;
+
+	switch (op) {
+	case OP_EQ:
+	case OP_NE:
+		break;
+	default:
+		if (WARN_ON_ONCE(op < PRED_FUNC_START))
+			return NULL;
+		pred_func_index = op - PRED_FUNC_START;
+		if (WARN_ON_ONCE(pred_func_index > PRED_FUNC_MAX))
+			return NULL;
+	}
 
 	switch (field_size) {
 	case 8:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_64;
 		else if (field_is_signed)
-			fn = pred_funcs_s64[op - PRED_FUNC_START];
+			fn = pred_funcs_s64[pred_func_index];
 		else
-			fn = pred_funcs_u64[op - PRED_FUNC_START];
+			fn = pred_funcs_u64[pred_func_index];
 		break;
 	case 4:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_32;
 		else if (field_is_signed)
-			fn = pred_funcs_s32[op - PRED_FUNC_START];
+			fn = pred_funcs_s32[pred_func_index];
 		else
-			fn = pred_funcs_u32[op - PRED_FUNC_START];
+			fn = pred_funcs_u32[pred_func_index];
 		break;
 	case 2:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_16;
 		else if (field_is_signed)
-			fn = pred_funcs_s16[op - PRED_FUNC_START];
+			fn = pred_funcs_s16[pred_func_index];
 		else
-			fn = pred_funcs_u16[op - PRED_FUNC_START];
+			fn = pred_funcs_u16[pred_func_index];
 		break;
 	case 1:
-		if (op == OP_EQ || op == OP_NE)
+		if (pred_func_index < 0)
 			fn = filter_pred_8;
 		else if (field_is_signed)
-			fn = pred_funcs_s8[op - PRED_FUNC_START];
+			fn = pred_funcs_s8[pred_func_index];
 		else
-			fn = pred_funcs_u8[op - PRED_FUNC_START];
+			fn = pred_funcs_u8[pred_func_index];
 		break;
 	}
 
Signature · 1 line
2.15.1
Signature · 1 line
Masami Hiramatsu <[email protected]>
maintainer
#35 lore raw

PATCH 3/3 tracing: Rewrite filter logic to be simpler and faster

In reply to Steven Rostedt

From: "Steven Rostedt (VMware)" <[email protected]>

Al Viro reviewed the filter logic of ftrace trace events and found it to be very troubling. It creates a binary tree based on the logic operators and walks it during tracing. He sent myself and Tom Zanussi a long explanation (and formal proof) of how to do the string parsing better and end up with a program array that can be simply iterated to come up with the correct results.

I took his ideas and his pseudo code and rewrote the filter logic based on them. In doing so, I was able to remove a lot of code, and have a much more condensed filter logic in the process. I wrote a very long comment describing the methadology that Al proposed in my own words. For more info on how this works, read the comment above predicate_parse().

---
 kernel/trace/trace.h               |   15 +-
 kernel/trace/trace_events_filter.c | 2308 ++++++++++++++++--------------------
 2 files changed, 1050 insertions(+), 1273 deletions(-)
kernel/trace/trace.h +5 -10
index 9de3e2a2f042..e6f82f74ad65 100644
@@ -1216,12 +1216,11 @@ struct ftrace_event_field {
 	int			is_signed;
 };
 
+struct prog_entry;
+
 struct event_filter {
-	int			n_preds;	/* Number assigned */
-	int			a_preds;	/* allocated */
-	struct filter_pred __rcu	*preds;
-	struct filter_pred __rcu	*root;
-	char				*filter_string;
+	struct prog_entry	*prog;
+	char			*filter_string;
 };
 
 struct event_subsystem {
@@ -1413,12 +1412,8 @@ struct filter_pred {
 	unsigned short		*ops;
 	struct ftrace_event_field *field;
 	int 			offset;
-	int 			not;
+	int			not;
 	int 			op;
-	unsigned short		index;
-	unsigned short		parent;
-	unsigned short		left;
-	unsigned short		right;
 };
 
 static inline bool is_string_field(struct ftrace_event_field *field)
kernel/trace/trace_events_filter.c +1045 -1263

2,689 lines not on this page · raw message

Signature · 1 line
2.15.1
maintainer
#36 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 1 line · On Fri, 09 Mar 2018 21:34:45 -0500 Steven Rostedt <[email protected]> wrote:
 2 files changed, 1050 insertions(+), 1273 deletions(-)

BTW, it's a bit bigger impact than 223 deletions. As I added a lot of comments to explain the logic better. Removing comments and white space from the modifications we have:

649 insertions(+), 1035 deletions(-)

That's 386 lines of code removed.

-- Steve

maintainer
#37 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

I don't know what the hell happened, but claws mail just inserted a ton of people into the Cc (I didn't add you). I noticed it just after I hit send. The added Cc looks like it came from the email right after the email I was replying to "Subject: Re: [PATCH v3] kernel.h: Skip single-eval logic on literals in min()/max()".

Sorry for the spam.

-- Steve

Quoted 9 lines · On Fri, 9 Mar 2018 22:10:19 -0500 Steven Rostedt <[email protected]> wrote:
Quoted 1 line · On Fri, 09 Mar 2018 21:34:45 -0500 Steven Rostedt <[email protected]> wrote:
 2 files changed, 1050 insertions(+), 1273 deletions(-)  

BTW, it's a bit bigger impact than 223 deletions. As I added a lot of comments to explain the logic better. Removing comments and white space from the modifications we have:

Quoted 5 lines

649 insertions(+), 1035 deletions(-)

That's 386 lines of code removed.

-- Steve

'

maintainer
#38 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 1 line · On Fri, 9 Mar 2018 22:15:23 -0500 Steven Rostedt <[email protected]> wrote:

Sorry for the spam.

A little more spam ;-)

I know what happened, as I'm able to recreate it. For those that use claws-mail, be careful.

I clicked on the email I wanted to reply to. Then I must have hit the down arrow key, as it moved the selected email to the next email. I hit "Reply All", and it showed the email I clicked on (as well as the subject), but the Cc list belonged to the email that was selected by the down arrow key.

That's a nasty subtle bug. I think I'll go report this to the claws folks.

-- Steve

maintainer
#39 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

In reply to Steven Rostedt

[ This time replying to the email I expected to ]

Quoted 1 line · On Fri, 09 Mar 2018 21:34:45 -0500 Steven Rostedt <[email protected]> wrote:
 2 files changed, 1050 insertions(+), 1273 deletions(-)  

BTW, it's a bit bigger impact than 223 deletions. As I added a lot of comments to explain the logic better. Removing comments and white space from the modifications we have:

649 insertions(+), 1035 deletions(-)

That's 386 lines of code removed.

-- Steve

#40 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

In reply to Steven Rostedt

On Fri, Mar 09, 2018 at 09:34:45PM -0500, Steven Rostedt wrote:

SNIP

kernel/trace/trace_events_filter.c enum { ERRORS }; · line 168

Quoted 11 lines
kernel/trace/trace_events_filter.c +9 -1
-/* If not of not match is equal to not of not, then it is a match */
+/*
+ * Without going into a formal proof, this explains the method that is used in
+ * parsing the logical expressions.
+ *
+ * For example, if we have: "a && !(!b || (c && g)) || d || e && !f"
+ * The first pass will convert it into the following program:
+ *
+ * n1: r=a;       l1: if (!r) goto l4;
+ * n2: r=b;       l2: if (!r) goto l4;

got stuck in here.. should that be 'goto l5' ?

jirka

Quoted 7 lines

+ * n3: r=c; r=!r; l3: if (r) goto l4;
+ * n4: r=g; r=!r; l4: if (r) goto l5;
+ * n5: r=d; l5: if (r) goto T
+ * n6: r=e; l6: if (!r) goto l7;
+ * n7: r=f; r=!r; l7: if (!r) goto F
+ * T: return TRUE
+ * F: return FALSE

jirka

maintainer
#41 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 5 lines · On Mon, 12 Mar 2018 13:42:46 +0100 Jiri Olsa <[email protected]> wrote:

On Fri, Mar 09, 2018 at 09:34:45PM -0500, Steven Rostedt wrote:

SNIP

Quoted 1 line
Quoted 12 lines

kernel/trace/trace_events_filter.c enum { ERRORS }; · line 168

Quoted 10 lines
kernel/trace/trace_events_filter.c +9 -1
-/* If not of not match is equal to not of not, then it is a match */
+/*
+ * Without going into a formal proof, this explains the method that is used in
+ * parsing the logical expressions.
+ *
+ * For example, if we have: "a && !(!b || (c && g)) || d || e && !f"
+ * The first pass will convert it into the following program:
+ *
+ * n1: r=a;       l1: if (!r) goto l4;
+ * n2: r=b;       l2: if (!r) goto l4;  

got stuck in here.. should that be 'goto l5' ?

Nope, this is correct. In fact, my user space implementation of the code is what generated this output.

!(!b || (c && g)) is the same as (b && (!c || !g)), so we can rewrite the above as:

a && b && (!c || !g) || d || e && !f

Which makes it more obvious why it is goto l4.

And since we have:

n1: r=a; l1: if (!r) goto l4;
n2: r=b; l2: if (!r) goto l4;
n3: r=c; r=!r; l3: if (r) goto l4;
n4: r=g; r=!r; l4: if (r) goto l5;
n5; r=d; l5: if (r) goto T;

If a is true, if (!r) is false so we continue to n2.
If b is false, then if (!r) is true, so we take the branch. Let's see what that does:

l4: if (r) goto l5;

But since we jumped because r is false, then this if statement is guaranteed to be false, and we continue to n5. Then we need to test d.

a && b && ... || d

You can see how that is correct. If a is true and then be is false, then we ignore the rest of the && statement and jump to testing the other side of || which is d.

-- Steve

Quoted 12 lines

jirka

Quoted 7 lines

+ * n3: r=c; r=!r; l3: if (r) goto l4;
+ * n4: r=g; r=!r; l4: if (r) goto l5;
+ * n5: r=d; l5: if (r) goto T
+ * n6: r=e; l6: if (!r) goto l7;
+ * n7: r=f; r=!r; l7: if (!r) goto F
+ * T: return TRUE
+ * F: return FALSE

jirka

#42 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

In reply to Steven Rostedt
Quoted 9 lines · Steven Rostedt

From: "Steven Rostedt (VMware)" <[email protected]>

Al Viro reviewed the filter logic of ftrace trace events and found it to be very troubling. It creates a binary tree based on the logic operators and walks it during tracing. He sent myself and Tom Zanussi a long explanation (and formal proof) of how to do the string parsing better and end up with a program array that can be simply iterated to come up with the correct results.

Quoted 8 lines

I took his ideas and his pseudo code and rewrote the filter logic based on them. In doing so, I was able to remove a lot of code, and have a much more condensed filter logic in the process. I wrote a very long comment describing the methadology that Al proposed in my own words. For more info on how this works, read the comment above predicate_parse().

got it crashed when clearing the filter via 'echo > filter'

jirka

---
ibm-x3650m4-01 login: [ 803.551062] BUG: unable to handle kernel paging request at ffff972acc154c34

[  803.558846] IP: process_preds+0x5e9/0x680
[  803.563318] PGD 251bbf067 P4D 251bbf067 PUD 0 
[  803.568280] Oops: 0000 [#1] SMP PTI
[  803.572172] Modules linked in: intel_rapl sb_edac x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm igb irqbypass crct10dif_pclmul ptp crc32_pclmul ipmi_ssmi_msghandler intel_rapl_perf wmi i2c_i801 mii lpc_ich xfs libcrc32c mgag200 i2c_algo_bit drm_kms_helper ttm crc32c_intel drm megaraid_sas
[  803.618767] CPU: 13 PID: 1297 Comm: bash Not tainted 4.16.0-rc4+ #96
[  803.625857] Hardware name: IBM System x3650 M4 : -[7915E2G]-/00Y7683, BIOS -[VVE124AUS-1.30]- 11/21/2012
[  803.636441] RIP: 0010:process_preds+0x5e9/0x680
[  803.641496] RSP: 0018:ffffa8aac2b9fd30 EFLAGS: 00010286
[  803.647327] RAX: ffff972375def4e0 RBX: ffff972575b84e08 RCX: 00000000fffffffe
[  803.655289] RDX: ffff972acc154c30 RSI: ffff972375def500 RDI: 0000000000023ee0
[  803.663251] RBP: ffff972575b84198 R08: ffff972377be3ee0 R09: ffff972575b84210
[  803.671213] R10: 0000000000000000 R11: 0000000040000000 R12: ffff972575b84ff0
[  803.679176] R13: 0000000000000001 R14: 00000000fffffff4 R15: ffff972575b84ff0
[  803.687139] FS:  00007f216a513700(0000) GS:ffff972377bc0000(0000) knlGS:0000000000000000
[  803.696169] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  803.702581] CR2: ffff972acc154c34 CR3: 0000000274478001 CR4: 00000000000606e0
[  803.710544] Call Trace:
[  803.713278]  create_filter+0x80/0xc0
[  803.717268]  apply_event_filter+0xb8/0x120
[  803.721837]  event_filter_write+0x5d/0xb0
[  803.726312]  __vfs_write+0x33/0x170
[  803.730207]  ? __inode_security_revalidate+0x4a/0x70
[  803.735740]  ? selinux_file_permission+0xce/0x120
[  803.740989]  ? _cond_resched+0x16/0x40
[  803.745173]  vfs_write+0xb0/0x190
[  803.748870]  SyS_write+0x52/0xc0
[  803.752473]  do_syscall_64+0x6e/0x180
[  803.756560]  entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[  803.762196] RIP: 0033:0x7f2169c00b50
[  803.766182] RSP: 002b:00007ffc35e05db8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[  803.774630] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007f2169c00b50
[  803.782592] RDX: 0000000000000001 RSI: 0000560031449e00 RDI: 0000000000000001
[  803.790555] RBP: 0000560031449e00 R08: 00007f2169ecb740 R09: 00007f216a513700
[  803.798517] R10: 0000560031453a00 R11: 0000000000000246 R12: 0000000000000001
[  803.806479] R13: 0000000000000001 R14: 00007f2169eca5e0 R15: 00007f2169ec63c0
[  803.814443] Code: 02 00 00 00 00 44 89 10 c7 40 04 00 00 00 00 83 e9 01 83 f9 ff 74 22 48 63 c1 48 c1 e0 04 48 01 f0 48 63 10 48 c1 e2 04 48 01 f2 <8b> 5a 04 39 58 04
[  803.835544] RIP: process_preds+0x5e9/0x680 RSP: ffffa8aac2b9fd30
[  803.842247] CR2: ffff972acc154c34
[  803.845945] ---[ end trace 81df1915c389f53d ]---
maintainer
#43 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 1 line · On Mon, 12 Mar 2018 16:10:17 +0100 Jiri Olsa <[email protected]> wrote:

got it crashed when clearing the filter via 'echo > filter'

Awesome. I'll go and test this out. Thanks!

Hmm, could you pull my tree and test my branch: ftrace/core. I may have already fixed this but haven't posted the latest (which I'll do before pushing to next).

-- Steve

#44 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 10 lines · Steven Rostedt
Quoted 1 line · On Mon, 12 Mar 2018 16:10:17 +0100 Jiri Olsa <[email protected]> wrote:

got it crashed when clearing the filter via 'echo > filter'

Awesome. I'll go and test this out. Thanks!

Hmm, could you pull my tree and test my branch: ftrace/core. I may have already fixed this but haven't posted the latest (which I'll do before pushing to next).

I couldn't apply your patches from mailbox so I used your ftrace/core already

jirka

maintainer
#45 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 6 lines · On Mon, 12 Mar 2018 19:54:14 +0100 Jiri Olsa <[email protected]> wrote:
Quoted 5 lines · Steven Rostedt
Quoted 1 line · On Mon, 12 Mar 2018 16:10:17 +0100 Jiri Olsa <[email protected]> wrote:

got it crashed when clearing the filter via 'echo > filter'

Quoted 8 lines
Quoted 5 lines

Awesome. I'll go and test this out. Thanks!

Hmm, could you pull my tree and test my branch: ftrace/core. I may have already fixed this but haven't posted the latest (which I'll do before pushing to next).

I couldn't apply your patches from mailbox so I used your ftrace/core already

Ug, I'm able to reproduce it. Damn, this passed my test suite. How did it not trigger this. I need to look at my test cases!

-- Steve

maintainer
#46 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

In reply to Jiri Olsa
Quoted 6 lines · On Mon, 12 Mar 2018 19:54:14 +0100 Jiri Olsa <[email protected]> wrote:
Quoted 5 lines · Steven Rostedt
Quoted 1 line · On Mon, 12 Mar 2018 16:10:17 +0100 Jiri Olsa <[email protected]> wrote:

got it crashed when clearing the filter via 'echo > filter'

Quoted 8 lines
Quoted 5 lines

Awesome. I'll go and test this out. Thanks!

Hmm, could you pull my tree and test my branch: ftrace/core. I may have already fixed this but haven't posted the latest (which I'll do before pushing to next).

I couldn't apply your patches from mailbox so I used your ftrace/core already

Jiri, If you apply the below, does it fix it for you?

-- Steve

kernel/trace/trace_events_filter.c +7 -4
index a5a131ec3c9c..4099e141188c 100644
@@ -1498,11 +1498,14 @@ static int process_preds(struct trace_event_call *call,
 		return ret;
 	}
 
-	prog = predicate_parse(filter_string, nr_parens, nr_preds,
+	if (!nr_preds) {
+		prog = NULL;
+	} else {
+		prog = predicate_parse(filter_string, nr_parens, nr_preds,
 			       parse_pred, call, pe);
-	if (IS_ERR(prog))
-		return PTR_ERR(prog);
-
+		if (IS_ERR(prog))
+			return PTR_ERR(prog);
+	}
 	rcu_assign_pointer(filter->prog, prog);
 	return 0;
 }
#47 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 11 lines · Steven Rostedt
Quoted 8 lines · On Mon, 12 Mar 2018 19:54:14 +0100 Jiri Olsa <[email protected]> wrote:
Quoted 7 lines · Steven Rostedt
Quoted 1 line · On Mon, 12 Mar 2018 16:10:17 +0100 Jiri Olsa <[email protected]> wrote:

got it crashed when clearing the filter via 'echo > filter'

Awesome. I'll go and test this out. Thanks!

Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

Hmm, could you pull my tree and test my branch: ftrace/core. I may have already fixed this but haven't posted the latest (which I'll do before pushing to next).

I couldn't apply your patches from mailbox so I used your ftrace/core already

Jiri, If you apply the below, does it fix it for you?

yes, the crash is gone and I can set filter ftrace/function, but I'm still having some issues put that filter through perf

  # perf record -e ftrace:function --filter "ip == 0xffffffffa41e8490" ls

but I might be just missing something.. it's been a while ;-) I'm looking to that

jirka

Quoted 26 lines

-- Steve

kernel/trace/trace_events_filter.c +7 -4
index a5a131ec3c9c..4099e141188c 100644
@@ -1498,11 +1498,14 @@ static int process_preds(struct trace_event_call *call,
 		return ret;
 	}
 
-	prog = predicate_parse(filter_string, nr_parens, nr_preds,
+	if (!nr_preds) {
+		prog = NULL;
+	} else {
+		prog = predicate_parse(filter_string, nr_parens, nr_preds,
 			       parse_pred, call, pe);
-	if (IS_ERR(prog))
-		return PTR_ERR(prog);
-
+		if (IS_ERR(prog))
+			return PTR_ERR(prog);
+	}
 	rcu_assign_pointer(filter->prog, prog);
 	return 0;
 }
maintainer
#48 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 3 lines · On Tue, 13 Mar 2018 11:14:01 +0100 Jiri Olsa <[email protected]> wrote:
Quoted 1 line

Jiri, If you apply the below, does it fix it for you?

yes, the crash is gone and I can set filter ftrace/function,

Great!

Quoted 5 lines

but I'm still having some issues put that filter through perf

  # perf record -e ftrace:function --filter "ip == 0xffffffffa41e8490" ls

but I might be just missing something.. it's been a while ;-) I'm looking to that

I have to ask. Did that work with the old code? The ftrace filter was special in the old code and I tried to simulate it in the new code. I'm not sure I checked if ip can take an address, but from what the code looked like, it wouldn't. It looked like it required a name of a function. Something that gets passed into "set_ftrace_filter" which is not an address.

So instead of doing something like:

perf record -e ftrace:function --filter "ip == 0xffffffff810ccfa0" ls

You would need to do

perf record -e ftrace:function --filter "ip == schedule_tail" ls

because perf doesn't use the filter for the function, it uses the ftrace_ops->hash tables. If it would simply take the address, we could just use the trace_events_filter logic, and not make it a special case.

-- Steve

#49 lore raw

Re: [PATCH 3/3] tracing: Rewrite filter logic to be simpler and faster

Quoted 26 lines · Steven Rostedt
Quoted 3 lines · On Tue, 13 Mar 2018 11:14:01 +0100 Jiri Olsa <[email protected]> wrote:
Quoted 1 line

Jiri, If you apply the below, does it fix it for you?

yes, the crash is gone and I can set filter ftrace/function,

Great!

Quoted 5 lines

but I'm still having some issues put that filter through perf

  # perf record -e ftrace:function --filter "ip == 0xffffffffa41e8490" ls

but I might be just missing something.. it's been a while ;-) I'm looking to that

I have to ask. Did that work with the old code? The ftrace filter was special in the old code and I tried to simulate it in the new code. I'm not sure I checked if ip can take an address, but from what the code looked like, it wouldn't. It looked like it required a name of a function. Something that gets passed into "set_ftrace_filter" which is not an address.

So instead of doing something like:

perf record -e ftrace:function --filter "ip == 0xffffffff810ccfa0" ls

Quoted 7 lines

You would need to do

perf record -e ftrace:function --filter "ip == schedule_tail" ls

because perf doesn't use the filter for the function, it uses the ftrace_ops->hash tables. If it would simply take the address, we could just use the trace_events_filter logic, and not make it a special case.

ok, that's what I've been missing.. for some reason I thought we need to pass the address.. everything checks out then

I checked few filters and it seems to work properly to me

thanks,
jirka

#50 lore raw

Re: [PATCH 0/3] tracing: Rewrite the function filter code

In reply to Steven Rostedt

On Fri, Mar 09, 2018 at 09:34:42PM -0500, Steven Rostedt wrote:

SNIP

Quoted 11 lines

Special thanks goes out to Al for his patience and his time that he spent in educating us in a proper logical parser.

Two patches were added to do some initial clean up. The last patch implements Al's suggestions. I wrote up a very lengthy comment describing what Al taught us in my own words (hopefully I got it right), and the implementation is similar to what Al showed with a few changes (again, hopefully I got that right too). I tested this with lots of different filters and it looks to be holding up.

Quoted 4 lines

Jiri,

This affects perf as well. I ran some tests and I believe I got it woking as it does today.

nice, it looks ok to me.. I guess the only concern is the filter for ftrace:function event, which uses the pred parsing code just to get the 'ip == ...' pairs and feeds it to ftrace_ops filter api

I'll check and make some tests

I think tracepoint events filters should be working without any special care because they use the same api as ftrace filters

jirka