Linux SPARSE checker discussions
 help / color / mirror / Atom feed
From: Bart Van Assche <bvanassche@acm.org>
To: linux-sparse@vger.kernel.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
	Bart Van Assche <bvanassche@acm.org>
Subject: [PATCH 13/73] expand: Fix static initializer folding and floating-point casts
Date: Mon,  9 Nov 2026 12:31:59 -0800	[thread overview]
Message-ID: <20261109203259.362845-14-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>

Fix constant folding and numeric conversions in expand.c:
- Avoid folding writable or volatile static objects and negative offsets
  in constant_symbol_value(), and guard volatile or bitfield dereferences
  in expand_dereference().
- Preserve unsigned integer-to-float and float-to-integer conversions in
  cast_value() while guarding against out-of-range and NaN host casts.
- Add regression test validation/expand-const-and-float.c.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 expand.c                            | 76 ++++++++++++++++++++++++++---
 validation/expand-const-and-float.c | 73 +++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 6 deletions(-)
 create mode 100644 validation/expand-const-and-float.c

diff --git a/expand.c b/expand.c
index bbb30ae605dc..208ce65a2442 100644
--- a/expand.c
+++ b/expand.c
@@ -97,6 +97,11 @@ void cast_value(struct expression *expr, struct symbol *newtype, struct expressi
 	if (is_float_type(newtype) || is_float_type(oldtype))
 		goto Float;
 
+	if (!is_bool_type(newtype) && new_size > 64 && old_size >= 64 &&
+	    new_size > old_size && (oldtype->ctype.modifiers & MOD_UNSIGNED) &&
+	    (old->value & (1ULL << 63)))
+		return;
+
 	// For pointers and integers, we can just move the value around
 	expr->type = EXPR_VALUE;
 	expr->taint = old->taint;
@@ -144,16 +149,60 @@ Int:
 
 Float:
 	if (!is_float_type(newtype)) {
-		value = (long long)old->fvalue;
+		long double fval = old->fvalue;
+
+		if (is_bool_type(newtype)) {
+			expr->type = EXPR_VALUE;
+			expr->taint = 0;
+			expr->ctype = newtype;
+			expr->value = (fval != 0);
+			return;
+		}
+		if (new_size <= 0 || new_size > 64 || !__builtin_isfinite(fval))
+			return;
+		if (newtype->ctype.modifiers & MOD_UNSIGNED) {
+			unsigned long long uval;
+			if (!(fval > -1.0L && fval < 0x1p64L))
+				return;
+			uval = (fval <= 0.0L) ? 0ULL : (unsigned long long)fval;
+			if (new_size < 64 && uval >= (1ULL << new_size))
+				return;
+			value = (long long)uval;
+		} else {
+			unsigned long long lim = 1ULL << (new_size - 1);
+			unsigned long long umag;
+			if (fval >= 0.0L) {
+				if (!(fval < 0x1p63L))
+					return;
+				umag = (unsigned long long)fval;
+				if (umag >= lim)
+					return;
+				value = (long long)umag;
+			} else {
+				long double mag = -fval;
+				if (!(mag < 0x1p64L))
+					return;
+				umag = (unsigned long long)mag;
+				if (umag > lim)
+					return;
+				value = (long long)(0ULL - umag);
+			}
+		}
 		expr->type = EXPR_VALUE;
 		expr->taint = 0;
 		expr->ctype = newtype;
 		goto Int;
 	}
 
-	if (!is_float_type(oldtype))
-		expr->fvalue = (long double)get_longlong(old);
-	else
+	if (!is_float_type(oldtype)) {
+		if ((oldtype->ctype.modifiers & MOD_UNSIGNED) &&
+		    old_size > 64 && (old->value & (1ULL << 63)))
+			return;
+		if (oldtype->ctype.modifiers & MOD_UNSIGNED)
+			expr->fvalue = (long double)(unsigned long long)get_longlong(old);
+		else
+			expr->fvalue = (long double)get_longlong(old);
+	} else
 		expr->fvalue = old->fvalue;
 
 	if (newtype->rank <= 0) {
@@ -200,6 +249,8 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
 	}
 	if (left->type != EXPR_VALUE)
 		return 0;
+	if (!ctype || ctype->bit_size <= 0 || ctype->bit_size > 64)
+		return 0;
 	l = left->value; r = right->value;
 	is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED);
 	mask = sign_bit(ctype->bit_size);
@@ -305,6 +356,8 @@ static int simplify_cmp_binop(struct expression *expr, struct symbol *ctype)
 
 	if (left->type != EXPR_VALUE || right->type != EXPR_VALUE)
 		return 0;
