[lttng-dev] [PATCH barectf 1/1] Fix: bitfield: shift undefined/implementation defined behaviors

Mathieu Desnoyers mathieu.desnoyers at efficios.com
Tue May 14 16:42:15 EDT 2019


bitfield.h uses the left shift operator with a left operand which
may be negative. The C99 standard states that shifting a negative
value is undefined.

When building with -Wshift-negative-value, we get this gcc warning:

In file included from /home/smarchi/src/babeltrace/include/babeltrace/ctfser-internal.h:44:0,
                 from /home/smarchi/src/babeltrace/ctfser/ctfser.c:42:
/home/smarchi/src/babeltrace/include/babeltrace/ctfser-internal.h: In function ‘bt_ctfser_write_unsigned_int’:
/home/smarchi/src/babeltrace/include/babeltrace/bitfield-internal.h:116:24: error: left shift of negative value [-Werror=shift-negative-value]
   mask = ~((~(type) 0) << (__start % ts));  \
                        ^
/home/smarchi/src/babeltrace/include/babeltrace/bitfield-internal.h:222:2: note: in expansion of macro ‘_bt_bitfield_write_le’
  _bt_bitfield_write_le(ptr, type, _start, _length, _v)
  ^~~~~~~~~~~~~~~~~~~~~
/home/smarchi/src/babeltrace/include/babeltrace/ctfser-internal.h:418:3: note: in expansion of macro ‘bt_bitfield_write_le’
   bt_bitfield_write_le(mmap_align_addr(ctfser->base_mma) +
   ^~~~~~~~~~~~~~~~~~~~

This boils down to the fact that the expression ~((uint8_t)0) has type
"signed int", which is used as an operand of the left shift.  This is due
to the integer promotion rules of C99 (6.3.3.1):

    If an int can represent all values of the original type, the value is
    converted to an int; otherwise, it is converted to an unsigned int.
    These are called the integer promotions. All other types are unchanged
    by the integer promotions.

We also need to cast the result explicitly into the left hand
side type to deal with:

warning: large integer implicitly truncated to unsigned type [-Woverflow]

The C99 standard states that a right shift has implementation-defined
behavior when shifting a signed negative value. Add a preprocessor check
that the compiler provides the expected behavior, else provide an
alternative implementation which guarantees the intended behavior.

A preprocessor check is also added to ensure that the compiler
representation for signed values is two's complement, which is expected
by this header.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
---
 barectf/templates.py | 173 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 146 insertions(+), 27 deletions(-)

diff --git a/barectf/templates.py b/barectf/templates.py
index eec677b..6625486 100644
--- a/barectf/templates.py
+++ b/barectf/templates.py
@@ -596,11 +596,9 @@ _BITFIELD = '''#ifndef _$PREFIX$BITFIELD_H
 #define _$PREFIX$BITFIELD_H
 
 /*
- * BabelTrace
- *
  * Bitfields read/write functions.
  *
- * Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
+ * Copyright 2010-2019 - Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -632,16 +630,137 @@ _BITFIELD = '''#ifndef _$PREFIX$BITFIELD_H
 
 #define $PREFIX$BYTE_ORDER $ENDIAN_DEF$
 
-/* We can't shift a int from 32 bit, >> 32 and << 32 on int is undefined */
-#define _$prefix$bt_piecewise_rshift(_vtype, _v, _shift) \\
+/*
+ * This bitfield header requires the compiler representation of signed
+ * integers to be two's complement.
+ */
+#if (-1 != ~0)
+#error "bitfield.h requires the compiler representation of signed integers to be two's complement."
+#endif
+
+/*
+ * Produce a build-time error if the condition `cond` is non-zero.
+ * Evaluates as a size_t expression.
+ */
+#define _$prefix$BT_BUILD_ASSERT(cond)					\\
+	sizeof(struct { int f:(2 * !!(cond) - 1); })
+
+/*
+ * Cast value `v` to an unsigned integer of the same size as `v`.
+ */
+#define _$prefix$bt_cast_value_to_unsigned(v)				\\
+	(sizeof(v) == sizeof(uint8_t) ? (uint8_t) (v) :			\\
+	sizeof(v) == sizeof(uint16_t) ? (uint16_t) (v) :		\\
+	sizeof(v) == sizeof(uint32_t) ? (uint32_t) (v) :		\\
+	sizeof(v) == sizeof(uint64_t) ? (uint64_t) (v) :		\\
+	_$prefix$BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t)))
+
+/*
+ * Cast value `v` to an unsigned integer type of the size of type `type`
+ * *without* sign-extension.
+ *
+ * The unsigned cast ensures that we're not shifting a negative value,
+ * which is undefined in C. However, this limits the maximum type size
+ * of `type` to 64-bit. Generate a compile-time error if the size of
+ * `type` is larger than 64-bit.
+ */
+#define _$prefix$bt_cast_value_to_unsigned_type(type, v)		\\
+	(sizeof(type) == sizeof(uint8_t) ?				\\
+		(uint8_t) _bt_cast_value_to_unsigned(v) :		\\
+	sizeof(type) == sizeof(uint16_t) ?				\\
+		(uint16_t) _bt_cast_value_to_unsigned(v) :		\\
+	sizeof(type) == sizeof(uint32_t) ?				\\
+		(uint32_t) _bt_cast_value_to_unsigned(v) :		\\
+	sizeof(type) == sizeof(uint64_t) ?				\\
+		(uint64_t) _bt_cast_value_to_unsigned(v) :		\\
+	_$prefix$BT_BUILD_ASSERT(sizeof(v) <= sizeof(uint64_t)))
+
+/*
+ * _$prefix$bt_fill_mask evaluates to a "type" integer with all bits set.
+ */
+#define _$prefix$bt_fill_mask(type)	((type) ~(type) 0)
+
+/*
+ * Left shift a value `v` of `shift` bits.
+ *
+ * The type of `v` can be signed or unsigned integer.
+ * The value of `shift` must be less than the size of `v` (in bits),
+ * otherwise the behavior is undefined.
+ * Evaluates to the result of the shift operation.
+ *
+ * According to the C99 standard, left shift of a left hand-side signed
+ * type is undefined if it has a negative value or if the result cannot
+ * be represented in the result type. This bitfield header discards the
+ * bits that are left-shifted beyond the result type representation,
+ * which is the behavior of an unsigned type left shift operation.
+ * Therefore, always perform left shift on an unsigned type.
+ */
+#define _$prefix$bt_lshift(_vtype, v, shift)				\\
+	((_vtype) (_$prefix$bt_cast_value_to_unsigned(v) << (shift)))
+
+/*
+ * Generate a mask of type `type` with the `length` least significant bits
+ * cleared, and the most significant bits set.
+ */
+#define _$prefix$bt_make_mask_complement(type, length)			\\
+	_$prefix$bt_lshift(type, _$prefix$bt_fill_mask(type), length)
+
+/*
+ * Generate a mask of type `type` with the `length` least significant bits
+ * set, and the most significant bits cleared.
+ */
+#define _$prefix$bt_make_mask(type, length)				\\
+	((type) ~_$prefix$bt_make_mask_complement(type, length))
+
+/*
+ * Right shift a value `v` of `shift` bits.
+ *
+ * The type of `v` can be signed or unsigned integer.
+ * The value of `shift` must be less than the size of `v` (in bits),
+ * otherwise the behavior is undefined.
+ * Evaluates to the result of the shift operation.
+ *
+ * According to the C99 standard, right shift of a left hand-side signed
+ * type which has a negative value is implementation defined. This
+ * bitfield header relies on the right shift implementation carrying the
+ * sign bit. If the compiler implementation has a different behavior,
+ * emulate carrying the sign bit.
+ */
+#if ((-1 >> 1) == -1)
+#define _$prefix$bt_rshift(_vtype, v, shift)	((v) >> (shift))
+#else
+#define _$prefix$bt_rshift(_vtype, v, shift)				\\
+	((_vtype) ((_$prefix$bt_cast_value_to_unsigned(v) >> (shift)) |	\\
+		((v) < 0 ? _$prefix$bt_make_mask_complement(_vtype,	\\
+			sizeof(v) * CHAR_BIT - (shift)) : 0)))
+#endif
+
+/*
+ * Right shift a signed or unsigned integer with `_shift` value being an
+ * arbitrary number of bits. `_v` is modified by this macro.
+ */
+#define _$prefix$bt_piecewise_rshift(_vtype, _v, _shift)		\\
+do {									\\
+	unsigned long _sb = (_shift) / (sizeof(_v) * CHAR_BIT - 1);	\\
+	unsigned long _final = (_shift) % (sizeof(_v) * CHAR_BIT - 1);	\\
+									\\
+	for (; _sb; _sb--)						\\
+		(_v) = _$prefix$bt_rshift(_vtype, _v, sizeof(_v) * CHAR_BIT - 1); \\
+	(_v) = _$prefix$bt_rshift(_vtype, _v, _final);			\\
+} while (0)
+
+/*
+ * Left shift a signed or unsigned integer with `_shift` value being an
+ * arbitrary number of bits. `_v` is modified by this macro.
+ */
+#define _$prefix$bt_piecewise_lshift(_vtype, _v, _shift)		\\
 do {									\\
-	unsigned long ___shift = (_shift);				\\
-	unsigned long sb = (___shift) / (sizeof(_v) * CHAR_BIT - 1);	\\
-	unsigned long final = (___shift) % (sizeof(_v) * CHAR_BIT - 1); \\
+	unsigned long _sb = (_shift) / (sizeof(_v) * CHAR_BIT - 1);	\\
+	unsigned long _final = (_shift) % (sizeof(_v) * CHAR_BIT - 1);	\\
 									\\
-	for (; sb; sb--)						\\
-		_v >>= sizeof(_v) * CHAR_BIT - 1;			\\
-	_v >>= final;							\\
+	for (; _sb; _sb--)						\\
+		(_v) = _$prefix$bt_lshift(_vtype, _v, sizeof(_v) * CHAR_BIT - 1); \\
+	(_v) = _$prefix$bt_lshift(_vtype, _v, _final);			\\
 } while (0)
 
 /*
@@ -664,7 +783,7 @@ do {									\\
 
 #define _$prefix$bt_bitfield_write_le(_ptr, type, _start, _length, _vtype, _v) \\
 do {									\\
-	_vtype __v = (_v);					\\
+	_vtype __v = (_v);						\\
 	type *__ptr = CAST_PTR(type *, _ptr);				\\
 	unsigned long __start = (_start), __length = (_length);		\\
 	type mask, cmask;						\\
@@ -681,15 +800,15 @@ do {									\\
 									\\
 	/* Trim v high bits */						\\
 	if (__length < sizeof(__v) * CHAR_BIT)				\\
