problems with qualifiers on arrays

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Consider the following example:

typedef int B[2][2];	// array of array of int
const B z;		// const array of array of int
int const (*r)[2] = z;	// pointer to array of const int

What we get in sparse is nasty:

B: SYM_NODE -> SYM_ARRAY -> SYM_ARRAY -> int_ctype
z: SYM_NODE[const] -> SYM_ARRAY -> SYM_ARRAY -> int_ctype
r: SYM_NODE -> SYM_PTR -> SYM_ARRAY[const] -> int_ctype

The trouble is, usual logics for finding qualifiers will happily stop
on SYM_PTR.  And so we get
a.c:3:21: warning: incorrect type in initializer (different modifiers)
a.c:3:21:    expected int const *[addressable] [toplevel] r[2]
a.c:3:21:    got int const [toplevel] *<noident>[2]

Of course, the code is 100% correct - qualified array of T is the same
type as array of qualified T, so the type of z is the same as array of
array of const int, which gets converted into pointer to array of const
int and initializer is fine.

The question is what to do with that mess.  We could switch all places that
look for qualifiers (and address_space) to proper loop (pick them from
node we are looking at, keep adding from deeper nodes as long as those
are SYM_ARRAY ones).  However, the PITA doesn't stop there.

The mess is created by typeof().  First of all, I'd fucked up back in '04
and switched to the wrong semantics in there; I _might_ have copied some
ancient version of gcc, or I might have fucked up, plain and simple.

Here's current (at least) gcc behaviour: typeof() keeps qualifiers.  I.e.
if we have const int *p, typeof(*p) will be const int.  We can (and probably
ought to) switch the handling of standard qualifiers to that behaviour.

However, it makes no sense whatsoever for noderef and address_space.
Think of it - we _can_ say
	typeof(*p) x = v;
	....
	use x
	....
even if *p is const; it's all right, since initializer is not an assignment.
We won't be able to modify x later on, but that's not fatal.  OTOH, if *p
is noderef (as it is e.g. for userland pointers), we won't be able to do
anything at all if x is noderef as well.  Worse, if we have address_space on
*p, we get absolutely bogus declaration - x is on stack, damnit, not in
user memory or iomem.   We _can't_ pass &x to something that expects such
pointers.

sparse strips *all* qualifiers - from the top level.  Which, again, is
implemented inconsistently wrt arrays.

So the question is, what do we do about that?  AFAICS, gcc semantics of
typeof is driven by two uses - one is about declaring variables in
polymorphic macros (and there stripping top-level qualifiers makes
perfect sense, even for traditional ones) and another is an atrocity
they've got for typeof(type) - things like #define pointer(T) typeof(T) *.
That, of course, wants to keep the damn things intact.

The thing is, we have places like put_user() where stripping address_space and
noderef is pretty much a must-have.  The typical use is
	typeof(*user_ptr) tmp = (v);			// conversions
	typeof(*user_ptr) __user *p = (user_ptr);	// local copy and
							//   decay
	assembler for copying tmp to userspace at address p

Note that we really, _really_ want the first assignment, regardless of
caring about multiple uses of argument in a macro.  We want proper
conversions.  We want v sign-expanded if *user_ptr is wider, etc.
And we sure as hell don't want a cascade of __builtin_choose_expr() by
something like __builtin_same_type_p().

I.e. I still think that we need stripping address_space and noderef.
If anybody has a saner approach, given the example above, I'd be
very greatful.  We _can_ rewrite such places in the kernel, there
are not too many of those.  I just don't see how to rewrite them
if we don't have a way to strip address_space and noderef...

Below is in assumption that we restore the C semantics wrt const et.al.
on typeof() and keep stripping address_space and noderef.  However,
if we e.g. introduce a new primitive specifically for stipping those
(and ifdef it away for !__CHECKER__), the text below still applies with
trivial modifications.

AFAICS, we need the following:
	* examine_base_type() should add the qualifiers from base type
if it's an array; that way we can avoid following the chains later on.
Question: what to do about context lists?
	* check the places where we look at qualifiers and address_space;
many are odd (in particular, type_difference() is doing very strange things)
	* examining SYM_TYPEOF should
		* convert SYM_TYPEOF to SYM_NODE
		* transfer ->ctype.modifiers of initializer (sans MOD_NODEREF)
		  into it (again, what about context list?)
		* discard SYM_NODE from initializer if present
		* if the top of initializer not SYM_ARRAY *or* if it has
		  neither noderef nor address_space - just make it the
		  base type of our node and be done with it.  That's
		  the normal case.
		* if we have a non-empty chain of SYM_ARRAY, follow it
		  down while we have qualifiers that need to be stripped,
		  creating a parallel chain of nodes without those qualifiers.
		  Attach it as base type of our node, attach the rest of
		  original to its bottom and be done with that.
	* AFAICS, our check for const target of assignment is broken; we
	  do not check for const subobjects.  New (internal) qualifier,
	  perhaps?  We could calculate it at type examination time easily
	  enough, but that would further complicate the rules for data
	  structures.  As it is, we have all qualifiers sitting in SYM_PTR,
	  (for pointer target), SYM_NODE and SYM_ARRAY (for type itself).
	  Here we would get new bit sitting on SYM_STRUCT and SYM_UNION
	  in addition to those (refering to type itself).  Any more elegant
	  suggestions?  We are bloody short on mod bits as it is...
	* in evaluate_cast() we need to be more accurate about finding address
	  space.  Not too hard...

Comments?  Linus, I'm afraid that you might be the only one who has a chance
to remember the entire area well enough ;-/  I'm trying to put together a
sane documentation of symbol.c and stuff around it, but...
-
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Newbies FAQ]     [LKML]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Trinity Fuzzer Tool]

  Powered by Linux