+	if (!ctype || ctype->bit_size <= 0 || ctype->bit_size > 64)
+		return 0;
 	l = left->value; r = right->value;
 	mask = sign_bit(ctype->bit_size);
 	sl = l; sr = r;
@@ -665,8 +718,16 @@ static struct expression *constant_symbol_value(struct symbol *sym, int offset)
 {
 	struct expression *value;
 
+	if (offset < 0)
+		return NULL;
+	if (sym->ctype.modifiers & MOD_VOLATILE)
+		return NULL;
 	if (sym->ctype.modifiers & MOD_ACCESS)
 		return NULL;
+	if ((sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_STATIC)) &&
+	    !(sym->ctype.modifiers & MOD_CONST) &&
+	    !sym->string && sym->ident)
+		return NULL;
 	value = sym->initializer;
 	if (!value)
 		return NULL;
@@ -707,6 +768,8 @@ static int expand_dereference(struct expression *expr)
 	 */
 	if (expr->ctype->ctype.modifiers & MOD_NODEREF)
 		warning(unop->pos, "dereference of noderef expression");
+	if (expr->ctype->ctype.modifiers & MOD_VOLATILE)
+		return SIDE_EFFECTS;
 
 	/*
 	 * Is it "symbol" or "symbol + offset"?
@@ -732,7 +795,7 @@ static int expand_dereference(struct expression *expr)
 			if (value->type == EXPR_VALUE) {
 				if (!is_integral_type(ctype))
 					return UNSAFE;
-				if (is_bitfield_type(value->ctype))
+				if (is_bitfield_type(ctype) || is_bitfield_type(value->ctype))
 					return UNSAFE;
 				expr->type = EXPR_VALUE;
 				expr->value = value->value;
@@ -866,7 +929,8 @@ static int expand_cast(struct expression *expr)
 	/* Simplify normal integer casts.. */
 	if (target->type == EXPR_VALUE || target->type == EXPR_FVALUE) {
 		cast_value(expr, expr->ctype, target);
-		return 0;
+		if (expr->type == EXPR_VALUE || expr->type == EXPR_FVALUE)
+			return 0;
 	}
 	return cost + 1;
 }
diff --git a/validation/expand-const-and-float.c b/validation/expand-const-and-float.c
new file mode 100644
index 000000000000..4f0403134cdc
--- /dev/null
+++ b/validation/expand-const-and-float.c
@@ -0,0 +1,73 @@
+static const volatile int cv = 42;
+
+int read_cv(void)
+{
+	return cv;
+}
+
+void vol_deref(const volatile int *p)
+{
+	*p;
+}
+
+static const int arr[2] = { 10, 20 };
+
+int read_neg_off(void)
+{
+	return *(&arr[0] - 1);
+}
+
+unsigned int float_neg_half(void)
+{
+	return (unsigned int)-0.5;
+}
+
+_Bool float_half_to_bool(void)
+{
+	return (_Bool)0.5;
+}
+
+int float_oob_pos(void)
+{
+	return (int)1e30;
+}
+
+unsigned int float_neg_one(void)
+{
+	return (unsigned int)-1.0;
+}
+
+unsigned long long float_oob_ull(void)
+{
+	return (unsigned long long)1e40;
+}
+
+int float_nan(void)
+{
+	return (int)__builtin_nan("");
+}
+
+int float_inf(void)
+{
+	return (int)__builtin_inf();
+}
+
+int nonconst_float_cast(int x)
+{
+	return (int)(x + 0.0);
+}
+
+/*
+ * check-name: expand-const-and-float
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: load\\.32.*\\[cv\\]
+ * check-output-contains: load\\.32.*%arg1
+ * check-output-contains: ret\\.32.*\\$0
+ * check-output-contains: ret\\.1.*\\$1
+ *
+ * check-error-start
+expand-const-and-float.c:17:26: warning: invalid access below 'arr' (-4 8)
+ * check-error-end
+ */

  parent reply	other threads:[~2026-11-09 20:33 UTC|newest]