-		__v &= ~((~(_vtype) 0) << __length);		\\
+		__v &= _$prefix$bt_make_mask(_vtype, __length);		\\
 									\\
 	/* We can now append v with a simple "or", shift it piece-wise */ \\
 	this_unit = start_unit;						\\
 	if (start_unit == end_unit - 1) {				\\
-		mask = ~((~(type) 0) << (__start % ts));		\\
+		mask = _$prefix$bt_make_mask(type, __start % ts);	\\
 		if (end % ts)						\\
-			mask |= (~(type) 0) << (end % ts);		\\
-		cmask = (type) __v << (__start % ts);			\\
+			mask |= _$prefix$bt_make_mask_complement(type, end % ts); \\
+		cmask = _$prefix$bt_lshift(type, (type) (__v), __start % ts); \\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
 		__ptr[this_unit] |= cmask;				\\
@@ -697,8 +816,8 @@ do {									\\
 	}								\\
 	if (__start % ts) {						\\
 		cshift = __start % ts;					\\
-		mask = ~((~(type) 0) << cshift);			\\
-		cmask = (type) __v << cshift;				\\
+		mask = _$prefix$bt_make_mask(type, cshift);		\\
+		cmask = _$prefix$bt_lshift(type, (type) (__v), cshift);	\\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
 		__ptr[this_unit] |= cmask;				\\
@@ -712,7 +831,7 @@ do {									\\
 		__start += ts;						\\
 	}								\\
 	if (end % ts) {							\\
-		mask = (~(type) 0) << (end % ts);			\\
+		mask = _$prefix$bt_make_mask_complement(type, end % ts); \\
 		cmask = (type) __v;					\\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
@@ -723,7 +842,7 @@ do {									\\
 
 #define _$prefix$bt_bitfield_write_be(_ptr, type, _start, _length, _vtype, _v) \\
 do {									\\
-	_vtype __v = (_v);					\\
+	_vtype __v = (_v);						\\
 	type *__ptr = CAST_PTR(type *, _ptr);				\\
 	unsigned long __start = (_start), __length = (_length);		\\
 	type mask, cmask;						\\
@@ -740,15 +859,15 @@ do {									\\
 									\\
 	/* Trim v high bits */						\\
 	if (__length < sizeof(__v) * CHAR_BIT)				\\
-		__v &= ~((~(_vtype) 0) << __length);			\\
+		__v &= _$prefix$bt_make_mask(_vtype, __length);		\\
 									\\
 	/* We can now append v with a simple "or", shift it piece-wise */ \\
 	this_unit = end_unit - 1;					\\
 	if (start_unit == end_unit - 1) {				\\
-		mask = ~((~(type) 0) << ((ts - (end % ts)) % ts));	\\
+		mask = _$prefix$bt_make_mask(type, (ts - (end % ts)) % ts); \\
 		if (__start % ts)					\\
-			mask |= (~((type) 0)) << (ts - (__start % ts));	\\
-		cmask = (type) __v << ((ts - (end % ts)) % ts);		\\
+			mask |= _$prefix$bt_make_mask_complement(type, ts - (__start % ts)); \\
+		cmask = _$prefix$bt_lshift(type, (type) (__v), (ts - (end % ts)) % ts); \\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
 		__ptr[this_unit] |= cmask;				\\
@@ -756,8 +875,8 @@ do {									\\
 	}								\\
 	if (end % ts) {							\\
 		cshift = end % ts;					\\
-		mask = ~((~(type) 0) << (ts - cshift));			\\
-		cmask = (type) __v << (ts - cshift);			\\
+		mask = _$prefix$bt_make_mask(type, ts - cshift);	\\
+		cmask = _$prefix$bt_lshift(type, (type) (__v), ts - cshift); \\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
 		__ptr[this_unit] |= cmask;				\\
@@ -771,7 +890,7 @@ do {									\\
 		end -= ts;						\\
 	}								\\
 	if (__start % ts) {						\\
-		mask = (~(type) 0) << (ts - (__start % ts));		\\
+		mask = _$prefix$bt_make_mask(type, ts - (__start % ts)); \\
 		cmask = (type) __v;					\\
 		cmask &= ~mask;						\\
 		__ptr[this_unit] &= mask;				\\
-- 
2.11.0



More information about the lttng-dev mailing list