From: Bart Van Assche <bvanassche@acm.org>
To: linux-sparse@vger.kernel.org
Cc: Bart Van Assche <bvanassche@acm.org>
Subject: [PATCH 13/20] expand: Avoid undefined shifts and signed overflow in constant folding
Date: Mon, 19 Oct 2026 10:56:26 -0700 [thread overview]
Message-ID: <20261019175633.415493-14-bvanassche@acm.org> (raw)
In-Reply-To: <20261019175633.415493-1-bvanassche@acm.org>
Include "bits.h" in expand.c, rewrite get_longlong() using bits_extend(),
use unsigned long long bitmasks with sign_bit() and bits_mask() across
cast_value(), simplify_int_binop(), simplify_cmp_binop(),
simplify_preop(), and __get_expression_value(), evaluate signed
multiplication in simplify_int_binop() via unsigned arithmetic, and clamp
shift counts in simplify_int_binop() to valid 64-bit host shift ranges.
Eliminate undefined host C behavior from out-of-range shifts (when type
bit_size is 0 or 128 or when folding over-wide constant shift expressions)
and from signed 64-bit multiplication overflow when folding constant
integer multiplications.
Fixes: 8d121d51b6f8 ("Split tree evaluation into two phases: the first phase does type evaluation, the second one does value evaluation and inline expansion.")
Fixes: 1c8c7d7237cf ("simplify_int_binop: split signed and unsigned operations and handle signed division overflow.")
Fixes: 411aed021d36 ("big-shift: don't take the modulo at expand time")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
expand.c | 52 +++++++++++++++++++++-------------------------------
1 file changed, 21 insertions(+), 31 deletions(-)
diff --git a/expand.c b/expand.c
index f14e718187bc..bbb30ae605dc 100644
--- a/expand.c
+++ b/expand.c
@@ -36,6 +36,7 @@
#include "lib.h"
#include "allocate.h"
+#include "bits.h"
#include "parse.h"
#include "token.h"
#include "symbol.h"
@@ -80,18 +81,8 @@ static int expand_symbol_expression(struct expression *expr)
static long long get_longlong(struct expression *expr)
{
- int no_expand = expr->ctype->ctype.modifiers & MOD_UNSIGNED;
- long long mask = 1ULL << (expr->ctype->bit_size - 1);
- long long value = expr->value;
- long long ormask, andmask;
-
- if (!(value & mask))
- no_expand = 1;
- andmask = mask | (mask-1);
- ormask = ~andmask;
- if (no_expand)
- ormask = 0;
- return (value & andmask) | ormask;
+ int is_signed = !(expr->ctype->ctype.modifiers & MOD_UNSIGNED);
+ return bits_extend(expr->value, expr->ctype->bit_size, is_signed);
}
void cast_value(struct expression *expr, struct symbol *newtype, struct expression *old)
@@ -99,8 +90,9 @@ void cast_value(struct expression *expr, struct symbol *newtype, struct expressi
struct symbol *oldtype = old->ctype;
int old_size = oldtype->bit_size;
int new_size = newtype->bit_size;
- long long value, mask, signmask;
- long long oldmask, oldsignmask, dropped;
+ long long value;
+ unsigned long long mask, signmask;
+ unsigned long long oldmask, oldsignmask, dropped;
if (is_float_type(newtype) || is_float_type(oldtype))
goto Float;
@@ -128,8 +120,8 @@ Int:
}
// Truncate it to the new size
- signmask = 1ULL << (new_size-1);
- mask = signmask | (signmask-1);
+ signmask = sign_bit(new_size);
+ mask = bits_mask(new_size);
expr->value = value & mask;
// Stop here unless checking for truncation
@@ -137,8 +129,8 @@ Int:
return;
// Check if we dropped any bits..
- oldsignmask = 1ULL << (old_size-1);
- oldmask = oldsignmask | (oldsignmask-1);
+ oldsignmask = sign_bit(old_size);
+ oldmask = bits_mask(old_size);
dropped = oldmask & ~mask;
// OK if the bits were (and still are) purely sign bits
@@ -210,7 +202,7 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
return 0;
l = left->value; r = right->value;
is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED);
- mask = 1ULL << (ctype->bit_size-1);
+ mask = sign_bit(ctype->bit_size);
sl = l; sr = r;
if (is_signed && (sl & mask))
sl |= ~(mask-1);
@@ -244,9 +236,6 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
break;
case SIGNED('*'):
- v = sl * sr;
- break;
-
case UNSIGNED('*'):
v = l * r;
break;
@@ -279,21 +268,21 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
case SIGNED(SPECIAL_LEFTSHIFT):
case UNSIGNED(SPECIAL_LEFTSHIFT):
- v = l << r;
+ v = r < 64 ? l << r : 0;
break;
case SIGNED(SPECIAL_RIGHTSHIFT):
- v = sl >> r;
+ v = sl >> (r < 63 ? r : 63);
break;
case UNSIGNED(SPECIAL_RIGHTSHIFT):
- v = l >> r;
+ v = r < 64 ? l >> r : 0;
break;
default:
return 0;
}
- mask = mask | (mask-1);
+ mask = bits_mask(ctype->bit_size);
expr->value = v & mask;
expr->type = EXPR_VALUE;
expr->taint = left->taint | right->taint;
@@ -317,7 +306,7 @@ static int simplify_cmp_binop(struct expression *expr, struct symbol *ctype)
if (left->type != EXPR_VALUE || right->type != EXPR_VALUE)
return 0;
l = left->value; r = right->value;
- mask = 1ULL << (ctype->bit_size-1);
+ mask = sign_bit(ctype->bit_size);
sl = l; sr = r;
if (sl & mask)
sl |= ~(mask-1);
@@ -773,7 +762,7 @@ static int simplify_preop(struct expression *expr)
if (op->type != EXPR_VALUE)
return 0;
- mask = 1ULL << (expr->ctype->bit_size-1);
+ mask = sign_bit(expr->ctype->bit_size);
v = op->value;
switch (expr->op) {
case '+': break;
@@ -786,7 +775,7 @@ static int simplify_preop(struct expression *expr)
case '~': v = ~v; break;
default: return 0;
}
- mask = mask | (mask-1);
+ mask = bits_mask(expr->ctype->bit_size);
expr->value = v & mask;
expr->type = EXPR_VALUE;
expr->taint = op->taint;
@@ -1377,7 +1366,8 @@ static inline int bad_integer_constant_expression(struct expression *expr)
static long long __get_expression_value(struct expression *expr, int strict)
{
- long long value, mask;
+ long long value;
+ unsigned long long mask;
struct symbol *ctype;
if (!expr)
@@ -1399,7 +1389,7 @@ static long long __get_expression_value(struct expression *expr, int strict)
}
value = expr->value;
- mask = 1ULL << (ctype->bit_size-1);
+ mask = sign_bit(ctype->bit_size);
if (value & mask) {
while (ctype->type != SYM_BASETYPE)
next prev parent reply other threads:[~2026-10-19 17:56 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-19 17:56 [PATCH 00/20] Sanitizer fixes, LLVM updates, and CI workflow Bart Van Assche
2026-10-19 17:56 ` [PATCH 01/20] doc: Update maintainer and mirror information Bart Van Assche
2026-10-19 17:56 ` [PATCH 02/20] ptrlist: Guard against null list in ptrlist operations Bart Van Assche
2026-10-19 17:56 ` [PATCH 03/20] bits: Guard against out-of-bounds bit shifts in bitmask helpers Bart Van Assche
2026-10-19 17:56 ` [PATCH 04/20] storage: Free hash table entries when freeing storage Bart Van Assche
2026-10-19 17:56 ` [PATCH 05/20] builtin: Bound argument array iteration in declare_builtin() Bart Van Assche
2026-10-19 17:56 ` [PATCH 06/20] c2xml: Use an unsigned shift in examine_modifiers() Bart Van Assche
2026-10-19 17:56 ` [PATCH 07/20] parse: Avoid incrementing rank on invalid long real specifiers Bart Van Assche
2026-10-19 17:56 ` [PATCH 08/20] pre-process: Avoid zero-length VLAs for parameterless macros Bart Van Assche
2026-10-19 17:56 ` [PATCH 09/20] pre-process: Avoid reading token->string on non-string tokens Bart Van Assche
2026-10-19 17:56 ` [PATCH 10/20] target: Avoid signed overflow in bit and byte conversion helpers Bart Van Assche
2026-10-19 17:56 ` [PATCH 11/20] evaluate: Use bitmask helpers in evaluate_ptr_add() Bart Van Assche
2026-10-19 17:56 ` [PATCH 12/20] linearize: Use bits_mask() in linearize_bitfield_insert() Bart Van Assche
2026-10-19 17:56 ` Bart Van Assche [this message]
2026-10-19 17:56 ` [PATCH 14/20] simplify: Avoid undefined shifts and signed overflow in simplification Bart Van Assche
2026-10-19 17:56 ` [PATCH 15/20] sparsec: Emit object files directly with llc -filetype=obj Bart Van Assche
2026-10-19 17:56 ` [PATCH 16/20] llvm: Stop calling LLVMConstGEP2() for string literals Bart Van Assche
2026-10-19 17:56 ` [PATCH 17/20] llvm: Free leaked allocations and LLVM objects Bart Van Assche
2026-10-19 17:56 ` [PATCH 18/20] parse: Accept an empty attribute argument list Bart Van Assche
2026-10-19 17:56 ` [PATCH 19/20] llvm: Use explicit LLVMContextRef instead of global context Bart Van Assche
2026-10-19 17:56 ` [PATCH 20/20] ci: Add a GitHub Actions workflow and CI scripts 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=20261019175633.415493-14-bvanassche@acm.org \
--to=bvanassche@acm.org \
--cc=linux-sparse@vger.kernel.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