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 07/73] pre-process: Fix macro expansion and include directive handling
Date: Mon, 9 Nov 2026 12:31:53 -0800 [thread overview]
Message-ID: <20261109203259.362845-8-bvanassche@acm.org> (raw)
In-Reply-To: <20261109203259.362845-1-bvanassche@acm.org>
Fix buffer bounds, unterminated macro arguments, and include path checks
in pre-process.c:
- Handle TOKEN_STREAMEND and TOKEN_STREAMBEGIN on unterminated macro
argument lists and bound token-pasting buffers in combine().
- Widen token_name_sequence() to PATH_MAX and check remaining capacity
with pointer subtraction to prevent buffer and pointer overflows.
- Reject directory paths in try_include() via fstat() and diagnose
incomplete defined() expressions in #if directives.
- Add regression tests in
validation/preprocessor/preprocess-crash-fixes.c and
validation/preprocessor/preprocess-errors-and-limits.c.
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
pre-process.c | 83 +++++++++++++++----
.../preprocessor/preprocess-crash-fixes.c | 25 ++++++
.../preprocess-errors-and-limits.c | 33 ++++++++
3 files changed, 123 insertions(+), 18 deletions(-)
create mode 100644 validation/preprocessor/preprocess-crash-fixes.c
create mode 100644 validation/preprocessor/preprocess-errors-and-limits.c
diff --git a/pre-process.c b/pre-process.c
index 7b6555677570..ca019d704a10 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -35,6 +35,7 @@
#include <fcntl.h>
#include <limits.h>
#include <time.h>
+#include <sys/stat.h>
#include "lib.h"
#include "allocate.h"
@@ -107,8 +108,12 @@ static int expand(struct token **, struct symbol *);
static void replace_with_string(struct token *token, const char *str)
{
- int size = strlen(str) + 1;
- struct string *s = __alloc_string(size);
+ size_t size = strlen(str) + 1;
+ struct string *s;
+
+ if (size > INT_MAX - sizeof(struct string))
+ die("string too large");
+ s = __alloc_string(size);
s->length = size;
memcpy(s->data, str, size);
@@ -312,6 +317,13 @@ struct arg {
int n_str;
};
+static inline bool stream_end_token(const struct token *t)
+{
+ return eof_token(t) ||
+ token_type(t) == TOKEN_STREAMEND ||
+ token_type(t) == TOKEN_STREAMBEGIN;
+}
+
static int collect_arguments(struct token *start, struct token *arglist, struct arg *args, struct token *what)
{
int wanted = arglist->count.normal;
@@ -322,7 +334,7 @@ static int collect_arguments(struct token *start, struct token *arglist, struct
if (!wanted) {
next = collect_arg(start, 0, &what->pos);
- if (eof_token(next))
+ if (stream_end_token(next))
goto Eclosing;
if (!eof_token(start->next) || !match_op(next, ')')) {
count++;
@@ -332,7 +344,7 @@ static int collect_arguments(struct token *start, struct token *arglist, struct
for (count = 0; count < wanted; count++) {
struct argcount *p = &arglist->next->count;
next = collect_arg(start, p->vararg, &what->pos);
- if (eof_token(next))
+ if (stream_end_token(next))
goto Eclosing;
if (p->vararg && wanted == 1 && eof_token(start->next))
break;
@@ -373,7 +385,7 @@ Emany:
next = collect_arg(next, 0, &what->pos);
count++;
}
- if (eof_token(next))
+ if (stream_end_token(next))
goto Eclosing;
sparse_error(what->pos, "macro \"%s\" passed %d arguments, but takes just %d",
show_token(what), count, wanted);
@@ -382,7 +394,10 @@ Eclosing:
sparse_error(what->pos, "unterminated argument list invoking macro \"%s\"",
show_token(what));
out:
- what->next = next->next;
+ if (stream_end_token(next))
+ what->next = next;
+ else
+ what->next = next->next;
return 0;
}
@@ -499,12 +514,22 @@ static enum token_type combine(struct token *left, struct token *right, char *p)
if (t2 != TOKEN_IDENT && t2 != TOKEN_NUMBER && t2 != TOKEN_SPECIAL)
return TOKEN_ERROR;
- strcpy(p, show_token(left));
- strcat(p, show_token(right));
- len = strlen(p);
+ {
+ const char *s1 = show_token(left);
+ int l1 = strlen(s1);
+ const char *s2;
+ int l2;
- if (len >= 256)
- return TOKEN_ERROR;
+ if (l1 > 4096)
+ return TOKEN_ERROR;
+ memcpy(p, s1, l1);
+ s2 = show_token(right);
+ l2 = strlen(s2);
+ if (l1 + l2 > 4096)
+ return TOKEN_ERROR;
+ memcpy(p + l1, s2, l2 + 1);
+ len = l1 + l2;
+ }
if (t1 == TOKEN_IDENT) {
if (t2 == TOKEN_SPECIAL)
@@ -537,7 +562,7 @@ static enum token_type combine(struct token *left, struct token *right, char *p)
static int merge(struct token *left, struct token *right)
{
- static char buffer[512];
+ static char buffer[4097];
enum token_type res = combine(left, right, buffer);
int n;
@@ -782,17 +807,22 @@ static int expand(struct token **list, struct symbol *sym)
static const char *token_name_sequence(struct token *token, int endop, struct token *start)
{
- static char buffer[256];
+ static char buffer[PATH_MAX];
char *ptr = buffer;
while (!eof_token(token) && !match_op(token, endop)) {
- int len;
+ size_t len, rem;
const char *val;
if (token_type(token) == TOKEN_STRING)
val = token->string->data;
else
val = show_token(token);
len = strlen(val);
+ rem = sizeof(buffer) - (size_t)(ptr - buffer);
+ if (len >= rem) {
+ sparse_error(token->pos, "header name too long");
+ return NULL;
+ }
memcpy(ptr, val, len);
ptr += len;
token = token->next;
@@ -896,23 +926,30 @@ static void set_stream_include_path(struct stream *stream)
static int try_include(struct position pos, const char *path, const char *filename, int flen, struct token **where, const char **next_path)
{
int fd;
+ struct stat st;
int plen = strlen(path);
- static char fullname[PATH_MAX];
+ char fullname[PATH_MAX];
+ if (!flen || plen + flen + 2 > sizeof(fullname))
+ return 0;
memcpy(fullname, path, plen);
if (plen && path[plen-1] != '/') {
fullname[plen] = '/';
plen++;
}
memcpy(fullname+plen, filename, flen);
+ fullname[plen + flen] = '\0';
if (already_tokenized(fullname))
return 1;
fd = open(fullname, O_RDONLY);
if (fd >= 0) {
- char *streamname = xmemdup(fullname, plen + flen);
- *where = tokenize(&pos, streamname, fd, *where, next_path);
+ if (fstat(fd, &st) == 0 && !S_ISDIR(st.st_mode)) {
+ char *streamname = xmemdup(fullname, plen + flen + 1);
+ *where = tokenize(&pos, streamname, fd, *where, next_path);
+ close(fd);
+ return 1;
+ }
close(fd);
- return 1;
}
return 0;
}
@@ -961,6 +998,8 @@ static int handle_include_path(struct stream *stream, struct token **list, struc
token = next->next;
filename = token_name_sequence(token, expect, token);
+ if (!filename)
+ return 1;
flen = strlen(filename) + 1;
/* Absolute path? */
@@ -1649,6 +1688,14 @@ static int expression_value(struct token **where)
list = &p->next;
}
+ if (state == 1 || state == 2) {
+ sparse_error((*beginning)->pos, "expected identifier after \"defined\"");
+ replace_with_bool(*beginning, 0);
+ (*beginning)->next = &eof_token_entry;
+ } else if (state == 3) {
+ sparse_error((*beginning)->pos, "missing ')' after \"defined\"");
+ }
+
p = constant_expression(*where, &expr);
if (!eof_token(p))
sparse_error(p->pos, "garbage at end: %s", show_token_sequence(p, 0));
diff --git a/validation/preprocessor/preprocess-crash-fixes.c b/validation/preprocessor/preprocess-crash-fixes.c
new file mode 100644
index 000000000000..92ea1ecde717
--- /dev/null
+++ b/validation/preprocessor/preprocess-crash-fixes.c
@@ -0,0 +1,25 @@
+#define REC_A(...) REC_B(__VA_ARGS__)
+#define REC_B(...) REC_A(__VA_ARGS__)
+extern int REC_A(int, ...);
+int rec_nonempty = sizeof(REC_A(1));
+
+/* Recursive macro with empty __VA_ARGS__ must terminate without crash */
+#define REC_E(...) REC_F(__VA_ARGS__)
+#define REC_F(...) REC_E(__VA_ARGS__)
+extern int REC_E(void);
+int rec_empty = sizeof(REC_E());
+
+#define ID(x) x
+int a = ID(1);
+
+/* Octal escape > 0377 */
+int oct_overflow = '\400';
+
+/*
+ * check-name: preprocess-crash-fixes
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+preprocessor/preprocess-crash-fixes.c:16:20: warning: octal escape sequence out of range
+ * check-error-end
+ */
diff --git a/validation/preprocessor/preprocess-errors-and-limits.c b/validation/preprocessor/preprocess-errors-and-limits.c
new file mode 100644
index 000000000000..7ad1265ba9c0
--- /dev/null
+++ b/validation/preprocessor/preprocess-errors-and-limits.c
@@ -0,0 +1,33 @@
+#if defined()
+#endif
+#if defined(FOO
+#endif
+#if defined
+#endif
+#if defined(
+#endif
+#include "."
+static const char *s = "\x";
+
+/* 300-character identifier */
+static int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = 42;
+
+static int get_val(void)
+{
+ return aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;
+}
+
+/*
+ * check-name: preprocess-errors-and-limits
+ * check-command: sparse $file
+ * check-exit-value: 1
+ *
+ * check-error-start
+preprocessor/preprocess-errors-and-limits.c:10:27: warning: \x used with no following hex digits
+preprocessor/preprocess-errors-and-limits.c:1:13: error: expected preprocessor identifier
+preprocessor/preprocess-errors-and-limits.c:3:13: error: missing ')' after "defined"
+preprocessor/preprocess-errors-and-limits.c:5:5: error: expected identifier after "defined"
+preprocessor/preprocess-errors-and-limits.c:7:5: error: expected identifier after "defined"
+preprocessor/preprocess-errors-and-limits.c:9:10: error: unable to open '.'
+ * check-error-end
+ */
next prev 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 ` Bart Van Assche [this message]
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 ` [PATCH 13/73] expand: Fix static initializer folding and floating-point casts Bart Van Assche
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-8-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