Thread overview: 74+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09 20:31 Bart Van Assche
2026-11-09 20:31 ` [PATCH 01/73] Makefile: Pass '--' to rm in clean target to handle dash-prefixed files Bart Van Assche
2026-11-09 20:31 ` [PATCH 02/73] sparse-llvm: Use LLVMCreateBuilderForModule() with LLVM 24 and later Bart Van Assche
2026-11-09 20:31 ` [PATCH 03/73] typeof_unqual: Strip qualifiers and address space from types and arrays Bart Van Assche
2026-11-09 20:31 ` [PATCH 04/73] bitfield: Pack consecutive _Bool bitfields into byte containers Bart Van Assche
2026-11-09 20:31 ` [PATCH 05/73] allocate: Harden slab allocation bounds, zeroing, and mmap checks Bart Van Assche
2026-11-09 20:31 ` [PATCH 06/73] tokenize: Support 4096-byte identifiers and fix escape bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 07/73] pre-process: Fix macro expansion and include directive handling Bart Van Assche
2026-11-09 20:31 ` [PATCH 08/73] options, lib: Harden warning buffers and command-line option parsing Bart Van Assche
2026-11-09 20:31 ` [PATCH 09/73] builtin: Fix atomic builtin type checking and object_size bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 10/73] parse, show-parse: Fix attribute propagation, enum overflow, and bounds Bart Van Assche
2026-11-09 20:31 ` [PATCH 11/73] target-openrisc: Set target_openrisc.mach to MACH_OPENRISC Bart Van Assche
2026-11-09 20:31 ` [PATCH 12/73] evaluate: Fix pointer arithmetic, incomplete types, and qualifiers Bart Van Assche
2026-11-09 20:31 ` Bart Van Assche [this message]
2026-11-09 20:32 ` [PATCH 14/73] inline: Copy union cast types and VLA bounds when inlining Bart Van Assche
2026-11-09 20:32 ` [PATCH 15/73] linearize, flow: Fix asm operands, switch case bounds, and DSE Bart Van Assche
2026-11-09 20:32 ` [PATCH 16/73] memops, ssa: Walk parent insns in reverse and skip mixed-size SSA Bart Van Assche
2026-11-09 20:32 ` [PATCH 17/73] simplify: Prevent overflow on LLONG_MIN negation and fix simplifications Bart Van Assche
2026-11-09 20:32 ` [PATCH 18/73] sparse: Gate context failure warning on Wcontext and catch underflow Bart Van Assche
2026-11-09 20:32 ` [PATCH 19/73] ast-model: Return AST_N_COLUMNS and inspect child nodes lazily Bart Van Assche
2026-11-09 20:32 ` [PATCH 20/73] c2xml: Guard against recursive symbols and free xmlGetProp() string Bart Van Assche
2026-11-09 20:32 ` [PATCH 21/73] compile-i386: Fix buffer bounds, unsigned '<=' codegen, and null checks Bart Van Assche
2026-11-09 20:32 ` [PATCH 22/73] ctags: Fix line comparison order and handle SYM_RESTRICT and SYM_LABEL Bart Van Assche
2026-11-09 20:32 ` [PATCH 23/73] dissect: Unwrap array element type via base_type() Bart Van Assche
2026-11-09 20:32 ` [PATCH 24/73] example: Fix show_op() rotating buffer dimensions Bart Van Assche
2026-11-09 20:32 ` [PATCH 25/73] graph: Check PSEUDO_SYM before dereferencing insn->func->sym Bart Van Assche
2026-11-09 20:32 ` [PATCH 26/73] scheck: Fix slice() upper bit index calculation Bart Van Assche
2026-11-09 20:32 ` [PATCH 27/73] sparse-llvm: Support arbitrary struct member counts and fix operand types Bart Van Assche
2026-11-09 20:32 ` [PATCH 28/73] inline: Duplicate generic expressions instead of evaluating them Bart Van Assche
2026-11-09 20:32 ` [PATCH 29/73] lib: Avoid mutating fmt before va_start() in warning() Bart Van Assche
2026-11-09 20:32 ` [PATCH 30/73] cse, canon: Cast pointers to uintptr_t before relational comparison Bart Van Assche
2026-11-09 20:32 ` [PATCH 31/73] context: Add "conditional context increment" model Bart Van Assche
2026-11-09 20:32 ` [PATCH 32/73] expand: Simplify signed constants early Bart Van Assche
2026-11-09 20:32 ` [PATCH 33/73] bitwise: Allow all-bits-set as a restricted value Bart Van Assche
2026-11-09 20:32 ` [PATCH 34/73] bitwise: Allow restricted ordered compares with unrestricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 35/73] bitwise: Keep unrestricted values unrestricted after a cast Bart Van Assche
2026-11-09 20:32 ` [PATCH 36/73] bitwise: Don't remove MOD_SPECIFIER from 'bitwise' types Bart Van Assche
2026-11-09 20:32 ` [PATCH 37/73] bitwise: Allow both regular casts and forced casts for restricted values Bart Van Assche
2026-11-09 20:32 ` [PATCH 38/73] clang-format: Add .clang-format configuration file Bart Van Assche
2026-11-09 20:32 ` [PATCH 39/73] options: Add support for -std=c23, -std=c2x and -std=gnu23 Bart Van Assche
2026-11-09 20:32 ` [PATCH 40/73] preprocessor: Add support for #elifdef and #elifndef Bart Van Assche
2026-11-09 20:32 ` [PATCH 41/73] preprocessor: Add support for __has_include and __has_include_next Bart Van Assche
2026-11-09 20:32 ` [PATCH 42/73] c23: Add support for static_assert, alignas, alignof, thread_local Bart Van Assche
2026-11-09 20:32 ` [PATCH 43/73] c23: Add support for bool, true, and false keywords Bart Van Assche
2026-11-09 20:32 ` [PATCH 44/73] c23: Add support for nullptr and nullptr_t Bart Van Assche
2026-11-09 20:32 ` [PATCH 45/73] c23: Add support for digit separators in numeric literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 46/73] c23: Support unnamed parameters in function definitions Bart Van Assche
2026-11-09 20:32 ` [PATCH 47/73] c23: Support variadic functions without named parameters Bart Van Assche
2026-11-09 20:32 ` [PATCH 48/73] c23: Support labels before declarations and at block ends Bart Van Assche
2026-11-09 20:32 ` [PATCH 49/73] c23: Support auto type inference Bart Van Assche
2026-11-09 20:32 ` [PATCH 50/73] c23: Support constexpr object specifier Bart Van Assche
2026-11-09 20:32 ` [PATCH 51/73] c23: Support enumerations with fixed underlying types Bart Van Assche
2026-11-09 20:32 ` [PATCH 52/73] c23: Support standard [[]] attribute syntax Bart Van Assche
2026-11-09 20:32 ` [PATCH 53/73] preprocessor: Add support for __has_c_attribute Bart Van Assche
2026-11-09 20:32 ` [PATCH 54/73] c23: Support empty initializer braces {} Bart Van Assche
2026-11-09 20:32 ` [PATCH 55/73] preprocessor: Add support for __has_embed Bart Van Assche
2026-11-09 20:32 ` [PATCH 56/73] preprocessor: Add support for #embed directive Bart Van Assche
2026-11-09 20:32 ` [PATCH 57/73] c23: Add support for u8 character constants and string literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 58/73] preprocessor: Add support for __VA_OPT__ Bart Van Assche
2026-11-09 20:32 ` [PATCH 59/73] c23: Treat empty parameter list () as (void) and reject K&R functions Bart Van Assche
2026-11-09 20:32 ` [PATCH 60/73] c23: Support storage-class specifiers in compound literals Bart Van Assche
2026-11-09 20:32 ` [PATCH 61/73] c23: Add support for _BitInt(N) Bart Van Assche
2026-11-09 20:32 ` [PATCH 62/73] enum: Support machine mode attributes on enumeration types Bart Van Assche
2026-11-09 20:32 ` [PATCH 63/73] enum: Support __attribute__((packed)) " Bart Van Assche
2026-11-09 20:32 ` [PATCH 64/73] expand: Ignore top-level non-array address space in compare_types() Bart Van Assche
2026-11-09 20:32 ` [PATCH 65/73] builtin: Support __builtin_infer_alloc_token() as a size_t ICE Bart Van Assche
2026-11-09 20:32 ` [PATCH 66/73] builtin: Add support for Linux kernel compiler builtins Bart Van Assche
2026-11-09 20:32 ` [PATCH 67/73] builtin: Add support for __builtin_has_attribute() Bart Van Assche
2026-11-09 20:32 ` [PATCH 68/73] cleanup: Support __attribute__((cleanup(...))) in linearize and -Wcontext Bart Van Assche
2026-11-09 20:32 ` [PATCH 69/73] builtin: Do not evaluate __builtin_clz(0) and __builtin_ctz(0) Bart Van Assche
2026-11-09 20:32 ` [PATCH 70/73] pre-process: Support long token and string expansions dynamically Bart Van Assche
2026-11-09 20:32 ` [PATCH 71/73] x86: Undefine conflicting bitness macros when parsing -m flags Bart Van Assche
2026-11-09 20:32 ` [PATCH 72/73] evaluate: Accept conditional expressions with only one void side Bart Van Assche
2026-11-09 20:32 ` [PATCH 73/73] ci: Add Linux kernel GitHub Actions workflow and build script Bart Van Assche

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261109203259.362845-14-bvanassche@acm.org \
    --to=bvanassche@acm.org \
    --cc=linux-sparse@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox