[PATCH 13/17] big-shift: use the type width for too big shift

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

 



Currently, during simplification, when it's checked if the shift
amount of a shift instruction is in range, it's not the
instruction's size (which is the same as the type's width)
that is used but the 'operand size' as returned by operand_size().

operand_size() is a bit like poor man's Value Range Propagation
or VRP without Propagation, and use the knowledge of previous
instructions to deduce the effective size of the operand.
For example, if the operand is the result of a narrowing cast
or have been ANDed with a known mask, or was a bitfield, the
size returned is the one of the cast or the mask and may be
smaller than its type.

A priori, using more precise knowledge is a good thing but in
the present case it's not because it causes warning for things
that are totally legal, meaningfull and used all the time.
For example, we don't want to warn in the following code:
	struct bf { int u:8; };
	int bf(struct bf *p) { return p->s << 24; }
Another situation where such warnings are not desirable is
when the shift instruction 'is far' from the one defining
the size, for example when the shift occurs in an inline function
and this inline function is called with a smaller type.

So, use the instruction size instead of the effective operand size
when checking the range of a shift instruction.

Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx>
---
 simplify.c               |  4 +--
 validation/shift-undef.c | 57 +++++++++++++++++++++++++++++++---------
 2 files changed, 47 insertions(+), 14 deletions(-)

diff --git a/simplify.c b/simplify.c
index 3b8e22c54..19ff47a32 100644
--- a/simplify.c
+++ b/simplify.c
@@ -547,9 +547,9 @@ static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long v
 	if (!value)
 		return replace_with_pseudo(insn, pseudo);
 
-	size = operand_size(insn, pseudo);
+	size = insn->size;
 	if (value >= size && !insn->tainted) {
-		warning(insn->pos, "right shift by bigger than source value");
+		warning(insn->pos, "shift by bigger than operand's width");
 		insn->tainted = 1;
 	}
 	switch (insn->opcode) {
diff --git a/validation/shift-undef.c b/validation/shift-undef.c
index aba226c19..bc4b2b847 100644
--- a/validation/shift-undef.c
+++ b/validation/shift-undef.c
@@ -74,6 +74,39 @@ int ok(int s, unsigned int u, int p)
 	return 1;
 }
 
+struct bf {
+	unsigned int u:8;
+	         int s:8;
+};
+
+int bf(struct bf *p)
+{
+	unsigned int r = 0;
+	r += p->s << 8;
+	r += p->s >> 8;
+	r += p->u >> 8;
+	return r;
+}
+
+/*
+ * The following is used in the kernel at several places
+ * It shouldn't emit any warnings.
+ */
+typedef unsigned long long u64;
+typedef unsigned       int u32;
+
+extern void hw_w32x2(u32 hi, u32 lo);
+
+inline void hw_w64(u64 val)
+{
+	hw_w32x2(val >> 32, (u32) val);
+}
+
+void hw_write(u32 val)
+{
+	hw_w64(val);
+}
+
 /*
  * check-name: shift too big or negative
  * check-command: sparse -Wno-decl $file
@@ -97,12 +130,12 @@ shift-undef.c:17:30: warning: shift too big (108) for type unsigned int
 shift-undef.c:18:30: warning: shift too big (4294967289) for type int
 shift-undef.c:19:30: warning: shift too big (4294967288) for type unsigned int
 shift-undef.c:20:30: warning: shift too big (4294967287) for type unsigned int
-shift-undef.c:21:29: warning: right shift by bigger than source value
-shift-undef.c:22:29: warning: right shift by bigger than source value
-shift-undef.c:23:29: warning: right shift by bigger than source value
-shift-undef.c:24:29: warning: right shift by bigger than source value
-shift-undef.c:25:29: warning: right shift by bigger than source value
-shift-undef.c:26:29: warning: right shift by bigger than source value
+shift-undef.c:21:29: warning: shift by bigger than operand's width
+shift-undef.c:22:29: warning: shift by bigger than operand's width
+shift-undef.c:23:29: warning: shift by bigger than operand's width
+shift-undef.c:24:29: warning: shift by bigger than operand's width
+shift-undef.c:25:29: warning: shift by bigger than operand's width
+shift-undef.c:26:29: warning: shift by bigger than operand's width
 shift-undef.c:32:11: warning: shift too big (100) for type int
 shift-undef.c:33:11: warning: shift too big (101) for type unsigned int
 shift-undef.c:34:11: warning: shift too big (102) for type unsigned int
@@ -121,11 +154,11 @@ shift-undef.c:46:30: warning: shift too big (108) for type unsigned int
 shift-undef.c:47:30: warning: shift too big (4294967289) for type int
 shift-undef.c:48:30: warning: shift too big (4294967288) for type unsigned int
 shift-undef.c:49:30: warning: shift too big (4294967287) for type unsigned int
-shift-undef.c:50:26: warning: right shift by bigger than source value
-shift-undef.c:51:26: warning: right shift by bigger than source value
-shift-undef.c:52:26: warning: right shift by bigger than source value
-shift-undef.c:53:26: warning: right shift by bigger than source value
-shift-undef.c:54:26: warning: right shift by bigger than source value
-shift-undef.c:55:26: warning: right shift by bigger than source value
+shift-undef.c:50:26: warning: shift by bigger than operand's width
+shift-undef.c:51:26: warning: shift by bigger than operand's width
+shift-undef.c:52:26: warning: shift by bigger than operand's width
+shift-undef.c:53:26: warning: shift by bigger than operand's width
+shift-undef.c:54:26: warning: shift by bigger than operand's width
+shift-undef.c:55:26: warning: shift by bigger than operand's width
  * check-error-end
  */
-- 
2.18.0

--
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